Catalog PyPI
PACKAGE EXPLORERView in registry

aas-submodel-to-py

PyPI

Generate Submodel-Specific classes with filled metainformation derived from submodel templates

aasclasscodegenerationpythonsubmodel
Versions collected1
Direct dependencies5From release 0.2.0
Last observed20 Oct, 17:04 UTC

README

Version 0.2.0 documentation

aas-submodel-to-py - AAS Submodel Python Code Generator

aas-submodel-to-py is a code generator tool built on top of the BaSyx-Python-SDK. It is designed to generate Submodel-specific classes and classes for its submodel elements with filled meta-information derived from submodel templates. These generated classes act as child classes of the BaSyx-Python-SDK classes and represent classes of the Asset Administration Shell Metamodel. The hierarchical structure of the generated submodel-specific class includes all the required submodel element-specific classes. Input files can be .aasx, .json, or .xml format (see Supported Input).

Examples

Example of generated classes

Here's a snippet of the classes generated from the Digital Nameplate 3.0.1 submodel template (... marks omitted lines). Each submodel element gets a class nested in the class of its submodel or collection. Its init takes the values of the element (e.g. value) and its submodel elements; metadata like semantic_id or description are keyword arguments, typed by a TypedDict per BaSyx class (e.g. PropertyMetadata), and default to the template's values, which template_defaults() returns. The classes build instances (kind=Instance), so template qualifiers like SMT/Cardinality, which are only allowed in templates, aren't taken over. Each module starts with a few helpers its classes share:

from typing import Any, Iterable, Optional, Tuple, TypedDict, Union, Unpack, cast
from basyx.aas import model as aas
from basyx.aas.model import datatypes as xsd


def global_ref(value: str) -> aas.ExternalReference:
    """External reference to the global identifier `value`, e.g. a semantic ID"""
    return aas.ExternalReference((aas.Key(aas.KeyTypes.GLOBAL_REFERENCE, value),))


def collect_submodel_elements(*elements: Any, number_repeated: bool = True) -> list[aas.SubmodelElement]:
    """Collect the submodel elements passed to an init in one list: single elements, unless None,
    and the elements of iterables, whose idShorts get an index if `number_repeated` (e.g. Keyword00)"""
    ...


class GeneratedSubmodelElementList(aas.SubmodelElementList):
    """SubmodelElementList, whose items may be instances of subclasses of type_value_list_element,
    such as the generated classes of its items"""
    ...


class MultiLanguagePropertyMetadata(TypedDict, total=False):
    """Metadata of aas.MultiLanguageProperty, passed as keyword arguments to its generated classes"""

    id_short: Optional[str]
    ...
    semantic_id: Optional[aas.Reference]
    ...


class Nameplate(aas.Submodel):

    class URIOfTheProduct(aas.Property):
        ...

    class ManufacturerName(aas.MultiLanguageProperty):

        @staticmethod
        def template_defaults() -> MultiLanguagePropertyMetadata:
            return {
                "id_short": r"ManufacturerName",
                "semantic_id": global_ref(r"0112/2///61987#ABA565#009"),
                "supplemental_semantic_id": (global_ref(r"0173-1#02-AAO677#004"),),
            }

        def __init__(self, value: aas.MultiLanguageTextType, **kwargs: Unpack[MultiLanguagePropertyMetadata]):

            super().__init__(value=value, **(self.template_defaults() | kwargs))

    ...

    class Markings(GeneratedSubmodelElementList):
        ...

    ...

    @staticmethod
    def template_defaults() -> SubmodelMetadata:
        return {
            "id_short": r"Nameplate",
            "description": aas.MultiLanguageTextType(
                dict_={r"en": r"Contains the nameplate information attached to the product"}
            ),
            "administration": aas.AdministrativeInformation(
                version=r"3",
                revision=r"0",
                ...
                template_id=r"https://admin-shell.io/idta-02006-3-0",
                ...
            ),
            "semantic_id": global_ref(r"https://admin-shell.io/idta/nameplate/3/0/Nameplate"),
        }

    def __init__(
        self,
        id_: str,
        uRIOfTheProduct: Union[xsd.AnyURI, URIOfTheProduct],
        manufacturerName: Union[aas.MultiLanguageTextType, ManufacturerName],
        ...
        yearOfConstruction: Optional[Union[str, YearOfConstruction]] = None,
        ...
        markings: Optional[Union[Iterable[Markings.Markings_item], Markings]] = None,
        ...
        **kwargs: Unpack[SubmodelMetadata],
    ):
        ...

Usage Example of the generated class

Here's an example of instantiating the generated Nameplate submodel. The classes generated from Digital Nameplate 3.0.1 are part of py-aas-submodels:

from basyx.aas.model import MultiLanguageTextType
from py_aas_submodels.digital_nameplate_3_0_1 import Nameplate

nameplate = Nameplate(
    id_="https://example.com/ids/sm/nameplate-001",
    uRIOfTheProduct="https://www.domain-abc.com/Model-Nr-1234",
    manufacturerName=MultiLanguageTextType({"de": "Muster AG"}),
    manufacturerProductDesignation=MultiLanguageTextType({"en": "ABC-123"}),
    addressInformation=Nameplate.AddressInformation(),
    orderCodeOfManufacturer="ABC-123-XYZ",
    yearOfConstruction="2022",
    serialNumber=Nameplate.SerialNumber("12345678"),
)

