From 9e4b1d30cbdbbe6ae3ef3349898ffcd6e7c0c133 Mon Sep 17 00:00:00 2001 From: Moritz Sommer Date: Mon, 1 Sep 2025 09:19:53 +0200 Subject: [PATCH 1/3] sdk: Add tutorial for Submodel navigation Previously, we lacked a tutorial demonstrating how to navigate a Submodel's hierarchy using IdShorts and IdShortPaths. This adds a simple tutorial for Submodel navigation and updates the documentation to reference it. The end user is shown how to navigate SubmodelElements, such as simple Properties, Property Collections, Property Lists and Collection Lists. Fixes #351 # Conflicts: # sdk/README.md --- sdk/README.md | 3 +- .../aas/examples/tutorial_navigate_aas.py | 140 ++++++++++++++++++ sdk/docs/source/tutorials/index.rst | 3 +- .../tutorials/tutorial_navigate_aas.rst | 7 + 4 files changed, 151 insertions(+), 2 deletions(-) create mode 100644 sdk/basyx/aas/examples/tutorial_navigate_aas.py create mode 100644 sdk/docs/source/tutorials/tutorial_navigate_aas.rst diff --git a/sdk/README.md b/sdk/README.md index 942f57555..7dbeb1b4d 100644 --- a/sdk/README.md +++ b/sdk/README.md @@ -124,7 +124,8 @@ write_aas_xml_file(file='Simple_Submodel.xml', data=data) For further examples and tutorials, check out the `basyx.aas.examples`-package. Here is a quick overview: * [`tutorial_create_simple_aas`](./basyx/aas/examples/tutorial_create_simple_aas.py): Create an Asset Administration Shell, including an Asset object and a Submodel -* [`tutorial_storage`](./basyx/aas/examples/tutorial_storage.py): Manage a larger number of Asset Administration Shells in an IdentifiableStore and resolve references +* [`tutorial_navigate_aas`](./basyx/aas/examples/tutorial_navigate_aas.py): Navigate Asset Administration Shell Submodels using IdShorts and IdShortPaths +* [`tutorial_storage`](./basyx/aas/examples/tutorial_storage.py): Manage a larger number of Asset Administration Shells in an ObjectStore and resolve references * [`tutorial_serialization_deserialization`](./basyx/aas/examples/tutorial_serialization_deserialization.py): Use the JSON and XML serialization/deserialization for single objects or full standard-compliant files * [`tutorial_aasx`](./basyx/aas/examples/tutorial_aasx.py): Export Asset Administration Shells with related objects and auxiliary files to AASX package files * [`tutorial_backend_couchdb`](./basyx/aas/examples/tutorial_backend_couchdb.py): Use the *CouchDBIdentifiableStore* to manage and retrieve AAS objects in a CouchDB document database diff --git a/sdk/basyx/aas/examples/tutorial_navigate_aas.py b/sdk/basyx/aas/examples/tutorial_navigate_aas.py new file mode 100644 index 000000000..089140d78 --- /dev/null +++ b/sdk/basyx/aas/examples/tutorial_navigate_aas.py @@ -0,0 +1,140 @@ +#!/usr/bin/env python3 +# This work is licensed under a Creative Commons CCZero 1.0 Universal License. +# See http://creativecommons.org/publicdomain/zero/1.0/ for more information. +""" +Tutorial for navigating a Submodel's hierarchy using IdShorts and IdShortPaths. +""" + +from basyx.aas import model + +# In this tutorial, you will learn how to create a Submodel with different kinds of SubmodelElements and how to navigate +# through them using IdShorts and IdShortPaths. +# +# Step-by-Step Guide: +# Step 1: Create a Submodel with a Property, a Property Collection, a Property List and a Collection List +# Step 2: Navigate through the Submodel using IdShorts and IdShortPaths + + +######################################################################## +# Step 1: Create a Submodel with a navigable SubmodelElement hierarchy # +######################################################################## + +# Step 1.1: Create a Submodel +submodel = model.Submodel(id_="https://iat.rwth-aachen.de/Simple_Submodel") + +# Step 1.2: Add a single Property to the Submodel +my_property = model.Property( + id_short="MyProperty", + value_type=model.datatypes.String, + value="I am a simple Property" +) +submodel.submodel_element.add(my_property) + +# Step 1.3: Add a Property Collection to the Submodel +my_property_collection = model.SubmodelElementCollection( + id_short="MyPropertyCollection", + value={ + model.Property( + id_short="MyProperty0", + value_type=model.datatypes.String, + value="I am the first of two Properties within a Property Collection" + ), + model.Property( + id_short="MyProperty1", + value_type=model.datatypes.String, + value="I am the second of two Properties within a Property Collection" + ) + } +) +submodel.submodel_element.add(my_property_collection) + +# Step 1.4: Add a Property List to the Submodel +my_property_list = model.SubmodelElementList( + id_short="MyPropertyList", + type_value_list_element=model.Property, + value_type_list_element=model.datatypes.String, + order_relevant=True, + value=[ + model.Property( + id_short=None, + value_type=model.datatypes.String, + value="I am Property 0 within a Property List" + ), + model.Property( + id_short=None, + value_type=model.datatypes.String, + value="I am Property 1 within a Property List" + ) + ] +) +submodel.submodel_element.add(my_property_list) + +# Step 1.5: Add a Collection List to the Submodel +my_property_collection_0 = model.SubmodelElementCollection( + id_short=None, + value={model.Property( + id_short="MyProperty", + value_type=model.datatypes.String, + value="I am a simple Property within Property Collection 0" + )} +) +my_property_collection_1 = model.SubmodelElementCollection( + id_short=None, + value={model.Property( + id_short="MyProperty", + value_type=model.datatypes.String, + value="I am a simple Property within Property Collection 1" + )} +) +my_property_collection_2 = model.SubmodelElementCollection( + id_short=None, + value={model.Property( + id_short="MyProperty", + value_type=model.datatypes.String, + value="I am a simple Property within Property Collection 2" + )} +) +my_collection_list = model.SubmodelElementList( + id_short="MyCollectionList", + type_value_list_element=model.SubmodelElementCollection, + order_relevant=True, + value=[my_property_collection_0, my_property_collection_1, my_property_collection_2] +) +submodel.submodel_element.add(my_collection_list) + + +######################################################################### +# Step 2: Navigate through the Submodel using IdShorts and IdShortPaths # +######################################################################### + +# Step 2.1: Access a single Property via its IdShort +my_property = submodel.get_referable("MyProperty") +print(f"my_property: id_short = {my_property.id_short}, value = {my_property.value}\n") + +# Step 2.2.1: Access a Property within a Property Collection step by step via its IdShort +my_property_collection = submodel.get_referable("MyPropertyCollection") +my_property_collection_0 = my_property_collection.get_referable("MyProperty0") +print(f"my_property_collection_0: id_short = {my_property_collection_0}, value = {my_property_collection_0.value}") + +# Step 2.2.2: Access a Property within a Property Collection via its IdShortPath +my_property_collection_1 = submodel.get_referable(["MyPropertyCollection", "MyProperty1"]) +print(f"my_property_collection_1: id_short = {my_property_collection_1}, value = {my_property_collection_1.value}\n") + +# Step 2.3.1: Access a Property within a Property List step by step via its index +my_property_list = submodel.get_referable("MyPropertyList") +my_property_list_0 = my_property_list.get_referable("0") +print(f"my_property_list_0: id_short = {my_property_list_0}, value = {my_property_list_0.value}") + +# Step 2.3.2: Access a Property within a Property List via its IdShortPath +my_property_list_1 = submodel.get_referable(["MyPropertyList", "1"]) +print(f"my_property_list_1: id_short = {my_property_list_1}, value = {my_property_list_1.value}\n") + +# Step 2.4.1: Access a Property within a Collection List step by step via its index and IdShort +my_collection_list = submodel.get_referable("MyCollectionList") +my_collection_list_0 = my_collection_list.get_referable("0") +my_collection_list_0_0 = my_collection_list_0.get_referable("MyProperty") +print(f"my_collection_list_0_0: id_short = {my_collection_list_0_0}, value = {my_collection_list_0_0.value}") + +# Step 2.4.2: Access a Property within a Collection List via its IdShortPath +my_collection_list_2_0 = submodel.get_referable(["MyCollectionList", "2", "MyProperty"]) +print(f"my_collection_list_2_0: id_short = {my_collection_list_2_0}, value = {my_collection_list_2_0.value}") diff --git a/sdk/docs/source/tutorials/index.rst b/sdk/docs/source/tutorials/index.rst index ce2fe76ad..dfb4149e3 100644 --- a/sdk/docs/source/tutorials/index.rst +++ b/sdk/docs/source/tutorials/index.rst @@ -7,7 +7,8 @@ Tutorials for working with the Eclipse BaSyx Python SDK :caption: Contents: tutorial_create_simple_aas - tutorial_serialization_deserialization + tutorial_navigate_aas tutorial_storage + tutorial_serialization_deserialization tutorial_backend_couchdb tutorial_aasx diff --git a/sdk/docs/source/tutorials/tutorial_navigate_aas.rst b/sdk/docs/source/tutorials/tutorial_navigate_aas.rst new file mode 100644 index 000000000..57c6a935b --- /dev/null +++ b/sdk/docs/source/tutorials/tutorial_navigate_aas.rst @@ -0,0 +1,7 @@ +Tutorial: Navigate Submodels +============================ + +.. _tutorial_navigate_aas: + +.. literalinclude:: ../../../basyx/aas/examples/tutorial_navigate_aas.py + :language: python From 7fedb3e26f9dbe06ca73808d0b3c12d1d6dcfe72 Mon Sep 17 00:00:00 2001 From: Moritz Sommer Date: Mon, 1 Sep 2025 10:24:49 +0200 Subject: [PATCH 2/3] sdk/basyx/aas/examples/tutorial_navigate_aas.py: Fix mypy errors --- .../aas/examples/tutorial_navigate_aas.py | 68 ++++++++++++++----- 1 file changed, 51 insertions(+), 17 deletions(-) diff --git a/sdk/basyx/aas/examples/tutorial_navigate_aas.py b/sdk/basyx/aas/examples/tutorial_navigate_aas.py index 089140d78..d9020648b 100644 --- a/sdk/basyx/aas/examples/tutorial_navigate_aas.py +++ b/sdk/basyx/aas/examples/tutorial_navigate_aas.py @@ -6,6 +6,7 @@ """ from basyx.aas import model +from typing import cast # In this tutorial, you will learn how to create a Submodel with different kinds of SubmodelElements and how to navigate # through them using IdShorts and IdShortPaths. @@ -108,33 +109,66 @@ ######################################################################### # Step 2.1: Access a single Property via its IdShort -my_property = submodel.get_referable("MyProperty") +my_property = cast(model.Property, submodel.get_referable("MyProperty")) print(f"my_property: id_short = {my_property.id_short}, value = {my_property.value}\n") # Step 2.2.1: Access a Property within a Property Collection step by step via its IdShort -my_property_collection = submodel.get_referable("MyPropertyCollection") -my_property_collection_0 = my_property_collection.get_referable("MyProperty0") -print(f"my_property_collection_0: id_short = {my_property_collection_0}, value = {my_property_collection_0.value}") +my_property_collection = cast(model.SubmodelElementCollection, submodel.get_referable("MyPropertyCollection")) +my_property_collection_property_0 = cast(model.Property, my_property_collection.get_referable("MyProperty0")) +print( + f"my_property_collection_property_0: " + f"id_short = {my_property_collection_property_0}, " + f"value = {my_property_collection_property_0.value}" +) # Step 2.2.2: Access a Property within a Property Collection via its IdShortPath -my_property_collection_1 = submodel.get_referable(["MyPropertyCollection", "MyProperty1"]) -print(f"my_property_collection_1: id_short = {my_property_collection_1}, value = {my_property_collection_1.value}\n") +my_property_collection_property_1 = cast( + model.Property, + submodel.get_referable(["MyPropertyCollection", "MyProperty1"]) +) +print( + f"my_property_collection_property_1: " + f"id_short = {my_property_collection_property_1}, " + f"value = {my_property_collection_property_1.value}\n" +) # Step 2.3.1: Access a Property within a Property List step by step via its index -my_property_list = submodel.get_referable("MyPropertyList") -my_property_list_0 = my_property_list.get_referable("0") -print(f"my_property_list_0: id_short = {my_property_list_0}, value = {my_property_list_0.value}") +my_property_list = cast(model.SubmodelElementList, submodel.get_referable("MyPropertyList")) +my_property_list_property_0 = cast(model.Property, my_property_list.get_referable("0")) +print( + f"my_property_list_property_0: " + f"id_short = {my_property_list_property_0}, " + f"value = {my_property_list_property_0.value}" +) # Step 2.3.2: Access a Property within a Property List via its IdShortPath -my_property_list_1 = submodel.get_referable(["MyPropertyList", "1"]) -print(f"my_property_list_1: id_short = {my_property_list_1}, value = {my_property_list_1.value}\n") +my_property_list_property_1 = cast(model.Property, submodel.get_referable(["MyPropertyList", "1"])) +print( + f"my_property_list_property_1: " + f"id_short = {my_property_list_property_1}, " + f"value = {my_property_list_property_1.value}\n" +) # Step 2.4.1: Access a Property within a Collection List step by step via its index and IdShort -my_collection_list = submodel.get_referable("MyCollectionList") -my_collection_list_0 = my_collection_list.get_referable("0") -my_collection_list_0_0 = my_collection_list_0.get_referable("MyProperty") -print(f"my_collection_list_0_0: id_short = {my_collection_list_0_0}, value = {my_collection_list_0_0.value}") +my_collection_list = cast(model.SubmodelElementList, submodel.get_referable("MyCollectionList")) +my_collection_list_collection_0 = cast(model.SubmodelElementCollection, my_collection_list.get_referable("0")) +my_collection_list_collection_0_property_0 = cast( + model.Property, + my_collection_list_collection_0.get_referable("MyProperty") +) +print( + f"my_collection_list_collection_0_property_0: " + f"id_short = {my_collection_list_collection_0_property_0}, " + f"value = {my_collection_list_collection_0_property_0.value}" +) # Step 2.4.2: Access a Property within a Collection List via its IdShortPath -my_collection_list_2_0 = submodel.get_referable(["MyCollectionList", "2", "MyProperty"]) -print(f"my_collection_list_2_0: id_short = {my_collection_list_2_0}, value = {my_collection_list_2_0.value}") +my_collection_list_collection_2_property_0 = cast( + model.Property, + submodel.get_referable(["MyCollectionList", "2", "MyProperty"]) +) +print( + f"my_collection_list_collection_2_property_0: " + f"id_short = {my_collection_list_collection_2_property_0}, " + f"value = {my_collection_list_collection_2_property_0.value}" +) From 77e44e75e25c55590701df7831542229e333e0fe Mon Sep 17 00:00:00 2001 From: Moritz Sommer Date: Tue, 24 Feb 2026 06:40:19 +0100 Subject: [PATCH 3/3] Resolve change requests --- .../aas/examples/tutorial_navigate_aas.py | 59 +++++++++++++------ sdk/docs/source/tutorials/index.rst | 2 +- 2 files changed, 43 insertions(+), 18 deletions(-) diff --git a/sdk/basyx/aas/examples/tutorial_navigate_aas.py b/sdk/basyx/aas/examples/tutorial_navigate_aas.py index d9020648b..2bbb9f89d 100644 --- a/sdk/basyx/aas/examples/tutorial_navigate_aas.py +++ b/sdk/basyx/aas/examples/tutorial_navigate_aas.py @@ -12,7 +12,28 @@ # through them using IdShorts and IdShortPaths. # # Step-by-Step Guide: -# Step 1: Create a Submodel with a Property, a Property Collection, a Property List and a Collection List +# Step 1: Create a Submodel with a Property, a SubmodelElementCollection of Properties, a SubmodelElementList of +# Properties and a SubmodelElementList of SubmodelElementCollections +# +# Submodel "https://iat.rwth-aachen.de/Simple_Submodel" +# ├── Property "MyProperty" +# │ +# ├── SubmodelElementCollection "MyPropertyCollection" +# │ ├── Property "MyProperty0" +# │ └── Property "MyProperty1" +# │ +# ├── SubmodelElementList "MyPropertyList" +# │ ├── Property [0] +# │ └── Property [1] +# │ +# └── SubmodelElementList "MyCollectionList" +# ├── SubmodelElementCollection [0] +# │ └── Property "MyProperty" +# ├── SubmodelElementCollection [1] +# │ └── Property "MyProperty" +# └── SubmodelElementCollection [2] +# └── Property "MyProperty" +# # Step 2: Navigate through the Submodel using IdShorts and IdShortPaths @@ -31,25 +52,25 @@ ) submodel.submodel_element.add(my_property) -# Step 1.3: Add a Property Collection to the Submodel +# Step 1.3: Add a SubmodelElementCollection of Properties to the Submodel my_property_collection = model.SubmodelElementCollection( id_short="MyPropertyCollection", value={ model.Property( id_short="MyProperty0", value_type=model.datatypes.String, - value="I am the first of two Properties within a Property Collection" + value="I am the first of two Properties within a SubmodelElementCollection" ), model.Property( id_short="MyProperty1", value_type=model.datatypes.String, - value="I am the second of two Properties within a Property Collection" + value="I am the second of two Properties within a SubmodelElementCollection" ) } ) submodel.submodel_element.add(my_property_collection) -# Step 1.4: Add a Property List to the Submodel +# Step 1.4: Add a SubmodelElementList of Properties to the Submodel my_property_list = model.SubmodelElementList( id_short="MyPropertyList", type_value_list_element=model.Property, @@ -59,24 +80,24 @@ model.Property( id_short=None, value_type=model.datatypes.String, - value="I am Property 0 within a Property List" + value="I am Property 0 within a SubmodelElementList" ), model.Property( id_short=None, value_type=model.datatypes.String, - value="I am Property 1 within a Property List" + value="I am Property 1 within a SubmodelElementList" ) ] ) submodel.submodel_element.add(my_property_list) -# Step 1.5: Add a Collection List to the Submodel +# Step 1.5: Add a SubmodelElementList of SubmodelElementCollections to the Submodel my_property_collection_0 = model.SubmodelElementCollection( id_short=None, value={model.Property( id_short="MyProperty", value_type=model.datatypes.String, - value="I am a simple Property within Property Collection 0" + value="I am a simple Property within SubmodelElementCollection 0" )} ) my_property_collection_1 = model.SubmodelElementCollection( @@ -84,7 +105,7 @@ value={model.Property( id_short="MyProperty", value_type=model.datatypes.String, - value="I am a simple Property within Property Collection 1" + value="I am a simple Property within SubmodelElementCollection 1" )} ) my_property_collection_2 = model.SubmodelElementCollection( @@ -92,7 +113,7 @@ value={model.Property( id_short="MyProperty", value_type=model.datatypes.String, - value="I am a simple Property within Property Collection 2" + value="I am a simple Property within SubmodelElementCollection 2" )} ) my_collection_list = model.SubmodelElementList( @@ -112,7 +133,8 @@ my_property = cast(model.Property, submodel.get_referable("MyProperty")) print(f"my_property: id_short = {my_property.id_short}, value = {my_property.value}\n") -# Step 2.2.1: Access a Property within a Property Collection step by step via its IdShort +# Step 2.2: Navigate through a SubmodelElementCollection of Properties +# Step 2.2.1: Access a Property within a SubmodelElementCollection step by step via its IdShort my_property_collection = cast(model.SubmodelElementCollection, submodel.get_referable("MyPropertyCollection")) my_property_collection_property_0 = cast(model.Property, my_property_collection.get_referable("MyProperty0")) print( @@ -121,7 +143,7 @@ f"value = {my_property_collection_property_0.value}" ) -# Step 2.2.2: Access a Property within a Property Collection via its IdShortPath +# Step 2.2.2: Access a Property within a SubmodelElementCollection via its IdShortPath my_property_collection_property_1 = cast( model.Property, submodel.get_referable(["MyPropertyCollection", "MyProperty1"]) @@ -132,7 +154,8 @@ f"value = {my_property_collection_property_1.value}\n" ) -# Step 2.3.1: Access a Property within a Property List step by step via its index +# Step 2.3: Navigate through a SubmodelElementList of Properties +# Step 2.3.1: Access a Property within a SubmodelElementList step by step via its index my_property_list = cast(model.SubmodelElementList, submodel.get_referable("MyPropertyList")) my_property_list_property_0 = cast(model.Property, my_property_list.get_referable("0")) print( @@ -141,7 +164,7 @@ f"value = {my_property_list_property_0.value}" ) -# Step 2.3.2: Access a Property within a Property List via its IdShortPath +# Step 2.3.2: Access a Property within a SubmodelElementList via its IdShortPath my_property_list_property_1 = cast(model.Property, submodel.get_referable(["MyPropertyList", "1"])) print( f"my_property_list_property_1: " @@ -149,7 +172,9 @@ f"value = {my_property_list_property_1.value}\n" ) -# Step 2.4.1: Access a Property within a Collection List step by step via its index and IdShort +# Step 2.4: Navigate through a SubmodelElementList of SubmodelElementCollections +# Step 2.4.1: Access a Property within a SubmodelElementList of SubmodelElementCollections step by step via its index +# and IdShort my_collection_list = cast(model.SubmodelElementList, submodel.get_referable("MyCollectionList")) my_collection_list_collection_0 = cast(model.SubmodelElementCollection, my_collection_list.get_referable("0")) my_collection_list_collection_0_property_0 = cast( @@ -162,7 +187,7 @@ f"value = {my_collection_list_collection_0_property_0.value}" ) -# Step 2.4.2: Access a Property within a Collection List via its IdShortPath +# Step 2.4.2: Access a Property within a SubmodelElementList of SubmodelElementCollections via its IdShortPath my_collection_list_collection_2_property_0 = cast( model.Property, submodel.get_referable(["MyCollectionList", "2", "MyProperty"]) diff --git a/sdk/docs/source/tutorials/index.rst b/sdk/docs/source/tutorials/index.rst index dfb4149e3..24f0282ab 100644 --- a/sdk/docs/source/tutorials/index.rst +++ b/sdk/docs/source/tutorials/index.rst @@ -10,5 +10,5 @@ Tutorials for working with the Eclipse BaSyx Python SDK tutorial_navigate_aas tutorial_storage tutorial_serialization_deserialization - tutorial_backend_couchdb tutorial_aasx + tutorial_backend_couchdb