Metadata-Version: 2.4
Name: py-aas-submodels
Version: 0.2.0
Summary: Generated AAS submodel classes
Author-email: Igor Garmaev <garmaev@gmx.net>
License: MIT
Project-URL: Homepage, https://github.com/rwth-iat/aas-submodel-template-to-py
Keywords: aas,submodel,generated,python
Classifier: Intended Audience :: Developers
Classifier: Programming Language :: Python :: 3
Classifier: License :: OSI Approved :: MIT License
Classifier: Operating System :: OS Independent
Requires-Python: >=3.11
Description-Content-Type: text/markdown
Requires-Dist: basyx-python-sdk>=2.1.0
Provides-Extra: test
Requires-Dist: pytest; extra == "test"
Requires-Dist: aas-core3.1>=1.0.0; extra == "test"

# py-aas-submodels

**py-aas-submodels** is a collection of typed Python classes for AAS submodel templates,
automatically generated from official [IDTA](https://industrialdigitaltwin.org/) templates
using [aas-submodel-to-py](../aas-submodel-to-py/). The classes extend the
[BaSyx Python SDK](https://github.com/eclipse-basyx/basyx-python-sdk) base classes and
pre-fill semantic metadata (semantic IDs, descriptions, administrative information) from the
templates. They build submodel instances (`kind=Instance`) without the template qualifiers
(e.g. `SMT/Cardinality`), which are only allowed in templates.

## Installation

```bash
pip install py-aas-submodels
```

## Available Submodels

Modules are generated from every `.json` template published in
[`admin-shell-io/submodel-templates`](https://github.com/admin-shell-io/submodel-templates/tree/main/published)
that contains a submodel (files containing only concept descriptions or a generic form are skipped).
Each module is named after the template, its part (if the template consists of several parts)
and its version, joined with underscores:

| Template file | Module |
|---|---|
| `Digital nameplate/3/0/1/...json` | `digital_nameplate_3_0_1` |
| `Contact Information/1/0/...json` | `contact_information_1_0` |
| `Digital Battery Passport/1_Digital Nameplate/1/0/...json` | `digital_battery_passport_1_digital_nameplate_1_0` |

Variants of a template file get a suffix:

| File name contains | Suffix | Example |
|---|---|---|
| `_forAASMetamodelV3.1` | `_metamodel_3_1` | `contact_information_1_0_1_metamodel_3_1` |
| `_without_examplevalues` | `_without_example_values` | `digital_battery_passport_1_digital_nameplate_1_0_without_example_values` |
| `Example` (instead of `Template`) | `_example` | `fire_protection_on_railway_vehicles_1_0_example` |
| `withOperations` | `_with_operations` | `time_series_data_1_1_1_with_operations` |

If names still collide, the additional modules get a double-underscore counter (`__1`, `__2`, ...).

Many template files generate the same classes, e.g. a template and its `_forAASMetamodelV3.1` variant.
Of identical modules, the one with the shortest name holds the classes, and the others are aliases
re-exporting them: `software_nameplate_1_0_1_metamodel_3_1.SoftwareNameplate` is
`software_nameplate_1_0.SoftwareNameplate`.

## Usage

```python
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",
    companyLogo=Nameplate.CompanyLogo(value="/aasx/logo.png"),
)
```

Required arguments are those specified as mandatory in the IDTA template (e.g. `Multiplicity=One`).
Optional submodel elements default to `None` and can be passed as keyword arguments.
Metadata (e.g. `semantic_id`, `description`) defaults to the template's values, returned by
`template_defaults()`, and can be replaced by keyword arguments, e.g. `Nameplate(..., id_short="Nameplate01")`.

A fuller example with a list, elements taking several values, and serialization to JSON with BaSyx:

```python
import json

from basyx.aas.adapter.json import AASToJsonEncoder
from basyx.aas.model import MultiLanguageTextType
from basyx.aas.model.datatypes import Date
from py_aas_submodels.digital_nameplate_3_0_1 import Nameplate

Marking = Nameplate.Markings.Markings_item

nameplate = Nameplate(
    id_="https://example.com/ids/sm/nameplate-002",
    uRIOfTheProduct="https://www.domain-abc.com/Model-Nr-1234",
    manufacturerName=MultiLanguageTextType({"en": "Example AG"}),
    manufacturerProductDesignation=MultiLanguageTextType({"en": "ABC-123"}),
    addressInformation=Nameplate.AddressInformation(),
    orderCodeOfManufacturer="ABC-123-XYZ",
    # Lists take their items
    markings=[
        Marking(
            markingName="CE",
            markingFile=Marking.MarkingFile("/aasx/ce.png", "image/png"),
            issueDate=Date(2024, 1, 15),
            # Elements taking several values are numbered: MarkingAdditionalText00, MarkingAdditionalText01
            markingAdditionalText=["Low Voltage Directive", "EMC Directive"],
        )
    ],
    # Metadata replaces the template's values, here the idShort "Nameplate"
    id_short="Nameplate01",
)

print(Nameplate.template_defaults()["id_short"])  # Nameplate
print(json.dumps(nameplate, cls=AASToJsonEncoder, indent=2))
```

## Generating Additional Submodels

To generate classes for your own submodel templates, use the
[aas-submodel-to-py](../aas-submodel-to-py/) code generator:

```bash
pip install aas-submodel-to-py
submodel_to_code -i path/to/template.aasx -o output.py
```

## Regenerating All Published Submodels

Use the repository script to regenerate all submodels from the
`published` directory of
[`admin-shell-io/submodel-templates`](https://github.com/admin-shell-io/submodel-templates):

```bash
python py-aas-submodels/regenerate_submodels.py
```

The script:
- Collects every `.json` template below `published/**`.
- Generates Python classes via `aas-submodel-to-py`.
- Applies file-name normalization/replacement for generated Python modules.
- Skips files without a submodel.
- Logs any conversion failures to `py-aas-submodels/generation_failures.log`.

A GitHub Actions workflow (`.github/workflows/regenerate-submodels.yml`) runs this
automatically whenever files under `aas-submodel-to-py/**` change on `master`, and daily
(on schedule) to pick up newly published submodels. It commits the
regenerated modules only if all [tests](#running-tests) pass.

## Running Tests

The tests check that every module imports and defines (or, as an alias, re-exports) a submodel class,
and that instances of the generated classes are valid AAS: they are serialized to JSON and XML with BaSyx, read back without
loss, and verified by [aas-core3.1](https://github.com/eclipse-aascw/aas-core3.1-python), an AAS SDK
independent of BaSyx:

```bash
pip install -e "./py-aas-submodels[test]"
python -m pytest py-aas-submodels/tests
```

`tests/test_template_conformance.py` compares instances of the generated classes with the template
files they were generated from: every element of the template must be in the instance, with its
semantic IDs, value type, descriptions and other metadata, and every mandatory element must be in an
instance with only the required arguments. By default, it compares a few templates in `tests/data`.
To compare all published templates, point `SUBMODEL_TEMPLATES_DIR` to the `published` directory of a
clone of [admin-shell-io/submodel-templates](https://github.com/admin-shell-io/submodel-templates):

```bash
SUBMODEL_TEMPLATES_DIR=/path/to/submodel-templates/published python -m pytest py-aas-submodels/tests/test_template_conformance.py
```

## License

This project is licensed under the MIT License. See the [LICENSE](../LICENSE) file for details.