All required submodel elements are positional arguments; optional elements default to None. Metadata is passed as keyword arguments, e.g. Nameplate(..., id_short="Nameplate01"), and replaces the template's value (None removes it); type checkers like mypy report unknown or wrongly typed metadata. Instead of submodel elements (e.g. Nameplate.SerialNumber("12345678")), raw values can be passed (e.g. yearOfConstruction="2022"); multi-language values are passed as MultiLanguageTextType. Properties with a default value in the template (SMT/DefaultValue, else SMT/InitialValue) can be created without value, e.g. PAMSpecificationSheet.GeneralInformation.SafetyMeasure(). Of elements marked as alternatives (SMT/EitherOr), only one may be given; passing more raises a ValueError. Values restricted by the template (SMT/AllowedValue, SMT/AllowedRange, FormChoices, enumeration) are checked as well and available as class attributes, e.g. SafetyMeasure.ALLOWED_VALUES == ("True", "False"). Lists (SubmodelElementList) take an iterable of their items or raw values, e.g. phases=["A1", "B2"]. List items have no idShort by default, as it is optional since AAS metamodel 3.1. The output is auto-formatted with Black and passes the type checker mypy.

Installation

Using PyPI:

pip install aas-submodel-to-py

Or from the repository root:

pip install -e ./aas-submodel-to-py

Supported Input

FormatAAS metamodel 3.1AAS metamodel 3.0
.json✅✅
.aasx✅❌
.xml✅❌

Files are read with the BaSyx Python SDK (≥ 2.1.0), which implements AAS metamodel 3.1 and can't read AASX and XML files of metamodel 3.0. Many templates on the IDTA website are still such files. For those, use the JSON version of the template, or its _forAASMetamodelV3.1 variant; both are published in admin-shell-io/submodel-templates.

For such files, as for any file without a submodel, the generator raises a NoSubmodelError (submodel_to_code exits with an error message) instead of writing a module.

Usage

Command Line

# Minimal: output .py is generated next to the input file
# e.g. IDTA-02006-2-0_Submodel_Digital Nameplate.aasx → IDTA_02006_2_0_Submodel_Digital_Nameplate.py
submodel_to_code -i /some/path/DigitalNameplate.aasx

# Write to a specific output directory (filename still auto-derived)
submodel_to_code -i /some/path/DigitalNameplate.aasx -d /some/other/path/

# Write to an explicit output file path
submodel_to_code -i /some/path/DigitalNameplate.aasx -o /some/path/output.py

# Overwrite an existing output file
submodel_to_code -i /some/path/DigitalNameplate.aasx --force
FlagLong formDescriptionRequired
-i--aas_pathInput AAS file (.aasx, .json, or .xml)Yes
-o--outpathOutput .py file path (mutually exclusive with -d)No
-d--outdirOutput directory; filename is derived from the input filename (mutually exclusive with -o)No
-f--forceOverwrite the output file if it already existsNo

If neither -o nor -d is given, the output file is written next to the input file with hyphens and spaces in the name replaced by underscores.

If the entry-point is not on PATH, use the module invocation:

python -m aas_submodel_to_py.submodel_to_code \
    -i /some/path/DigitalNameplate.aasx \
    -o /some/path/output.py

Python API

from aas_submodel_to_py import SubmodelCodegen

codegen = SubmodelCodegen()

# Generate from an AAS file (.aasx, .json, or .xml)
codegen.generate_from(
    input_file="/some/path/DigitalNameplate.aasx",
    output_file="nameplate.py"
)

# If you already have an AAS object store loaded in memory
codegen.generate_from_obj_store(
    obj_store=my_existing_store,
    output_file="output.py"
)

Running Tests

From the repository root:

pip install -e "./aas-submodel-to-py[test]"
python -m pytest aas-submodel-to-py/tests

The Tests workflow runs these tests, and the import checks of all modules in py-aas-submodels, on Python 3.11 and 3.14 for every push to master/develop and every pull request.

Support and Contribution

If you encounter any issues, or want to contribute to the project, feel free to open an issue or a pull request. Your contributions are always welcome!

License

This project is licensed under the MIT License. See the LICENSE file for details.

Downloads & popularity

Awaiting observations

No metric observations yet

Download counts and popularity will appear here as the collector receives them.

Versions 1

Collected releases
VersionPublishedDirect dependenciesOpen version
0.2.012 Oct 20265

Package advisories

0
No matching advisories collected

This describes our collected data; it is not a security assessment.

Repository

PENDING
rwth-iat/aas-submodel-template-to-py

Shared repository data · GitHub

License detected in repository
Not collected

Metadata history

Meaningful changes
Current collected state Current

Observed since 20 Oct, 17:04 UTC

No superseded metadata states collected. Unchanged observations do not create new revisions.

Record identity

package:b4d6583baf1600f0d1ca1f00311bedbbf38e9c6bcb1bc8a7b31ac20ae6466c14