diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index caab87d0b..a5e2c2549 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -53,7 +53,7 @@ jobs:
# that you want to use to test the serialization adapter against.
# Currently, we need to update this manually, however I'm afraid this is not possible to automatically infer,
# since it's heavily dependant of the version of the AAS specification we support.
- AAS_SPECS_RELEASE_TAG: "IDTA-01001-3-0-1_schemasV3.0.8"
+ AAS_SPECS_RELEASE_TAG: "IDTA-01001-3-1-2"
services:
couchdb:
image: couchdb:3
@@ -203,7 +203,6 @@ jobs:
chmod +x ./etc/scripts/set_copyright_year.sh
./etc/scripts/set_copyright_year.sh --check
-
compliance-tool-test:
# This job runs the unittests on the python versions specified down at the matrix
runs-on: ubuntu-latest
@@ -216,12 +215,17 @@ jobs:
steps:
- uses: actions/checkout@v4
+ with:
+ # TODO(#592): revisit when the sdk<->compliance_tool version pinning is fixed.
+ # Need tags so setuptools_scm builds the local sdk as >=1.0.0, else the loose
+ # `basyx-python-sdk>=1.0.0` pin lets pip replace it with the PyPI release.
+ fetch-depth: 0
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}
- name: Install Python dependencies
- # install the local sdk in editable mode so it does not get overwritten
+ # install the local sdk first so its version satisfies the >=1.0.0 pin and pip keeps it instead of PyPI
run: |
python -m pip install --upgrade pip
python -m pip install ../sdk
@@ -243,12 +247,17 @@ jobs:
working-directory: ./compliance_tool
steps:
- uses: actions/checkout@v4
+ with:
+ # TODO(#592): revisit when the sdk<->compliance_tool version pinning is fixed.
+ # Need tags so setuptools_scm builds the local sdk as >=1.0.0, else the loose
+ # `basyx-python-sdk>=1.0.0` pin lets pip replace it with the PyPI release.
+ fetch-depth: 0
- name: Set up Python ${{ env.X_PYTHON_MIN_VERSION }}
uses: actions/setup-python@v5
with:
python-version: ${{ env.X_PYTHON_MIN_VERSION }}
- name: Install Python dependencies
- # install the local sdk in editable mode so it does not get overwritten
+ # install the local sdk first so its version satisfies the >=1.0.0 pin and pip keeps it instead of PyPI
run: |
python -m pip install --upgrade pip
python -m pip install ../sdk
@@ -292,7 +301,7 @@ jobs:
defaults:
run:
- working-directory: ./server/app
+ working-directory: ./server
steps:
- uses: actions/checkout@v4
- name: Set up Python ${{ env.X_PYTHON_MIN_VERSION }}
@@ -302,26 +311,26 @@ jobs:
- name: Install Python dependencies
run: |
python -m pip install --upgrade pip
- python -m pip install ../../sdk
+ python -m pip install ../sdk
python -m pip install .[dev]
- name: Check typing with MyPy
run: |
- python -m mypy .
+ python -m mypy app test
- name: Check code style with PyCodestyle
run: |
- python -m pycodestyle --count --max-line-length 120 .
+ python -m pycodestyle --count --max-line-length 120 app test
- server-package:
+ server-repository-docker:
# This job checks if we can build our server package
runs-on: ubuntu-latest
defaults:
run:
- working-directory: ./server
+ working-directory: ./server/docker/repository
steps:
- uses: actions/checkout@v4
- name: Build the Docker image
run: |
- docker build -t basyx-python-server -f Dockerfile ..
+ docker build -t basyx-python-server -f Dockerfile ../../..
- name: Run container
run: |
docker run -d --name basyx-python-server basyx-python-server
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
index 2f147c17a..715769f2f 100644
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -4,6 +4,9 @@ on:
release:
types: [published]
+env:
+ TARGET_PLATFORM: "linux/amd64,linux/arm/v7,linux/arm64"
+
jobs:
sdk-publish:
# This job publishes the package to PyPI
@@ -59,7 +62,54 @@ jobs:
with:
password: ${{ secrets.PYPI_ORG_TOKEN }}
- server-publish:
+ server-repository-publish:
+ # This job publishes the server docker image to DockerHub
+ runs-on: ubuntu-latest
+ defaults:
+ run:
+ working-directory: ./server
+ steps:
+ - name: Checkout Repository
+ uses: actions/checkout@v4
+
+ - name: Extract Docker image metadata
+ id: meta
+ uses: docker/metadata-action@v5
+ with:
+ images: eclipsebasyx/basyx-python-repository
+ # This fetches the latest git tag and adds an additional "latest" to it, so e.g. `2.1.0,latest`
+ tags: |
+ type=semver,pattern={{version}}
+ type=raw,value=latest
+ labels: |
+ org.opencontainers.image.title=BaSyx Python Repository Service
+ org.opencontainers.image.description=Eclipse BaSyx Python SDK - Repository HTTP Server
+ org.opencontainers.image.source=https://github.com/eclipse-basyx/basyx-python-sdk/tree/main/server
+ org.opencontainers.image.licenses=MIT
+
+ - name: Log in to Docker Hub
+ uses: docker/login-action@v4
+ with:
+ username: ${{ secrets.DOCKER_HUB_USER }}
+ password: ${{ secrets.DOCKER_HUB_TOKEN }}
+
+ - name: Setup QEMU
+ uses: docker/setup-qemu-action@v4
+
+ - name: Set up Docker Buildx
+ uses: docker/setup-buildx-action@v4
+
+ - name: Build and Push Repository Docker Image
+ uses: docker/build-push-action@v6
+ with:
+ context: .
+ file: ./server/docker/repository/Dockerfile
+ platforms: ${{ env.TARGET_PLATFORM }}
+ push: true
+ tags: ${{ steps.meta.outputs.tags }}
+ labels: ${{ steps.meta.outputs.labels }}
+
+ server-discovery-publish:
# This job publishes the server docker image to DockerHub
runs-on: ubuntu-latest
defaults:
@@ -73,14 +123,14 @@ jobs:
id: meta
uses: docker/metadata-action@v5
with:
- images: eclipsebasyx/basyx-python-server
+ images: eclipsebasyx/basyx-python-discovery
# This fetches the latest git tag and adds an additional "latest" to it, so e.g. `2.1.0,latest`
tags: |
type=semver,pattern={{version}}
type=raw,value=latest
labels: |
- org.opencontainers.image.title=BaSyx Python Server
- org.opencontainers.image.description=Eclipse BaSyx Python SDK - HTTP Server
+ org.opencontainers.image.title=BaSyx Python Discovery Service
+ org.opencontainers.image.description=Eclipse BaSyx Python SDK - Discovery HTTP Server
org.opencontainers.image.source=https://github.com/eclipse-basyx/basyx-python-sdk/tree/main/server
org.opencontainers.image.licenses=MIT
@@ -90,11 +140,65 @@ jobs:
username: ${{ secrets.DOCKER_HUB_USER }}
password: ${{ secrets.DOCKER_HUB_TOKEN }}
- - name: Build and Push Docker Image
+ - name: Setup QEMU
+ uses: docker/setup-qemu-action@v4
+
+ - name: Set up Docker Buildx
+ uses: docker/setup-buildx-action@v4
+
+ - name: Build and Push Repository Docker Image
+ uses: docker/build-push-action@v6
+ with:
+ context: .
+ file: ./server/docker/discovery/Dockerfile
+ platforms: ${{ env.TARGET_PLATFORM }}
+ push: true
+ tags: ${{ steps.meta.outputs.tags }}
+ labels: ${{ steps.meta.outputs.labels }}
+
+ server-registry-publish:
+ # This job publishes the server docker image to DockerHub
+ runs-on: ubuntu-latest
+ defaults:
+ run:
+ working-directory: ./server
+ steps:
+ - name: Checkout Repository
+ uses: actions/checkout@v4
+
+ - name: Extract Docker image metadata
+ id: meta
+ uses: docker/metadata-action@v5
+ with:
+ images: eclipsebasyx/basyx-python-registry
+ # This fetches the latest git tag and adds an additional "latest" to it, so e.g. `2.1.0,latest`
+ tags: |
+ type=semver,pattern={{version}}
+ type=raw,value=latest
+ labels: |
+ org.opencontainers.image.title=BaSyx Python Registry Service
+ org.opencontainers.image.description=Eclipse BaSyx Python SDK - Registry HTTP Server
+ org.opencontainers.image.source=https://github.com/eclipse-basyx/basyx-python-sdk/tree/main/server
+ org.opencontainers.image.licenses=MIT
+
+ - name: Log in to Docker Hub
+ uses: docker/login-action@v4
+ with:
+ username: ${{ secrets.DOCKER_HUB_USER }}
+ password: ${{ secrets.DOCKER_HUB_TOKEN }}
+
+ - name: Setup QEMU
+ uses: docker/setup-qemu-action@v4
+
+ - name: Set up Docker Buildx
+ uses: docker/setup-buildx-action@v4
+
+ - name: Build and Push Repository Docker Image
uses: docker/build-push-action@v6
with:
context: .
- file: ./server/Dockerfile # Todo: Update paths
+ file: ./server/docker/registry/Dockerfile
+ platforms: ${{ env.TARGET_PLATFORM }}
push: true
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
diff --git a/.gitignore b/.gitignore
index fe9b6f9c4..1efa31acc 100644
--- a/.gitignore
+++ b/.gitignore
@@ -32,5 +32,8 @@ compliance_tool/aas_compliance_tool/version.py
server/app/version.py
# Ignore the content of the server storage
-server/input/
-server/storage/
+server/example_configurations/repository_standalone/input/
+server/example_configurations/repository_standalone/storage/
+server/example_configurations/registry_standalone/input/
+server/example_configurations/registry_standalone/storage/
+server/example_configurations/discovery_standalone/storage/
diff --git a/LICENSE b/LICENSE
index d28a7cd9e..ec26b0c41 100644
--- a/LICENSE
+++ b/LICENSE
@@ -1,6 +1,6 @@
MIT License
-Copyright (c) 2019-2022 the Eclipse BaSyx Authors
+Copyright (c) 2019-2026 the Eclipse BaSyx Authors
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
diff --git a/README.md b/README.md
index 9894d320c..f3a64d167 100644
--- a/README.md
+++ b/README.md
@@ -7,13 +7,14 @@ for Industry 4.0 Systems.
These are the implemented AAS specifications of the [current SDK release](https://github.com/eclipse-basyx/basyx-python-sdk/releases/latest), which can be also found on [PyPI](https://pypi.org/project/basyx-python-sdk/):
-| Specification | Version |
-|---------------------------------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
-| Part 1: Metamodel | [v3.0.1 (01001-3-0-1)](https://industrialdigitaltwin.org/wp-content/uploads/2024/06/IDTA-01001-3-0-1_SpecificationAssetAdministrationShell_Part1_Metamodel.pdf) |
-| Schemata (JSONSchema, XSD) | [v3.0.8 (IDTA-01001-3-0-1_schemasV3.0.8)](https://github.com/admin-shell-io/aas-specs/releases/tag/IDTA-01001-3-0-1_schemasV3.0.8) |
-| Part 2: API | [v3.0 (01002-3-0)](https://industrialdigitaltwin.org/en/wp-content/uploads/sites/2/2023/06/IDTA-01002-3-0_SpecificationAssetAdministrationShell_Part2_API_.pdf) |
-| Part 3a: Data Specification IEC 61360 | [v3.0 (01003-a-3-0)](https://industrialdigitaltwin.org/wp-content/uploads/2023/04/IDTA-01003-a-3-0_SpecificationAssetAdministrationShell_Part3a_DataSpecification_IEC61360.pdf) |
-| Part 5: Package File Format (AASX) | [v3.0 (01005-3-0)](https://industrialdigitaltwin.org/wp-content/uploads/2023/04/IDTA-01005-3-0_SpecificationAssetAdministrationShell_Part5_AASXPackageFileFormat.pdf) |
+| Specification | Version |
+|---------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------|
+| Part 1: Metamodel | [v3.1.2 (01001-3-1-2)](https://industrialdigitaltwin.io/aas-specifications/IDTA-01001/v3.1.2/index.html) |
+| Schemata (JSONSchema, XSD) | [v3.1.2 (IDTA-01001-3-1-2)](https://github.com/admin-shell-io/aas-specs-metamodel/releases/tag/v3.1.2) |
+| Part 2: API | [v3.1.1 (01002)](https://industrialdigitaltwin.org/en/wp-content/uploads/sites/2/2025/08/IDTA-01002-3-1-1_AAS-Specification_Part2_API.pdf) |
+| Part 3a: Data Specification IEC 61360 | [v3.1.1 (01003-a)](https://industrialdigitaltwin.org/wp-content/uploads/2025/08/IDTA-01003-a-3-1-1_AAS-Specification_Part3a_DataSpecification.pdf) |
+| Part 5: Package File Format (AASX) | [v3.1 (01005)](https://industrialdigitaltwin.org/wp-content/uploads/2025/06/IDTA_01005-25-01_AAS-Specification_Part5_AASXPackageFileFormat.pdf) |
+
If you need support to an older version of the specifications, please refer to our [prior releases](https://github.com/eclipse-basyx/basyx-python-sdk/releases).
Each of them has a similar table at the top of the release notes.
@@ -22,7 +23,7 @@ Each of them has a similar table at the top of the release notes.
This repository is structured into separate packages.
The `sdk` directory provides the AAS metamodel as Python objects and fundamental functionalities to handle AAS.
The `server` implements a specification-compliant Docker HTTP server for AASs.
-The `compliance_tool` is to be determined.
+The `compliance_tool` is a command-line tool for checking whether JSON and XML files comply with the AAS specification.
* [SDK](./sdk/README.md):
* Modelling of AASs as Python objects
@@ -35,6 +36,9 @@ The `compliance_tool` is to be determined.
* [Server](./server/README.md): Docker Image of a specification compliant HTTP Server implementing the interfaces:
* Asset Administration Shell Repository
* Submodel Repository
+ * Asset Administration Shell Registry Service
+ * Submodel Registry Service
+ * Discovery
* [Compliance Tool](./compliance_tool/README.md): A command-line tool for checking compliance of JSON and XML files
to the specification of the AAS
diff --git a/compliance_tool/README.md b/compliance_tool/README.md
index 185e157a0..be49156a9 100644
--- a/compliance_tool/README.md
+++ b/compliance_tool/README.md
@@ -10,6 +10,38 @@ Shell elements
* check if the data in a given xml, json or aasx file is the same as the example data
* check if two given xml, json or aasx files contain the same Asset Administration Shell elements in any order
+## Installation
+
+### Default installation
+Install the latest release from PyPI:
+
+```bash
+pip install basyx-compliance-tool
+```
+
+### Developer installation
+When working from a checkout of this repository, install the sibling SDK from the source tree *first*, then the
+compliance tool:
+
+```bash
+pip install ./sdk
+pip install ./compliance_tool
+```
+
+Installing the local SDK first is what lets the tool check against your in-development metamodel: `pip` keeps the
+already-installed local SDK instead of pulling a release from PyPI.
+
+> [!IMPORTANT]
+> The compliance checks are only as current as the installed `basyx-python-sdk` — the tool reports compliance against
+> whatever metamodel version that SDK implements, not against the version named in this README. Because the dependency
+> is currently declared loosely (`basyx-python-sdk>=1.0.0`), `pip` will **not** replace an SDK that is already installed
+> and satisfies that constraint, even if it is older than the one you intend to test against. This happens silently, with
+> no error. To be sure you are checking against the intended metamodel, install or upgrade the SDK explicitly, e.g. the
+> local source tree with `pip install ./sdk` or a specific release with `pip install --upgrade "basyx-python-sdk==2.1.0"`.
+>
+> This manual version matching is a temporary workaround; see #592.
+
+## Usage
Invoking should work with either `python -m aas_compliance_tool.cli` or (when installed correctly and PATH is set
correctly) with `aas-compliance-check` on the command line.
diff --git a/compliance_tool/aas_compliance_tool/cli.py b/compliance_tool/aas_compliance_tool/cli.py
index 3ffd7d219..1c10bed91 100644
--- a/compliance_tool/aas_compliance_tool/cli.py
+++ b/compliance_tool/aas_compliance_tool/cli.py
@@ -1,4 +1,4 @@
-# Copyright (c) 2025 the Eclipse BaSyx Authors
+# Copyright (c) 2026 the Eclipse BaSyx Authors
#
# This program and the accompanying materials are made available under the terms of the MIT License, available in
# the LICENSE file of this project.
@@ -37,12 +37,11 @@ def parse_cli_arguments() -> argparse.ArgumentParser:
'Asset Administration Shell" specification of Plattform Industrie 4.0. \n\n'
'This tool has five features: \n'
'1. create a xml or json file or an AASX file using xml or json files with example aas elements\n'
- '2. check if a given xml or json file is compliant with the official json or xml aas schema and '
- 'is deserializable\n'
+ '2. check if a given xml or json file is deserializable and therefore compliant with the schema\n'
'3. check if the data in a given xml, json or aasx file is the same as the example data\n'
'4. check if two given xml, json or aasx files contain the same aas elements in any order\n\n'
- 'As a first argument, the feature must be specified (create, schema, deserialization, example, '
- 'files) or in short (c, s, d, e or f).\n'
+ 'As a first argument, the feature must be specified (create, deserialization, example, '
+ 'files) or in short (c, d, e or f).\n'
'Depending the chosen feature, different additional arguments must be specified:\n'
'create or c: path to the file which shall be created (file_1)\n'
'deseriable or d: file to be checked (file_1)\n'
@@ -50,7 +49,7 @@ def parse_cli_arguments() -> argparse.ArgumentParser:
'file_compare or f: files to compare (file_1, file_2)\n,'
'In any case, it must be specified whether the (given or created) files are json (--json) or '
'xml (--xml).\n'
- 'All features except "schema" support reading/writing AASX packages instead of plain XML or JSON '
+ 'All features support reading/writing AASX packages instead of plain XML or JSON '
'files via the --aasx option.\n\n'
'Additionally, the tool offers some extra features for more convenient usage:\n\n'
'a. Different levels of verbosity:\n'
@@ -63,7 +62,7 @@ def parse_cli_arguments() -> argparse.ArgumentParser:
' With -l or --logfile, a path to the file where the logfiles shall be created can be specified.',
formatter_class=argparse.RawTextHelpFormatter)
- parser.add_argument('action', choices=['create', 'c', 'schema', 's', 'deserialization', 'd', 'example', 'e',
+ parser.add_argument('action', choices=['create', 'c', 'deserialization', 'd', 'example', 'e',
'files', 'f'],
help='c or create: creates a file with example data\n'
'd or deserialization: checks if a given file is compliance with the official schema and '
@@ -76,7 +75,7 @@ def parse_cli_arguments() -> argparse.ArgumentParser:
parser.add_argument('-v', '--verbose', help="Print detailed information for each check. Multiple -v options "
"increase the verbosity. 1: Detailed error information, 2: Additional "
"detailed success information", action='count', default=0)
- parser.add_argument('-q', '--quite', help="no information output if successful", action='store_true')
+ parser.add_argument('-q', '--quiet', help="no information output if successful", action='store_true')
group = parser.add_mutually_exclusive_group(required=True)
group.add_argument('--json', help="Use AAS json format when checking or creating files", action='store_true')
group.add_argument('--xml', help="Use AAS xml format when checking or creating files", action='store_true')
@@ -177,9 +176,8 @@ def main():
**data_checker_kwargs)
else:
parser.error("f or files requires two file path.")
- exit()
- if manager.status is Status.SUCCESS and args.quite:
+ if manager.status is Status.SUCCESS and args.quiet:
exit()
print(manager.format_state_manager(args.verbose))
diff --git a/compliance_tool/aas_compliance_tool/compliance_check_aasx.py b/compliance_tool/aas_compliance_tool/compliance_check_aasx.py
index 9dfbb982c..82b25766d 100644
--- a/compliance_tool/aas_compliance_tool/compliance_check_aasx.py
+++ b/compliance_tool/aas_compliance_tool/compliance_check_aasx.py
@@ -1,4 +1,4 @@
-# Copyright (c) 2025 the Eclipse BaSyx Authors
+# Copyright (c) 2026 the Eclipse BaSyx Authors
#
# This program and the accompanying materials are made available under the terms of the MIT License, available in
# the LICENSE file of this project.
@@ -13,7 +13,7 @@
"""
import datetime
import logging
-from typing import Optional, Tuple
+from typing import Optional, Tuple, cast
import io
from lxml import etree # type: ignore
@@ -31,7 +31,7 @@
def check_deserialization(file_path: str, state_manager: ComplianceToolStateManager,
file_info: Optional[str] = None) \
- -> Tuple[model.DictObjectStore, aasx.DictSupplementaryFileContainer, pyecma376_2.OPCCoreProperties]:
+ -> Tuple[model.DictIdentifiableStore, aasx.DictSupplementaryFileContainer, pyecma376_2.OPCCoreProperties]:
"""
Read a AASX file and reports any issues using the given
:class:`~basyx.aas.compliance_tool.state_manager.ComplianceToolStateManager`
@@ -68,87 +68,24 @@ def check_deserialization(file_path: str, state_manager: ComplianceToolStateMana
state_manager.set_step_status_from_log()
state_manager.add_step('Read file')
state_manager.set_step_status(Status.NOT_EXECUTED)
- return model.DictObjectStore(), aasx.DictSupplementaryFileContainer(), pyecma376_2.OPCCoreProperties()
+ return model.DictIdentifiableStore(), aasx.DictSupplementaryFileContainer(), pyecma376_2.OPCCoreProperties()
try:
# read given file
state_manager.add_step('Read file')
- obj_store: model.DictObjectStore[model.Identifiable] = model.DictObjectStore()
+ identifiable_store: model.DictIdentifiableStore[model.Identifiable] = model.DictIdentifiableStore()
files = aasx.DictSupplementaryFileContainer()
- reader.read_into(obj_store, files)
+ reader.read_into(identifiable_store, files)
new_cp = reader.get_core_properties()
state_manager.set_step_status(Status.SUCCESS)
except (ValueError, KeyError) as error:
logger.error(error)
state_manager.set_step_status(Status.FAILED)
- return model.DictObjectStore(), aasx.DictSupplementaryFileContainer(), pyecma376_2.OPCCoreProperties()
+ return model.DictIdentifiableStore(), aasx.DictSupplementaryFileContainer(), pyecma376_2.OPCCoreProperties()
finally:
reader.close()
- return obj_store, files, new_cp
-
-
-def check_schema(file_path: str, state_manager: ComplianceToolStateManager) -> None:
- """
- Checks a given file against the official json schema and reports any issues using the given
- :class:`~basyx.aas.compliance_tool.state_manager.ComplianceToolStateManager`
-
- Opens the file and checks if the data inside is stored in XML or JSON. Then calls the respective compliance tool
- schema check
- """
- logger = logging.getLogger('compliance_check')
- logger.addHandler(state_manager)
- logger.propagate = False
- logger.setLevel(logging.INFO)
-
- # create handler to get logger info
- logger_deserialization = logging.getLogger(aasx.__name__)
- logger_deserialization.addHandler(state_manager)
- logger_deserialization.propagate = False
- logger_deserialization.setLevel(logging.INFO)
-
- state_manager.add_step('Open file')
- try:
- # open given file
- reader = aasx.AASXReader(file_path)
- state_manager.set_step_status_from_log()
- except ValueError as error:
- logger.error(error)
- state_manager.set_step_status_from_log()
- state_manager.add_step('Read file')
- state_manager.set_step_status(Status.NOT_EXECUTED)
- return
-
- try:
- # read given file (Find XML and JSON parts)
- state_manager.add_step('Read file')
- core_rels = reader.reader.get_related_parts_by_type()
- try:
- aasx_origin_part = core_rels[aasx.RELATIONSHIP_TYPE_AASX_ORIGIN][0]
- except IndexError as e:
- raise ValueError("Not a valid AASX file: aasx-origin Relationship is missing.") from e
- state_manager.set_step_status(Status.SUCCESS)
- for aas_part in reader.reader.get_related_parts_by_type(aasx_origin_part)[
- aasx.RELATIONSHIP_TYPE_AAS_SPEC]:
- content_type = reader.reader.get_content_type(aas_part)
- extension = aas_part.split("/")[-1].split(".")[-1]
- with reader.reader.open_part(aas_part) as p:
- if content_type.split(";")[0] in (
- "text/xml", "application/xml") or content_type == "" and extension == "xml":
- logger.debug("Parsing AAS objects from XML stream in OPC part {} ...".format(aas_part))
- compliance_check_xml._check_schema(p, state_manager)
- elif content_type.split(";")[0] == "application/json" \
- or content_type == "" and extension == "json":
- logger.debug("Parsing AAS objects from JSON stream in OPC part {} ...".format(aas_part))
- compliance_check_json._check_schema(io.TextIOWrapper(p, encoding='utf-8-sig'), state_manager)
- else:
- raise ValueError("Could not determine part format of AASX part {} (Content Type: {}, extension: {}"
- .format(aas_part, content_type, extension))
- except ValueError as error:
- logger.error(error)
- state_manager.set_step_status(Status.FAILED)
- finally:
- reader.close()
+ return identifiable_store, files, new_cp
def check_aas_example(file_path: str, state_manager: ComplianceToolStateManager, **kwargs) -> None:
@@ -174,25 +111,29 @@ def check_aas_example(file_path: str, state_manager: ComplianceToolStateManager,
logger_example.propagate = False
logger_example.setLevel(logging.INFO)
- obj_store, files, cp_new = check_deserialization(file_path, state_manager)
+ identifiable_store, files, cp_new = check_deserialization(file_path, state_manager)
if state_manager.status in (Status.FAILED, Status.NOT_EXECUTED):
state_manager.add_step('Check if data is equal to example data')
state_manager.set_step_status(Status.NOT_EXECUTED)
state_manager.add_step('Check if core properties are equal')
state_manager.set_step_status(Status.NOT_EXECUTED)
+ state_manager.add_step('Check if supplementary files are equal')
+ state_manager.set_step_status(Status.NOT_EXECUTED)
return
checker = AASDataChecker(raise_immediately=False, **kwargs)
state_manager.add_step('Check if data is equal to example data')
example_data = create_example_aas_binding()
- checker.check_object_store(obj_store, example_data)
+ checker.check_identifiable_store(identifiable_store, example_data)
state_manager.add_log_records_from_data_checker(checker)
if state_manager.status in (Status.FAILED, Status.NOT_EXECUTED):
state_manager.add_step('Check if core properties are equal')
state_manager.set_step_status(Status.NOT_EXECUTED)
+ state_manager.add_step('Check if supplementary files are equal')
+ state_manager.set_step_status(Status.NOT_EXECUTED)
return
state_manager.add_step('Check if core properties are equal')
@@ -208,57 +149,46 @@ def check_aas_example(file_path: str, state_manager: ComplianceToolStateManager,
cp.title = "Test Title"
checker2 = DataChecker(raise_immediately=False)
- try:
- assert isinstance(cp_new.created, datetime.datetime)
- checker2.check(isinstance(cp_new.created, datetime.datetime), "core property created must be of type datetime",
- created=type(cp_new.created))
- duration = cp_new.created - cp.created
+ if checker2.check(isinstance(cp_new.created, datetime.datetime), "core property created must be of type datetime",
+ created=type(cp_new.created)):
+ duration = cast(datetime.datetime, cp_new.created) - cp.created
checker2.check(duration.microseconds < 20, "created must be {}".format(cp.created), created=cp_new.created)
- except AssertionError:
- checker2.check(isinstance(cp_new.created, datetime.datetime), "core property created must be of type datetime",
- created=type(cp_new.created))
checker2.check(cp_new.creator == cp.creator, "creator must be {}".format(cp.creator), creator=cp_new.creator)
checker2.check(cp_new.description == cp.description, "description must be {}".format(cp.description),
description=cp_new.description)
checker2.check(cp_new.lastModifiedBy == cp.lastModifiedBy, "lastModifiedBy must be {}".format(cp.lastModifiedBy),
lastModifiedBy=cp_new.lastModifiedBy)
- try:
- assert isinstance(cp_new.modified, datetime.datetime)
- checker2.check(isinstance(cp_new.modified, datetime.datetime), "modified bust be of type datetime",
- modified=type(cp_new.modified))
- duration = cp_new.modified - cp.modified
+
+ if checker2.check(isinstance(cp_new.modified, datetime.datetime), "modified must be of type datetime",
+ modified=type(cp_new.modified)):
+ duration = cast(datetime.datetime, cp_new.modified) - cp.modified
checker2.check(duration.microseconds < 20, "modified must be {}".format(cp.modified), modified=cp_new.modified)
- except AssertionError:
- checker2.check(isinstance(cp_new.modified, datetime.datetime), "modified bust be of type datetime",
- modified=type(cp_new.modified))
+
checker2.check(cp_new.revision == cp.revision, "revision must be {}".format(cp.revision), revision=cp_new.revision)
checker2.check(cp_new.version == cp.version, "version must be {}".format(cp.version), version=cp_new.version)
checker2.check(cp_new.title == cp.title, "title must be {}".format(cp.title), title=cp_new.title)
+ state_manager.add_log_records_from_data_checker(checker2)
+
# Check if file in file object is the same
+ state_manager.add_step('Check if supplementary files are equal')
+ file_checker = DataChecker(raise_immediately=False)
+
list_of_id_shorts = ["ExampleSubmodelCollection", "ExampleFile"]
- obj = example_data.get_identifiable("https://acplt.org/Test_Submodel")
- for id_short in list_of_id_shorts:
- obj = obj.get_referable(id_short)
- obj2 = obj_store.get_identifiable("https://acplt.org/Test_Submodel")
+ identifiable = example_data.get_item("https://example.org/Test_Submodel")
for id_short in list_of_id_shorts:
- obj2 = obj2.get_referable(id_short)
- try:
- sha_file = files.get_sha256(obj.value)
- except KeyError as error:
- state_manager.add_log_records_from_data_checker(checker2)
- logger.error(error)
- state_manager.set_step_status(Status.FAILED)
- return
+ identifiable = identifiable.get_referable(id_short)
+ file_name = identifiable.value
+ if file_checker.check(file_name in files, f"Supplementary File {file_name} must exist"):
+ test_file_checksum = 'b18229b24a4ee92c6c2b6bc6a8018563b17472f1150d35d5a5945afeb447ed44'
+ file_checker.check(
+ files.get_sha256(file_name).hex() == test_file_checksum,
+ f"Supplementary File {file_name} checksum must be '{test_file_checksum}'.",
+ value=files.get_sha256(file_name)
+ )
- checker2.check(sha_file == files.get_sha256(obj2.value), "File of {} must be {}.".format(obj.value, obj2.value),
- value=obj2.value)
- state_manager.add_log_records_from_data_checker(checker2)
- if state_manager.status in (Status.FAILED, Status.NOT_EXECUTED):
- state_manager.set_step_status(Status.FAILED)
- else:
- state_manager.set_step_status(Status.SUCCESS)
+ state_manager.add_log_records_from_data_checker(file_checker)
def check_aasx_files_equivalence(file_path_1: str, file_path_2: str, state_manager: ComplianceToolStateManager,
@@ -280,42 +210,84 @@ def check_aasx_files_equivalence(file_path_1: str, file_path_2: str, state_manag
logger.propagate = False
logger.setLevel(logging.INFO)
- obj_store_1, files_1, cp_1 = check_deserialization(file_path_1, state_manager, 'first')
+ identifiable_store_1, files_1, cp_1 = check_deserialization(file_path_1, state_manager, 'first')
- obj_store_2, files_2, cp_2 = check_deserialization(file_path_2, state_manager, 'second')
+ identifiable_store_2, files_2, cp_2 = check_deserialization(file_path_2, state_manager, 'second')
- if state_manager.status is Status.FAILED:
+ if state_manager.status >= Status.FAILED:
state_manager.add_step('Check if data in files are equal')
state_manager.set_step_status(Status.NOT_EXECUTED)
state_manager.add_step('Check if core properties are equal')
state_manager.set_step_status(Status.NOT_EXECUTED)
+ state_manager.add_step('Check if supplementary files are equal')
+ state_manager.set_step_status(Status.NOT_EXECUTED)
return
checker = AASDataChecker(raise_immediately=False, **kwargs)
try:
state_manager.add_step('Check if data in files are equal')
- checker.check_object_store(obj_store_1, obj_store_2)
+ checker.check_identifiable_store(identifiable_store_1, identifiable_store_2)
except (KeyError, AssertionError) as error:
state_manager.set_step_status(Status.FAILED)
logger.error(error)
state_manager.add_step('Check if core properties are equal')
state_manager.set_step_status(Status.NOT_EXECUTED)
+ state_manager.add_step('Check if supplementary files are equal')
+ state_manager.set_step_status(Status.NOT_EXECUTED)
return
state_manager.add_log_records_from_data_checker(checker)
- if state_manager.status is Status.FAILED:
+ if state_manager.status >= Status.FAILED:
state_manager.add_step('Check if core properties are equal')
state_manager.set_step_status(Status.NOT_EXECUTED)
+ state_manager.add_step('Check if supplementary files are equal')
+ state_manager.set_step_status(Status.NOT_EXECUTED)
return
state_manager.add_step('Check if core properties are equal')
checker2 = DataChecker(raise_immediately=False)
- assert (isinstance(cp_1.created, datetime.datetime))
- assert (isinstance(cp_2.created, datetime.datetime))
- duration = cp_1.created - cp_2.created
+ checker2.check(isinstance(cp_1.created, datetime.datetime),
+ "core property created of first file must be of type datetime",
+ created=type(cp_1.created))
+ checker2.check(isinstance(cp_2.created, datetime.datetime),
+ "core property created of second file must be of type datetime",
+ created=type(cp_2.created))
+
+ if any(True for _ in checker2.failed_checks):
+ state_manager.add_log_records_from_data_checker(checker2)
+ return
+
+ duration = cast(datetime.datetime, cp_1.created) - cast(datetime.datetime, cp_2.created)
checker2.check(duration.microseconds < 20, "created must be {}".format(cp_1.created), value=cp_2.created)
checker2.check(cp_1.creator == cp_2.creator, "creator must be {}".format(cp_1.creator), value=cp_2.creator)
checker2.check(cp_1.lastModifiedBy == cp_2.lastModifiedBy, "lastModifiedBy must be {}".format(cp_1.lastModifiedBy),
value=cp_2.lastModifiedBy)
+ checker2.check(cp_1.revision == cp_2.revision, "revision must be {}".format(cp_2.revision), revision=cp_1.revision)
+ checker2.check(cp_1.version == cp_2.version, "version must be {}".format(cp_2.version), version=cp_1.version)
+ checker2.check(cp_1.title == cp_2.title, "title must be {}".format(cp_2.title), title=cp_1.title)
state_manager.add_log_records_from_data_checker(checker2)
+
+ state_manager.add_step('Check if supplementary files are equal')
+
+ file_checker = DataChecker(raise_immediately=False)
+ for file_name in files_1:
+ both_contain = file_checker.check(file_name in files_2,
+ "second file must contain supplementary file {}".format(file_name))
+ if both_contain:
+ expected_type = files_1.get_content_type(file_name)
+ file_checker.check(expected_type == files_2.get_content_type(file_name),
+ f"second file must contain supplementary file {file_name}"
+ " with content-type {expected_type}",
+ content_type=files_2.get_content_type(file_name))
+ expected_checksum = files_1.get_sha256(file_name)
+ file_checker.check(expected_checksum == files_2.get_sha256(file_name),
+ f"second file must contain supplementary file {file_name}"
+ f" with sha256 {expected_checksum.hex()}",
+ checksum=files_2.get_sha256(file_name).hex())
+
+ for file_name in files_2:
+ file_checker.check(file_name in files_1,
+ "first file must contain supplementary file {}".format(file_name))
+
+ state_manager.add_log_records_from_data_checker(file_checker)
diff --git a/compliance_tool/aas_compliance_tool/compliance_check_json.py b/compliance_tool/aas_compliance_tool/compliance_check_json.py
index 2050f570c..e50332ecc 100644
--- a/compliance_tool/aas_compliance_tool/compliance_check_json.py
+++ b/compliance_tool/aas_compliance_tool/compliance_check_json.py
@@ -1,4 +1,4 @@
-# Copyright (c) 2025 the Eclipse BaSyx Authors
+# Copyright (c) 2026 the Eclipse BaSyx Authors
#
# This program and the accompanying materials are made available under the terms of the MIT License, available in
# the LICENSE file of this project.
@@ -23,86 +23,8 @@
from aas_compliance_tool.state_manager import ComplianceToolStateManager, Status
-JSON_SCHEMA_FILE = os.path.join(os.path.dirname(__file__), 'schemas/aasJSONSchema.json')
-
-
-def check_schema(file_path: str, state_manager: ComplianceToolStateManager) -> None:
- """
- Checks a given file against the official json schema and reports any issues using the given
- :class:`~basyx.aas.compliance_tool.state_manager.ComplianceToolStateManager`
-
- Add the steps: `Open file`, `Read file and check if it is conform to the json syntax` and `Validate file against
- official json schema`
-
- :param file_path: Path to the file which should be checked
- :param state_manager: :class:`~basyx.aas.compliance_tool.state_manager.ComplianceToolStateManager` to log the steps
- """
- logger = logging.getLogger('compliance_check')
- logger.addHandler(state_manager)
- logger.propagate = False
- logger.setLevel(logging.INFO)
-
- state_manager.add_step('Open file')
- try:
- # open given file
- file_to_be_checked = open(file_path, 'r', encoding='utf-8-sig')
- except IOError as error:
- state_manager.set_step_status(Status.FAILED)
- logger.error(error)
- state_manager.add_step('Read file and check if it is conform to the json syntax')
- state_manager.set_step_status(Status.NOT_EXECUTED)
- state_manager.add_step('Validate file against official json schema')
- state_manager.set_step_status(Status.NOT_EXECUTED)
- return
- return _check_schema(file_to_be_checked, state_manager)
-
-
-def _check_schema(file_to_be_checked: IO[str], state_manager: ComplianceToolStateManager):
- logger = logging.getLogger('compliance_check')
- logger.addHandler(state_manager)
- logger.propagate = False
- logger.setLevel(logging.INFO)
-
- try:
- with file_to_be_checked:
- state_manager.set_step_status(Status.SUCCESS)
- # read given file and check if it is conform to the json syntax
- state_manager.add_step('Read file and check if it is conform to the json syntax')
- json_to_be_checked = json.load(file_to_be_checked)
- state_manager.set_step_status(Status.SUCCESS)
- except json.decoder.JSONDecodeError as error:
- state_manager.set_step_status(Status.FAILED)
- logger.error(error)
- state_manager.add_step('Validate file against official json schema')
- state_manager.set_step_status(Status.NOT_EXECUTED)
- return
-
- # load json schema
- with open(JSON_SCHEMA_FILE, 'r', encoding='utf-8-sig') as json_file:
- aas_json_schema = json.load(json_file)
- state_manager.add_step('Validate file against official json schema')
-
- # validate given file against schema
- try:
- import jsonschema # type: ignore
- except ImportError as error:
- state_manager.set_step_status(Status.NOT_EXECUTED)
- logger.error("Python package 'jsonschema' is required for validating the JSON file.", error)
- return
-
- try:
- jsonschema.validate(instance=json_to_be_checked, schema=aas_json_schema)
- except jsonschema.exceptions.ValidationError as error:
- state_manager.set_step_status(Status.FAILED)
- logger.error(error)
- return
-
- state_manager.set_step_status(Status.SUCCESS)
- return
-
-
def check_deserialization(file_path: str, state_manager: ComplianceToolStateManager,
- file_info: Optional[str] = None) -> model.DictObjectStore:
+ file_info: Optional[str] = None) -> model.DictIdentifiableStore:
"""
Deserializes a JSON AAS file and reports any issues using the given
:class:`~basyx.aas.compliance_tool.state_manager.ComplianceToolStateManager`
@@ -112,7 +34,7 @@ def check_deserialization(file_path: str, state_manager: ComplianceToolStateMana
:param file_path: Given file which should be deserialized
:param state_manager: :class:`~basyx.aas.compliance_tool.state_manager.ComplianceToolStateManager` to log the steps
:param file_info: Additional information about the file for name of the steps
- :return: The deserialized :class:`~basyx.aas.model.provider.DictObjectStore`
+ :return: The deserialized :class:`~basyx.aas.model.provider.DictIdentifiableStore`
"""
logger = logging.getLogger('compliance_check')
logger.addHandler(state_manager)
@@ -140,7 +62,7 @@ def check_deserialization(file_path: str, state_manager: ComplianceToolStateMana
else:
state_manager.add_step('Read file and check if it is deserializable')
state_manager.set_step_status(Status.NOT_EXECUTED)
- return model.DictObjectStore()
+ return model.DictIdentifiableStore()
with file_to_be_checked:
state_manager.set_step_status(Status.SUCCESS)
@@ -149,11 +71,11 @@ def check_deserialization(file_path: str, state_manager: ComplianceToolStateMana
state_manager.add_step('Read file {} and check if it is deserializable'.format(file_info))
else:
state_manager.add_step('Read file and check if it is deserializable')
- obj_store = json_deserialization.read_aas_json_file(file_to_be_checked, failsafe=True)
+ identifiable_store = json_deserialization.read_aas_json_file(file_to_be_checked, failsafe=True)
state_manager.set_step_status_from_log()
- return obj_store
+ return identifiable_store
def check_aas_example(file_path: str, state_manager: ComplianceToolStateManager, **kwargs) -> None:
@@ -174,7 +96,7 @@ def check_aas_example(file_path: str, state_manager: ComplianceToolStateManager,
logger_example.propagate = False
logger_example.setLevel(logging.INFO)
- obj_store = check_deserialization(file_path, state_manager)
+ identifiable_store = check_deserialization(file_path, state_manager)
if state_manager.status in (Status.FAILED, Status.NOT_EXECUTED):
state_manager.add_step('Check if data is equal to example data')
@@ -184,7 +106,7 @@ def check_aas_example(file_path: str, state_manager: ComplianceToolStateManager,
checker = AASDataChecker(raise_immediately=False, **kwargs)
state_manager.add_step('Check if data is equal to example data')
- checker.check_object_store(obj_store, create_example())
+ checker.check_identifiable_store(identifiable_store, create_example())
state_manager.add_log_records_from_data_checker(checker)
@@ -208,9 +130,9 @@ def check_json_files_equivalence(file_path_1: str, file_path_2: str, state_manag
logger.propagate = False
logger.setLevel(logging.INFO)
- obj_store_1 = check_deserialization(file_path_1, state_manager, 'first')
+ identifiable_store_1 = check_deserialization(file_path_1, state_manager, 'first')
- obj_store_2 = check_deserialization(file_path_2, state_manager, 'second')
+ identifiable_store_2 = check_deserialization(file_path_2, state_manager, 'second')
if state_manager.status is Status.FAILED:
state_manager.add_step('Check if data in files are equal')
@@ -220,7 +142,7 @@ def check_json_files_equivalence(file_path_1: str, file_path_2: str, state_manag
checker = AASDataChecker(raise_immediately=False, **kwargs)
try:
state_manager.add_step('Check if data in files are equal')
- checker.check_object_store(obj_store_1, obj_store_2)
+ checker.check_identifiable_store(identifiable_store_1, identifiable_store_2)
except (KeyError, AssertionError) as error:
state_manager.set_step_status(Status.FAILED)
logger.error(error)
diff --git a/compliance_tool/aas_compliance_tool/compliance_check_xml.py b/compliance_tool/aas_compliance_tool/compliance_check_xml.py
index 6d4e10c55..eeb9924c6 100644
--- a/compliance_tool/aas_compliance_tool/compliance_check_xml.py
+++ b/compliance_tool/aas_compliance_tool/compliance_check_xml.py
@@ -1,4 +1,4 @@
-# Copyright (c) 2025 the Eclipse BaSyx Authors
+# Copyright (c) 2026 the Eclipse BaSyx Authors
#
# This program and the accompanying materials are made available under the terms of the MIT License, available in
# the LICENSE file of this project.
@@ -23,85 +23,8 @@
from aas_compliance_tool.state_manager import ComplianceToolStateManager, Status
-XML_SCHEMA_FILE = os.path.join(os.path.dirname(__file__), 'schemas/aasXMLSchema.xsd')
-
-
-def check_schema(file_path: str, state_manager: ComplianceToolStateManager) -> None:
- """
- Checks a given file against the official xml schema and reports any issues using the given
- :class:`~basyx.aas.compliance_tool.state_manager.ComplianceToolStateManager`
-
- Add the steps: `Open file`, `Read file`, `Check if it is conform to the xml syntax` and `Validate file against
- official xml schema`
-
- :param file_path: Path to the file which should be checked
- :param state_manager: :class:`~basyx.aas.compliance_tool.state_manager.ComplianceToolStateManager` to log the steps
- """
- logger = logging.getLogger('compliance_check')
- logger.addHandler(state_manager)
- logger.propagate = False
- logger.setLevel(logging.INFO)
-
- state_manager.add_step('Open file')
- try:
- # open given file
- file_to_be_checked = open(file_path, 'rb')
- state_manager.set_step_status(Status.SUCCESS)
- except IOError as error:
- state_manager.set_step_status(Status.FAILED)
- logger.error(error)
- state_manager.add_step('Read file and check if it is conform to the xml syntax')
- state_manager.set_step_status(Status.NOT_EXECUTED)
- state_manager.add_step('Validate file against official xml schema')
- state_manager.set_step_status(Status.NOT_EXECUTED)
- return
- return _check_schema(file_to_be_checked, state_manager)
-
-
-def _check_schema(file_to_be_checked, state_manager):
- logger = logging.getLogger('compliance_check')
- logger.addHandler(state_manager)
- logger.propagate = False
- logger.setLevel(logging.INFO)
-
- state_manager.add_step('Read file and check if it is conform to the xml syntax')
- try:
- # read given file and check if it is conform to the xml syntax
- parser = etree.XMLParser(remove_blank_text=True, remove_comments=True)
- etree.parse(file_to_be_checked, parser)
- state_manager.set_step_status(Status.SUCCESS)
- except etree.XMLSyntaxError as error:
- state_manager.set_step_status(Status.FAILED)
- logger.error(error)
- state_manager.add_step('Validate file against official xml schema')
- state_manager.set_step_status(Status.NOT_EXECUTED)
- file_to_be_checked.close()
- return
- except Exception:
- file_to_be_checked.close()
- raise
-
- # load aas xml schema
- aas_xml_schema = etree.XMLSchema(file=XML_SCHEMA_FILE)
- parser = etree.XMLParser(schema=aas_xml_schema)
-
- state_manager.add_step('Validate file against official xml schema')
- # validate given file against schema
- try:
- file_to_be_checked.seek(0) # Reset reading file offset (cursor) to the beginning of the file
- with file_to_be_checked:
- etree.parse(file_to_be_checked, parser=parser)
- except etree.ParseError as error:
- state_manager.set_step_status(Status.FAILED)
- logger.error(error)
- return
-
- state_manager.set_step_status(Status.SUCCESS)
- return
-
-
def check_deserialization(file_path: str, state_manager: ComplianceToolStateManager,
- file_info: Optional[str] = None) -> model.DictObjectStore:
+ file_info: Optional[str] = None) -> model.DictIdentifiableStore:
"""
Deserializes a XML AAS file and reports any issues using the given
:class:`~basyx.aas.compliance_tool.state_manager.ComplianceToolStateManager`
@@ -139,7 +62,7 @@ def check_deserialization(file_path: str, state_manager: ComplianceToolStateMana
else:
state_manager.add_step('Read file and check if it is deserializable')
state_manager.set_step_status(Status.NOT_EXECUTED)
- return model.DictObjectStore()
+ return model.DictIdentifiableStore()
with file_to_be_checked:
state_manager.set_step_status(Status.SUCCESS)
@@ -148,11 +71,11 @@ def check_deserialization(file_path: str, state_manager: ComplianceToolStateMana
state_manager.add_step('Read file {} and check if it is deserializable'.format(file_info))
else:
state_manager.add_step('Read file and check if it is deserializable')
- obj_store = xml_deserialization.read_aas_xml_file(file_to_be_checked, failsafe=True)
+ identifiable_store = xml_deserialization.read_aas_xml_file(file_to_be_checked, failsafe=True)
state_manager.set_step_status_from_log()
- return obj_store
+ return identifiable_store
def check_aas_example(file_path: str, state_manager: ComplianceToolStateManager, **kwargs) -> None:
@@ -173,7 +96,7 @@ def check_aas_example(file_path: str, state_manager: ComplianceToolStateManager,
logger_example.propagate = False
logger_example.setLevel(logging.INFO)
- obj_store = check_deserialization(file_path, state_manager)
+ identifiable_store = check_deserialization(file_path, state_manager)
if state_manager.status in (Status.FAILED, Status.NOT_EXECUTED):
state_manager.add_step('Check if data is equal to example data')
@@ -183,7 +106,7 @@ def check_aas_example(file_path: str, state_manager: ComplianceToolStateManager,
checker = AASDataChecker(raise_immediately=False, **kwargs)
state_manager.add_step('Check if data is equal to example data')
- checker.check_object_store(obj_store, create_example())
+ checker.check_identifiable_store(identifiable_store, create_example())
state_manager.add_log_records_from_data_checker(checker)
@@ -207,9 +130,9 @@ def check_xml_files_equivalence(file_path_1: str, file_path_2: str, state_manage
logger.propagate = False
logger.setLevel(logging.INFO)
- obj_store_1 = check_deserialization(file_path_1, state_manager, 'first')
+ identifiable_store_1 = check_deserialization(file_path_1, state_manager, 'first')
- obj_store_2 = check_deserialization(file_path_2, state_manager, 'second')
+ identifiable_store_2 = check_deserialization(file_path_2, state_manager, 'second')
if state_manager.status is Status.FAILED:
state_manager.add_step('Check if data in files are equal')
@@ -219,7 +142,7 @@ def check_xml_files_equivalence(file_path_1: str, file_path_2: str, state_manage
checker = AASDataChecker(raise_immediately=False, **kwargs)
try:
state_manager.add_step('Check if data in files are equal')
- checker.check_object_store(obj_store_1, obj_store_2)
+ checker.check_identifiable_store(identifiable_store_1, identifiable_store_2)
except (KeyError, AssertionError) as error:
state_manager.set_step_status(Status.FAILED)
logger.error(error)
diff --git a/compliance_tool/aas_compliance_tool/schemas/aasJSONSchema.json b/compliance_tool/aas_compliance_tool/schemas/aasJSONSchema.json
deleted file mode 100644
index f48db4d17..000000000
--- a/compliance_tool/aas_compliance_tool/schemas/aasJSONSchema.json
+++ /dev/null
@@ -1,1528 +0,0 @@
-{
- "$schema": "https://json-schema.org/draft/2019-09/schema",
- "title": "AssetAdministrationShellEnvironment",
- "type": "object",
- "allOf": [
- {
- "$ref": "#/definitions/Environment"
- }
- ],
- "$id": "https://admin-shell.io/aas/3/0",
- "definitions": {
- "AasSubmodelElements": {
- "type": "string",
- "enum": [
- "AnnotatedRelationshipElement",
- "BasicEventElement",
- "Blob",
- "Capability",
- "DataElement",
- "Entity",
- "EventElement",
- "File",
- "MultiLanguageProperty",
- "Operation",
- "Property",
- "Range",
- "ReferenceElement",
- "RelationshipElement",
- "SubmodelElement",
- "SubmodelElementCollection",
- "SubmodelElementList"
- ]
- },
- "AbstractLangString": {
- "type": "object",
- "properties": {
- "language": {
- "type": "string",
- "pattern": "^(([a-zA-Z]{2,3}(-[a-zA-Z]{3}(-[a-zA-Z]{3}){2})?|[a-zA-Z]{4}|[a-zA-Z]{5,8})(-[a-zA-Z]{4})?(-([a-zA-Z]{2}|[0-9]{3}))?(-(([a-zA-Z0-9]){5,8}|[0-9]([a-zA-Z0-9]){3}))*(-[0-9A-WY-Za-wy-z](-([a-zA-Z0-9]){2,8})+)*(-[xX](-([a-zA-Z0-9]){1,8})+)?|[xX](-([a-zA-Z0-9]){1,8})+|((en-GB-oed|i-ami|i-bnn|i-default|i-enochian|i-hak|i-klingon|i-lux|i-mingo|i-navajo|i-pwn|i-tao|i-tay|i-tsu|sgn-BE-FR|sgn-BE-NL|sgn-CH-DE)|(art-lojban|cel-gaulish|no-bok|no-nyn|zh-guoyu|zh-hakka|zh-min|zh-min-nan|zh-xiang)))$"
- },
- "text": {
- "type": "string",
- "minLength": 1,
- "pattern": "^([\\t\\n\\r -\ud7ff\ue000-\ufffd]|\\ud800[\\udc00-\\udfff]|[\\ud801-\\udbfe][\\udc00-\\udfff]|\\udbff[\\udc00-\\udfff])*$"
- }
- },
- "required": [
- "language",
- "text"
- ]
- },
- "AdministrativeInformation": {
- "allOf": [
- {
- "$ref": "#/definitions/HasDataSpecification"
- },
- {
- "properties": {
- "version": {
- "type": "string",
- "allOf": [
- {
- "minLength": 1,
- "maxLength": 4
- },
- {
- "pattern": "^([\\t\\n\\r -\ud7ff\ue000-\ufffd]|\\ud800[\\udc00-\\udfff]|[\\ud801-\\udbfe][\\udc00-\\udfff]|\\udbff[\\udc00-\\udfff])*$"
- },
- {
- "pattern": "^(0|[1-9][0-9]*)$"
- }
- ]
- },
- "revision": {
- "type": "string",
- "allOf": [
- {
- "minLength": 1,
- "maxLength": 4
- },
- {
- "pattern": "^([\\t\\n\\r -\ud7ff\ue000-\ufffd]|\\ud800[\\udc00-\\udfff]|[\\ud801-\\udbfe][\\udc00-\\udfff]|\\udbff[\\udc00-\\udfff])*$"
- },
- {
- "pattern": "^(0|[1-9][0-9]*)$"
- }
- ]
- },
- "creator": {
- "$ref": "#/definitions/Reference"
- },
- "templateId": {
- "type": "string",
- "minLength": 1,
- "maxLength": 2000,
- "pattern": "^([\\t\\n\\r -\ud7ff\ue000-\ufffd]|\\ud800[\\udc00-\\udfff]|[\\ud801-\\udbfe][\\udc00-\\udfff]|\\udbff[\\udc00-\\udfff])*$"
- }
- }
- }
- ]
- },
- "AnnotatedRelationshipElement": {
- "allOf": [
- {
- "$ref": "#/definitions/RelationshipElement_abstract"
- },
- {
- "properties": {
- "annotations": {
- "type": "array",
- "items": {
- "$ref": "#/definitions/DataElement_choice"
- },
- "minItems": 1
- },
- "modelType": {
- "const": "AnnotatedRelationshipElement"
- }
- }
- }
- ]
- },
- "AssetAdministrationShell": {
- "allOf": [
- {
- "$ref": "#/definitions/Identifiable"
- },
- {
- "$ref": "#/definitions/HasDataSpecification"
- },
- {
- "properties": {
- "derivedFrom": {
- "$ref": "#/definitions/Reference"
- },
- "assetInformation": {
- "$ref": "#/definitions/AssetInformation"
- },
- "submodels": {
- "type": "array",
- "items": {
- "$ref": "#/definitions/Reference"
- },
- "minItems": 1
- },
- "modelType": {
- "const": "AssetAdministrationShell"
- }
- },
- "required": [
- "assetInformation"
- ]
- }
- ]
- },
- "AssetInformation": {
- "type": "object",
- "properties": {
- "assetKind": {
- "$ref": "#/definitions/AssetKind"
- },
- "globalAssetId": {
- "type": "string",
- "minLength": 1,
- "maxLength": 2000,
- "pattern": "^([\\t\\n\\r -\ud7ff\ue000-\ufffd]|\\ud800[\\udc00-\\udfff]|[\\ud801-\\udbfe][\\udc00-\\udfff]|\\udbff[\\udc00-\\udfff])*$"
- },
- "specificAssetIds": {
- "type": "array",
- "items": {
- "$ref": "#/definitions/SpecificAssetId"
- },
- "minItems": 1
- },
- "assetType": {
- "type": "string",
- "minLength": 1,
- "maxLength": 2000,
- "pattern": "^([\\t\\n\\r -\ud7ff\ue000-\ufffd]|\\ud800[\\udc00-\\udfff]|[\\ud801-\\udbfe][\\udc00-\\udfff]|\\udbff[\\udc00-\\udfff])*$"
- },
- "defaultThumbnail": {
- "$ref": "#/definitions/Resource"
- }
- },
- "required": [
- "assetKind"
- ]
- },
- "AssetKind": {
- "type": "string",
- "enum": [
- "Instance",
- "NotApplicable",
- "Type"
- ]
- },
- "BasicEventElement": {
- "allOf": [
- {
- "$ref": "#/definitions/EventElement"
- },
- {
- "properties": {
- "observed": {
- "$ref": "#/definitions/Reference"
- },
- "direction": {
- "$ref": "#/definitions/Direction"
- },
- "state": {
- "$ref": "#/definitions/StateOfEvent"
- },
- "messageTopic": {
- "type": "string",
- "minLength": 1,
- "maxLength": 255,
- "pattern": "^([\\t\\n\\r -\ud7ff\ue000-\ufffd]|\\ud800[\\udc00-\\udfff]|[\\ud801-\\udbfe][\\udc00-\\udfff]|\\udbff[\\udc00-\\udfff])*$"
- },
- "messageBroker": {
- "$ref": "#/definitions/Reference"
- },
- "lastUpdate": {
- "type": "string",
- "pattern": "^-?(([1-9][0-9][0-9][0-9]+)|(0[0-9][0-9][0-9]))-((0[1-9])|(1[0-2]))-((0[1-9])|([12][0-9])|(3[01]))T(((([01][0-9])|(2[0-3])):[0-5][0-9]:([0-5][0-9])(\\.[0-9]+)?)|24:00:00(\\.0+)?)(Z|\\+00:00|-00:00)$"
- },
- "minInterval": {
- "type": "string",
- "pattern": "^-?P((([0-9]+Y([0-9]+M)?([0-9]+D)?|([0-9]+M)([0-9]+D)?|([0-9]+D))(T(([0-9]+H)([0-9]+M)?([0-9]+(\\.[0-9]+)?S)?|([0-9]+M)([0-9]+(\\.[0-9]+)?S)?|([0-9]+(\\.[0-9]+)?S)))?)|(T(([0-9]+H)([0-9]+M)?([0-9]+(\\.[0-9]+)?S)?|([0-9]+M)([0-9]+(\\.[0-9]+)?S)?|([0-9]+(\\.[0-9]+)?S))))$"
- },
- "maxInterval": {
- "type": "string",
- "pattern": "^-?P((([0-9]+Y([0-9]+M)?([0-9]+D)?|([0-9]+M)([0-9]+D)?|([0-9]+D))(T(([0-9]+H)([0-9]+M)?([0-9]+(\\.[0-9]+)?S)?|([0-9]+M)([0-9]+(\\.[0-9]+)?S)?|([0-9]+(\\.[0-9]+)?S)))?)|(T(([0-9]+H)([0-9]+M)?([0-9]+(\\.[0-9]+)?S)?|([0-9]+M)([0-9]+(\\.[0-9]+)?S)?|([0-9]+(\\.[0-9]+)?S))))$"
- },
- "modelType": {
- "const": "BasicEventElement"
- }
- },
- "required": [
- "observed",
- "direction",
- "state"
- ]
- }
- ]
- },
- "Blob": {
- "allOf": [
- {
- "$ref": "#/definitions/DataElement"
- },
- {
- "properties": {
- "value": {
- "type": "string",
- "contentEncoding": "base64"
- },
- "contentType": {
- "type": "string",
- "allOf": [
- {
- "minLength": 1,
- "maxLength": 100
- },
- {
- "pattern": "^([\\t\\n\\r -\ud7ff\ue000-\ufffd]|\\ud800[\\udc00-\\udfff]|[\\ud801-\\udbfe][\\udc00-\\udfff]|\\udbff[\\udc00-\\udfff])*$"
- },
- {
- "pattern": "^([!#$%&'*+\\-.^_`|~0-9a-zA-Z])+/([!#$%&'*+\\-.^_`|~0-9a-zA-Z])+([ \\t]*;[ \\t]*([!#$%&'*+\\-.^_`|~0-9a-zA-Z])+=(([!#$%&'*+\\-.^_`|~0-9a-zA-Z])+|\"(([\\t !#-\\[\\]-~]|[\u0080-\u00ff])|\\\\([\\t !-~]|[\u0080-\u00ff]))*\"))*$"
- }
- ]
- },
- "modelType": {
- "const": "Blob"
- }
- },
- "required": [
- "contentType"
- ]
- }
- ]
- },
- "Capability": {
- "allOf": [
- {
- "$ref": "#/definitions/SubmodelElement"
- },
- {
- "properties": {
- "modelType": {
- "const": "Capability"
- }
- }
- }
- ]
- },
- "ConceptDescription": {
- "allOf": [
- {
- "$ref": "#/definitions/Identifiable"
- },
- {
- "$ref": "#/definitions/HasDataSpecification"
- },
- {
- "properties": {
- "isCaseOf": {
- "type": "array",
- "items": {
- "$ref": "#/definitions/Reference"
- },
- "minItems": 1
- },
- "modelType": {
- "const": "ConceptDescription"
- }
- }
- }
- ]
- },
- "DataElement": {
- "$ref": "#/definitions/SubmodelElement"
- },
- "DataElement_choice": {
- "oneOf": [
- {
- "$ref": "#/definitions/Blob"
- },
- {
- "$ref": "#/definitions/File"
- },
- {
- "$ref": "#/definitions/MultiLanguageProperty"
- },
- {
- "$ref": "#/definitions/Property"
- },
- {
- "$ref": "#/definitions/Range"
- },
- {
- "$ref": "#/definitions/ReferenceElement"
- }
- ]
- },
- "DataSpecificationContent": {
- "type": "object",
- "properties": {
- "modelType": {
- "$ref": "#/definitions/ModelType"
- }
- },
- "required": [
- "modelType"
- ]
- },
- "DataSpecificationContent_choice": {
- "oneOf": [
- {
- "$ref": "#/definitions/DataSpecificationIec61360"
- }
- ]
- },
- "DataSpecificationIec61360": {
- "allOf": [
- {
- "$ref": "#/definitions/DataSpecificationContent"
- },
- {
- "properties": {
- "preferredName": {
- "type": "array",
- "items": {
- "$ref": "#/definitions/LangStringPreferredNameTypeIec61360"
- },
- "minItems": 1
- },
- "shortName": {
- "type": "array",
- "items": {
- "$ref": "#/definitions/LangStringShortNameTypeIec61360"
- },
- "minItems": 1
- },
- "unit": {
- "type": "string",
- "minLength": 1,
- "pattern": "^([\\t\\n\\r -\ud7ff\ue000-\ufffd]|\\ud800[\\udc00-\\udfff]|[\\ud801-\\udbfe][\\udc00-\\udfff]|\\udbff[\\udc00-\\udfff])*$"
- },
- "unitId": {
- "$ref": "#/definitions/Reference"
- },
- "sourceOfDefinition": {
- "type": "string",
- "minLength": 1,
- "pattern": "^([\\t\\n\\r -\ud7ff\ue000-\ufffd]|\\ud800[\\udc00-\\udfff]|[\\ud801-\\udbfe][\\udc00-\\udfff]|\\udbff[\\udc00-\\udfff])*$"
- },
- "symbol": {
- "type": "string",
- "minLength": 1,
- "pattern": "^([\\t\\n\\r -\ud7ff\ue000-\ufffd]|\\ud800[\\udc00-\\udfff]|[\\ud801-\\udbfe][\\udc00-\\udfff]|\\udbff[\\udc00-\\udfff])*$"
- },
- "dataType": {
- "$ref": "#/definitions/DataTypeIec61360"
- },
- "definition": {
- "type": "array",
- "items": {
- "$ref": "#/definitions/LangStringDefinitionTypeIec61360"
- },
- "minItems": 1
- },
- "valueFormat": {
- "type": "string",
- "minLength": 1,
- "pattern": "^([\\t\\n\\r -\ud7ff\ue000-\ufffd]|\\ud800[\\udc00-\\udfff]|[\\ud801-\\udbfe][\\udc00-\\udfff]|\\udbff[\\udc00-\\udfff])*$"
- },
- "valueList": {
- "$ref": "#/definitions/ValueList"
- },
- "value": {
- "type": "string",
- "minLength": 1,
- "maxLength": 2000,
- "pattern": "^([\\t\\n\\r -\ud7ff\ue000-\ufffd]|\\ud800[\\udc00-\\udfff]|[\\ud801-\\udbfe][\\udc00-\\udfff]|\\udbff[\\udc00-\\udfff])*$"
- },
- "levelType": {
- "$ref": "#/definitions/LevelType"
- },
- "modelType": {
- "const": "DataSpecificationIec61360"
- }
- },
- "required": [
- "preferredName"
- ]
- }
- ]
- },
- "DataTypeDefXsd": {
- "type": "string",
- "enum": [
- "xs:anyURI",
- "xs:base64Binary",
- "xs:boolean",
- "xs:byte",
- "xs:date",
- "xs:dateTime",
- "xs:decimal",
- "xs:double",
- "xs:duration",
- "xs:float",
- "xs:gDay",
- "xs:gMonth",
- "xs:gMonthDay",
- "xs:gYear",
- "xs:gYearMonth",
- "xs:hexBinary",
- "xs:int",
- "xs:integer",
- "xs:long",
- "xs:negativeInteger",
- "xs:nonNegativeInteger",
- "xs:nonPositiveInteger",
- "xs:positiveInteger",
- "xs:short",
- "xs:string",
- "xs:time",
- "xs:unsignedByte",
- "xs:unsignedInt",
- "xs:unsignedLong",
- "xs:unsignedShort"
- ]
- },
- "DataTypeIec61360": {
- "type": "string",
- "enum": [
- "BLOB",
- "BOOLEAN",
- "DATE",
- "FILE",
- "HTML",
- "INTEGER_COUNT",
- "INTEGER_CURRENCY",
- "INTEGER_MEASURE",
- "IRDI",
- "IRI",
- "RATIONAL",
- "RATIONAL_MEASURE",
- "REAL_COUNT",
- "REAL_CURRENCY",
- "REAL_MEASURE",
- "STRING",
- "STRING_TRANSLATABLE",
- "TIME",
- "TIMESTAMP"
- ]
- },
- "Direction": {
- "type": "string",
- "enum": [
- "input",
- "output"
- ]
- },
- "EmbeddedDataSpecification": {
- "type": "object",
- "properties": {
- "dataSpecification": {
- "$ref": "#/definitions/Reference"
- },
- "dataSpecificationContent": {
- "$ref": "#/definitions/DataSpecificationContent_choice"
- }
- },
- "required": [
- "dataSpecification",
- "dataSpecificationContent"
- ]
- },
- "Entity": {
- "allOf": [
- {
- "$ref": "#/definitions/SubmodelElement"
- },
- {
- "properties": {
- "statements": {
- "type": "array",
- "items": {
- "$ref": "#/definitions/SubmodelElement_choice"
- },
- "minItems": 1
- },
- "entityType": {
- "$ref": "#/definitions/EntityType"
- },
- "globalAssetId": {
- "type": "string",
- "minLength": 1,
- "maxLength": 2000,
- "pattern": "^([\\t\\n\\r -\ud7ff\ue000-\ufffd]|\\ud800[\\udc00-\\udfff]|[\\ud801-\\udbfe][\\udc00-\\udfff]|\\udbff[\\udc00-\\udfff])*$"
- },
- "specificAssetIds": {
- "type": "array",
- "items": {
- "$ref": "#/definitions/SpecificAssetId"
- },
- "minItems": 1
- },
- "modelType": {
- "const": "Entity"
- }
- },
- "required": [
- "entityType"
- ]
- }
- ]
- },
- "EntityType": {
- "type": "string",
- "enum": [
- "CoManagedEntity",
- "SelfManagedEntity"
- ]
- },
- "Environment": {
- "type": "object",
- "properties": {
- "assetAdministrationShells": {
- "type": "array",
- "items": {
- "$ref": "#/definitions/AssetAdministrationShell"
- },
- "minItems": 1
- },
- "submodels": {
- "type": "array",
- "items": {
- "$ref": "#/definitions/Submodel"
- },
- "minItems": 1
- },
- "conceptDescriptions": {
- "type": "array",
- "items": {
- "$ref": "#/definitions/ConceptDescription"
- },
- "minItems": 1
- }
- }
- },
- "EventElement": {
- "$ref": "#/definitions/SubmodelElement"
- },
- "EventPayload": {
- "type": "object",
- "properties": {
- "source": {
- "$ref": "#/definitions/Reference"
- },
- "sourceSemanticId": {
- "$ref": "#/definitions/Reference"
- },
- "observableReference": {
- "$ref": "#/definitions/Reference"
- },
- "observableSemanticId": {
- "$ref": "#/definitions/Reference"
- },
- "topic": {
- "type": "string",
- "minLength": 1,
- "maxLength": 255,
- "pattern": "^([\\t\\n\\r -\ud7ff\ue000-\ufffd]|\\ud800[\\udc00-\\udfff]|[\\ud801-\\udbfe][\\udc00-\\udfff]|\\udbff[\\udc00-\\udfff])*$"
- },
- "subjectId": {
- "$ref": "#/definitions/Reference"
- },
- "timeStamp": {
- "type": "string",
- "pattern": "^-?(([1-9][0-9][0-9][0-9]+)|(0[0-9][0-9][0-9]))-((0[1-9])|(1[0-2]))-((0[1-9])|([12][0-9])|(3[01]))T(((([01][0-9])|(2[0-3])):[0-5][0-9]:([0-5][0-9])(\\.[0-9]+)?)|24:00:00(\\.0+)?)(Z|\\+00:00|-00:00)$"
- },
- "payload": {
- "type": "string",
- "contentEncoding": "base64"
- }
- },
- "required": [
- "source",
- "observableReference",
- "timeStamp"
- ]
- },
- "Extension": {
- "allOf": [
- {
- "$ref": "#/definitions/HasSemantics"
- },
- {
- "properties": {
- "name": {
- "type": "string",
- "minLength": 1,
- "maxLength": 128,
- "pattern": "^([\\t\\n\\r -\ud7ff\ue000-\ufffd]|\\ud800[\\udc00-\\udfff]|[\\ud801-\\udbfe][\\udc00-\\udfff]|\\udbff[\\udc00-\\udfff])*$"
- },
- "valueType": {
- "$ref": "#/definitions/DataTypeDefXsd"
- },
- "value": {
- "type": "string"
- },
- "refersTo": {
- "type": "array",
- "items": {
- "$ref": "#/definitions/Reference"
- },
- "minItems": 1
- }
- },
- "required": [
- "name"
- ]
- }
- ]
- },
- "File": {
- "allOf": [
- {
- "$ref": "#/definitions/DataElement"
- },
- {
- "properties": {
- "value": {
- "type": "string"
- },
- "contentType": {
- "type": "string",
- "allOf": [
- {
- "minLength": 1,
- "maxLength": 100
- },
- {
- "pattern": "^([\\t\\n\\r -\ud7ff\ue000-\ufffd]|\\ud800[\\udc00-\\udfff]|[\\ud801-\\udbfe][\\udc00-\\udfff]|\\udbff[\\udc00-\\udfff])*$"
- },
- {
- "pattern": "^([!#$%&'*+\\-.^_`|~0-9a-zA-Z])+/([!#$%&'*+\\-.^_`|~0-9a-zA-Z])+([ \\t]*;[ \\t]*([!#$%&'*+\\-.^_`|~0-9a-zA-Z])+=(([!#$%&'*+\\-.^_`|~0-9a-zA-Z])+|\"(([\\t !#-\\[\\]-~]|[\u0080-\u00ff])|\\\\([\\t !-~]|[\u0080-\u00ff]))*\"))*$"
- }
- ]
- },
- "modelType": {
- "const": "File"
- }
- },
- "required": [
- "contentType"
- ]
- }
- ]
- },
- "HasDataSpecification": {
- "type": "object",
- "properties": {
- "embeddedDataSpecifications": {
- "type": "array",
- "items": {
- "$ref": "#/definitions/EmbeddedDataSpecification"
- },
- "minItems": 1
- }
- }
- },
- "HasExtensions": {
- "type": "object",
- "properties": {
- "extensions": {
- "type": "array",
- "items": {
- "$ref": "#/definitions/Extension"
- },
- "minItems": 1
- }
- }
- },
- "HasKind": {
- "type": "object",
- "properties": {
- "kind": {
- "$ref": "#/definitions/ModellingKind"
- }
- }
- },
- "HasSemantics": {
- "type": "object",
- "properties": {
- "semanticId": {
- "$ref": "#/definitions/Reference"
- },
- "supplementalSemanticIds": {
- "type": "array",
- "items": {
- "$ref": "#/definitions/Reference"
- },
- "minItems": 1
- }
- }
- },
- "Identifiable": {
- "allOf": [
- {
- "$ref": "#/definitions/Referable"
- },
- {
- "properties": {
- "administration": {
- "$ref": "#/definitions/AdministrativeInformation"
- },
- "id": {
- "type": "string",
- "minLength": 1,
- "maxLength": 2000,
- "pattern": "^([\\t\\n\\r -\ud7ff\ue000-\ufffd]|\\ud800[\\udc00-\\udfff]|[\\ud801-\\udbfe][\\udc00-\\udfff]|\\udbff[\\udc00-\\udfff])*$"
- }
- },
- "required": [
- "id"
- ]
- }
- ]
- },
- "Key": {
- "type": "object",
- "properties": {
- "type": {
- "$ref": "#/definitions/KeyTypes"
- },
- "value": {
- "type": "string",
- "minLength": 1,
- "maxLength": 2000,
- "pattern": "^([\\t\\n\\r -\ud7ff\ue000-\ufffd]|\\ud800[\\udc00-\\udfff]|[\\ud801-\\udbfe][\\udc00-\\udfff]|\\udbff[\\udc00-\\udfff])*$"
- }
- },
- "required": [
- "type",
- "value"
- ]
- },
- "KeyTypes": {
- "type": "string",
- "enum": [
- "AnnotatedRelationshipElement",
- "AssetAdministrationShell",
- "BasicEventElement",
- "Blob",
- "Capability",
- "ConceptDescription",
- "DataElement",
- "Entity",
- "EventElement",
- "File",
- "FragmentReference",
- "GlobalReference",
- "Identifiable",
- "MultiLanguageProperty",
- "Operation",
- "Property",
- "Range",
- "Referable",
- "ReferenceElement",
- "RelationshipElement",
- "Submodel",
- "SubmodelElement",
- "SubmodelElementCollection",
- "SubmodelElementList"
- ]
- },
- "LangStringDefinitionTypeIec61360": {
- "allOf": [
- {
- "$ref": "#/definitions/AbstractLangString"
- },
- {
- "properties": {
- "text": {
- "maxLength": 1023
- }
- }
- }
- ]
- },
- "LangStringNameType": {
- "allOf": [
- {
- "$ref": "#/definitions/AbstractLangString"
- },
- {
- "properties": {
- "text": {
- "maxLength": 128
- }
- }
- }
- ]
- },
- "LangStringPreferredNameTypeIec61360": {
- "allOf": [
- {
- "$ref": "#/definitions/AbstractLangString"
- },
- {
- "properties": {
- "text": {
- "maxLength": 255
- }
- }
- }
- ]
- },
- "LangStringShortNameTypeIec61360": {
- "allOf": [
- {
- "$ref": "#/definitions/AbstractLangString"
- },
- {
- "properties": {
- "text": {
- "maxLength": 18
- }
- }
- }
- ]
- },
- "LangStringTextType": {
- "allOf": [
- {
- "$ref": "#/definitions/AbstractLangString"
- },
- {
- "properties": {
- "text": {
- "maxLength": 1023
- }
- }
- }
- ]
- },
- "LevelType": {
- "type": "object",
- "properties": {
- "min": {
- "type": "boolean"
- },
- "nom": {
- "type": "boolean"
- },
- "typ": {
- "type": "boolean"
- },
- "max": {
- "type": "boolean"
- }
- },
- "required": [
- "min",
- "nom",
- "typ",
- "max"
- ]
- },
- "ModelType": {
- "type": "string",
- "enum": [
- "AnnotatedRelationshipElement",
- "AssetAdministrationShell",
- "BasicEventElement",
- "Blob",
- "Capability",
- "ConceptDescription",
- "DataSpecificationIec61360",
- "Entity",
- "File",
- "MultiLanguageProperty",
- "Operation",
- "Property",
- "Range",
- "ReferenceElement",
- "RelationshipElement",
- "Submodel",
- "SubmodelElementCollection",
- "SubmodelElementList"
- ]
- },
- "ModellingKind": {
- "type": "string",
- "enum": [
- "Instance",
- "Template"
- ]
- },
- "MultiLanguageProperty": {
- "allOf": [
- {
- "$ref": "#/definitions/DataElement"
- },
- {
- "properties": {
- "value": {
- "type": "array",
- "items": {
- "$ref": "#/definitions/LangStringTextType"
- },
- "minItems": 1
- },
- "valueId": {
- "$ref": "#/definitions/Reference"
- },
- "modelType": {
- "const": "MultiLanguageProperty"
- }
- }
- }
- ]
- },
- "Operation": {
- "allOf": [
- {
- "$ref": "#/definitions/SubmodelElement"
- },
- {
- "properties": {
- "inputVariables": {
- "type": "array",
- "items": {
- "$ref": "#/definitions/OperationVariable"
- },
- "minItems": 1
- },
- "outputVariables": {
- "type": "array",
- "items": {
- "$ref": "#/definitions/OperationVariable"
- },
- "minItems": 1
- },
- "inoutputVariables": {
- "type": "array",
- "items": {
- "$ref": "#/definitions/OperationVariable"
- },
- "minItems": 1
- },
- "modelType": {
- "const": "Operation"
- }
- }
- }
- ]
- },
- "OperationVariable": {
- "type": "object",
- "properties": {
- "value": {
- "$ref": "#/definitions/SubmodelElement_choice"
- }
- },
- "required": [
- "value"
- ]
- },
- "Property": {
- "allOf": [
- {
- "$ref": "#/definitions/DataElement"
- },
- {
- "properties": {
- "valueType": {
- "$ref": "#/definitions/DataTypeDefXsd"
- },
- "value": {
- "type": "string"
- },
- "valueId": {
- "$ref": "#/definitions/Reference"
- },
- "modelType": {
- "const": "Property"
- }
- },
- "required": [
- "valueType"
- ]
- }
- ]
- },
- "Qualifiable": {
- "type": "object",
- "properties": {
- "qualifiers": {
- "type": "array",
- "items": {
- "$ref": "#/definitions/Qualifier"
- },
- "minItems": 1
- },
- "modelType": {
- "$ref": "#/definitions/ModelType"
- }
- },
- "required": [
- "modelType"
- ]
- },
- "Qualifier": {
- "allOf": [
- {
- "$ref": "#/definitions/HasSemantics"
- },
- {
- "properties": {
- "kind": {
- "$ref": "#/definitions/QualifierKind"
- },
- "type": {
- "type": "string",
- "minLength": 1,
- "maxLength": 128,
- "pattern": "^([\\t\\n\\r -\ud7ff\ue000-\ufffd]|\\ud800[\\udc00-\\udfff]|[\\ud801-\\udbfe][\\udc00-\\udfff]|\\udbff[\\udc00-\\udfff])*$"
- },
- "valueType": {
- "$ref": "#/definitions/DataTypeDefXsd"
- },
- "value": {
- "type": "string"
- },
- "valueId": {
- "$ref": "#/definitions/Reference"
- }
- },
- "required": [
- "type",
- "valueType"
- ]
- }
- ]
- },
- "QualifierKind": {
- "type": "string",
- "enum": [
- "ConceptQualifier",
- "TemplateQualifier",
- "ValueQualifier"
- ]
- },
- "Range": {
- "allOf": [
- {
- "$ref": "#/definitions/DataElement"
- },
- {
- "properties": {
- "valueType": {
- "$ref": "#/definitions/DataTypeDefXsd"
- },
- "min": {
- "type": "string"
- },
- "max": {
- "type": "string"
- },
- "modelType": {
- "const": "Range"
- }
- },
- "required": [
- "valueType"
- ]
- }
- ]
- },
- "Referable": {
- "allOf": [
- {
- "$ref": "#/definitions/HasExtensions"
- },
- {
- "properties": {
- "category": {
- "type": "string",
- "minLength": 1,
- "maxLength": 128,
- "pattern": "^([\\t\\n\\r -\ud7ff\ue000-\ufffd]|\\ud800[\\udc00-\\udfff]|[\\ud801-\\udbfe][\\udc00-\\udfff]|\\udbff[\\udc00-\\udfff])*$"
- },
- "idShort": {
- "type": "string",
- "allOf": [
- {
- "minLength": 1,
- "maxLength": 128
- },
- {
- "pattern": "^([\\t\\n\\r -\ud7ff\ue000-\ufffd]|\\ud800[\\udc00-\\udfff]|[\\ud801-\\udbfe][\\udc00-\\udfff]|\\udbff[\\udc00-\\udfff])*$"
- },
- {
- "pattern": "^[a-zA-Z][a-zA-Z0-9_]*$"
- }
- ]
- },
- "displayName": {
- "type": "array",
- "items": {
- "$ref": "#/definitions/LangStringNameType"
- },
- "minItems": 1
- },
- "description": {
- "type": "array",
- "items": {
- "$ref": "#/definitions/LangStringTextType"
- },
- "minItems": 1
- },
- "modelType": {
- "$ref": "#/definitions/ModelType"
- }
- },
- "required": [
- "modelType"
- ]
- }
- ]
- },
- "Reference": {
- "type": "object",
- "properties": {
- "type": {
- "$ref": "#/definitions/ReferenceTypes"
- },
- "referredSemanticId": {
- "$ref": "#/definitions/Reference"
- },
- "keys": {
- "type": "array",
- "items": {
- "$ref": "#/definitions/Key"
- },
- "minItems": 1
- }
- },
- "required": [
- "type",
- "keys"
- ]
- },
- "ReferenceElement": {
- "allOf": [
- {
- "$ref": "#/definitions/DataElement"
- },
- {
- "properties": {
- "value": {
- "$ref": "#/definitions/Reference"
- },
- "modelType": {
- "const": "ReferenceElement"
- }
- }
- }
- ]
- },
- "ReferenceTypes": {
- "type": "string",
- "enum": [
- "ExternalReference",
- "ModelReference"
- ]
- },
- "RelationshipElement": {
- "allOf": [
- {
- "$ref": "#/definitions/RelationshipElement_abstract"
- },
- {
- "properties": {
- "modelType": {
- "const": "RelationshipElement"
- }
- }
- }
- ]
- },
- "RelationshipElement_abstract": {
- "allOf": [
- {
- "$ref": "#/definitions/SubmodelElement"
- },
- {
- "properties": {
- "first": {
- "$ref": "#/definitions/Reference"
- },
- "second": {
- "$ref": "#/definitions/Reference"
- }
- },
- "required": [
- "first",
- "second"
- ]
- }
- ]
- },
- "RelationshipElement_choice": {
- "oneOf": [
- {
- "$ref": "#/definitions/RelationshipElement"
- },
- {
- "$ref": "#/definitions/AnnotatedRelationshipElement"
- }
- ]
- },
- "Resource": {
- "type": "object",
- "properties": {
- "path": {
- "type": "string",
- "allOf": [
- {
- "minLength": 1,
- "maxLength": 2000
- },
- {
- "pattern": "^([\\t\\n\\r -\ud7ff\ue000-\ufffd]|\\ud800[\\udc00-\\udfff]|[\\ud801-\\udbfe][\\udc00-\\udfff]|\\udbff[\\udc00-\\udfff])*$"
- },
- {
- "pattern": "^file:(//((localhost|(\\[((([0-9A-Fa-f]{1,4}:){6}([0-9A-Fa-f]{1,4}:[0-9A-Fa-f]{1,4}|([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\\.([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\\.([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\\.([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5]))|::([0-9A-Fa-f]{1,4}:){5}([0-9A-Fa-f]{1,4}:[0-9A-Fa-f]{1,4}|([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\\.([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\\.([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\\.([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5]))|([0-9A-Fa-f]{1,4})?::([0-9A-Fa-f]{1,4}:){4}([0-9A-Fa-f]{1,4}:[0-9A-Fa-f]{1,4}|([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\\.([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\\.([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\\.([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5]))|(([0-9A-Fa-f]{1,4}:)?[0-9A-Fa-f]{1,4})?::([0-9A-Fa-f]{1,4}:){3}([0-9A-Fa-f]{1,4}:[0-9A-Fa-f]{1,4}|([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\\.([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\\.([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\\.([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5]))|(([0-9A-Fa-f]{1,4}:){2}[0-9A-Fa-f]{1,4})?::([0-9A-Fa-f]{1,4}:){2}([0-9A-Fa-f]{1,4}:[0-9A-Fa-f]{1,4}|([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\\.([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\\.([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\\.([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5]))|(([0-9A-Fa-f]{1,4}:){3}[0-9A-Fa-f]{1,4})?::[0-9A-Fa-f]{1,4}:([0-9A-Fa-f]{1,4}:[0-9A-Fa-f]{1,4}|([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\\.([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\\.([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\\.([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5]))|(([0-9A-Fa-f]{1,4}:){4}[0-9A-Fa-f]{1,4})?::([0-9A-Fa-f]{1,4}:[0-9A-Fa-f]{1,4}|([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\\.([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\\.([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\\.([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5]))|(([0-9A-Fa-f]{1,4}:){5}[0-9A-Fa-f]{1,4})?::[0-9A-Fa-f]{1,4}|(([0-9A-Fa-f]{1,4}:){6}[0-9A-Fa-f]{1,4})?::)|[vV][0-9A-Fa-f]+\\.([a-zA-Z0-9\\-._~]|[!$&'()*+,;=]|:)+)\\]|([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\\.([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\\.([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])\\.([0-9]|[1-9][0-9]|1[0-9]{2}|2[0-4][0-9]|25[0-5])|([a-zA-Z0-9\\-._~]|%[0-9A-Fa-f][0-9A-Fa-f]|[!$&'()*+,;=])*)))?/((([a-zA-Z0-9\\-._~]|%[0-9A-Fa-f][0-9A-Fa-f]|[!$&'()*+,;=]|[:@]))+(/(([a-zA-Z0-9\\-._~]|%[0-9A-Fa-f][0-9A-Fa-f]|[!$&'()*+,;=]|[:@]))*)*)?|/((([a-zA-Z0-9\\-._~]|%[0-9A-Fa-f][0-9A-Fa-f]|[!$&'()*+,;=]|[:@]))+(/(([a-zA-Z0-9\\-._~]|%[0-9A-Fa-f][0-9A-Fa-f]|[!$&'()*+,;=]|[:@]))*)*)?)$"
- }
- ]
- },
- "contentType": {
- "type": "string",
- "allOf": [
- {
- "minLength": 1,
- "maxLength": 100
- },
- {
- "pattern": "^([\\t\\n\\r -\ud7ff\ue000-\ufffd]|\\ud800[\\udc00-\\udfff]|[\\ud801-\\udbfe][\\udc00-\\udfff]|\\udbff[\\udc00-\\udfff])*$"
- },
- {
- "pattern": "^([!#$%&'*+\\-.^_`|~0-9a-zA-Z])+/([!#$%&'*+\\-.^_`|~0-9a-zA-Z])+([ \\t]*;[ \\t]*([!#$%&'*+\\-.^_`|~0-9a-zA-Z])+=(([!#$%&'*+\\-.^_`|~0-9a-zA-Z])+|\"(([\\t !#-\\[\\]-~]|[\u0080-\u00ff])|\\\\([\\t !-~]|[\u0080-\u00ff]))*\"))*$"
- }
- ]
- }
- },
- "required": [
- "path"
- ]
- },
- "SpecificAssetId": {
- "allOf": [
- {
- "$ref": "#/definitions/HasSemantics"
- },
- {
- "properties": {
- "name": {
- "type": "string",
- "minLength": 1,
- "maxLength": 64,
- "pattern": "^([\\t\\n\\r -\ud7ff\ue000-\ufffd]|\\ud800[\\udc00-\\udfff]|[\\ud801-\\udbfe][\\udc00-\\udfff]|\\udbff[\\udc00-\\udfff])*$"
- },
- "value": {
- "type": "string",
- "minLength": 1,
- "maxLength": 2000,
- "pattern": "^([\\t\\n\\r -\ud7ff\ue000-\ufffd]|\\ud800[\\udc00-\\udfff]|[\\ud801-\\udbfe][\\udc00-\\udfff]|\\udbff[\\udc00-\\udfff])*$"
- },
- "externalSubjectId": {
- "$ref": "#/definitions/Reference"
- }
- },
- "required": [
- "name",
- "value"
- ]
- }
- ]
- },
- "StateOfEvent": {
- "type": "string",
- "enum": [
- "off",
- "on"
- ]
- },
- "Submodel": {
- "allOf": [
- {
- "$ref": "#/definitions/Identifiable"
- },
- {
- "$ref": "#/definitions/HasKind"
- },
- {
- "$ref": "#/definitions/HasSemantics"
- },
- {
- "$ref": "#/definitions/Qualifiable"
- },
- {
- "$ref": "#/definitions/HasDataSpecification"
- },
- {
- "properties": {
- "submodelElements": {
- "type": "array",
- "items": {
- "$ref": "#/definitions/SubmodelElement_choice"
- },
- "minItems": 1
- },
- "modelType": {
- "const": "Submodel"
- }
- }
- }
- ]
- },
- "SubmodelElement": {
- "allOf": [
- {
- "$ref": "#/definitions/Referable"
- },
- {
- "$ref": "#/definitions/HasSemantics"
- },
- {
- "$ref": "#/definitions/Qualifiable"
- },
- {
- "$ref": "#/definitions/HasDataSpecification"
- }
- ]
- },
- "SubmodelElementCollection": {
- "allOf": [
- {
- "$ref": "#/definitions/SubmodelElement"
- },
- {
- "properties": {
- "value": {
- "type": "array",
- "items": {
- "$ref": "#/definitions/SubmodelElement_choice"
- },
- "minItems": 1
- },
- "modelType": {
- "const": "SubmodelElementCollection"
- }
- }
- }
- ]
- },
- "SubmodelElementList": {
- "allOf": [
- {
- "$ref": "#/definitions/SubmodelElement"
- },
- {
- "properties": {
- "orderRelevant": {
- "type": "boolean"
- },
- "semanticIdListElement": {
- "$ref": "#/definitions/Reference"
- },
- "typeValueListElement": {
- "$ref": "#/definitions/AasSubmodelElements"
- },
- "valueTypeListElement": {
- "$ref": "#/definitions/DataTypeDefXsd"
- },
- "value": {
- "type": "array",
- "items": {
- "$ref": "#/definitions/SubmodelElement_choice"
- },
- "minItems": 1
- },
- "modelType": {
- "const": "SubmodelElementList"
- }
- },
- "required": [
- "typeValueListElement"
- ]
- }
- ]
- },
- "SubmodelElement_choice": {
- "oneOf": [
- {
- "$ref": "#/definitions/RelationshipElement"
- },
- {
- "$ref": "#/definitions/AnnotatedRelationshipElement"
- },
- {
- "$ref": "#/definitions/BasicEventElement"
- },
- {
- "$ref": "#/definitions/Blob"
- },
- {
- "$ref": "#/definitions/Capability"
- },
- {
- "$ref": "#/definitions/Entity"
- },
- {
- "$ref": "#/definitions/File"
- },
- {
- "$ref": "#/definitions/MultiLanguageProperty"
- },
- {
- "$ref": "#/definitions/Operation"
- },
- {
- "$ref": "#/definitions/Property"
- },
- {
- "$ref": "#/definitions/Range"
- },
- {
- "$ref": "#/definitions/ReferenceElement"
- },
- {
- "$ref": "#/definitions/SubmodelElementCollection"
- },
- {
- "$ref": "#/definitions/SubmodelElementList"
- }
- ]
- },
- "ValueList": {
- "type": "object",
- "properties": {
- "valueReferencePairs": {
- "type": "array",
- "items": {
- "$ref": "#/definitions/ValueReferencePair"
- },
- "minItems": 1
- }
- },
- "required": [
- "valueReferencePairs"
- ]
- },
- "ValueReferencePair": {
- "type": "object",
- "properties": {
- "value": {
- "type": "string",
- "minLength": 1,
- "maxLength": 2000,
- "pattern": "^([\\t\\n\\r -\ud7ff\ue000-\ufffd]|\\ud800[\\udc00-\\udfff]|[\\ud801-\\udbfe][\\udc00-\\udfff]|\\udbff[\\udc00-\\udfff])*$"
- },
- "valueId": {
- "$ref": "#/definitions/Reference"
- }
- },
- "required": [
- "value",
- "valueId"
- ]
- }
- }
-}
\ No newline at end of file
diff --git a/compliance_tool/aas_compliance_tool/schemas/aasXMLSchema.xsd b/compliance_tool/aas_compliance_tool/schemas/aasXMLSchema.xsd
deleted file mode 100644
index 25d7a52b9..000000000
--- a/compliance_tool/aas_compliance_tool/schemas/aasXMLSchema.xsd
+++ /dev/null
@@ -1,1344 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/compliance_tool/aas_compliance_tool/state_manager.py b/compliance_tool/aas_compliance_tool/state_manager.py
index 33153038f..1efe51991 100644
--- a/compliance_tool/aas_compliance_tool/state_manager.py
+++ b/compliance_tool/aas_compliance_tool/state_manager.py
@@ -1,4 +1,4 @@
-# Copyright (c) 2025 the Eclipse BaSyx Authors
+# Copyright (c) 2026 the Eclipse BaSyx Authors
#
# This program and the accompanying materials are made available under the terms of the MIT License, available in
# the LICENSE file of this project.
@@ -26,7 +26,7 @@ class Status(enum.IntEnum):
:cvar NOT_EXECUTED:
"""
SUCCESS = 0
- SUCCESS_WITH_WARNINGS = 1
+ SUCCESS_WITH_WARNINGS = 1 # never used
FAILED = 2
NOT_EXECUTED = 3
diff --git a/compliance_tool/pyproject.toml b/compliance_tool/pyproject.toml
index e235bdc9b..64f8adf8b 100644
--- a/compliance_tool/pyproject.toml
+++ b/compliance_tool/pyproject.toml
@@ -20,7 +20,7 @@ root = ".." # Defines the path to the root of the repository
version_file = "aas_compliance_tool/version.py"
[project]
-name = "aas_compliance_tool"
+name = "basyx-compliance-tool"
dynamic = ["version"]
description = "AAS compliance checker based on the Eclipse BaSyx Python SDK"
authors = [
@@ -52,6 +52,9 @@ dev = [
[project.urls]
"Homepage" = "https://github.com/eclipse-basyx/basyx-python-sdk"
+[project.scripts]
+aas-compliance-check = "aas_compliance_tool.cli:main"
+
[tool.setuptools]
packages = { find = { include = ["aas_compliance_tool*"], exclude = ["test*"] } }
diff --git a/compliance_tool/test/__init__.py b/compliance_tool/test/__init__.py
index a0c327cb0..e69de29bb 100644
--- a/compliance_tool/test/__init__.py
+++ b/compliance_tool/test/__init__.py
@@ -1,28 +0,0 @@
-import os
-import zipfile
-
-AASX_FILES = ("test_demo_full_example_json_aasx",
- "test_demo_full_example_xml_aasx",
- "test_demo_full_example_xml_wrong_attribute_aasx",
- "test_empty_aasx")
-
-
-def _zip_directory(directory_path, zip_file_path):
- """Zip a directory recursively."""
- with zipfile.ZipFile(zip_file_path, 'w', zipfile.ZIP_DEFLATED) as zipf:
- for root, _, files in os.walk(directory_path):
- for file in files:
- file_path = os.path.join(root, file)
- arcname = os.path.relpath(file_path, directory_path)
- zipf.write(file_path, arcname=arcname)
-
-
-def generate_aasx_files():
- """Zip dirs and create test AASX files."""
- script_dir = os.path.dirname(__file__)
- for i in AASX_FILES:
- _zip_directory(os.path.join(script_dir, "files", i),
- os.path.join(script_dir, "files", i.rstrip("_aasx") + ".aasx"))
-
-
-generate_aasx_files()
diff --git a/compliance_tool/test/_test_helper.py b/compliance_tool/test/_test_helper.py
new file mode 100644
index 000000000..bfcac3fd4
--- /dev/null
+++ b/compliance_tool/test/_test_helper.py
@@ -0,0 +1,57 @@
+import io
+from typing import Literal, Type, Optional
+import datetime
+import logging
+
+import pyecma376_2
+
+from basyx.aas.examples.data import create_example_aas_binding, TEST_PDF_FILE
+
+
+def create_example_aas_core_properties() -> pyecma376_2.OPCCoreProperties:
+ """Create core properties similar to the example AASX file."""
+
+ cp = pyecma376_2.OPCCoreProperties()
+ cp.created = datetime.datetime(2020, 1, 1, 0, 0, 0)
+ cp.creator = "Eclipse BaSyx Python Testing Framework"
+ cp.description = "Test_Description"
+ cp.lastModifiedBy = "Eclipse BaSyx Python Testing Framework Compliance Tool"
+ cp.modified = datetime.datetime(2020, 1, 1, 0, 0, 1)
+ cp.revision = "1.0"
+ cp.version = "2.0.1"
+ cp.title = "Test Title"
+ return cp
+
+
+def create_read_into_mock(file: Literal['TestFile', 'TestFileWrong', None]):
+ """"Creates side effect function for the AASXReader.read_into mock"""
+
+ def fill_stores(store, file_store, **kwargs) -> None:
+ for item in create_example_aas_binding():
+ store.add(item)
+
+ if file == 'TestFile':
+ with open(TEST_PDF_FILE, 'rb') as f:
+ file_store.add_file("/TestFile.pdf", f, "application/pdf")
+ elif file == 'TestFileWrong':
+ file_store.add_file("/TestFile.pdf", io.BytesIO(b"dummy"), "application/pdf")
+ return fill_stores
+
+
+def create_mock_effect(
+ module: str,
+ level: Literal['error', 'warning', 'info', 'debug'],
+ error_cls: Type[Exception] = ValueError,
+ error_msg: Optional[str] = None
+):
+ """Create mock function, that raises or logs error (based on `failsafe` argument)"""
+
+ error_msg = error_msg or f"Test {level}!"
+
+ def mock_error(*args, **kwargs):
+ if kwargs.get('failsafe', True):
+ getattr(logging.getLogger(module), level)(error_msg)
+ else:
+ raise error_cls(error_msg)
+
+ return mock_error
diff --git a/compliance_tool/test/files/test_demo_full_example.json b/compliance_tool/test/files/test_demo_full_example.json
deleted file mode 100644
index 68e154f73..000000000
--- a/compliance_tool/test/files/test_demo_full_example.json
+++ /dev/null
@@ -1,3210 +0,0 @@
-{
- "assetAdministrationShells": [
- {
- "idShort": "TestAssetAdministrationShell",
- "description": [
- {
- "language": "en-US",
- "text": "An Example Asset Administration Shell for the test application"
- },
- {
- "language": "de",
- "text": "Ein Beispiel-Verwaltungsschale f\u00fcr eine Test-Anwendung"
- }
- ],
- "modelType": "AssetAdministrationShell",
- "id": "https://acplt.org/Test_AssetAdministrationShell",
- "administration": {
- "version": "9",
- "revision": "0",
- "creator": {
- "type": "ExternalReference",
- "keys": [
- {
- "type": "GlobalReference",
- "value": "http://acplt.org/AdministrativeInformation/Test_AssetAdministrationShell"
- }
- ]
- },
- "templateId": "http://acplt.org/AdministrativeInformationTemplates/Test_AssetAdministrationShell"
- },
- "derivedFrom": {
- "type": "ModelReference",
- "keys": [
- {
- "type": "AssetAdministrationShell",
- "value": "https://acplt.org/TestAssetAdministrationShell2"
- }
- ]
- },
- "assetInformation": {
- "assetKind": "Instance",
- "globalAssetId": "http://acplt.org/TestAsset/",
- "specificAssetIds": [
- {
- "name": "TestKey",
- "value": "TestValue",
- "externalSubjectId": {
- "type": "ExternalReference",
- "keys": [
- {
- "type": "GlobalReference",
- "value": "http://acplt.org/SpecificAssetId/"
- }
- ]
- },
- "semanticId": {
- "type": "ExternalReference",
- "keys": [
- {
- "type": "GlobalReference",
- "value": "http://acplt.org/SpecificAssetId/"
- }
- ]
- }
- }
- ],
- "assetType": "http://acplt.org/TestAssetType/",
- "defaultThumbnail": {
- "path": "file:///path/to/thumbnail.png",
- "contentType": "image/png"
- }
- },
- "submodels": [
- {
- "type": "ModelReference",
- "keys": [
- {
- "type": "Submodel",
- "value": "https://acplt.org/Test_Submodel"
- }
- ],
- "referredSemanticId": {
- "type": "ExternalReference",
- "keys": [
- {
- "type": "GlobalReference",
- "value": "http://acplt.org/SubmodelTemplates/ExampleSubmodel"
- }
- ]
- }
- },
- {
- "type": "ModelReference",
- "keys": [
- {
- "type": "Submodel",
- "value": "http://acplt.org/Submodels/Assets/TestAsset/BillOfMaterial"
- }
- ]
- },
- {
- "type": "ModelReference",
- "keys": [
- {
- "type": "Submodel",
- "value": "http://acplt.org/Submodels/Assets/TestAsset/Identification"
- }
- ],
- "referredSemanticId": {
- "type": "ModelReference",
- "keys": [
- {
- "type": "Submodel",
- "value": "http://acplt.org/SubmodelTemplates/AssetIdentification"
- }
- ]
- }
- }
- ],
- "embeddedDataSpecifications": [
- {
- "dataSpecification": {
- "type": "ExternalReference",
- "keys": [
- {
- "type": "GlobalReference",
- "value": "https://admin-shell.io/DataSpecificationTemplates/DataSpecificationIEC61360/3/0"
- }
- ]
- },
- "dataSpecificationContent": {
- "modelType": "DataSpecificationIec61360",
- "preferredName": [
- {
- "language": "de",
- "text": "Test Specification"
- },
- {
- "language": "en-US",
- "text": "TestSpecification"
- }
- ],
- "dataType": "REAL_MEASURE",
- "definition": [
- {
- "language": "de",
- "text": "Dies ist eine Data Specification f\u00fcr Testzwecke"
- },
- {
- "language": "en-US",
- "text": "This is a DataSpecification for testing purposes"
- }
- ],
- "shortName": [
- {
- "language": "de",
- "text": "Test Spec"
- },
- {
- "language": "en-US",
- "text": "TestSpec"
- }
- ],
- "unit": "SpaceUnit",
- "unitId": {
- "type": "ExternalReference",
- "keys": [
- {
- "type": "GlobalReference",
- "value": "http://acplt.org/Units/SpaceUnit"
- }
- ]
- },
- "sourceOfDefinition": "http://acplt.org/DataSpec/ExampleDef",
- "symbol": "SU",
- "valueFormat": "M",
- "valueList": {
- "valueReferencePairs": [
- {
- "value": "exampleValue",
- "valueId": {
- "type": "ExternalReference",
- "keys": [
- {
- "type": "GlobalReference",
- "value": "http://acplt.org/ValueId/ExampleValueId"
- }
- ]
- }
- },
- {
- "value": "exampleValue2",
- "valueId": {
- "type": "ExternalReference",
- "keys": [
- {
- "type": "GlobalReference",
- "value": "http://acplt.org/ValueId/ExampleValueId2"
- }
- ]
- }
- }
- ]
- },
- "value": "TEST",
- "valueId": {
- "type": "ExternalReference",
- "keys": [
- {
- "type": "GlobalReference",
- "value": "http://acplt.org/Values/TestValueId"
- }
- ]
- },
- "levelType": {
- "min": true,
- "max": true,
- "nom": false,
- "typ": false
- }
- }
- }
- ]
- },
- {
- "modelType": "AssetAdministrationShell",
- "id": "https://acplt.org/Test_AssetAdministrationShell_Mandatory",
- "assetInformation": {
- "assetKind": "Instance",
- "globalAssetId": "http://acplt.org/Test_Asset_Mandatory/"
- },
- "submodels": [
- {
- "type": "ModelReference",
- "keys": [
- {
- "type": "Submodel",
- "value": "https://acplt.org/Test_Submodel2_Mandatory"
- }
- ]
- },
- {
- "type": "ModelReference",
- "keys": [
- {
- "type": "Submodel",
- "value": "https://acplt.org/Test_Submodel_Mandatory"
- }
- ]
- }
- ]
- },
- {
- "modelType": "AssetAdministrationShell",
- "id": "https://acplt.org/Test_AssetAdministrationShell2_Mandatory",
- "assetInformation": {
- "assetKind": "Instance",
- "globalAssetId": "http://acplt.org/TestAsset2_Mandatory/"
- }
- },
- {
- "idShort": "TestAssetAdministrationShell",
- "description": [
- {
- "language": "en-US",
- "text": "An Example Asset Administration Shell for the test application"
- },
- {
- "language": "de",
- "text": "Ein Beispiel-Verwaltungsschale f\u00fcr eine Test-Anwendung"
- }
- ],
- "modelType": "AssetAdministrationShell",
- "id": "https://acplt.org/Test_AssetAdministrationShell_Missing",
- "administration": {
- "version": "9",
- "revision": "0"
- },
- "assetInformation": {
- "assetKind": "Instance",
- "globalAssetId": "http://acplt.org/Test_Asset_Missing/",
- "specificAssetIds": [
- {
- "name": "TestKey",
- "value": "TestValue",
- "externalSubjectId": {
- "type": "ExternalReference",
- "keys": [
- {
- "type": "GlobalReference",
- "value": "http://acplt.org/SpecificAssetId/"
- }
- ]
- }
- }
- ],
- "defaultThumbnail": {
- "path": "file:///TestFile.pdf",
- "contentType": "application/pdf"
- }
- },
- "submodels": [
- {
- "type": "ModelReference",
- "keys": [
- {
- "type": "Submodel",
- "value": "https://acplt.org/Test_Submodel_Missing"
- }
- ]
- }
- ]
- }
- ],
- "submodels": [
- {
- "idShort": "Identification",
- "description": [
- {
- "language": "en-US",
- "text": "An example asset identification submodel for the test application"
- },
- {
- "language": "de",
- "text": "Ein Beispiel-Identifikations-Submodel f\u00fcr eine Test-Anwendung"
- }
- ],
- "modelType": "Submodel",
- "id": "http://acplt.org/Submodels/Assets/TestAsset/Identification",
- "administration": {
- "version": "9",
- "revision": "0",
- "creator": {
- "type": "ExternalReference",
- "keys": [
- {
- "type": "GlobalReference",
- "value": "http://acplt.org/AdministrativeInformation/TestAsset/Identification"
- }
- ]
- },
- "templateId": "http://acplt.org/AdministrativeInformationTemplates/TestAsset/Identification"
- },
- "semanticId": {
- "type": "ModelReference",
- "keys": [
- {
- "type": "Submodel",
- "value": "http://acplt.org/SubmodelTemplates/AssetIdentification"
- }
- ]
- },
- "submodelElements": [
- {
- "extensions": [
- {
- "value": "ExampleExtensionValue",
- "refersTo": [
- {
- "type": "ModelReference",
- "keys": [
- {
- "type": "AssetAdministrationShell",
- "value": "http://acplt.org/RefersTo/ExampleRefersTo"
- }
- ]
- }
- ],
- "valueType": "xs:string",
- "name": "ExampleExtension"
- }
- ],
- "idShort": "ManufacturerName",
- "category": "PARAMETER",
- "description": [
- {
- "language": "en-US",
- "text": "Legally valid designation of the natural or judicial person which is directly responsible for the design, production, packaging and labeling of a product in respect to its being brought into circulation."
- },
- {
- "language": "de",
- "text": "Bezeichnung f\u00fcr eine nat\u00fcrliche oder juristische Person, die f\u00fcr die Auslegung, Herstellung und Verpackung sowie die Etikettierung eines Produkts im Hinblick auf das 'Inverkehrbringen' im eigenen Namen verantwortlich ist"
- }
- ],
- "modelType": "Property",
- "semanticId": {
- "type": "ExternalReference",
- "keys": [
- {
- "type": "GlobalReference",
- "value": "0173-1#02-AAO677#002"
- }
- ]
- },
- "qualifiers": [
- {
- "value": "50",
- "valueId": {
- "type": "ExternalReference",
- "keys": [
- {
- "type": "GlobalReference",
- "value": "http://acplt.org/ValueId/ExampleValueId"
- }
- ]
- },
- "valueType": "xs:int",
- "type": "http://acplt.org/Qualifier/ExampleQualifier2",
- "kind": "TemplateQualifier"
- },
- {
- "value": "100",
- "valueId": {
- "type": "ExternalReference",
- "keys": [
- {
- "type": "GlobalReference",
- "value": "http://acplt.org/ValueId/ExampleValueId"
- }
- ]
- },
- "valueType": "xs:int",
- "type": "http://acplt.org/Qualifier/ExampleQualifier",
- "kind": "ConceptQualifier"
- }
- ],
- "value": "ACPLT",
- "valueId": {
- "type": "ExternalReference",
- "keys": [
- {
- "type": "GlobalReference",
- "value": "http://acplt.org/ValueId/ExampleValueId"
- }
- ]
- },
- "valueType": "xs:string"
- },
- {
- "idShort": "InstanceId",
- "category": "PARAMETER",
- "description": [
- {
- "language": "en-US",
- "text": "Legally valid designation of the natural or judicial person which is directly responsible for the design, production, packaging and labeling of a product in respect to its being brought into circulation."
- },
- {
- "language": "de",
- "text": "Bezeichnung f\u00fcr eine nat\u00fcrliche oder juristische Person, die f\u00fcr die Auslegung, Herstellung und Verpackung sowie die Etikettierung eines Produkts im Hinblick auf das 'Inverkehrbringen' im eigenen Namen verantwortlich ist"
- }
- ],
- "modelType": "Property",
- "semanticId": {
- "type": "ExternalReference",
- "keys": [
- {
- "type": "GlobalReference",
- "value": "http://opcfoundation.org/UA/DI/1.1/DeviceType/Serialnumber"
- }
- ]
- },
- "qualifiers": [
- {
- "value": "2023-04-07T16:59:54.870123",
- "valueId": {
- "type": "ExternalReference",
- "keys": [
- {
- "type": "GlobalReference",
- "value": "http://acplt.org/ValueId/ExampleValueId"
- }
- ]
- },
- "valueType": "xs:dateTime",
- "type": "http://acplt.org/Qualifier/ExampleQualifier3",
- "kind": "ValueQualifier"
- }
- ],
- "value": "978-8234-234-342",
- "valueId": {
- "type": "ExternalReference",
- "keys": [
- {
- "type": "GlobalReference",
- "value": "http://acplt.org/ValueId/ExampleValueId"
- }
- ]
- },
- "valueType": "xs:string"
- }
- ]
- },
- {
- "idShort": "BillOfMaterial",
- "description": [
- {
- "language": "en-US",
- "text": "An example bill of material submodel for the test application"
- },
- {
- "language": "de",
- "text": "Ein Beispiel-BillOfMaterial-Submodel f\u00fcr eine Test-Anwendung"
- }
- ],
- "modelType": "Submodel",
- "id": "http://acplt.org/Submodels/Assets/TestAsset/BillOfMaterial",
- "administration": {
- "version": "9",
- "templateId": "http://acplt.org/AdministrativeInformationTemplates/TestAsset/BillOfMaterial"
- },
- "semanticId": {
- "type": "ModelReference",
- "keys": [
- {
- "type": "Submodel",
- "value": "http://acplt.org/SubmodelTemplates/BillOfMaterial"
- }
- ]
- },
- "submodelElements": [
- {
- "idShort": "ExampleEntity",
- "category": "PARAMETER",
- "description": [
- {
- "language": "en-US",
- "text": "Legally valid designation of the natural or judicial person which is directly responsible for the design, production, packaging and labeling of a product in respect to its being brought into circulation."
- },
- {
- "language": "de",
- "text": "Bezeichnung f\u00fcr eine nat\u00fcrliche oder juristische Person, die f\u00fcr die Auslegung, Herstellung und Verpackung sowie die Etikettierung eines Produkts im Hinblick auf das 'Inverkehrbringen' im eigenen Namen verantwortlich ist"
- }
- ],
- "modelType": "Entity",
- "semanticId": {
- "type": "ExternalReference",
- "keys": [
- {
- "type": "GlobalReference",
- "value": "http://opcfoundation.org/UA/DI/1.1/DeviceType/Serialnumber"
- }
- ]
- },
- "statements": [
- {
- "idShort": "ExampleProperty2",
- "category": "CONSTANT",
- "description": [
- {
- "language": "en-US",
- "text": "Example Property object"
- },
- {
- "language": "de",
- "text": "Beispiel Property Element"
- }
- ],
- "modelType": "Property",
- "semanticId": {
- "type": "ExternalReference",
- "keys": [
- {
- "type": "GlobalReference",
- "value": "http://acplt.org/Properties/ExampleProperty"
- }
- ]
- },
- "value": "exampleValue2",
- "valueId": {
- "type": "ExternalReference",
- "keys": [
- {
- "type": "GlobalReference",
- "value": "http://acplt.org/ValueId/ExampleValueId"
- }
- ]
- },
- "valueType": "xs:string"
- },
- {
- "idShort": "ExampleProperty",
- "category": "CONSTANT",
- "description": [
- {
- "language": "en-US",
- "text": "Example Property object"
- },
- {
- "language": "de",
- "text": "Beispiel Property Element"
- }
- ],
- "modelType": "Property",
- "semanticId": {
- "type": "ExternalReference",
- "keys": [
- {
- "type": "GlobalReference",
- "value": "http://acplt.org/Properties/ExampleProperty"
- }
- ]
- },
- "value": "exampleValue",
- "valueId": {
- "type": "ExternalReference",
- "keys": [
- {
- "type": "GlobalReference",
- "value": "http://acplt.org/ValueId/ExampleValueId"
- }
- ]
- },
- "valueType": "xs:string",
- "embeddedDataSpecifications": [
- {
- "dataSpecification": {
- "type": "ExternalReference",
- "keys": [
- {
- "type": "GlobalReference",
- "value": "https://admin-shell.io/DataSpecificationTemplates/DataSpecificationIEC61360/3/0"
- }
- ]
- },
- "dataSpecificationContent": {
- "modelType": "DataSpecificationIec61360",
- "preferredName": [
- {
- "language": "de",
- "text": "Test Specification"
- },
- {
- "language": "en-US",
- "text": "TestSpecification"
- }
- ],
- "dataType": "REAL_MEASURE",
- "definition": [
- {
- "language": "de",
- "text": "Dies ist eine Data Specification f\u00fcr Testzwecke"
- },
- {
- "language": "en-US",
- "text": "This is a DataSpecification for testing purposes"
- }
- ],
- "shortName": [
- {
- "language": "de",
- "text": "Test Spec"
- },
- {
- "language": "en-US",
- "text": "TestSpec"
- }
- ],
- "unit": "SpaceUnit",
- "unitId": {
- "type": "ExternalReference",
- "keys": [
- {
- "type": "GlobalReference",
- "value": "http://acplt.org/Units/SpaceUnit"
- }
- ]
- },
- "sourceOfDefinition": "http://acplt.org/DataSpec/ExampleDef",
- "symbol": "SU",
- "valueFormat": "M",
- "valueList": {
- "valueReferencePairs": [
- {
- "value": "exampleValue",
- "valueId": {
- "type": "ExternalReference",
- "keys": [
- {
- "type": "GlobalReference",
- "value": "http://acplt.org/ValueId/ExampleValueId"
- }
- ]
- },
- "valueType": "xs:string"
- },
- {
- "value": "exampleValue2",
- "valueId": {
- "type": "ExternalReference",
- "keys": [
- {
- "type": "GlobalReference",
- "value": "http://acplt.org/ValueId/ExampleValueId2"
- }
- ]
- },
- "valueType": "xs:string"
- }
- ]
- },
- "value": "TEST",
- "valueId": {
- "type": "ExternalReference",
- "keys": [
- {
- "type": "GlobalReference",
- "value": "http://acplt.org/Values/TestValueId"
- }
- ]
- },
- "levelType": {
- "min": true,
- "max": true,
- "nom": false,
- "typ": false
- }
- }
- }
- ]
- }
- ],
- "entityType": "SelfManagedEntity",
- "globalAssetId": "http://acplt.org/TestAsset/",
- "specificAssetIds": [
- {
- "name": "TestKey",
- "value": "TestValue",
- "externalSubjectId": {
- "type": "ExternalReference",
- "keys": [
- {
- "type": "GlobalReference",
- "value": "http://acplt.org/SpecificAssetId/"
- }
- ]
- }
- }
- ]
- },
- {
- "idShort": "ExampleEntity2",
- "category": "PARAMETER",
- "description": [
- {
- "language": "en-US",
- "text": "Legally valid designation of the natural or judicial person which is directly responsible for the design, production, packaging and labeling of a product in respect to its being brought into circulation."
- },
- {
- "language": "de",
- "text": "Bezeichnung f\u00fcr eine nat\u00fcrliche oder juristische Person, die f\u00fcr die Auslegung, Herstellung und Verpackung sowie die Etikettierung eines Produkts im Hinblick auf das 'Inverkehrbringen' im eigenen Namen verantwortlich ist"
- }
- ],
- "modelType": "Entity",
- "semanticId": {
- "type": "ExternalReference",
- "keys": [
- {
- "type": "GlobalReference",
- "value": "http://opcfoundation.org/UA/DI/1.1/DeviceType/Serialnumber"
- }
- ]
- },
- "entityType": "CoManagedEntity"
- }
- ]
- },
- {
- "idShort": "TestSubmodel",
- "description": [
- {
- "language": "en-US",
- "text": "An example submodel for the test application"
- },
- {
- "language": "de",
- "text": "Ein Beispiel-Teilmodell f\u00fcr eine Test-Anwendung"
- }
- ],
- "modelType": "Submodel",
- "id": "https://acplt.org/Test_Submodel",
- "administration": {
- "version": "9",
- "revision": "0",
- "creator": {
- "type": "ExternalReference",
- "keys": [
- {
- "type": "GlobalReference",
- "value": "http://acplt.org/AdministrativeInformation/Test_Submodel"
- }
- ]
- }
- },
- "semanticId": {
- "type": "ExternalReference",
- "keys": [
- {
- "type": "GlobalReference",
- "value": "http://acplt.org/SubmodelTemplates/ExampleSubmodel"
- }
- ]
- },
- "submodelElements": [
- {
- "idShort": "ExampleRelationshipElement",
- "category": "PARAMETER",
- "description": [
- {
- "language": "en-US",
- "text": "Example RelationshipElement object"
- },
- {
- "language": "de",
- "text": "Beispiel RelationshipElement Element"
- }
- ],
- "modelType": "RelationshipElement",
- "semanticId": {
- "type": "ModelReference",
- "keys": [
- {
- "type": "ConceptDescription",
- "value": "https://acplt.org/Test_ConceptDescription"
- }
- ]
- },
- "first": {
- "type": "ModelReference",
- "keys": [
- {
- "type": "Submodel",
- "value": "http://acplt.org/Test_Submodel"
- },
- {
- "type": "Property",
- "value": "ExampleProperty"
- }
- ]
- },
- "second": {
- "type": "ModelReference",
- "keys": [
- {
- "type": "Submodel",
- "value": "http://acplt.org/Test_Submodel"
- },
- {
- "type": "Property",
- "value": "ExampleProperty2"
- }
- ]
- }
- },
- {
- "idShort": "ExampleAnnotatedRelationshipElement",
- "category": "PARAMETER",
- "description": [
- {
- "language": "en-US",
- "text": "Example AnnotatedRelationshipElement object"
- },
- {
- "language": "de",
- "text": "Beispiel AnnotatedRelationshipElement Element"
- }
- ],
- "modelType": "AnnotatedRelationshipElement",
- "semanticId": {
- "type": "ExternalReference",
- "keys": [
- {
- "type": "GlobalReference",
- "value": "http://acplt.org/RelationshipElements/ExampleAnnotatedRelationshipElement"
- }
- ]
- },
- "first": {
- "type": "ModelReference",
- "keys": [
- {
- "type": "Submodel",
- "value": "http://acplt.org/Test_Submodel"
- },
- {
- "type": "Property",
- "value": "ExampleProperty"
- }
- ]
- },
- "second": {
- "type": "ModelReference",
- "keys": [
- {
- "type": "Submodel",
- "value": "http://acplt.org/Test_Submodel"
- },
- {
- "type": "Property",
- "value": "ExampleProperty2"
- }
- ]
- },
- "annotations": [
- {
- "idShort": "ExampleAnnotatedProperty",
- "category": "PARAMETER",
- "modelType": "Property",
- "value": "exampleValue",
- "valueType": "xs:string"
- },
- {
- "idShort": "ExampleAnnotatedRange",
- "category": "PARAMETER",
- "modelType": "Range",
- "valueType": "xs:integer",
- "min": "1",
- "max": "5"
- }
- ]
- },
- {
- "idShort": "ExampleOperation",
- "category": "PARAMETER",
- "description": [
- {
- "language": "en-US",
- "text": "Example Operation object"
- },
- {
- "language": "de",
- "text": "Beispiel Operation Element"
- }
- ],
- "modelType": "Operation",
- "semanticId": {
- "type": "ExternalReference",
- "keys": [
- {
- "type": "GlobalReference",
- "value": "http://acplt.org/Operations/ExampleOperation"
- }
- ]
- },
- "inputVariables": [
- {
- "value": {
- "idShort": "ExamplePropertyInput",
- "displayName": [
- {
- "language": "en-US",
- "text": "ExampleProperty"
- },
- {
- "language": "de",
- "text": "BeispielProperty"
- }
- ],
- "category": "CONSTANT",
- "description": [
- {
- "language": "en-US",
- "text": "Example Property object"
- },
- {
- "language": "de",
- "text": "Beispiel Property Element"
- }
- ],
- "modelType": "Property",
- "semanticId": {
- "type": "ExternalReference",
- "keys": [
- {
- "type": "GlobalReference",
- "value": "http://acplt.org/Properties/ExamplePropertyInput"
- }
- ]
- },
- "kind": "Template",
- "value": "exampleValue",
- "valueId": {
- "type": "ExternalReference",
- "keys": [
- {
- "type": "GlobalReference",
- "value": "http://acplt.org/ValueId/ExampleValueId"
- }
- ]
- },
- "valueType": "xs:string"
- }
- }
- ],
- "outputVariables": [
- {
- "value": {
- "idShort": "ExamplePropertyOutput",
- "displayName": [
- {
- "language": "en-US",
- "text": "ExampleProperty"
- },
- {
- "language": "de",
- "text": "BeispielProperty"
- }
- ],
- "category": "CONSTANT",
- "description": [
- {
- "language": "en-US",
- "text": "Example Property object"
- },
- {
- "language": "de",
- "text": "Beispiel Property Element"
- }
- ],
- "modelType": "Property",
- "semanticId": {
- "type": "ExternalReference",
- "keys": [
- {
- "type": "GlobalReference",
- "value": "http://acplt.org/Properties/ExamplePropertyOutput"
- }
- ]
- },
- "kind": "Template",
- "value": "exampleValue",
- "valueId": {
- "type": "ExternalReference",
- "keys": [
- {
- "type": "GlobalReference",
- "value": "http://acplt.org/ValueId/ExampleValueId"
- }
- ]
- },
- "valueType": "xs:string"
- }
- }
- ],
- "inoutputVariables": [
- {
- "value": {
- "idShort": "ExamplePropertyInOutput",
- "displayName": [
- {
- "language": "en-US",
- "text": "ExampleProperty"
- },
- {
- "language": "de",
- "text": "BeispielProperty"
- }
- ],
- "category": "CONSTANT",
- "description": [
- {
- "language": "en-US",
- "text": "Example Property object"
- },
- {
- "language": "de",
- "text": "Beispiel Property Element"
- }
- ],
- "modelType": "Property",
- "semanticId": {
- "type": "ExternalReference",
- "keys": [
- {
- "type": "GlobalReference",
- "value": "http://acplt.org/Properties/ExamplePropertyInOutput"
- }
- ]
- },
- "kind": "Template",
- "value": "exampleValue",
- "valueId": {
- "type": "ExternalReference",
- "keys": [
- {
- "type": "GlobalReference",
- "value": "http://acplt.org/ValueId/ExampleValueId"
- }
- ]
- },
- "valueType": "xs:string"
- }
- }
- ]
- },
- {
- "idShort": "ExampleCapability",
- "category": "PARAMETER",
- "description": [
- {
- "language": "en-US",
- "text": "Example Capability object"
- },
- {
- "language": "de",
- "text": "Beispiel Capability Element"
- }
- ],
- "modelType": "Capability",
- "semanticId": {
- "type": "ExternalReference",
- "keys": [
- {
- "type": "GlobalReference",
- "value": "http://acplt.org/Capabilities/ExampleCapability"
- }
- ]
- }
- },
- {
- "idShort": "ExampleBasicEventElement",
- "category": "PARAMETER",
- "description": [
- {
- "language": "en-US",
- "text": "Example BasicEventElement object"
- },
- {
- "language": "de",
- "text": "Beispiel BasicEventElement Element"
- }
- ],
- "modelType": "BasicEventElement",
- "semanticId": {
- "type": "ExternalReference",
- "keys": [
- {
- "type": "GlobalReference",
- "value": "http://acplt.org/Events/ExampleBasicEventElement"
- }
- ]
- },
- "observed": {
- "type": "ModelReference",
- "keys": [
- {
- "type": "Submodel",
- "value": "http://acplt.org/Test_Submodel"
- },
- {
- "type": "Property",
- "value": "ExampleProperty"
- }
- ]
- },
- "direction": "output",
- "state": "on",
- "messageTopic": "ExampleTopic",
- "messageBroker": {
- "type": "ModelReference",
- "keys": [
- {
- "type": "Submodel",
- "value": "http://acplt.org/ExampleMessageBroker"
- }
- ]
- },
- "lastUpdate": "2022-11-12T23:50:23.123456+00:00",
- "minInterval": "PT0.000001S",
- "maxInterval": "P1Y2M3DT4H5M6.123456S"
- },
- {
- "idShort": "ExampleSubmodelCollection",
- "category": "PARAMETER",
- "description": [
- {
- "language": "en-US",
- "text": "Example SubmodelElementCollection object"
- },
- {
- "language": "de",
- "text": "Beispiel SubmodelElementCollection Element"
- }
- ],
- "modelType": "SubmodelElementCollection",
- "semanticId": {
- "type": "ExternalReference",
- "keys": [
- {
- "type": "GlobalReference",
- "value": "http://acplt.org/SubmodelElementCollections/ExampleSubmodelElementCollection"
- }
- ]
- },
- "value": [
- {
- "idShort": "ExampleBlob",
- "category": "PARAMETER",
- "description": [
- {
- "language": "en-US",
- "text": "Example Blob object"
- },
- {
- "language": "de",
- "text": "Beispiel Blob Element"
- }
- ],
- "modelType": "Blob",
- "semanticId": {
- "type": "ExternalReference",
- "keys": [
- {
- "type": "GlobalReference",
- "value": "http://acplt.org/Blobs/ExampleBlob"
- }
- ]
- },
- "contentType": "application/pdf",
- "value": "AQIDBAU="
- },
- {
- "idShort": "ExampleFile",
- "category": "PARAMETER",
- "description": [
- {
- "language": "en-US",
- "text": "Example File object"
- },
- {
- "language": "de",
- "text": "Beispiel File Element"
- }
- ],
- "modelType": "File",
- "semanticId": {
- "type": "ExternalReference",
- "keys": [
- {
- "type": "GlobalReference",
- "value": "http://acplt.org/Files/ExampleFile"
- }
- ]
- },
- "value": "/TestFile.pdf",
- "contentType": "application/pdf"
- },
- {
- "idShort": "ExampleFileURI",
- "category": "CONSTANT",
- "description": [
- {
- "language": "en-US",
- "text": "Details of the Asset Administration Shell \u2014 An example for an external file reference"
- },
- {
- "language": "de",
- "text": "Details of the Asset Administration Shell \u2013 Ein Beispiel f\u00fcr eine extern referenzierte Datei"
- }
- ],
- "modelType": "File",
- "semanticId": {
- "type": "ExternalReference",
- "keys": [
- {
- "type": "GlobalReference",
- "value": "http://acplt.org/Files/ExampleFile"
- }
- ]
- },
- "value": "https://www.plattform-i40.de/PI40/Redaktion/DE/Downloads/Publikation/Details-of-the-Asset-Administration-Shell-Part1.pdf?__blob=publicationFile&v=5",
- "contentType": "application/pdf"
- },
- {
- "idShort": "ExampleMultiLanguageProperty",
- "category": "CONSTANT",
- "description": [
- {
- "language": "en-US",
- "text": "Example MultiLanguageProperty object"
- },
- {
- "language": "de",
- "text": "Beispiel MultiLanguageProperty Element"
- }
- ],
- "modelType": "MultiLanguageProperty",
- "semanticId": {
- "type": "ExternalReference",
- "keys": [
- {
- "type": "GlobalReference",
- "value": "http://acplt.org/MultiLanguageProperties/ExampleMultiLanguageProperty"
- }
- ],
- "referredSemanticId": {
- "type": "ExternalReference",
- "keys": [
- {
- "type": "GlobalReference",
- "value": "http://acplt.org/Properties/ExampleProperty/Referred"
- }
- ]
- }
- },
- "value": [
- {
- "language": "en-US",
- "text": "Example value of a MultiLanguageProperty element"
- },
- {
- "language": "de",
- "text": "Beispielwert f\u00fcr ein MultiLanguageProperty-Element"
- }
- ],
- "valueId": {
- "type": "ExternalReference",
- "keys": [
- {
- "type": "GlobalReference",
- "value": "http://acplt.org/ValueId/ExampleMultiLanguageValueId"
- }
- ]
- }
- },
- {
- "idShort": "ExampleRange",
- "category": "PARAMETER",
- "description": [
- {
- "language": "en-US",
- "text": "Example Range object"
- },
- {
- "language": "de",
- "text": "Beispiel Range Element"
- }
- ],
- "modelType": "Range",
- "semanticId": {
- "type": "ExternalReference",
- "keys": [
- {
- "type": "GlobalReference",
- "value": "http://acplt.org/Ranges/ExampleRange"
- }
- ]
- },
- "valueType": "xs:int",
- "min": "0",
- "max": "100"
- },
- {
- "idShort": "ExampleReferenceElement",
- "category": "PARAMETER",
- "description": [
- {
- "language": "en-US",
- "text": "Example Reference Element object"
- },
- {
- "language": "de",
- "text": "Beispiel Reference Element Element"
- }
- ],
- "modelType": "ReferenceElement",
- "semanticId": {
- "type": "ExternalReference",
- "keys": [
- {
- "type": "GlobalReference",
- "value": "http://acplt.org/ReferenceElements/ExampleReferenceElement"
- }
- ]
- },
- "value": {
- "type": "ModelReference",
- "keys": [
- {
- "type": "Submodel",
- "value": "http://acplt.org/Test_Submodel"
- },
- {
- "type": "Property",
- "value": "ExampleProperty"
- }
- ]
- }
- },
- {
- "idShort": "ExampleSubmodelList",
- "typeValueListElement": "Property",
- "valueTypeListElement": "xs:string",
- "semanticIdListElement": {
- "type": "ExternalReference",
- "keys": [
- {
- "type": "GlobalReference",
- "value": "http://acplt.org/Properties/ExampleProperty"
- }
- ]
- },
- "orderRelevant": true,
- "category": "PARAMETER",
- "description": [
- {
- "language": "en-US",
- "text": "Example SubmodelElementList object"
- },
- {
- "language": "de",
- "text": "Beispiel SubmodelElementList Element"
- }
- ],
- "modelType": "SubmodelElementList",
- "semanticId": {
- "type": "ExternalReference",
- "keys": [
- {
- "type": "GlobalReference",
- "value": "http://acplt.org/SubmodelElementLists/ExampleSubmodelElementList"
- }
- ]
- },
- "value": [
- {
- "displayName": [
- {
- "language": "en-US",
- "text": "ExampleProperty"
- },
- {
- "language": "de",
- "text": "BeispielProperty"
- }
- ],
- "category": "CONSTANT",
- "description": [
- {
- "language": "en-US",
- "text": "Example Property object"
- },
- {
- "language": "de",
- "text": "Beispiel Property Element"
- }
- ],
- "modelType": "Property",
- "semanticId": {
- "type": "ExternalReference",
- "keys": [
- {
- "type": "GlobalReference",
- "value": "http://acplt.org/Properties/ExampleProperty"
- }
- ]
- },
- "supplementalSemanticIds": [
- {
- "type": "ExternalReference",
- "keys": [
- {
- "type": "GlobalReference",
- "value": "http://acplt.org/Properties/ExampleProperty/SupplementalId1"
- }
- ]
- },
- {
- "type": "ExternalReference",
- "keys": [
- {
- "type": "GlobalReference",
- "value": "http://acplt.org/Properties/ExampleProperty/SupplementalId2"
- }
- ]
- }
- ],
- "value": "exampleValue",
- "valueId": {
- "type": "ExternalReference",
- "keys": [
- {
- "type": "GlobalReference",
- "value": "http://acplt.org/ValueId/ExampleValueId"
- }
- ]
- },
- "valueType": "xs:string",
- "embeddedDataSpecifications": [
- {
- "dataSpecification": {
- "type": "ExternalReference",
- "keys": [
- {
- "type": "GlobalReference",
- "value": "https://admin-shell.io/DataSpecificationTemplates/DataSpecificationIEC61360/3/0"
- }
- ]
- },
- "dataSpecificationContent": {
- "modelType": "DataSpecificationIec61360",
- "preferredName": [
- {
- "language": "de",
- "text": "Test Specification"
- },
- {
- "language": "en-US",
- "text": "TestSpecification"
- }
- ],
- "dataType": "REAL_MEASURE",
- "definition": [
- {
- "language": "de",
- "text": "Dies ist eine Data Specification f\u00fcr Testzwecke"
- },
- {
- "language": "en-US",
- "text": "This is a DataSpecification for testing purposes"
- }
- ],
- "shortName": [
- {
- "language": "de",
- "text": "Test Spec"
- },
- {
- "language": "en-US",
- "text": "TestSpec"
- }
- ],
- "unit": "SpaceUnit",
- "unitId": {
- "type": "ExternalReference",
- "keys": [
- {
- "type": "GlobalReference",
- "value": "http://acplt.org/Units/SpaceUnit"
- }
- ]
- },
- "sourceOfDefinition": "http://acplt.org/DataSpec/ExampleDef",
- "symbol": "SU",
- "valueFormat": "M",
- "valueList": {
- "valueReferencePairs": [
- {
- "value": "exampleValue",
- "valueId": {
- "type": "ExternalReference",
- "keys": [
- {
- "type": "GlobalReference",
- "value": "http://acplt.org/ValueId/ExampleValueId"
- }
- ]
- }
- },
- {
- "value": "exampleValue2",
- "valueId": {
- "type": "ExternalReference",
- "keys": [
- {
- "type": "GlobalReference",
- "value": "http://acplt.org/ValueId/ExampleValueId2"
- }
- ]
- }
- }
- ]
- },
- "value": "TEST",
- "valueId": {
- "type": "ExternalReference",
- "keys": [
- {
- "type": "GlobalReference",
- "value": "http://acplt.org/Values/TestValueId"
- }
- ]
- },
- "levelType": {
- "min": true,
- "max": true,
- "nom": false,
- "typ": false
- }
- }
- }
- ]
- },
- {
- "displayName": [
- {
- "language": "en-US",
- "text": "ExampleProperty"
- },
- {
- "language": "de",
- "text": "BeispielProperty"
- }
- ],
- "category": "CONSTANT",
- "description": [
- {
- "language": "en-US",
- "text": "Example Property object"
- },
- {
- "language": "de",
- "text": "Beispiel Property Element"
- }
- ],
- "modelType": "Property",
- "semanticId": {
- "type": "ExternalReference",
- "keys": [
- {
- "type": "GlobalReference",
- "value": "http://acplt.org/Properties/ExampleProperty"
- }
- ]
- },
- "supplementalSemanticIds": [
- {
- "type": "ExternalReference",
- "keys": [
- {
- "type": "GlobalReference",
- "value": "http://acplt.org/Properties/ExampleProperty2/SupplementalId"
- }
- ]
- }
- ],
- "value": "exampleValue",
- "valueId": {
- "type": "ExternalReference",
- "keys": [
- {
- "type": "GlobalReference",
- "value": "http://acplt.org/ValueId/ExampleValueId"
- }
- ]
- },
- "valueType": "xs:string"
- }
- ]
- }
- ]
- }
- ]
- },
- {
- "modelType": "Submodel",
- "id": "https://acplt.org/Test_Submodel_Mandatory",
- "submodelElements": [
- {
- "idShort": "ExampleRelationshipElement",
- "modelType": "RelationshipElement",
- "first": {
- "type": "ModelReference",
- "keys": [
- {
- "type": "Submodel",
- "value": "http://acplt.org/Test_Submodel"
- },
- {
- "type": "Property",
- "value": "ExampleProperty"
- }
- ]
- },
- "second": {
- "type": "ModelReference",
- "keys": [
- {
- "type": "Submodel",
- "value": "http://acplt.org/Test_Submodel"
- },
- {
- "type": "Property",
- "value": "ExampleProperty"
- }
- ]
- }
- },
- {
- "idShort": "ExampleAnnotatedRelationshipElement",
- "modelType": "AnnotatedRelationshipElement",
- "first": {
- "type": "ModelReference",
- "keys": [
- {
- "type": "Submodel",
- "value": "http://acplt.org/Test_Submodel"
- },
- {
- "type": "Property",
- "value": "ExampleProperty"
- }
- ]
- },
- "second": {
- "type": "ModelReference",
- "keys": [
- {
- "type": "Submodel",
- "value": "http://acplt.org/Test_Submodel"
- },
- {
- "type": "Property",
- "value": "ExampleProperty"
- }
- ]
- }
- },
- {
- "idShort": "ExampleOperation",
- "modelType": "Operation"
- },
- {
- "idShort": "ExampleCapability",
- "modelType": "Capability"
- },
- {
- "idShort": "ExampleBasicEventElement",
- "modelType": "BasicEventElement",
- "observed": {
- "type": "ModelReference",
- "keys": [
- {
- "type": "Submodel",
- "value": "http://acplt.org/Test_Submodel"
- },
- {
- "type": "Property",
- "value": "ExampleProperty"
- }
- ]
- },
- "direction": "input",
- "state": "off"
- },
- {
- "idShort": "ExampleSubmodelList",
- "typeValueListElement": "SubmodelElementCollection",
- "modelType": "SubmodelElementList",
- "value": [
- {
- "modelType": "SubmodelElementCollection",
- "value": [
- {
- "idShort": "ExampleBlob",
- "modelType": "Blob",
- "contentType": "application/pdf"
- },
- {
- "idShort": "ExampleFile",
- "modelType": "File",
- "contentType": "application/pdf"
- },
- {
- "idShort": "ExampleMultiLanguageProperty",
- "category": "PARAMETER",
- "modelType": "MultiLanguageProperty"
- },
- {
- "idShort": "ExampleProperty",
- "category": "PARAMETER",
- "modelType": "Property",
- "valueType": "xs:string"
- },
- {
- "idShort": "ExampleRange",
- "category": "PARAMETER",
- "modelType": "Range",
- "valueType": "xs:int"
- },
- {
- "idShort": "ExampleReferenceElement",
- "category": "PARAMETER",
- "modelType": "ReferenceElement"
- }
- ]
- },
- {
- "modelType": "SubmodelElementCollection"
- }
- ]
- },
- {
- "idShort": "ExampleSubmodelList2",
- "typeValueListElement": "Capability",
- "modelType": "SubmodelElementList"
- }
- ]
- },
- {
- "modelType": "Submodel",
- "id": "https://acplt.org/Test_Submodel2_Mandatory"
- },
- {
- "idShort": "TestSubmodel",
- "description": [
- {
- "language": "en-US",
- "text": "An example submodel for the test application"
- },
- {
- "language": "de",
- "text": "Ein Beispiel-Teilmodell f\u00fcr eine Test-Anwendung"
- }
- ],
- "modelType": "Submodel",
- "id": "https://acplt.org/Test_Submodel_Missing",
- "administration": {
- "version": "9",
- "revision": "0"
- },
- "semanticId": {
- "type": "ExternalReference",
- "keys": [
- {
- "type": "GlobalReference",
- "value": "http://acplt.org/SubmodelTemplates/ExampleSubmodel"
- }
- ]
- },
- "submodelElements": [
- {
- "idShort": "ExampleRelationshipElement",
- "category": "PARAMETER",
- "description": [
- {
- "language": "en-US",
- "text": "Example RelationshipElement object"
- },
- {
- "language": "de",
- "text": "Beispiel RelationshipElement Element"
- }
- ],
- "modelType": "RelationshipElement",
- "semanticId": {
- "type": "ExternalReference",
- "keys": [
- {
- "type": "GlobalReference",
- "value": "http://acplt.org/RelationshipElements/ExampleRelationshipElement"
- }
- ]
- },
- "first": {
- "type": "ModelReference",
- "keys": [
- {
- "type": "Submodel",
- "value": "http://acplt.org/Test_Submodel"
- },
- {
- "type": "Property",
- "value": "ExampleProperty"
- }
- ]
- },
- "second": {
- "type": "ModelReference",
- "keys": [
- {
- "type": "Submodel",
- "value": "http://acplt.org/Test_Submodel"
- },
- {
- "type": "Property",
- "value": "ExampleProperty"
- }
- ]
- }
- },
- {
- "idShort": "ExampleAnnotatedRelationshipElement",
- "category": "PARAMETER",
- "description": [
- {
- "language": "en-US",
- "text": "Example AnnotatedRelationshipElement object"
- },
- {
- "language": "de",
- "text": "Beispiel AnnotatedRelationshipElement Element"
- }
- ],
- "modelType": "AnnotatedRelationshipElement",
- "semanticId": {
- "type": "ExternalReference",
- "keys": [
- {
- "type": "GlobalReference",
- "value": "http://acplt.org/RelationshipElements/ExampleAnnotatedRelationshipElement"
- }
- ]
- },
- "first": {
- "type": "ModelReference",
- "keys": [
- {
- "type": "Submodel",
- "value": "http://acplt.org/Test_Submodel"
- },
- {
- "type": "Property",
- "value": "ExampleProperty"
- }
- ]
- },
- "second": {
- "type": "ModelReference",
- "keys": [
- {
- "type": "Submodel",
- "value": "http://acplt.org/Test_Submodel"
- },
- {
- "type": "Property",
- "value": "ExampleProperty"
- }
- ]
- },
- "annotations": [
- {
- "idShort": "ExampleAnnotatedRange",
- "category": "PARAMETER",
- "modelType": "Range",
- "valueType": "xs:integer",
- "min": "1",
- "max": "5"
- },
- {
- "idShort": "ExampleAnnotatedProperty",
- "category": "PARAMETER",
- "modelType": "Property",
- "value": "exampleValue",
- "valueType": "xs:string"
- }
- ]
- },
- {
- "idShort": "ExampleOperation",
- "category": "PARAMETER",
- "description": [
- {
- "language": "en-US",
- "text": "Example Operation object"
- },
- {
- "language": "de",
- "text": "Beispiel Operation Element"
- }
- ],
- "modelType": "Operation",
- "semanticId": {
- "type": "ExternalReference",
- "keys": [
- {
- "type": "GlobalReference",
- "value": "http://acplt.org/Operations/ExampleOperation"
- }
- ]
- },
- "inputVariables": [
- {
- "value": {
- "idShort": "ExamplePropertyInput",
- "displayName": [
- {
- "language": "en-US",
- "text": "ExampleProperty"
- },
- {
- "language": "de",
- "text": "BeispielProperty"
- }
- ],
- "category": "CONSTANT",
- "description": [
- {
- "language": "en-US",
- "text": "Example Property object"
- },
- {
- "language": "de",
- "text": "Beispiel Property Element"
- }
- ],
- "modelType": "Property",
- "semanticId": {
- "type": "ExternalReference",
- "keys": [
- {
- "type": "GlobalReference",
- "value": "http://acplt.org/Properties/ExamplePropertyInput"
- }
- ]
- },
- "kind": "Template",
- "value": "exampleValue",
- "valueId": {
- "type": "ExternalReference",
- "keys": [
- {
- "type": "GlobalReference",
- "value": "http://acplt.org/ValueId/ExampleValueId"
- }
- ]
- },
- "valueType": "xs:string"
- }
- }
- ],
- "outputVariables": [
- {
- "value": {
- "idShort": "ExamplePropertyOutput",
- "displayName": [
- {
- "language": "en-US",
- "text": "ExampleProperty"
- },
- {
- "language": "de",
- "text": "BeispielProperty"
- }
- ],
- "category": "CONSTANT",
- "description": [
- {
- "language": "en-US",
- "text": "Example Property object"
- },
- {
- "language": "de",
- "text": "Beispiel Property Element"
- }
- ],
- "modelType": "Property",
- "semanticId": {
- "type": "ExternalReference",
- "keys": [
- {
- "type": "GlobalReference",
- "value": "http://acplt.org/Properties/ExamplePropertyOutput"
- }
- ]
- },
- "kind": "Template",
- "value": "exampleValue",
- "valueId": {
- "type": "ExternalReference",
- "keys": [
- {
- "type": "GlobalReference",
- "value": "http://acplt.org/ValueId/ExampleValueId"
- }
- ]
- },
- "valueType": "xs:string"
- }
- }
- ],
- "inoutputVariables": [
- {
- "value": {
- "idShort": "ExamplePropertyInOutput",
- "displayName": [
- {
- "language": "en-US",
- "text": "ExampleProperty"
- },
- {
- "language": "de",
- "text": "BeispielProperty"
- }
- ],
- "category": "CONSTANT",
- "description": [
- {
- "language": "en-US",
- "text": "Example Property object"
- },
- {
- "language": "de",
- "text": "Beispiel Property Element"
- }
- ],
- "modelType": "Property",
- "semanticId": {
- "type": "ExternalReference",
- "keys": [
- {
- "type": "GlobalReference",
- "value": "http://acplt.org/Properties/ExamplePropertyInOutput"
- }
- ]
- },
- "kind": "Template",
- "value": "exampleValue",
- "valueId": {
- "type": "ExternalReference",
- "keys": [
- {
- "type": "GlobalReference",
- "value": "http://acplt.org/ValueId/ExampleValueId"
- }
- ]
- },
- "valueType": "xs:string"
- }
- }
- ]
- },
- {
- "idShort": "ExampleCapability",
- "category": "PARAMETER",
- "description": [
- {
- "language": "en-US",
- "text": "Example Capability object"
- },
- {
- "language": "de",
- "text": "Beispiel Capability Element"
- }
- ],
- "modelType": "Capability",
- "semanticId": {
- "type": "ExternalReference",
- "keys": [
- {
- "type": "GlobalReference",
- "value": "http://acplt.org/Capabilities/ExampleCapability"
- }
- ]
- }
- },
- {
- "idShort": "ExampleBasicEventElement",
- "category": "PARAMETER",
- "description": [
- {
- "language": "en-US",
- "text": "Example BasicEventElement object"
- },
- {
- "language": "de",
- "text": "Beispiel BasicEventElement Element"
- }
- ],
- "modelType": "BasicEventElement",
- "semanticId": {
- "type": "ExternalReference",
- "keys": [
- {
- "type": "GlobalReference",
- "value": "http://acplt.org/Events/ExampleBasicEventElement"
- }
- ]
- },
- "observed": {
- "type": "ModelReference",
- "keys": [
- {
- "type": "Submodel",
- "value": "http://acplt.org/Test_Submodel"
- },
- {
- "type": "Property",
- "value": "ExampleProperty"
- }
- ]
- },
- "direction": "output",
- "state": "on",
- "messageTopic": "ExampleTopic",
- "messageBroker": {
- "type": "ModelReference",
- "keys": [
- {
- "type": "Submodel",
- "value": "http://acplt.org/ExampleMessageBroker"
- }
- ]
- },
- "lastUpdate": "2022-11-12T23:50:23.123456+00:00",
- "minInterval": "PT0.000001S",
- "maxInterval": "P1Y2M3DT4H5M6.123456S"
- },
- {
- "idShort": "ExampleSubmodelCollection",
- "category": "PARAMETER",
- "description": [
- {
- "language": "en-US",
- "text": "Example SubmodelElementCollection object"
- },
- {
- "language": "de",
- "text": "Beispiel SubmodelElementCollection Element"
- }
- ],
- "modelType": "SubmodelElementCollection",
- "semanticId": {
- "type": "ExternalReference",
- "keys": [
- {
- "type": "GlobalReference",
- "value": "http://acplt.org/SubmodelElementCollections/ExampleSubmodelElementCollection"
- }
- ]
- },
- "value": [
- {
- "idShort": "ExampleBlob",
- "category": "PARAMETER",
- "description": [
- {
- "language": "en-US",
- "text": "Example Blob object"
- },
- {
- "language": "de",
- "text": "Beispiel Blob Element"
- }
- ],
- "modelType": "Blob",
- "semanticId": {
- "type": "ExternalReference",
- "keys": [
- {
- "type": "GlobalReference",
- "value": "http://acplt.org/Blobs/ExampleBlob"
- }
- ]
- },
- "contentType": "application/pdf",
- "value": "AQIDBAU="
- },
- {
- "idShort": "ExampleFile",
- "category": "PARAMETER",
- "description": [
- {
- "language": "en-US",
- "text": "Example File object"
- },
- {
- "language": "de",
- "text": "Beispiel File Element"
- }
- ],
- "modelType": "File",
- "semanticId": {
- "type": "ExternalReference",
- "keys": [
- {
- "type": "GlobalReference",
- "value": "http://acplt.org/Files/ExampleFile"
- }
- ]
- },
- "value": "/TestFile.pdf",
- "contentType": "application/pdf"
- },
- {
- "idShort": "ExampleMultiLanguageProperty",
- "category": "CONSTANT",
- "description": [
- {
- "language": "en-US",
- "text": "Example MultiLanguageProperty object"
- },
- {
- "language": "de",
- "text": "Beispiel MultiLanguageProperty Element"
- }
- ],
- "modelType": "MultiLanguageProperty",
- "semanticId": {
- "type": "ExternalReference",
- "keys": [
- {
- "type": "GlobalReference",
- "value": "http://acplt.org/MultiLanguageProperties/ExampleMultiLanguageProperty"
- }
- ]
- },
- "value": [
- {
- "language": "en-US",
- "text": "Example value of a MultiLanguageProperty element"
- },
- {
- "language": "de",
- "text": "Beispielwert f\u00fcr ein MultiLanguageProperty-Element"
- }
- ]
- },
- {
- "idShort": "ExampleProperty",
- "category": "CONSTANT",
- "description": [
- {
- "language": "en-US",
- "text": "Example Property object"
- },
- {
- "language": "de",
- "text": "Beispiel Property Element"
- }
- ],
- "modelType": "Property",
- "semanticId": {
- "type": "ExternalReference",
- "keys": [
- {
- "type": "GlobalReference",
- "value": "http://acplt.org/Properties/ExampleProperty"
- }
- ]
- },
- "qualifiers": [
- {
- "valueType": "xs:string",
- "type": "http://acplt.org/Qualifier/ExampleQualifier"
- }
- ],
- "value": "exampleValue",
- "valueType": "xs:string"
- },
- {
- "idShort": "ExampleRange",
- "category": "PARAMETER",
- "description": [
- {
- "language": "en-US",
- "text": "Example Range object"
- },
- {
- "language": "de",
- "text": "Beispiel Range Element"
- }
- ],
- "modelType": "Range",
- "semanticId": {
- "type": "ExternalReference",
- "keys": [
- {
- "type": "GlobalReference",
- "value": "http://acplt.org/Ranges/ExampleRange"
- }
- ]
- },
- "valueType": "xs:int",
- "min": "0",
- "max": "100"
- },
- {
- "idShort": "ExampleReferenceElement",
- "category": "PARAMETER",
- "description": [
- {
- "language": "en-US",
- "text": "Example Reference Element object"
- },
- {
- "language": "de",
- "text": "Beispiel Reference Element Element"
- }
- ],
- "modelType": "ReferenceElement",
- "semanticId": {
- "type": "ExternalReference",
- "keys": [
- {
- "type": "GlobalReference",
- "value": "http://acplt.org/ReferenceElements/ExampleReferenceElement"
- }
- ]
- },
- "value": {
- "type": "ModelReference",
- "keys": [
- {
- "type": "Submodel",
- "value": "http://acplt.org/Test_Submodel"
- },
- {
- "type": "Property",
- "value": "ExampleProperty"
- }
- ]
- }
- }
- ]
- }
- ]
- },
- {
- "idShort": "TestSubmodel",
- "description": [
- {
- "language": "en-US",
- "text": "An example submodel for the test application"
- },
- {
- "language": "de",
- "text": "Ein Beispiel-Teilmodell f\u00fcr eine Test-Anwendung"
- }
- ],
- "modelType": "Submodel",
- "id": "https://acplt.org/Test_Submodel_Template",
- "administration": {
- "version": "9",
- "revision": "0"
- },
- "semanticId": {
- "type": "ExternalReference",
- "keys": [
- {
- "type": "GlobalReference",
- "value": "http://acplt.org/SubmodelTemplates/ExampleSubmodel"
- }
- ]
- },
- "kind": "Template",
- "submodelElements": [
- {
- "idShort": "ExampleRelationshipElement",
- "category": "PARAMETER",
- "description": [
- {
- "language": "en-US",
- "text": "Example RelationshipElement object"
- },
- {
- "language": "de",
- "text": "Beispiel RelationshipElement Element"
- }
- ],
- "modelType": "RelationshipElement",
- "semanticId": {
- "type": "ExternalReference",
- "keys": [
- {
- "type": "GlobalReference",
- "value": "http://acplt.org/RelationshipElements/ExampleRelationshipElement"
- }
- ]
- },
- "kind": "Template",
- "first": {
- "type": "ModelReference",
- "keys": [
- {
- "type": "Submodel",
- "value": "http://acplt.org/Test_Submodel"
- },
- {
- "type": "Property",
- "value": "ExampleProperty"
- }
- ]
- },
- "second": {
- "type": "ModelReference",
- "keys": [
- {
- "type": "Submodel",
- "value": "http://acplt.org/Test_Submodel"
- },
- {
- "type": "Property",
- "value": "ExampleProperty"
- }
- ]
- }
- },
- {
- "idShort": "ExampleAnnotatedRelationshipElement",
- "category": "PARAMETER",
- "description": [
- {
- "language": "en-US",
- "text": "Example AnnotatedRelationshipElement object"
- },
- {
- "language": "de",
- "text": "Beispiel AnnotatedRelationshipElement Element"
- }
- ],
- "modelType": "AnnotatedRelationshipElement",
- "semanticId": {
- "type": "ExternalReference",
- "keys": [
- {
- "type": "GlobalReference",
- "value": "http://acplt.org/RelationshipElements/ExampleAnnotatedRelationshipElement"
- }
- ]
- },
- "kind": "Template",
- "first": {
- "type": "ModelReference",
- "keys": [
- {
- "type": "Submodel",
- "value": "http://acplt.org/Test_Submodel"
- },
- {
- "type": "Property",
- "value": "ExampleProperty"
- }
- ]
- },
- "second": {
- "type": "ModelReference",
- "keys": [
- {
- "type": "Submodel",
- "value": "http://acplt.org/Test_Submodel"
- },
- {
- "type": "Property",
- "value": "ExampleProperty"
- }
- ]
- }
- },
- {
- "idShort": "ExampleOperation",
- "category": "PARAMETER",
- "description": [
- {
- "language": "en-US",
- "text": "Example Operation object"
- },
- {
- "language": "de",
- "text": "Beispiel Operation Element"
- }
- ],
- "modelType": "Operation",
- "semanticId": {
- "type": "ExternalReference",
- "keys": [
- {
- "type": "GlobalReference",
- "value": "http://acplt.org/Operations/ExampleOperation"
- }
- ]
- },
- "kind": "Template",
- "inputVariables": [
- {
- "value": {
- "idShort": "ExamplePropertyInput",
- "category": "CONSTANT",
- "description": [
- {
- "language": "en-US",
- "text": "Example Property object"
- },
- {
- "language": "de",
- "text": "Beispiel Property Element"
- }
- ],
- "modelType": "Property",
- "semanticId": {
- "type": "ExternalReference",
- "keys": [
- {
- "type": "GlobalReference",
- "value": "http://acplt.org/Properties/ExamplePropertyInput"
- }
- ]
- },
- "kind": "Template",
- "valueType": "xs:string"
- }
- }
- ],
- "outputVariables": [
- {
- "value": {
- "idShort": "ExamplePropertyOutput",
- "category": "CONSTANT",
- "description": [
- {
- "language": "en-US",
- "text": "Example Property object"
- },
- {
- "language": "de",
- "text": "Beispiel Property Element"
- }
- ],
- "modelType": "Property",
- "semanticId": {
- "type": "ExternalReference",
- "keys": [
- {
- "type": "GlobalReference",
- "value": "http://acplt.org/Properties/ExamplePropertyOutput"
- }
- ]
- },
- "kind": "Template",
- "valueType": "xs:string"
- }
- }
- ],
- "inoutputVariables": [
- {
- "value": {
- "idShort": "ExamplePropertyInOutput",
- "category": "CONSTANT",
- "description": [
- {
- "language": "en-US",
- "text": "Example Property object"
- },
- {
- "language": "de",
- "text": "Beispiel Property Element"
- }
- ],
- "modelType": "Property",
- "semanticId": {
- "type": "ExternalReference",
- "keys": [
- {
- "type": "GlobalReference",
- "value": "http://acplt.org/Properties/ExamplePropertyInOutput"
- }
- ]
- },
- "kind": "Template",
- "valueType": "xs:string"
- }
- }
- ]
- },
- {
- "idShort": "ExampleCapability",
- "category": "PARAMETER",
- "description": [
- {
- "language": "en-US",
- "text": "Example Capability object"
- },
- {
- "language": "de",
- "text": "Beispiel Capability Element"
- }
- ],
- "modelType": "Capability",
- "semanticId": {
- "type": "ExternalReference",
- "keys": [
- {
- "type": "GlobalReference",
- "value": "http://acplt.org/Capabilities/ExampleCapability"
- }
- ]
- },
- "kind": "Template"
- },
- {
- "idShort": "ExampleBasicEventElement",
- "category": "PARAMETER",
- "description": [
- {
- "language": "en-US",
- "text": "Example BasicEventElement object"
- },
- {
- "language": "de",
- "text": "Beispiel BasicEventElement Element"
- }
- ],
- "modelType": "BasicEventElement",
- "semanticId": {
- "type": "ExternalReference",
- "keys": [
- {
- "type": "GlobalReference",
- "value": "http://acplt.org/Events/ExampleBasicEventElement"
- }
- ]
- },
- "kind": "Template",
- "observed": {
- "type": "ModelReference",
- "keys": [
- {
- "type": "Submodel",
- "value": "http://acplt.org/Test_Submodel"
- },
- {
- "type": "Property",
- "value": "ExampleProperty"
- }
- ]
- },
- "direction": "output",
- "state": "on",
- "messageTopic": "ExampleTopic",
- "messageBroker": {
- "type": "ModelReference",
- "keys": [
- {
- "type": "Submodel",
- "value": "http://acplt.org/ExampleMessageBroker"
- }
- ]
- },
- "lastUpdate": "2022-11-12T23:50:23.123456+00:00",
- "minInterval": "PT0.000001S",
- "maxInterval": "P1Y2M3DT4H5M6.123456S"
- },
- {
- "idShort": "ExampleSubmodelList",
- "typeValueListElement": "SubmodelElementCollection",
- "semanticIdListElement": {
- "type": "ExternalReference",
- "keys": [
- {
- "type": "GlobalReference",
- "value": "http://acplt.org/SubmodelElementCollections/ExampleSubmodelElementCollection"
- }
- ]
- },
- "orderRelevant": true,
- "category": "PARAMETER",
- "description": [
- {
- "language": "en-US",
- "text": "Example SubmodelElementList object"
- },
- {
- "language": "de",
- "text": "Beispiel SubmodelElementList Element"
- }
- ],
- "modelType": "SubmodelElementList",
- "semanticId": {
- "type": "ExternalReference",
- "keys": [
- {
- "type": "GlobalReference",
- "value": "http://acplt.org/SubmodelElementLists/ExampleSubmodelElementList"
- }
- ]
- },
- "kind": "Template",
- "value": [
- {
- "category": "PARAMETER",
- "description": [
- {
- "language": "en-US",
- "text": "Example SubmodelElementCollection object"
- },
- {
- "language": "de",
- "text": "Beispiel SubmodelElementCollection Element"
- }
- ],
- "modelType": "SubmodelElementCollection",
- "semanticId": {
- "type": "ExternalReference",
- "keys": [
- {
- "type": "GlobalReference",
- "value": "http://acplt.org/SubmodelElementCollections/ExampleSubmodelElementCollection"
- }
- ]
- },
- "kind": "Template",
- "value": [
- {
- "idShort": "ExampleProperty",
- "category": "CONSTANT",
- "description": [
- {
- "language": "en-US",
- "text": "Example Property object"
- },
- {
- "language": "de",
- "text": "Beispiel Property Element"
- }
- ],
- "modelType": "Property",
- "semanticId": {
- "type": "ExternalReference",
- "keys": [
- {
- "type": "GlobalReference",
- "value": "http://acplt.org/Properties/ExampleProperty"
- }
- ]
- },
- "kind": "Template",
- "valueType": "xs:string"
- },
- {
- "idShort": "ExampleMultiLanguageProperty",
- "category": "CONSTANT",
- "description": [
- {
- "language": "en-US",
- "text": "Example MultiLanguageProperty object"
- },
- {
- "language": "de",
- "text": "Beispiel MultiLanguageProperty Element"
- }
- ],
- "modelType": "MultiLanguageProperty",
- "semanticId": {
- "type": "ExternalReference",
- "keys": [
- {
- "type": "GlobalReference",
- "value": "http://acplt.org/MultiLanguageProperties/ExampleMultiLanguageProperty"
- }
- ]
- },
- "kind": "Template"
- },
- {
- "idShort": "ExampleRange",
- "category": "PARAMETER",
- "description": [
- {
- "language": "en-US",
- "text": "Example Range object"
- },
- {
- "language": "de",
- "text": "Beispiel Range Element"
- }
- ],
- "modelType": "Range",
- "semanticId": {
- "type": "ExternalReference",
- "keys": [
- {
- "type": "GlobalReference",
- "value": "http://acplt.org/Ranges/ExampleRange"
- }
- ]
- },
- "kind": "Template",
- "valueType": "xs:int",
- "max": "100"
- },
- {
- "idShort": "ExampleRange2",
- "category": "PARAMETER",
- "description": [
- {
- "language": "en-US",
- "text": "Example Range object"
- },
- {
- "language": "de",
- "text": "Beispiel Range Element"
- }
- ],
- "modelType": "Range",
- "semanticId": {
- "type": "ExternalReference",
- "keys": [
- {
- "type": "GlobalReference",
- "value": "http://acplt.org/Ranges/ExampleRange"
- }
- ]
- },
- "kind": "Template",
- "valueType": "xs:int",
- "min": "0"
- },
- {
- "idShort": "ExampleBlob",
- "category": "PARAMETER",
- "description": [
- {
- "language": "en-US",
- "text": "Example Blob object"
- },
- {
- "language": "de",
- "text": "Beispiel Blob Element"
- }
- ],
- "modelType": "Blob",
- "semanticId": {
- "type": "ExternalReference",
- "keys": [
- {
- "type": "GlobalReference",
- "value": "http://acplt.org/Blobs/ExampleBlob"
- }
- ]
- },
- "kind": "Template",
- "contentType": "application/pdf"
- },
- {
- "idShort": "ExampleFile",
- "category": "PARAMETER",
- "description": [
- {
- "language": "en-US",
- "text": "Example File object"
- },
- {
- "language": "de",
- "text": "Beispiel File Element"
- }
- ],
- "modelType": "File",
- "semanticId": {
- "type": "ExternalReference",
- "keys": [
- {
- "type": "GlobalReference",
- "value": "http://acplt.org/Files/ExampleFile"
- }
- ]
- },
- "kind": "Template",
- "contentType": "application/pdf"
- },
- {
- "idShort": "ExampleReferenceElement",
- "category": "PARAMETER",
- "description": [
- {
- "language": "en-US",
- "text": "Example Reference Element object"
- },
- {
- "language": "de",
- "text": "Beispiel Reference Element Element"
- }
- ],
- "modelType": "ReferenceElement",
- "semanticId": {
- "type": "ExternalReference",
- "keys": [
- {
- "type": "GlobalReference",
- "value": "http://acplt.org/ReferenceElements/ExampleReferenceElement"
- }
- ]
- },
- "kind": "Template"
- }
- ]
- },
- {
- "category": "PARAMETER",
- "description": [
- {
- "language": "en-US",
- "text": "Example SubmodelElementCollection object"
- },
- {
- "language": "de",
- "text": "Beispiel SubmodelElementCollection Element"
- }
- ],
- "modelType": "SubmodelElementCollection",
- "semanticId": {
- "type": "ExternalReference",
- "keys": [
- {
- "type": "GlobalReference",
- "value": "http://acplt.org/SubmodelElementCollections/ExampleSubmodelElementCollection"
- }
- ]
- },
- "kind": "Template"
- }
- ]
- },
- {
- "idShort": "ExampleSubmodelList2",
- "typeValueListElement": "Capability",
- "semanticIdListElement": {
- "type": "ExternalReference",
- "keys": [
- {
- "type": "GlobalReference",
- "value": "http://acplt.org/SubmodelElementCollections/ExampleSubmodelElementCollection"
- }
- ]
- },
- "orderRelevant": true,
- "category": "PARAMETER",
- "description": [
- {
- "language": "en-US",
- "text": "Example SubmodelElementList object"
- },
- {
- "language": "de",
- "text": "Beispiel SubmodelElementList Element"
- }
- ],
- "modelType": "SubmodelElementList",
- "semanticId": {
- "type": "ExternalReference",
- "keys": [
- {
- "type": "GlobalReference",
- "value": "http://acplt.org/SubmodelElementLists/ExampleSubmodelElementList"
- }
- ]
- },
- "kind": "Template"
- }
- ]
- }
- ],
- "conceptDescriptions": [
- {
- "idShort": "TestConceptDescription",
- "description": [
- {
- "language": "en-US",
- "text": "An example concept description for the test application"
- },
- {
- "language": "de",
- "text": "Ein Beispiel-ConceptDescription f\u00fcr eine Test-Anwendung"
- }
- ],
- "modelType": "ConceptDescription",
- "id": "https://acplt.org/Test_ConceptDescription",
- "administration": {
- "version": "9",
- "revision": "0",
- "creator": {
- "type": "ExternalReference",
- "keys": [
- {
- "type": "GlobalReference",
- "value": "http://acplt.org/AdministrativeInformation/Test_ConceptDescription"
- }
- ]
- },
- "templateId": "http://acplt.org/AdministrativeInformationTemplates/Test_ConceptDescription",
- "embeddedDataSpecifications": [
- {
- "dataSpecification": {
- "type": "ExternalReference",
- "keys": [
- {
- "type": "GlobalReference",
- "value": "https://admin-shell.io/DataSpecificationTemplates/DataSpecificationIEC61360/3/0"
- }
- ]
- },
- "dataSpecificationContent": {
- "modelType": "DataSpecificationIec61360",
- "preferredName": [
- {
- "language": "de",
- "text": "Test Specification"
- },
- {
- "language": "en-US",
- "text": "TestSpecification"
- }
- ],
- "dataType": "REAL_MEASURE",
- "definition": [
- {
- "language": "de",
- "text": "Dies ist eine Data Specification f\u00fcr Testzwecke"
- },
- {
- "language": "en-US",
- "text": "This is a DataSpecification for testing purposes"
- }
- ],
- "shortName": [
- {
- "language": "de",
- "text": "Test Spec"
- },
- {
- "language": "en-US",
- "text": "TestSpec"
- }
- ],
- "unit": "SpaceUnit",
- "unitId": {
- "type": "ExternalReference",
- "keys": [
- {
- "type": "GlobalReference",
- "value": "http://acplt.org/Units/SpaceUnit"
- }
- ]
- },
- "sourceOfDefinition": "http://acplt.org/DataSpec/ExampleDef",
- "symbol": "SU",
- "valueFormat": "M",
- "valueList": {
- "valueReferencePairs": [
- {
- "value": "exampleValue",
- "valueId": {
- "type": "ExternalReference",
- "keys": [
- {
- "type": "GlobalReference",
- "value": "http://acplt.org/ValueId/ExampleValueId"
- }
- ]
- }
- },
- {
- "value": "exampleValue2",
- "valueId": {
- "type": "ExternalReference",
- "keys": [
- {
- "type": "GlobalReference",
- "value": "http://acplt.org/ValueId/ExampleValueId2"
- }
- ]
- }
- }
- ]
- },
- "value": "TEST",
- "valueId": {
- "type": "ExternalReference",
- "keys": [
- {
- "type": "GlobalReference",
- "value": "http://acplt.org/Values/TestValueId"
- }
- ]
- },
- "levelType": {
- "min": true,
- "max": true,
- "nom": false,
- "typ": false
- }
- }
- }
- ]
- },
- "isCaseOf": [
- {
- "type": "ExternalReference",
- "keys": [
- {
- "type": "GlobalReference",
- "value": "http://acplt.org/DataSpecifications/ConceptDescriptions/TestConceptDescription"
- }
- ]
- }
- ]
- },
- {
- "modelType": "ConceptDescription",
- "id": "https://acplt.org/Test_ConceptDescription_Mandatory"
- },
- {
- "idShort": "TestConceptDescription",
- "description": [
- {
- "language": "en-US",
- "text": "An example concept description for the test application"
- },
- {
- "language": "de",
- "text": "Ein Beispiel-ConceptDescription f\u00fcr eine Test-Anwendung"
- }
- ],
- "modelType": "ConceptDescription",
- "id": "https://acplt.org/Test_ConceptDescription_Missing",
- "administration": {
- "version": "9",
- "revision": "0"
- }
- }
- ]
-}
\ No newline at end of file
diff --git a/compliance_tool/test/files/test_demo_full_example.xml b/compliance_tool/test/files/test_demo_full_example.xml
deleted file mode 100644
index 759e3c403..000000000
--- a/compliance_tool/test/files/test_demo_full_example.xml
+++ /dev/null
@@ -1,2991 +0,0 @@
-
-
-
-
- TestAssetAdministrationShell
-
-
- en-US
- An Example Asset Administration Shell for the test application
-
-
- de
- Ein Beispiel-Verwaltungsschale für eine Test-Anwendung
-
-
-
- 9
- 0
-
- ExternalReference
-
-
- GlobalReference
- http://acplt.org/AdministrativeInformation/Test_AssetAdministrationShell
-
-
-
- http://acplt.org/AdministrativeInformationTemplates/Test_AssetAdministrationShell
-
- https://acplt.org/Test_AssetAdministrationShell
-
-
-
- ExternalReference
-
-
- GlobalReference
- https://admin-shell.io/DataSpecificationTemplates/DataSpecificationIEC61360/3/0
-
-
-
-
-
-
-
- de
- Test Specification
-
-
- en-US
- TestSpecification
-
-
-
-
- de
- Test Spec
-
-
- en-US
- TestSpec
-
-
- SpaceUnit
-
- ExternalReference
-
-
- GlobalReference
- http://acplt.org/Units/SpaceUnit
-
-
-
- http://acplt.org/DataSpec/ExampleDef
- SU
- REAL_MEASURE
-
-
- de
- Dies ist eine Data Specification für Testzwecke
-
-
- en-US
- This is a DataSpecification for testing purposes
-
-
- M
-
-
-
- exampleValue
-
- ExternalReference
-
-
- GlobalReference
- http://acplt.org/ValueId/ExampleValueId
-
-
-
-
-
- exampleValue2
-
- ExternalReference
-
-
- GlobalReference
- http://acplt.org/ValueId/ExampleValueId2
-
-
-
-
-
-
- TEST
-
- true
- false
- false
- true
-
-
-
-
-
-
- ModelReference
-
-
- AssetAdministrationShell
- https://acplt.org/TestAssetAdministrationShell2
-
-
-
-
- Instance
- http://acplt.org/TestAsset/
-
-
-
- ExternalReference
-
-
- GlobalReference
- http://acplt.org/SpecificAssetId/
-
-
-
- TestKey
- TestValue
-
- ExternalReference
-
-
- GlobalReference
- http://acplt.org/SpecificAssetId/
-
-
-
-
-
- http://acplt.org/TestAssetType/
-
- file:///path/to/thumbnail.png
- image/png
-
-
-
-
- ModelReference
-
- ModelReference
-
-
- Submodel
- http://acplt.org/SubmodelTemplates/AssetIdentification
-
-
-
-
-
- Submodel
- http://acplt.org/Submodels/Assets/TestAsset/Identification
-
-
-
-
- ModelReference
-
- ExternalReference
-
-
- GlobalReference
- http://acplt.org/SubmodelTemplates/ExampleSubmodel
-
-
-
-
-
- Submodel
- https://acplt.org/Test_Submodel
-
-
-
-
- ModelReference
-
-
- Submodel
- http://acplt.org/Submodels/Assets/TestAsset/BillOfMaterial
-
-
-
-
-
-
- https://acplt.org/Test_AssetAdministrationShell_Mandatory
-
- Instance
- http://acplt.org/Test_Asset_Mandatory/
-
-
-
- ModelReference
-
-
- Submodel
- https://acplt.org/Test_Submodel2_Mandatory
-
-
-
-
- ModelReference
-
-
- Submodel
- https://acplt.org/Test_Submodel_Mandatory
-
-
-
-
-
-
- https://acplt.org/Test_AssetAdministrationShell2_Mandatory
-
- Instance
- http://acplt.org/TestAsset2_Mandatory/
-
-
-
- TestAssetAdministrationShell
-
-
- en-US
- An Example Asset Administration Shell for the test application
-
-
- de
- Ein Beispiel-Verwaltungsschale für eine Test-Anwendung
-
-
-
- 9
- 0
-
- https://acplt.org/Test_AssetAdministrationShell_Missing
-
- Instance
- http://acplt.org/Test_Asset_Missing/
-
-
- TestKey
- TestValue
-
- ExternalReference
-
-
- GlobalReference
- http://acplt.org/SpecificAssetId/
-
-
-
-
-
-
- file:///TestFile.pdf
- application/pdf
-
-
-
-
- ModelReference
-
-
- Submodel
- https://acplt.org/Test_Submodel_Missing
-
-
-
-
-
-
-
-
- Identification
-
-
- en-US
- An example asset identification submodel for the test application
-
-
- de
- Ein Beispiel-Identifikations-Submodel für eine Test-Anwendung
-
-
-
- 9
- 0
-
- ExternalReference
-
-
- GlobalReference
- http://acplt.org/AdministrativeInformation/TestAsset/Identification
-
-
-
- http://acplt.org/AdministrativeInformationTemplates/TestAsset/Identification
-
- http://acplt.org/Submodels/Assets/TestAsset/Identification
- Instance
-
- ModelReference
-
-
- Submodel
- http://acplt.org/SubmodelTemplates/AssetIdentification
-
-
-
-
-
-
-
- ExampleExtension
- xs:string
- ExampleExtensionValue
-
-
- ModelReference
-
-
- AssetAdministrationShell
- http://acplt.org/RefersTo/ExampleRefersTo
-
-
-
-
-
-
- PARAMETER
- ManufacturerName
-
-
- en-US
- Legally valid designation of the natural or judicial person which is directly responsible for the design, production, packaging and labeling of a product in respect to its being brought into circulation.
-
-
- de
- Bezeichnung für eine natürliche oder juristische Person, die für die Auslegung, Herstellung und Verpackung sowie die Etikettierung eines Produkts im Hinblick auf das 'Inverkehrbringen' im eigenen Namen verantwortlich ist
-
-
-
- ExternalReference
-
-
- GlobalReference
- 0173-1#02-AAO677#002
-
-
-
-
-
- ConceptQualifier
- http://acplt.org/Qualifier/ExampleQualifier
- xs:int
- 100
-
- ExternalReference
-
-
- GlobalReference
- http://acplt.org/ValueId/ExampleValueId
-
-
-
-
-
- TemplateQualifier
- http://acplt.org/Qualifier/ExampleQualifier2
- xs:int
- 50
-
- ExternalReference
-
-
- GlobalReference
- http://acplt.org/ValueId/ExampleValueId
-
-
-
-
-
- xs:string
- ACPLT
-
- ExternalReference
-
-
- GlobalReference
- http://acplt.org/ValueId/ExampleValueId
-
-
-
-
-
- PARAMETER
- InstanceId
-
-
- en-US
- Legally valid designation of the natural or judicial person which is directly responsible for the design, production, packaging and labeling of a product in respect to its being brought into circulation.
-
-
- de
- Bezeichnung für eine natürliche oder juristische Person, die für die Auslegung, Herstellung und Verpackung sowie die Etikettierung eines Produkts im Hinblick auf das 'Inverkehrbringen' im eigenen Namen verantwortlich ist
-
-
-
- ExternalReference
-
-
- GlobalReference
- http://opcfoundation.org/UA/DI/1.1/DeviceType/Serialnumber
-
-
-
-
-
- ValueQualifier
- http://acplt.org/Qualifier/ExampleQualifier3
- xs:dateTime
- 2023-04-07T16:59:54.870123
-
- ExternalReference
-
-
- GlobalReference
- http://acplt.org/ValueId/ExampleValueId
-
-
-
-
-
- xs:string
- 978-8234-234-342
-
- ExternalReference
-
-
- GlobalReference
- http://acplt.org/ValueId/ExampleValueId
-
-
-
-
-
-
-
- BillOfMaterial
-
-
- en-US
- An example bill of material submodel for the test application
-
-
- de
- Ein Beispiel-BillOfMaterial-Submodel für eine Test-Anwendung
-
-
-
- 9
- http://acplt.org/AdministrativeInformationTemplates/TestAsset/BillOfMaterial
-
- http://acplt.org/Submodels/Assets/TestAsset/BillOfMaterial
- Instance
-
- ModelReference
-
-
- Submodel
- http://acplt.org/SubmodelTemplates/BillOfMaterial
-
-
-
-
-
- PARAMETER
- ExampleEntity
-
-
- en-US
- Legally valid designation of the natural or judicial person which is directly responsible for the design, production, packaging and labeling of a product in respect to its being brought into circulation.
-
-
- de
- Bezeichnung für eine natürliche oder juristische Person, die für die Auslegung, Herstellung und Verpackung sowie die Etikettierung eines Produkts im Hinblick auf das 'Inverkehrbringen' im eigenen Namen verantwortlich ist
-
-
-
- ExternalReference
-
-
- GlobalReference
- http://opcfoundation.org/UA/DI/1.1/DeviceType/Serialnumber
-
-
-
-
-
- CONSTANT
- ExampleProperty2
-
-
- en-US
- Example Property object
-
-
- de
- Beispiel Property Element
-
-
-
- ExternalReference
-
-
- GlobalReference
- http://acplt.org/Properties/ExampleProperty
-
-
-
- xs:string
- exampleValue2
-
- ExternalReference
-
-
- GlobalReference
- http://acplt.org/ValueId/ExampleValueId
-
-
-
-
-
- CONSTANT
- ExampleProperty
-
-
- en-US
- Example Property object
-
-
- de
- Beispiel Property Element
-
-
-
- ExternalReference
-
-
- GlobalReference
- http://acplt.org/Properties/ExampleProperty
-
-
-
- xs:string
- exampleValue
-
- ExternalReference
-
-
- GlobalReference
- http://acplt.org/ValueId/ExampleValueId
-
-
-
-
-
- SelfManagedEntity
- http://acplt.org/TestAsset/
-
-
- TestKey
- TestValue
-
- ExternalReference
-
-
- GlobalReference
- http://acplt.org/SpecificAssetId/
-
-
-
-
-
-
-
- PARAMETER
- ExampleEntity2
-
-
- en-US
- Legally valid designation of the natural or judicial person which is directly responsible for the design, production, packaging and labeling of a product in respect to its being brought into circulation.
-
-
- de
- Bezeichnung für eine natürliche oder juristische Person, die für die Auslegung, Herstellung und Verpackung sowie die Etikettierung eines Produkts im Hinblick auf das 'Inverkehrbringen' im eigenen Namen verantwortlich ist
-
-
-
- ExternalReference
-
-
- GlobalReference
- http://opcfoundation.org/UA/DI/1.1/DeviceType/Serialnumber
-
-
-
- CoManagedEntity
-
-
-
-
- TestSubmodel
-
-
- en-US
- An example submodel for the test application
-
-
- de
- Ein Beispiel-Teilmodell für eine Test-Anwendung
-
-
-
- 9
- 0
-
- ExternalReference
-
-
- GlobalReference
- http://acplt.org/AdministrativeInformation/Test_Submodel
-
-
-
-
- https://acplt.org/Test_Submodel
- Instance
-
- ExternalReference
-
-
- GlobalReference
- http://acplt.org/SubmodelTemplates/ExampleSubmodel
-
-
-
-
-
- PARAMETER
- ExampleRelationshipElement
-
-
- en-US
- Example RelationshipElement object
-
-
- de
- Beispiel RelationshipElement Element
-
-
-
- ModelReference
-
-
- ConceptDescription
- https://acplt.org/Test_ConceptDescription
-
-
-
-
- ModelReference
-
-
- Submodel
- http://acplt.org/Test_Submodel
-
-
- Property
- ExampleProperty
-
-
-
-
- ModelReference
-
-
- Submodel
- http://acplt.org/Test_Submodel
-
-
- Property
- ExampleProperty2
-
-
-
-
-
- PARAMETER
- ExampleAnnotatedRelationshipElement
-
-
- en-US
- Example AnnotatedRelationshipElement object
-
-
- de
- Beispiel AnnotatedRelationshipElement Element
-
-
-
- ExternalReference
-
-
- GlobalReference
- http://acplt.org/RelationshipElements/ExampleAnnotatedRelationshipElement
-
-
-
-
- ModelReference
-
-
- Submodel
- http://acplt.org/Test_Submodel
-
-
- Property
- ExampleProperty
-
-
-
-
- ModelReference
-
-
- Submodel
- http://acplt.org/Test_Submodel
-
-
- Property
- ExampleProperty2
-
-
-
-
-
- PARAMETER
- ExampleAnnotatedProperty
- xs:string
- exampleValue
-
-
- PARAMETER
- ExampleAnnotatedRange
- xs:integer
- 1
- 5
-
-
-
-
- PARAMETER
- ExampleOperation
-
-
- en-US
- Example Operation object
-
-
- de
- Beispiel Operation Element
-
-
-
- ExternalReference
-
-
- GlobalReference
- http://acplt.org/Operations/ExampleOperation
-
-
-
-
-
-
-
- CONSTANT
- ExamplePropertyInput
-
-
- en-US
- ExampleProperty
-
-
- de
- BeispielProperty
-
-
-
-
- en-US
- Example Property object
-
-
- de
- Beispiel Property Element
-
-
-
- ExternalReference
-
-
- GlobalReference
- http://acplt.org/Properties/ExamplePropertyInput
-
-
-
- xs:string
- exampleValue
-
- ExternalReference
-
-
- GlobalReference
- http://acplt.org/ValueId/ExampleValueId
-
-
-
-
-
-
-
-
-
-
-
- CONSTANT
- ExamplePropertyOutput
-
-
- en-US
- ExampleProperty
-
-
- de
- BeispielProperty
-
-
-
-
- en-US
- Example Property object
-
-
- de
- Beispiel Property Element
-
-
-
- ExternalReference
-
-
- GlobalReference
- http://acplt.org/Properties/ExamplePropertyOutput
-
-
-
- xs:string
- exampleValue
-
- ExternalReference
-
-
- GlobalReference
- http://acplt.org/ValueId/ExampleValueId
-
-
-
-
-
-
-
-
-
-
-
- CONSTANT
- ExamplePropertyInOutput
-
-
- en-US
- ExampleProperty
-
-
- de
- BeispielProperty
-
-
-
-
- en-US
- Example Property object
-
-
- de
- Beispiel Property Element
-
-
-
- ExternalReference
-
-
- GlobalReference
- http://acplt.org/Properties/ExamplePropertyInOutput
-
-
-
- xs:string
- exampleValue
-
- ExternalReference
-
-
- GlobalReference
- http://acplt.org/ValueId/ExampleValueId
-
-
-
-
-
-
-
-
-
- PARAMETER
- ExampleCapability
-
-
- en-US
- Example Capability object
-
-
- de
- Beispiel Capability Element
-
-
-
- ExternalReference
-
-
- GlobalReference
- http://acplt.org/Capabilities/ExampleCapability
-
-
-
-
-
- PARAMETER
- ExampleBasicEventElement
-
-
- en-US
- Example BasicEventElement object
-
-
- de
- Beispiel BasicEventElement Element
-
-
-
- ExternalReference
-
-
- GlobalReference
- http://acplt.org/Events/ExampleBasicEventElement
-
-
-
-
- ModelReference
-
-
- Submodel
- http://acplt.org/Test_Submodel
-
-
- Property
- ExampleProperty
-
-
-
- output
- on
- ExampleTopic
-
- ModelReference
-
-
- Submodel
- http://acplt.org/ExampleMessageBroker
-
-
-
- 2022-11-12T23:50:23.123456+00:00
- PT0.000001S
- P1Y2M3DT4H5M6.123456S
-
-
- PARAMETER
- ExampleSubmodelCollection
-
-
- en-US
- Example SubmodelElementCollection object
-
-
- de
- Beispiel SubmodelElementCollection Element
-
-
-
- ExternalReference
-
-
- GlobalReference
- http://acplt.org/SubmodelElementCollections/ExampleSubmodelElementCollection
-
-
-
-
-
- PARAMETER
- ExampleBlob
-
-
- en-US
- Example Blob object
-
-
- de
- Beispiel Blob Element
-
-
-
- ExternalReference
-
-
- GlobalReference
- http://acplt.org/Blobs/ExampleBlob
-
-
-
- AQIDBAU=
- application/pdf
-
-
- PARAMETER
- ExampleFile
-
-
- en-US
- Example File object
-
-
- de
- Beispiel File Element
-
-
-
- ExternalReference
-
-
- GlobalReference
- http://acplt.org/Files/ExampleFile
-
-
-
- /TestFile.pdf
- application/pdf
-
-
- CONSTANT
- ExampleFileURI
-
-
- en-US
- Details of the Asset Administration Shell — An example for an external file reference
-
-
- de
- Details of the Asset Administration Shell – Ein Beispiel für eine extern referenzierte Datei
-
-
-
- ExternalReference
-
-
- GlobalReference
- http://acplt.org/Files/ExampleFile
-
-
-
- https://www.plattform-i40.de/PI40/Redaktion/DE/Downloads/Publikation/Details-of-the-Asset-Administration-Shell-Part1.pdf?__blob=publicationFile&v=5
- application/pdf
-
-
- PARAMETER
- ExampleSubmodelList
-
-
- en-US
- Example SubmodelElementList object
-
-
- de
- Beispiel SubmodelElementList Element
-
-
-
- ExternalReference
-
-
- GlobalReference
- http://acplt.org/SubmodelElementLists/ExampleSubmodelElementList
-
-
-
- true
-
- ExternalReference
-
-
- GlobalReference
- http://acplt.org/Properties/ExampleProperty
-
-
-
- Property
- xs:string
-
-
- CONSTANT
-
-
- en-US
- ExampleProperty
-
-
- de
- BeispielProperty
-
-
-
-
- en-US
- Example Property object
-
-
- de
- Beispiel Property Element
-
-
-
- ExternalReference
-
-
- GlobalReference
- http://acplt.org/Properties/ExampleProperty
-
-
-
-
-
- ExternalReference
-
-
- GlobalReference
- http://acplt.org/Properties/ExampleProperty/SupplementalId1
-
-
-
-
- ExternalReference
-
-
- GlobalReference
- http://acplt.org/Properties/ExampleProperty/SupplementalId2
-
-
-
-
-
-
-
- ExternalReference
-
-
- GlobalReference
- https://admin-shell.io/DataSpecificationTemplates/DataSpecificationIEC61360/3/0
-
-
-
-
-
-
-
- de
- Test Specification
-
-
- en-US
- TestSpecification
-
-
-
-
- de
- Test Spec
-
-
- en-US
- TestSpec
-
-
- SpaceUnit
-
- ExternalReference
-
-
- GlobalReference
- http://acplt.org/Units/SpaceUnit
-
-
-
- http://acplt.org/DataSpec/ExampleDef
- SU
- REAL_MEASURE
-
-
- de
- Dies ist eine Data Specification für Testzwecke
-
-
- en-US
- This is a DataSpecification for testing purposes
-
-
- M
-
-
-
- exampleValue
-
- ExternalReference
-
-
- GlobalReference
- http://acplt.org/ValueId/ExampleValueId
-
-
-
-
-
- exampleValue2
-
- ExternalReference
-
-
- GlobalReference
- http://acplt.org/ValueId/ExampleValueId2
-
-
-
-
-
-
- TEST
-
- true
- false
- false
- true
-
-
-
-
-
- xs:string
- exampleValue
-
- ExternalReference
-
-
- GlobalReference
- http://acplt.org/ValueId/ExampleValueId
-
-
-
-
-
- CONSTANT
-
-
- en-US
- ExampleProperty
-
-
- de
- BeispielProperty
-
-
-
-
- en-US
- Example Property object
-
-
- de
- Beispiel Property Element
-
-
-
- ExternalReference
-
-
- GlobalReference
- http://acplt.org/Properties/ExampleProperty
-
-
-
-
-
- ExternalReference
-
-
- GlobalReference
- http://acplt.org/Properties/ExampleProperty2/SupplementalId
-
-
-
-
- xs:string
- exampleValue
-
- ExternalReference
-
-
- GlobalReference
- http://acplt.org/ValueId/ExampleValueId
-
-
-
-
-
-
-
- CONSTANT
- ExampleMultiLanguageProperty
-
-
- en-US
- Example MultiLanguageProperty object
-
-
- de
- Beispiel MultiLanguageProperty Element
-
-
-
- ExternalReference
-
- ExternalReference
-
-
- GlobalReference
- http://acplt.org/Properties/ExampleProperty/Referred
-
-
-
-
-
- GlobalReference
- http://acplt.org/MultiLanguageProperties/ExampleMultiLanguageProperty
-
-
-
-
-
- en-US
- Example value of a MultiLanguageProperty element
-
-
- de
- Beispielwert für ein MultiLanguageProperty-Element
-
-
-
- ExternalReference
-
-
- GlobalReference
- http://acplt.org/ValueId/ExampleMultiLanguageValueId
-
-
-
-
-
- PARAMETER
- ExampleRange
-
-
- en-US
- Example Range object
-
-
- de
- Beispiel Range Element
-
-
-
- ExternalReference
-
-
- GlobalReference
- http://acplt.org/Ranges/ExampleRange
-
-
-
- xs:int
- 0
- 100
-
-
- PARAMETER
- ExampleReferenceElement
-
-
- en-US
- Example Reference Element object
-
-
- de
- Beispiel Reference Element Element
-
-
-
- ExternalReference
-
-
- GlobalReference
- http://acplt.org/ReferenceElements/ExampleReferenceElement
-
-
-
-
- ModelReference
-
-
- Submodel
- http://acplt.org/Test_Submodel
-
-
- Property
- ExampleProperty
-
-
-
-
-
-
-
-
-
- https://acplt.org/Test_Submodel_Mandatory
- Instance
-
-
- ExampleRelationshipElement
-
- ModelReference
-
-
- Submodel
- http://acplt.org/Test_Submodel
-
-
- Property
- ExampleProperty
-
-
-
-
- ModelReference
-
-
- Submodel
- http://acplt.org/Test_Submodel
-
-
- Property
- ExampleProperty
-
-
-
-
-
- ExampleAnnotatedRelationshipElement
-
- ModelReference
-
-
- Submodel
- http://acplt.org/Test_Submodel
-
-
- Property
- ExampleProperty
-
-
-
-
- ModelReference
-
-
- Submodel
- http://acplt.org/Test_Submodel
-
-
- Property
- ExampleProperty
-
-
-
-
-
- ExampleOperation
-
-
- ExampleCapability
-
-
- ExampleBasicEventElement
-
- ModelReference
-
-
- Submodel
- http://acplt.org/Test_Submodel
-
-
- Property
- ExampleProperty
-
-
-
- input
- off
-
-
- ExampleSubmodelList
- SubmodelElementCollection
-
-
-
-
- ExampleBlob
-
- application/pdf
-
-
- ExampleFile
- application/pdf
-
-
- PARAMETER
- ExampleMultiLanguageProperty
-
-
- PARAMETER
- ExampleProperty
- xs:string
-
-
- PARAMETER
- ExampleRange
- xs:int
-
-
- PARAMETER
- ExampleReferenceElement
-
-
-
-
-
-
-
-
- ExampleSubmodelList2
- Capability
-
-
-
-
- https://acplt.org/Test_Submodel2_Mandatory
- Instance
-
-
- TestSubmodel
-
-
- en-US
- An example submodel for the test application
-
-
- de
- Ein Beispiel-Teilmodell für eine Test-Anwendung
-
-
-
- 9
- 0
-
- https://acplt.org/Test_Submodel_Missing
- Instance
-
- ExternalReference
-
-
- GlobalReference
- http://acplt.org/SubmodelTemplates/ExampleSubmodel
-
-
-
-
-
- PARAMETER
- ExampleRelationshipElement
-
-
- en-US
- Example RelationshipElement object
-
-
- de
- Beispiel RelationshipElement Element
-
-
-
- ExternalReference
-
-
- GlobalReference
- http://acplt.org/RelationshipElements/ExampleRelationshipElement
-
-
-
-
- ModelReference
-
-
- Submodel
- http://acplt.org/Test_Submodel
-
-
- Property
- ExampleProperty
-
-
-
-
- ModelReference
-
-
- Submodel
- http://acplt.org/Test_Submodel
-
-
- Property
- ExampleProperty
-
-
-
-
-
- PARAMETER
- ExampleAnnotatedRelationshipElement
-
-
- en-US
- Example AnnotatedRelationshipElement object
-
-
- de
- Beispiel AnnotatedRelationshipElement Element
-
-
-
- ExternalReference
-
-
- GlobalReference
- http://acplt.org/RelationshipElements/ExampleAnnotatedRelationshipElement
-
-
-
-
- ModelReference
-
-
- Submodel
- http://acplt.org/Test_Submodel
-
-
- Property
- ExampleProperty
-
-
-
-
- ModelReference
-
-
- Submodel
- http://acplt.org/Test_Submodel
-
-
- Property
- ExampleProperty
-
-
-
-
-
- PARAMETER
- ExampleAnnotatedRange
- xs:integer
- 1
- 5
-
-
- PARAMETER
- ExampleAnnotatedProperty
- xs:string
- exampleValue
-
-
-
-
- PARAMETER
- ExampleOperation
-
-
- en-US
- Example Operation object
-
-
- de
- Beispiel Operation Element
-
-
-
- ExternalReference
-
-
- GlobalReference
- http://acplt.org/Operations/ExampleOperation
-
-
-
-
-
-
-
- CONSTANT
- ExamplePropertyInput
-
-
- en-US
- ExampleProperty
-
-
- de
- BeispielProperty
-
-
-
-
- en-US
- Example Property object
-
-
- de
- Beispiel Property Element
-
-
-
- ExternalReference
-
-
- GlobalReference
- http://acplt.org/Properties/ExamplePropertyInput
-
-
-
- xs:string
- exampleValue
-
- ExternalReference
-
-
- GlobalReference
- http://acplt.org/ValueId/ExampleValueId
-
-
-
-
-
-
-
-
-
-
-
- CONSTANT
- ExamplePropertyOutput
-
-
- en-US
- ExampleProperty
-
-
- de
- BeispielProperty
-
-
-
-
- en-US
- Example Property object
-
-
- de
- Beispiel Property Element
-
-
-
- ExternalReference
-
-
- GlobalReference
- http://acplt.org/Properties/ExamplePropertyOutput
-
-
-
- xs:string
- exampleValue
-
- ExternalReference
-
-
- GlobalReference
- http://acplt.org/ValueId/ExampleValueId
-
-
-
-
-
-
-
-
-
-
-
- CONSTANT
- ExamplePropertyInOutput
-
-
- en-US
- ExampleProperty
-
-
- de
- BeispielProperty
-
-
-
-
- en-US
- Example Property object
-
-
- de
- Beispiel Property Element
-
-
-
- ExternalReference
-
-
- GlobalReference
- http://acplt.org/Properties/ExamplePropertyInOutput
-
-
-
- xs:string
- exampleValue
-
- ExternalReference
-
-
- GlobalReference
- http://acplt.org/ValueId/ExampleValueId
-
-
-
-
-
-
-
-
-
- PARAMETER
- ExampleCapability
-
-
- en-US
- Example Capability object
-
-
- de
- Beispiel Capability Element
-
-
-
- ExternalReference
-
-
- GlobalReference
- http://acplt.org/Capabilities/ExampleCapability
-
-
-
-
-
- PARAMETER
- ExampleBasicEventElement
-
-
- en-US
- Example BasicEventElement object
-
-
- de
- Beispiel BasicEventElement Element
-
-
-
- ExternalReference
-
-
- GlobalReference
- http://acplt.org/Events/ExampleBasicEventElement
-
-
-
-
- ModelReference
-
-
- Submodel
- http://acplt.org/Test_Submodel
-
-
- Property
- ExampleProperty
-
-
-
- output
- on
- ExampleTopic
-
- ModelReference
-
-
- Submodel
- http://acplt.org/ExampleMessageBroker
-
-
-
- 2022-11-12T23:50:23.123456+00:00
- PT0.000001S
- P1Y2M3DT4H5M6.123456S
-
-
- PARAMETER
- ExampleSubmodelCollection
-
-
- en-US
- Example SubmodelElementCollection object
-
-
- de
- Beispiel SubmodelElementCollection Element
-
-
-
- ExternalReference
-
-
- GlobalReference
- http://acplt.org/SubmodelElementCollections/ExampleSubmodelElementCollection
-
-
-
-
-
- PARAMETER
- ExampleBlob
-
-
- en-US
- Example Blob object
-
-
- de
- Beispiel Blob Element
-
-
-
- ExternalReference
-
-
- GlobalReference
- http://acplt.org/Blobs/ExampleBlob
-
-
-
- AQIDBAU=
- application/pdf
-
-
- PARAMETER
- ExampleFile
-
-
- en-US
- Example File object
-
-
- de
- Beispiel File Element
-
-
-
- ExternalReference
-
-
- GlobalReference
- http://acplt.org/Files/ExampleFile
-
-
-
- /TestFile.pdf
- application/pdf
-
-
- CONSTANT
- ExampleMultiLanguageProperty
-
-
- en-US
- Example MultiLanguageProperty object
-
-
- de
- Beispiel MultiLanguageProperty Element
-
-
-
- ExternalReference
-
-
- GlobalReference
- http://acplt.org/MultiLanguageProperties/ExampleMultiLanguageProperty
-
-
-
-
-
- en-US
- Example value of a MultiLanguageProperty element
-
-
- de
- Beispielwert für ein MultiLanguageProperty-Element
-
-
-
-
- CONSTANT
- ExampleProperty
-
-
- en-US
- Example Property object
-
-
- de
- Beispiel Property Element
-
-
-
- ExternalReference
-
-
- GlobalReference
- http://acplt.org/Properties/ExampleProperty
-
-
-
-
-
- http://acplt.org/Qualifier/ExampleQualifier
- xs:string
-
-
- xs:string
- exampleValue
-
-
- PARAMETER
- ExampleRange
-
-
- en-US
- Example Range object
-
-
- de
- Beispiel Range Element
-
-
-
- ExternalReference
-
-
- GlobalReference
- http://acplt.org/Ranges/ExampleRange
-
-
-
- xs:int
- 0
- 100
-
-
- PARAMETER
- ExampleReferenceElement
-
-
- en-US
- Example Reference Element object
-
-
- de
- Beispiel Reference Element Element
-
-
-
- ExternalReference
-
-
- GlobalReference
- http://acplt.org/ReferenceElements/ExampleReferenceElement
-
-
-
-
- ModelReference
-
-
- Submodel
- http://acplt.org/Test_Submodel
-
-
- Property
- ExampleProperty
-
-
-
-
-
-
-
-
-
- TestSubmodel
-
-
- en-US
- An example submodel for the test application
-
-
- de
- Ein Beispiel-Teilmodell für eine Test-Anwendung
-
-
-
- 9
- 0
-
- https://acplt.org/Test_Submodel_Template
- Template
-
- ExternalReference
-
-
- GlobalReference
- http://acplt.org/SubmodelTemplates/ExampleSubmodel
-
-
-
-
-
- PARAMETER
- ExampleRelationshipElement
-
-
- en-US
- Example RelationshipElement object
-
-
- de
- Beispiel RelationshipElement Element
-
-
-
- ExternalReference
-
-
- GlobalReference
- http://acplt.org/RelationshipElements/ExampleRelationshipElement
-
-
-
-
- ModelReference
-
-
- Submodel
- http://acplt.org/Test_Submodel
-
-
- Property
- ExampleProperty
-
-
-
-
- ModelReference
-
-
- Submodel
- http://acplt.org/Test_Submodel
-
-
- Property
- ExampleProperty
-
-
-
-
-
- PARAMETER
- ExampleAnnotatedRelationshipElement
-
-
- en-US
- Example AnnotatedRelationshipElement object
-
-
- de
- Beispiel AnnotatedRelationshipElement Element
-
-
-
- ExternalReference
-
-
- GlobalReference
- http://acplt.org/RelationshipElements/ExampleAnnotatedRelationshipElement
-
-
-
-
- ModelReference
-
-
- Submodel
- http://acplt.org/Test_Submodel
-
-
- Property
- ExampleProperty
-
-
-
-
- ModelReference
-
-
- Submodel
- http://acplt.org/Test_Submodel
-
-
- Property
- ExampleProperty
-
-
-
-
-
- PARAMETER
- ExampleOperation
-
-
- en-US
- Example Operation object
-
-
- de
- Beispiel Operation Element
-
-
-
- ExternalReference
-
-
- GlobalReference
- http://acplt.org/Operations/ExampleOperation
-
-
-
-
-
-
-
- CONSTANT
- ExamplePropertyInput
-
-
- en-US
- Example Property object
-
-
- de
- Beispiel Property Element
-
-
-
- ExternalReference
-
-
- GlobalReference
- http://acplt.org/Properties/ExamplePropertyInput
-
-
-
- xs:string
-
-
-
-
-
-
-
-
- CONSTANT
- ExamplePropertyOutput
-
-
- en-US
- Example Property object
-
-
- de
- Beispiel Property Element
-
-
-
- ExternalReference
-
-
- GlobalReference
- http://acplt.org/Properties/ExamplePropertyOutput
-
-
-
- xs:string
-
-
-
-
-
-
-
-
- CONSTANT
- ExamplePropertyInOutput
-
-
- en-US
- Example Property object
-
-
- de
- Beispiel Property Element
-
-
-
- ExternalReference
-
-
- GlobalReference
- http://acplt.org/Properties/ExamplePropertyInOutput
-
-
-
- xs:string
-
-
-
-
-
-
- PARAMETER
- ExampleCapability
-
-
- en-US
- Example Capability object
-
-
- de
- Beispiel Capability Element
-
-
-
- ExternalReference
-
-
- GlobalReference
- http://acplt.org/Capabilities/ExampleCapability
-
-
-
-
-
- PARAMETER
- ExampleBasicEventElement
-
-
- en-US
- Example BasicEventElement object
-
-
- de
- Beispiel BasicEventElement Element
-
-
-
- ExternalReference
-
-
- GlobalReference
- http://acplt.org/Events/ExampleBasicEventElement
-
-
-
-
- ModelReference
-
-
- Submodel
- http://acplt.org/Test_Submodel
-
-
- Property
- ExampleProperty
-
-
-
- output
- on
- ExampleTopic
-
- ModelReference
-
-
- Submodel
- http://acplt.org/ExampleMessageBroker
-
-
-
- 2022-11-12T23:50:23.123456+00:00
- PT0.000001S
- P1Y2M3DT4H5M6.123456S
-
-
- PARAMETER
- ExampleSubmodelList
-
-
- en-US
- Example SubmodelElementList object
-
-
- de
- Beispiel SubmodelElementList Element
-
-
-
- ExternalReference
-
-
- GlobalReference
- http://acplt.org/SubmodelElementLists/ExampleSubmodelElementList
-
-
-
- true
-
- ExternalReference
-
-
- GlobalReference
- http://acplt.org/SubmodelElementCollections/ExampleSubmodelElementCollection
-
-
-
- SubmodelElementCollection
-
-
- PARAMETER
-
-
- en-US
- Example SubmodelElementCollection object
-
-
- de
- Beispiel SubmodelElementCollection Element
-
-
-
- ExternalReference
-
-
- GlobalReference
- http://acplt.org/SubmodelElementCollections/ExampleSubmodelElementCollection
-
-
-
-
-
- CONSTANT
- ExampleProperty
-
-
- en-US
- Example Property object
-
-
- de
- Beispiel Property Element
-
-
-
- ExternalReference
-
-
- GlobalReference
- http://acplt.org/Properties/ExampleProperty
-
-
-
- xs:string
-
-
- CONSTANT
- ExampleMultiLanguageProperty
-
-
- en-US
- Example MultiLanguageProperty object
-
-
- de
- Beispiel MultiLanguageProperty Element
-
-
-
- ExternalReference
-
-
- GlobalReference
- http://acplt.org/MultiLanguageProperties/ExampleMultiLanguageProperty
-
-
-
-
-
- PARAMETER
- ExampleRange
-
-
- en-US
- Example Range object
-
-
- de
- Beispiel Range Element
-
-
-
- ExternalReference
-
-
- GlobalReference
- http://acplt.org/Ranges/ExampleRange
-
-
-
- xs:int
- 100
-
-
- PARAMETER
- ExampleRange2
-
-
- en-US
- Example Range object
-
-
- de
- Beispiel Range Element
-
-
-
- ExternalReference
-
-
- GlobalReference
- http://acplt.org/Ranges/ExampleRange
-
-
-
- xs:int
- 0
-
-
- PARAMETER
- ExampleBlob
-
-
- en-US
- Example Blob object
-
-
- de
- Beispiel Blob Element
-
-
-
- ExternalReference
-
-
- GlobalReference
- http://acplt.org/Blobs/ExampleBlob
-
-
-
-
- application/pdf
-
-
- PARAMETER
- ExampleFile
-
-
- en-US
- Example File object
-
-
- de
- Beispiel File Element
-
-
-
- ExternalReference
-
-
- GlobalReference
- http://acplt.org/Files/ExampleFile
-
-
-
- application/pdf
-
-
- PARAMETER
- ExampleReferenceElement
-
-
- en-US
- Example Reference Element object
-
-
- de
- Beispiel Reference Element Element
-
-
-
- ExternalReference
-
-
- GlobalReference
- http://acplt.org/ReferenceElements/ExampleReferenceElement
-
-
-
-
-
-
-
- PARAMETER
-
-
- en-US
- Example SubmodelElementCollection object
-
-
- de
- Beispiel SubmodelElementCollection Element
-
-
-
- ExternalReference
-
-
- GlobalReference
- http://acplt.org/SubmodelElementCollections/ExampleSubmodelElementCollection
-
-
-
-
-
-
-
- PARAMETER
- ExampleSubmodelList2
-
-
- en-US
- Example SubmodelElementList object
-
-
- de
- Beispiel SubmodelElementList Element
-
-
-
- ExternalReference
-
-
- GlobalReference
- http://acplt.org/SubmodelElementLists/ExampleSubmodelElementList
-
-
-
- true
-
- ExternalReference
-
-
- GlobalReference
- http://acplt.org/SubmodelElementCollections/ExampleSubmodelElementCollection
-
-
-
- Capability
-
-
-
-
-
-
- TestConceptDescription
-
-
- en-US
- An example concept description for the test application
-
-
- de
- Ein Beispiel-ConceptDescription für eine Test-Anwendung
-
-
-
-
-
-
- ExternalReference
-
-
- GlobalReference
- https://admin-shell.io/DataSpecificationTemplates/DataSpecificationIEC61360/3/0
-
-
-
-
-
-
-
- de
- Test Specification
-
-
- en-US
- TestSpecification
-
-
-
-
- de
- Test Spec
-
-
- en-US
- TestSpec
-
-
- SpaceUnit
-
- ExternalReference
-
-
- GlobalReference
- http://acplt.org/Units/SpaceUnit
-
-
-
- http://acplt.org/DataSpec/ExampleDef
- SU
- REAL_MEASURE
-
-
- de
- Dies ist eine Data Specification für Testzwecke
-
-
- en-US
- This is a DataSpecification for testing purposes
-
-
- M
-
-
-
- exampleValue
-
- ExternalReference
-
-
- GlobalReference
- http://acplt.org/ValueId/ExampleValueId
-
-
-
-
-
- exampleValue2
-
- ExternalReference
-
-
- GlobalReference
- http://acplt.org/ValueId/ExampleValueId2
-
-
-
-
-
-
- TEST
-
- true
- false
- false
- true
-
-
-
-
-
- 9
- 0
-
- ExternalReference
-
-
- GlobalReference
- http://acplt.org/AdministrativeInformation/Test_ConceptDescription
-
-
-
- http://acplt.org/AdministrativeInformationTemplates/Test_ConceptDescription
-
- https://acplt.org/Test_ConceptDescription
-
-
- ExternalReference
-
-
- GlobalReference
- http://acplt.org/DataSpecifications/ConceptDescriptions/TestConceptDescription
-
-
-
-
-
-
- https://acplt.org/Test_ConceptDescription_Mandatory
-
-
- TestConceptDescription
-
-
- en-US
- An example concept description for the test application
-
-
- de
- Ein Beispiel-ConceptDescription für eine Test-Anwendung
-
-
-
- 9
- 0
-
- https://acplt.org/Test_ConceptDescription_Missing
-
-
-
diff --git a/compliance_tool/test/files/test_demo_full_example_json_aasx/TestFile.pdf b/compliance_tool/test/files/test_demo_full_example_json_aasx/TestFile.pdf
deleted file mode 100644
index 2bccbec5f..000000000
Binary files a/compliance_tool/test/files/test_demo_full_example_json_aasx/TestFile.pdf and /dev/null differ
diff --git a/compliance_tool/test/files/test_demo_full_example_json_aasx/[Content_Types].xml b/compliance_tool/test/files/test_demo_full_example_json_aasx/[Content_Types].xml
deleted file mode 100644
index 4d0bdc9aa..000000000
--- a/compliance_tool/test/files/test_demo_full_example_json_aasx/[Content_Types].xml
+++ /dev/null
@@ -1,2 +0,0 @@
-
-
\ No newline at end of file
diff --git a/compliance_tool/test/files/test_demo_full_example_json_aasx/_rels/.rels b/compliance_tool/test/files/test_demo_full_example_json_aasx/_rels/.rels
deleted file mode 100644
index 9758543f3..000000000
--- a/compliance_tool/test/files/test_demo_full_example_json_aasx/_rels/.rels
+++ /dev/null
@@ -1,2 +0,0 @@
-
-
\ No newline at end of file
diff --git a/compliance_tool/test/files/test_demo_full_example_json_aasx/aasx/_rels/aasx-origin.rels b/compliance_tool/test/files/test_demo_full_example_json_aasx/aasx/_rels/aasx-origin.rels
deleted file mode 100644
index 3ec0a479e..000000000
--- a/compliance_tool/test/files/test_demo_full_example_json_aasx/aasx/_rels/aasx-origin.rels
+++ /dev/null
@@ -1,2 +0,0 @@
-
-
\ No newline at end of file
diff --git a/compliance_tool/test/files/test_demo_full_example_json_aasx/aasx/_rels/data.json.rels b/compliance_tool/test/files/test_demo_full_example_json_aasx/aasx/_rels/data.json.rels
deleted file mode 100644
index 43350edd0..000000000
--- a/compliance_tool/test/files/test_demo_full_example_json_aasx/aasx/_rels/data.json.rels
+++ /dev/null
@@ -1,2 +0,0 @@
-
-
\ No newline at end of file
diff --git a/compliance_tool/test/files/test_demo_full_example_json_aasx/aasx/data.json b/compliance_tool/test/files/test_demo_full_example_json_aasx/aasx/data.json
deleted file mode 100644
index 7ddb5f17c..000000000
--- a/compliance_tool/test/files/test_demo_full_example_json_aasx/aasx/data.json
+++ /dev/null
@@ -1,3218 +0,0 @@
-{
- "assetAdministrationShells": [
- {
- "idShort": "TestAssetAdministrationShell",
- "description": [
- {
- "language": "en-US",
- "text": "An Example Asset Administration Shell for the test application"
- },
- {
- "language": "de",
- "text": "Ein Beispiel-Verwaltungsschale f\u00fcr eine Test-Anwendung"
- }
- ],
- "modelType": "AssetAdministrationShell",
- "id": "https://acplt.org/Test_AssetAdministrationShell",
- "administration": {
- "version": "9",
- "revision": "0",
- "creator": {
- "type": "ExternalReference",
- "keys": [
- {
- "type": "GlobalReference",
- "value": "http://acplt.org/AdministrativeInformation/Test_AssetAdministrationShell"
- }
- ]
- },
- "templateId": "http://acplt.org/AdministrativeInformationTemplates/Test_AssetAdministrationShell"
- },
- "derivedFrom": {
- "type": "ModelReference",
- "keys": [
- {
- "type": "AssetAdministrationShell",
- "value": "https://acplt.org/TestAssetAdministrationShell2"
- }
- ]
- },
- "assetInformation": {
- "assetKind": "Instance",
- "globalAssetId": "http://acplt.org/TestAsset/",
- "specificAssetIds": [
- {
- "name": "TestKey",
- "value": "TestValue",
- "externalSubjectId": {
- "type": "ExternalReference",
- "keys": [
- {
- "type": "GlobalReference",
- "value": "http://acplt.org/SpecificAssetId/"
- }
- ]
- },
- "semanticId": {
- "type": "ExternalReference",
- "keys": [
- {
- "type": "GlobalReference",
- "value": "http://acplt.org/SpecificAssetId/"
- }
- ]
- }
- }
- ],
- "defaultThumbnail": {
- "path": "file:///path/to/thumbnail.png",
- "contentType": "image/png"
- }
- },
- "submodels": [
- {
- "type": "ModelReference",
- "keys": [
- {
- "type": "Submodel",
- "value": "https://acplt.org/Test_Submodel"
- }
- ],
- "referredSemanticId": {
- "type": "ExternalReference",
- "keys": [
- {
- "type": "GlobalReference",
- "value": "http://acplt.org/SubmodelTemplates/ExampleSubmodel"
- }
- ]
- }
- },
- {
- "type": "ModelReference",
- "keys": [
- {
- "type": "Submodel",
- "value": "http://acplt.org/Submodels/Assets/TestAsset/BillOfMaterial"
- }
- ]
- },
- {
- "type": "ModelReference",
- "keys": [
- {
- "type": "Submodel",
- "value": "http://acplt.org/Submodels/Assets/TestAsset/Identification"
- }
- ],
- "referredSemanticId": {
- "type": "ModelReference",
- "keys": [
- {
- "type": "Submodel",
- "value": "http://acplt.org/SubmodelTemplates/AssetIdentification"
- }
- ]
- }
- },
- {
- "type": "ModelReference",
- "keys": [
- {
- "type": "Submodel",
- "value": "https://acplt.org/Test_Submodel_Template"
- }
- ]
- }
- ],
- "embeddedDataSpecifications": [
- {
- "dataSpecification": {
- "type": "ExternalReference",
- "keys": [
- {
- "type": "GlobalReference",
- "value": "https://admin-shell.io/DataSpecificationTemplates/DataSpecificationIEC61360/3/0"
- }
- ]
- },
- "dataSpecificationContent": {
- "modelType": "DataSpecificationIec61360",
- "preferredName": [
- {
- "language": "de",
- "text": "Test Specification"
- },
- {
- "language": "en-US",
- "text": "TestSpecification"
- }
- ],
- "dataType": "REAL_MEASURE",
- "definition": [
- {
- "language": "de",
- "text": "Dies ist eine Data Specification f\u00fcr Testzwecke"
- },
- {
- "language": "en-US",
- "text": "This is a DataSpecification for testing purposes"
- }
- ],
- "shortName": [
- {
- "language": "de",
- "text": "Test Spec"
- },
- {
- "language": "en-US",
- "text": "TestSpec"
- }
- ],
- "unit": "SpaceUnit",
- "unitId": {
- "type": "ExternalReference",
- "keys": [
- {
- "type": "GlobalReference",
- "value": "http://acplt.org/Units/SpaceUnit"
- }
- ]
- },
- "sourceOfDefinition": "http://acplt.org/DataSpec/ExampleDef",
- "symbol": "SU",
- "valueFormat": "M",
- "valueList": {
- "valueReferencePairs": [
- {
- "value": "exampleValue",
- "valueId": {
- "type": "ExternalReference",
- "keys": [
- {
- "type": "GlobalReference",
- "value": "http://acplt.org/ValueId/ExampleValueId"
- }
- ]
- }
- },
- {
- "value": "exampleValue2",
- "valueId": {
- "type": "ExternalReference",
- "keys": [
- {
- "type": "GlobalReference",
- "value": "http://acplt.org/ValueId/ExampleValueId2"
- }
- ]
- }
- }
- ]
- },
- "value": "TEST",
- "valueId": {
- "type": "ExternalReference",
- "keys": [
- {
- "type": "GlobalReference",
- "value": "http://acplt.org/Values/TestValueId"
- }
- ]
- },
- "levelType": {
- "min": true,
- "max": true,
- "nom": false,
- "typ": false
- }
- }
- }
- ]
- },
- {
- "modelType": "AssetAdministrationShell",
- "id": "https://acplt.org/Test_AssetAdministrationShell_Mandatory",
- "assetInformation": {
- "assetKind": "Instance",
- "globalAssetId": "http://acplt.org/Test_Asset_Mandatory/"
- },
- "submodels": [
- {
- "type": "ModelReference",
- "keys": [
- {
- "type": "Submodel",
- "value": "https://acplt.org/Test_Submodel2_Mandatory"
- }
- ]
- },
- {
- "type": "ModelReference",
- "keys": [
- {
- "type": "Submodel",
- "value": "https://acplt.org/Test_Submodel_Mandatory"
- }
- ]
- }
- ]
- },
- {
- "modelType": "AssetAdministrationShell",
- "id": "https://acplt.org/Test_AssetAdministrationShell2_Mandatory",
- "assetInformation": {
- "assetKind": "Instance",
- "globalAssetId": "http://acplt.org/TestAsset2_Mandatory/"
- }
- },
- {
- "idShort": "TestAssetAdministrationShell",
- "description": [
- {
- "language": "en-US",
- "text": "An Example Asset Administration Shell for the test application"
- },
- {
- "language": "de",
- "text": "Ein Beispiel-Verwaltungsschale f\u00fcr eine Test-Anwendung"
- }
- ],
- "modelType": "AssetAdministrationShell",
- "id": "https://acplt.org/Test_AssetAdministrationShell_Missing",
- "administration": {
- "version": "9",
- "revision": "0"
- },
- "assetInformation": {
- "assetKind": "Instance",
- "globalAssetId": "http://acplt.org/Test_Asset_Missing/",
- "specificAssetIds": [
- {
- "name": "TestKey",
- "value": "TestValue",
- "externalSubjectId": {
- "type": "ExternalReference",
- "keys": [
- {
- "type": "GlobalReference",
- "value": "http://acplt.org/SpecificAssetId/"
- }
- ]
- }
- }
- ],
- "defaultThumbnail": {
- "path": "file:///TestFile.pdf",
- "contentType": "application/pdf"
- }
- },
- "submodels": [
- {
- "type": "ModelReference",
- "keys": [
- {
- "type": "Submodel",
- "value": "https://acplt.org/Test_Submodel_Missing"
- }
- ]
- }
- ]
- }
- ],
- "submodels": [
- {
- "idShort": "Identification",
- "description": [
- {
- "language": "en-US",
- "text": "An example asset identification submodel for the test application"
- },
- {
- "language": "de",
- "text": "Ein Beispiel-Identifikations-Submodel f\u00fcr eine Test-Anwendung"
- }
- ],
- "modelType": "Submodel",
- "id": "http://acplt.org/Submodels/Assets/TestAsset/Identification",
- "administration": {
- "version": "9",
- "revision": "0",
- "creator": {
- "type": "ExternalReference",
- "keys": [
- {
- "type": "GlobalReference",
- "value": "http://acplt.org/AdministrativeInformation/TestAsset/Identification"
- }
- ]
- },
- "templateId": "http://acplt.org/AdministrativeInformationTemplates/TestAsset/Identification"
- },
- "semanticId": {
- "type": "ModelReference",
- "keys": [
- {
- "type": "Submodel",
- "value": "http://acplt.org/SubmodelTemplates/AssetIdentification"
- }
- ]
- },
- "submodelElements": [
- {
- "extensions": [
- {
- "value": "ExampleExtensionValue",
- "refersTo": [
- {
- "type": "ModelReference",
- "keys": [
- {
- "type": "AssetAdministrationShell",
- "value": "http://acplt.org/RefersTo/ExampleRefersTo"
- }
- ]
- }
- ],
- "valueType": "xs:string",
- "name": "ExampleExtension"
- }
- ],
- "idShort": "ManufacturerName",
- "category": "PARAMETER",
- "description": [
- {
- "language": "en-US",
- "text": "Legally valid designation of the natural or judicial person which is directly responsible for the design, production, packaging and labeling of a product in respect to its being brought into circulation."
- },
- {
- "language": "de",
- "text": "Bezeichnung f\u00fcr eine nat\u00fcrliche oder juristische Person, die f\u00fcr die Auslegung, Herstellung und Verpackung sowie die Etikettierung eines Produkts im Hinblick auf das 'Inverkehrbringen' im eigenen Namen verantwortlich ist"
- }
- ],
- "modelType": "Property",
- "semanticId": {
- "type": "ExternalReference",
- "keys": [
- {
- "type": "GlobalReference",
- "value": "0173-1#02-AAO677#002"
- }
- ]
- },
- "qualifiers": [
- {
- "value": "50",
- "valueId": {
- "type": "ExternalReference",
- "keys": [
- {
- "type": "GlobalReference",
- "value": "http://acplt.org/ValueId/ExampleValueId"
- }
- ]
- },
- "valueType": "xs:int",
- "type": "http://acplt.org/Qualifier/ExampleQualifier2",
- "kind": "TemplateQualifier"
- },
- {
- "value": "100",
- "valueId": {
- "type": "ExternalReference",
- "keys": [
- {
- "type": "GlobalReference",
- "value": "http://acplt.org/ValueId/ExampleValueId"
- }
- ]
- },
- "valueType": "xs:int",
- "type": "http://acplt.org/Qualifier/ExampleQualifier",
- "kind": "ConceptQualifier"
- }
- ],
- "value": "ACPLT",
- "valueId": {
- "type": "ExternalReference",
- "keys": [
- {
- "type": "GlobalReference",
- "value": "http://acplt.org/ValueId/ExampleValueId"
- }
- ]
- },
- "valueType": "xs:string"
- },
- {
- "idShort": "InstanceId",
- "category": "PARAMETER",
- "description": [
- {
- "language": "en-US",
- "text": "Legally valid designation of the natural or judicial person which is directly responsible for the design, production, packaging and labeling of a product in respect to its being brought into circulation."
- },
- {
- "language": "de",
- "text": "Bezeichnung f\u00fcr eine nat\u00fcrliche oder juristische Person, die f\u00fcr die Auslegung, Herstellung und Verpackung sowie die Etikettierung eines Produkts im Hinblick auf das 'Inverkehrbringen' im eigenen Namen verantwortlich ist"
- }
- ],
- "modelType": "Property",
- "semanticId": {
- "type": "ExternalReference",
- "keys": [
- {
- "type": "GlobalReference",
- "value": "http://opcfoundation.org/UA/DI/1.1/DeviceType/Serialnumber"
- }
- ]
- },
- "qualifiers": [
- {
- "value": "2023-04-07T16:59:54.870123",
- "valueId": {
- "type": "ExternalReference",
- "keys": [
- {
- "type": "GlobalReference",
- "value": "http://acplt.org/ValueId/ExampleValueId"
- }
- ]
- },
- "valueType": "xs:dateTime",
- "type": "http://acplt.org/Qualifier/ExampleQualifier3",
- "kind": "ValueQualifier"
- }
- ],
- "value": "978-8234-234-342",
- "valueId": {
- "type": "ExternalReference",
- "keys": [
- {
- "type": "GlobalReference",
- "value": "http://acplt.org/ValueId/ExampleValueId"
- }
- ]
- },
- "valueType": "xs:string"
- }
- ]
- },
- {
- "idShort": "BillOfMaterial",
- "description": [
- {
- "language": "en-US",
- "text": "An example bill of material submodel for the test application"
- },
- {
- "language": "de",
- "text": "Ein Beispiel-BillOfMaterial-Submodel f\u00fcr eine Test-Anwendung"
- }
- ],
- "modelType": "Submodel",
- "id": "http://acplt.org/Submodels/Assets/TestAsset/BillOfMaterial",
- "administration": {
- "version": "9",
- "templateId": "http://acplt.org/AdministrativeInformationTemplates/TestAsset/BillOfMaterial"
- },
- "semanticId": {
- "type": "ModelReference",
- "keys": [
- {
- "type": "Submodel",
- "value": "http://acplt.org/SubmodelTemplates/BillOfMaterial"
- }
- ]
- },
- "submodelElements": [
- {
- "idShort": "ExampleEntity",
- "category": "PARAMETER",
- "description": [
- {
- "language": "en-US",
- "text": "Legally valid designation of the natural or judicial person which is directly responsible for the design, production, packaging and labeling of a product in respect to its being brought into circulation."
- },
- {
- "language": "de",
- "text": "Bezeichnung f\u00fcr eine nat\u00fcrliche oder juristische Person, die f\u00fcr die Auslegung, Herstellung und Verpackung sowie die Etikettierung eines Produkts im Hinblick auf das 'Inverkehrbringen' im eigenen Namen verantwortlich ist"
- }
- ],
- "modelType": "Entity",
- "semanticId": {
- "type": "ExternalReference",
- "keys": [
- {
- "type": "GlobalReference",
- "value": "http://opcfoundation.org/UA/DI/1.1/DeviceType/Serialnumber"
- }
- ]
- },
- "statements": [
- {
- "idShort": "ExampleProperty2",
- "category": "CONSTANT",
- "description": [
- {
- "language": "en-US",
- "text": "Example Property object"
- },
- {
- "language": "de",
- "text": "Beispiel Property Element"
- }
- ],
- "modelType": "Property",
- "semanticId": {
- "type": "ExternalReference",
- "keys": [
- {
- "type": "GlobalReference",
- "value": "http://acplt.org/Properties/ExampleProperty"
- }
- ]
- },
- "value": "exampleValue2",
- "valueId": {
- "type": "ExternalReference",
- "keys": [
- {
- "type": "GlobalReference",
- "value": "http://acplt.org/ValueId/ExampleValueId"
- }
- ]
- },
- "valueType": "xs:string"
- },
- {
- "idShort": "ExampleProperty",
- "category": "CONSTANT",
- "description": [
- {
- "language": "en-US",
- "text": "Example Property object"
- },
- {
- "language": "de",
- "text": "Beispiel Property Element"
- }
- ],
- "modelType": "Property",
- "semanticId": {
- "type": "ExternalReference",
- "keys": [
- {
- "type": "GlobalReference",
- "value": "http://acplt.org/Properties/ExampleProperty"
- }
- ]
- },
- "value": "exampleValue",
- "valueId": {
- "type": "ExternalReference",
- "keys": [
- {
- "type": "GlobalReference",
- "value": "http://acplt.org/ValueId/ExampleValueId"
- }
- ]
- },
- "valueType": "xs:string",
- "embeddedDataSpecifications": [
- {
- "dataSpecification": {
- "type": "ExternalReference",
- "keys": [
- {
- "type": "GlobalReference",
- "value": "https://admin-shell.io/DataSpecificationTemplates/DataSpecificationIEC61360/3/0"
- }
- ]
- },
- "dataSpecificationContent": {
- "modelType": "DataSpecificationIec61360",
- "preferredName": [
- {
- "language": "de",
- "text": "Test Specification"
- },
- {
- "language": "en-US",
- "text": "TestSpecification"
- }
- ],
- "dataType": "REAL_MEASURE",
- "definition": [
- {
- "language": "de",
- "text": "Dies ist eine Data Specification f\u00fcr Testzwecke"
- },
- {
- "language": "en-US",
- "text": "This is a DataSpecification for testing purposes"
- }
- ],
- "shortName": [
- {
- "language": "de",
- "text": "Test Spec"
- },
- {
- "language": "en-US",
- "text": "TestSpec"
- }
- ],
- "unit": "SpaceUnit",
- "unitId": {
- "type": "ExternalReference",
- "keys": [
- {
- "type": "GlobalReference",
- "value": "http://acplt.org/Units/SpaceUnit"
- }
- ]
- },
- "sourceOfDefinition": "http://acplt.org/DataSpec/ExampleDef",
- "symbol": "SU",
- "valueFormat": "M",
- "valueList": {
- "valueReferencePairs": [
- {
- "value": "exampleValue",
- "valueId": {
- "type": "ExternalReference",
- "keys": [
- {
- "type": "GlobalReference",
- "value": "http://acplt.org/ValueId/ExampleValueId"
- }
- ]
- },
- "valueType": "xs:string"
- },
- {
- "value": "exampleValue2",
- "valueId": {
- "type": "ExternalReference",
- "keys": [
- {
- "type": "GlobalReference",
- "value": "http://acplt.org/ValueId/ExampleValueId2"
- }
- ]
- },
- "valueType": "xs:string"
- }
- ]
- },
- "value": "TEST",
- "valueId": {
- "type": "ExternalReference",
- "keys": [
- {
- "type": "GlobalReference",
- "value": "http://acplt.org/Values/TestValueId"
- }
- ]
- },
- "levelType": {
- "min": true,
- "max": true,
- "nom": false,
- "typ": false
- }
- }
- }
- ]
- }
- ],
- "entityType": "SelfManagedEntity",
- "globalAssetId": "http://acplt.org/TestAsset/",
- "specificAssetIds": [
- {
- "name": "TestKey",
- "value": "TestValue",
- "externalSubjectId": {
- "type": "ExternalReference",
- "keys": [
- {
- "type": "GlobalReference",
- "value": "http://acplt.org/SpecificAssetId/"
- }
- ]
- }
- }
- ]
- },
- {
- "idShort": "ExampleEntity2",
- "category": "PARAMETER",
- "description": [
- {
- "language": "en-US",
- "text": "Legally valid designation of the natural or judicial person which is directly responsible for the design, production, packaging and labeling of a product in respect to its being brought into circulation."
- },
- {
- "language": "de",
- "text": "Bezeichnung f\u00fcr eine nat\u00fcrliche oder juristische Person, die f\u00fcr die Auslegung, Herstellung und Verpackung sowie die Etikettierung eines Produkts im Hinblick auf das 'Inverkehrbringen' im eigenen Namen verantwortlich ist"
- }
- ],
- "modelType": "Entity",
- "semanticId": {
- "type": "ExternalReference",
- "keys": [
- {
- "type": "GlobalReference",
- "value": "http://opcfoundation.org/UA/DI/1.1/DeviceType/Serialnumber"
- }
- ]
- },
- "entityType": "CoManagedEntity"
- }
- ]
- },
- {
- "idShort": "TestSubmodel",
- "description": [
- {
- "language": "en-US",
- "text": "An example submodel for the test application"
- },
- {
- "language": "de",
- "text": "Ein Beispiel-Teilmodell f\u00fcr eine Test-Anwendung"
- }
- ],
- "modelType": "Submodel",
- "id": "https://acplt.org/Test_Submodel",
- "administration": {
- "version": "9",
- "revision": "0",
- "creator": {
- "type": "ExternalReference",
- "keys": [
- {
- "type": "GlobalReference",
- "value": "http://acplt.org/AdministrativeInformation/Test_Submodel"
- }
- ]
- }
- },
- "semanticId": {
- "type": "ExternalReference",
- "keys": [
- {
- "type": "GlobalReference",
- "value": "http://acplt.org/SubmodelTemplates/ExampleSubmodel"
- }
- ]
- },
- "submodelElements": [
- {
- "idShort": "ExampleRelationshipElement",
- "category": "PARAMETER",
- "description": [
- {
- "language": "en-US",
- "text": "Example RelationshipElement object"
- },
- {
- "language": "de",
- "text": "Beispiel RelationshipElement Element"
- }
- ],
- "modelType": "RelationshipElement",
- "semanticId": {
- "type": "ModelReference",
- "keys": [
- {
- "type": "ConceptDescription",
- "value": "https://acplt.org/Test_ConceptDescription"
- }
- ]
- },
- "first": {
- "type": "ModelReference",
- "keys": [
- {
- "type": "Submodel",
- "value": "http://acplt.org/Test_Submodel"
- },
- {
- "type": "Property",
- "value": "ExampleProperty"
- }
- ]
- },
- "second": {
- "type": "ModelReference",
- "keys": [
- {
- "type": "Submodel",
- "value": "http://acplt.org/Test_Submodel"
- },
- {
- "type": "Property",
- "value": "ExampleProperty2"
- }
- ]
- }
- },
- {
- "idShort": "ExampleAnnotatedRelationshipElement",
- "category": "PARAMETER",
- "description": [
- {
- "language": "en-US",
- "text": "Example AnnotatedRelationshipElement object"
- },
- {
- "language": "de",
- "text": "Beispiel AnnotatedRelationshipElement Element"
- }
- ],
- "modelType": "AnnotatedRelationshipElement",
- "semanticId": {
- "type": "ExternalReference",
- "keys": [
- {
- "type": "GlobalReference",
- "value": "http://acplt.org/RelationshipElements/ExampleAnnotatedRelationshipElement"
- }
- ]
- },
- "first": {
- "type": "ModelReference",
- "keys": [
- {
- "type": "Submodel",
- "value": "http://acplt.org/Test_Submodel"
- },
- {
- "type": "Property",
- "value": "ExampleProperty"
- }
- ]
- },
- "second": {
- "type": "ModelReference",
- "keys": [
- {
- "type": "Submodel",
- "value": "http://acplt.org/Test_Submodel"
- },
- {
- "type": "Property",
- "value": "ExampleProperty2"
- }
- ]
- },
- "annotations": [
- {
- "idShort": "ExampleAnnotatedProperty",
- "category": "PARAMETER",
- "modelType": "Property",
- "value": "exampleValue",
- "valueType": "xs:string"
- },
- {
- "idShort": "ExampleAnnotatedRange",
- "category": "PARAMETER",
- "modelType": "Range",
- "valueType": "xs:integer",
- "min": "1",
- "max": "5"
- }
- ]
- },
- {
- "idShort": "ExampleOperation",
- "category": "PARAMETER",
- "description": [
- {
- "language": "en-US",
- "text": "Example Operation object"
- },
- {
- "language": "de",
- "text": "Beispiel Operation Element"
- }
- ],
- "modelType": "Operation",
- "semanticId": {
- "type": "ExternalReference",
- "keys": [
- {
- "type": "GlobalReference",
- "value": "http://acplt.org/Operations/ExampleOperation"
- }
- ]
- },
- "inputVariables": [
- {
- "value": {
- "idShort": "ExamplePropertyInput",
- "displayName": [
- {
- "language": "en-US",
- "text": "ExampleProperty"
- },
- {
- "language": "de",
- "text": "BeispielProperty"
- }
- ],
- "category": "CONSTANT",
- "description": [
- {
- "language": "en-US",
- "text": "Example Property object"
- },
- {
- "language": "de",
- "text": "Beispiel Property Element"
- }
- ],
- "modelType": "Property",
- "semanticId": {
- "type": "ExternalReference",
- "keys": [
- {
- "type": "GlobalReference",
- "value": "http://acplt.org/Properties/ExamplePropertyInput"
- }
- ]
- },
- "kind": "Template",
- "value": "exampleValue",
- "valueId": {
- "type": "ExternalReference",
- "keys": [
- {
- "type": "GlobalReference",
- "value": "http://acplt.org/ValueId/ExampleValueId"
- }
- ]
- },
- "valueType": "xs:string"
- }
- }
- ],
- "outputVariables": [
- {
- "value": {
- "idShort": "ExamplePropertyOutput",
- "displayName": [
- {
- "language": "en-US",
- "text": "ExampleProperty"
- },
- {
- "language": "de",
- "text": "BeispielProperty"
- }
- ],
- "category": "CONSTANT",
- "description": [
- {
- "language": "en-US",
- "text": "Example Property object"
- },
- {
- "language": "de",
- "text": "Beispiel Property Element"
- }
- ],
- "modelType": "Property",
- "semanticId": {
- "type": "ExternalReference",
- "keys": [
- {
- "type": "GlobalReference",
- "value": "http://acplt.org/Properties/ExamplePropertyOutput"
- }
- ]
- },
- "kind": "Template",
- "value": "exampleValue",
- "valueId": {
- "type": "ExternalReference",
- "keys": [
- {
- "type": "GlobalReference",
- "value": "http://acplt.org/ValueId/ExampleValueId"
- }
- ]
- },
- "valueType": "xs:string"
- }
- }
- ],
- "inoutputVariables": [
- {
- "value": {
- "idShort": "ExamplePropertyInOutput",
- "displayName": [
- {
- "language": "en-US",
- "text": "ExampleProperty"
- },
- {
- "language": "de",
- "text": "BeispielProperty"
- }
- ],
- "category": "CONSTANT",
- "description": [
- {
- "language": "en-US",
- "text": "Example Property object"
- },
- {
- "language": "de",
- "text": "Beispiel Property Element"
- }
- ],
- "modelType": "Property",
- "semanticId": {
- "type": "ExternalReference",
- "keys": [
- {
- "type": "GlobalReference",
- "value": "http://acplt.org/Properties/ExamplePropertyInOutput"
- }
- ]
- },
- "kind": "Template",
- "value": "exampleValue",
- "valueId": {
- "type": "ExternalReference",
- "keys": [
- {
- "type": "GlobalReference",
- "value": "http://acplt.org/ValueId/ExampleValueId"
- }
- ]
- },
- "valueType": "xs:string"
- }
- }
- ]
- },
- {
- "idShort": "ExampleCapability",
- "category": "PARAMETER",
- "description": [
- {
- "language": "en-US",
- "text": "Example Capability object"
- },
- {
- "language": "de",
- "text": "Beispiel Capability Element"
- }
- ],
- "modelType": "Capability",
- "semanticId": {
- "type": "ExternalReference",
- "keys": [
- {
- "type": "GlobalReference",
- "value": "http://acplt.org/Capabilities/ExampleCapability"
- }
- ]
- }
- },
- {
- "idShort": "ExampleBasicEventElement",
- "category": "PARAMETER",
- "description": [
- {
- "language": "en-US",
- "text": "Example BasicEventElement object"
- },
- {
- "language": "de",
- "text": "Beispiel BasicEventElement Element"
- }
- ],
- "modelType": "BasicEventElement",
- "semanticId": {
- "type": "ExternalReference",
- "keys": [
- {
- "type": "GlobalReference",
- "value": "http://acplt.org/Events/ExampleBasicEventElement"
- }
- ]
- },
- "observed": {
- "type": "ModelReference",
- "keys": [
- {
- "type": "Submodel",
- "value": "http://acplt.org/Test_Submodel"
- },
- {
- "type": "Property",
- "value": "ExampleProperty"
- }
- ]
- },
- "direction": "output",
- "state": "on",
- "messageTopic": "ExampleTopic",
- "messageBroker": {
- "type": "ModelReference",
- "keys": [
- {
- "type": "Submodel",
- "value": "http://acplt.org/ExampleMessageBroker"
- }
- ]
- },
- "lastUpdate": "2022-11-12T23:50:23.123456+00:00",
- "minInterval": "PT0.000001S",
- "maxInterval": "P1Y2M3DT4H5M6.123456S"
- },
- {
- "idShort": "ExampleSubmodelCollection",
- "category": "PARAMETER",
- "description": [
- {
- "language": "en-US",
- "text": "Example SubmodelElementCollection object"
- },
- {
- "language": "de",
- "text": "Beispiel SubmodelElementCollection Element"
- }
- ],
- "modelType": "SubmodelElementCollection",
- "semanticId": {
- "type": "ExternalReference",
- "keys": [
- {
- "type": "GlobalReference",
- "value": "http://acplt.org/SubmodelElementCollections/ExampleSubmodelElementCollection"
- }
- ]
- },
- "value": [
- {
- "idShort": "ExampleBlob",
- "category": "PARAMETER",
- "description": [
- {
- "language": "en-US",
- "text": "Example Blob object"
- },
- {
- "language": "de",
- "text": "Beispiel Blob Element"
- }
- ],
- "modelType": "Blob",
- "semanticId": {
- "type": "ExternalReference",
- "keys": [
- {
- "type": "GlobalReference",
- "value": "http://acplt.org/Blobs/ExampleBlob"
- }
- ]
- },
- "contentType": "application/pdf",
- "value": "AQIDBAU="
- },
- {
- "idShort": "ExampleFile",
- "category": "PARAMETER",
- "description": [
- {
- "language": "en-US",
- "text": "Example File object"
- },
- {
- "language": "de",
- "text": "Beispiel File Element"
- }
- ],
- "modelType": "File",
- "semanticId": {
- "type": "ExternalReference",
- "keys": [
- {
- "type": "GlobalReference",
- "value": "http://acplt.org/Files/ExampleFile"
- }
- ]
- },
- "value": "/TestFile.pdf",
- "contentType": "application/pdf"
- },
- {
- "idShort": "ExampleFileURI",
- "category": "CONSTANT",
- "description": [
- {
- "language": "en-US",
- "text": "Details of the Asset Administration Shell \u2014 An example for an external file reference"
- },
- {
- "language": "de",
- "text": "Details of the Asset Administration Shell \u2013 Ein Beispiel f\u00fcr eine extern referenzierte Datei"
- }
- ],
- "modelType": "File",
- "semanticId": {
- "type": "ExternalReference",
- "keys": [
- {
- "type": "GlobalReference",
- "value": "http://acplt.org/Files/ExampleFile"
- }
- ]
- },
- "value": "https://www.plattform-i40.de/PI40/Redaktion/DE/Downloads/Publikation/Details-of-the-Asset-Administration-Shell-Part1.pdf?__blob=publicationFile&v=5",
- "contentType": "application/pdf"
- },
- {
- "idShort": "ExampleMultiLanguageProperty",
- "category": "CONSTANT",
- "description": [
- {
- "language": "en-US",
- "text": "Example MultiLanguageProperty object"
- },
- {
- "language": "de",
- "text": "Beispiel MultiLanguageProperty Element"
- }
- ],
- "modelType": "MultiLanguageProperty",
- "semanticId": {
- "type": "ExternalReference",
- "keys": [
- {
- "type": "GlobalReference",
- "value": "http://acplt.org/MultiLanguageProperties/ExampleMultiLanguageProperty"
- }
- ],
- "referredSemanticId": {
- "type": "ExternalReference",
- "keys": [
- {
- "type": "GlobalReference",
- "value": "http://acplt.org/Properties/ExampleProperty/Referred"
- }
- ]
- }
- },
- "value": [
- {
- "language": "en-US",
- "text": "Example value of a MultiLanguageProperty element"
- },
- {
- "language": "de",
- "text": "Beispielwert f\u00fcr ein MultiLanguageProperty-Element"
- }
- ],
- "valueId": {
- "type": "ExternalReference",
- "keys": [
- {
- "type": "GlobalReference",
- "value": "http://acplt.org/ValueId/ExampleMultiLanguageValueId"
- }
- ]
- }
- },
- {
- "idShort": "ExampleRange",
- "category": "PARAMETER",
- "description": [
- {
- "language": "en-US",
- "text": "Example Range object"
- },
- {
- "language": "de",
- "text": "Beispiel Range Element"
- }
- ],
- "modelType": "Range",
- "semanticId": {
- "type": "ExternalReference",
- "keys": [
- {
- "type": "GlobalReference",
- "value": "http://acplt.org/Ranges/ExampleRange"
- }
- ]
- },
- "valueType": "xs:int",
- "min": "0",
- "max": "100"
- },
- {
- "idShort": "ExampleReferenceElement",
- "category": "PARAMETER",
- "description": [
- {
- "language": "en-US",
- "text": "Example Reference Element object"
- },
- {
- "language": "de",
- "text": "Beispiel Reference Element Element"
- }
- ],
- "modelType": "ReferenceElement",
- "semanticId": {
- "type": "ExternalReference",
- "keys": [
- {
- "type": "GlobalReference",
- "value": "http://acplt.org/ReferenceElements/ExampleReferenceElement"
- }
- ]
- },
- "value": {
- "type": "ModelReference",
- "keys": [
- {
- "type": "Submodel",
- "value": "http://acplt.org/Test_Submodel"
- },
- {
- "type": "Property",
- "value": "ExampleProperty"
- }
- ]
- }
- },
- {
- "idShort": "ExampleSubmodelList",
- "typeValueListElement": "Property",
- "valueTypeListElement": "xs:string",
- "semanticIdListElement": {
- "type": "ExternalReference",
- "keys": [
- {
- "type": "GlobalReference",
- "value": "http://acplt.org/Properties/ExampleProperty"
- }
- ]
- },
- "orderRelevant": true,
- "category": "PARAMETER",
- "description": [
- {
- "language": "en-US",
- "text": "Example SubmodelElementList object"
- },
- {
- "language": "de",
- "text": "Beispiel SubmodelElementList Element"
- }
- ],
- "modelType": "SubmodelElementList",
- "semanticId": {
- "type": "ExternalReference",
- "keys": [
- {
- "type": "GlobalReference",
- "value": "http://acplt.org/SubmodelElementLists/ExampleSubmodelElementList"
- }
- ]
- },
- "value": [
- {
- "displayName": [
- {
- "language": "en-US",
- "text": "ExampleProperty"
- },
- {
- "language": "de",
- "text": "BeispielProperty"
- }
- ],
- "category": "CONSTANT",
- "description": [
- {
- "language": "en-US",
- "text": "Example Property object"
- },
- {
- "language": "de",
- "text": "Beispiel Property Element"
- }
- ],
- "modelType": "Property",
- "semanticId": {
- "type": "ExternalReference",
- "keys": [
- {
- "type": "GlobalReference",
- "value": "http://acplt.org/Properties/ExampleProperty"
- }
- ]
- },
- "supplementalSemanticIds": [
- {
- "type": "ExternalReference",
- "keys": [
- {
- "type": "GlobalReference",
- "value": "http://acplt.org/Properties/ExampleProperty/SupplementalId1"
- }
- ]
- },
- {
- "type": "ExternalReference",
- "keys": [
- {
- "type": "GlobalReference",
- "value": "http://acplt.org/Properties/ExampleProperty/SupplementalId2"
- }
- ]
- }
- ],
- "value": "exampleValue",
- "valueId": {
- "type": "ExternalReference",
- "keys": [
- {
- "type": "GlobalReference",
- "value": "http://acplt.org/ValueId/ExampleValueId"
- }
- ]
- },
- "valueType": "xs:string",
- "embeddedDataSpecifications": [
- {
- "dataSpecification": {
- "type": "ExternalReference",
- "keys": [
- {
- "type": "GlobalReference",
- "value": "https://admin-shell.io/DataSpecificationTemplates/DataSpecificationIEC61360/3/0"
- }
- ]
- },
- "dataSpecificationContent": {
- "modelType": "DataSpecificationIec61360",
- "preferredName": [
- {
- "language": "de",
- "text": "Test Specification"
- },
- {
- "language": "en-US",
- "text": "TestSpecification"
- }
- ],
- "dataType": "REAL_MEASURE",
- "definition": [
- {
- "language": "de",
- "text": "Dies ist eine Data Specification f\u00fcr Testzwecke"
- },
- {
- "language": "en-US",
- "text": "This is a DataSpecification for testing purposes"
- }
- ],
- "shortName": [
- {
- "language": "de",
- "text": "Test Spec"
- },
- {
- "language": "en-US",
- "text": "TestSpec"
- }
- ],
- "unit": "SpaceUnit",
- "unitId": {
- "type": "ExternalReference",
- "keys": [
- {
- "type": "GlobalReference",
- "value": "http://acplt.org/Units/SpaceUnit"
- }
- ]
- },
- "sourceOfDefinition": "http://acplt.org/DataSpec/ExampleDef",
- "symbol": "SU",
- "valueFormat": "M",
- "valueList": {
- "valueReferencePairs": [
- {
- "value": "exampleValue",
- "valueId": {
- "type": "ExternalReference",
- "keys": [
- {
- "type": "GlobalReference",
- "value": "http://acplt.org/ValueId/ExampleValueId"
- }
- ]
- }
- },
- {
- "value": "exampleValue2",
- "valueId": {
- "type": "ExternalReference",
- "keys": [
- {
- "type": "GlobalReference",
- "value": "http://acplt.org/ValueId/ExampleValueId2"
- }
- ]
- }
- }
- ]
- },
- "value": "TEST",
- "valueId": {
- "type": "ExternalReference",
- "keys": [
- {
- "type": "GlobalReference",
- "value": "http://acplt.org/Values/TestValueId"
- }
- ]
- },
- "levelType": {
- "min": true,
- "max": true,
- "nom": false,
- "typ": false
- }
- }
- }
- ]
- },
- {
- "displayName": [
- {
- "language": "en-US",
- "text": "ExampleProperty"
- },
- {
- "language": "de",
- "text": "BeispielProperty"
- }
- ],
- "category": "CONSTANT",
- "description": [
- {
- "language": "en-US",
- "text": "Example Property object"
- },
- {
- "language": "de",
- "text": "Beispiel Property Element"
- }
- ],
- "modelType": "Property",
- "semanticId": {
- "type": "ExternalReference",
- "keys": [
- {
- "type": "GlobalReference",
- "value": "http://acplt.org/Properties/ExampleProperty"
- }
- ]
- },
- "supplementalSemanticIds": [
- {
- "type": "ExternalReference",
- "keys": [
- {
- "type": "GlobalReference",
- "value": "http://acplt.org/Properties/ExampleProperty2/SupplementalId"
- }
- ]
- }
- ],
- "value": "exampleValue",
- "valueId": {
- "type": "ExternalReference",
- "keys": [
- {
- "type": "GlobalReference",
- "value": "http://acplt.org/ValueId/ExampleValueId"
- }
- ]
- },
- "valueType": "xs:string"
- }
- ]
- }
- ]
- }
- ]
- },
- {
- "modelType": "Submodel",
- "id": "https://acplt.org/Test_Submodel_Mandatory",
- "submodelElements": [
- {
- "idShort": "ExampleRelationshipElement",
- "modelType": "RelationshipElement",
- "first": {
- "type": "ModelReference",
- "keys": [
- {
- "type": "Submodel",
- "value": "http://acplt.org/Test_Submodel"
- },
- {
- "type": "Property",
- "value": "ExampleProperty"
- }
- ]
- },
- "second": {
- "type": "ModelReference",
- "keys": [
- {
- "type": "Submodel",
- "value": "http://acplt.org/Test_Submodel"
- },
- {
- "type": "Property",
- "value": "ExampleProperty"
- }
- ]
- }
- },
- {
- "idShort": "ExampleAnnotatedRelationshipElement",
- "modelType": "AnnotatedRelationshipElement",
- "first": {
- "type": "ModelReference",
- "keys": [
- {
- "type": "Submodel",
- "value": "http://acplt.org/Test_Submodel"
- },
- {
- "type": "Property",
- "value": "ExampleProperty"
- }
- ]
- },
- "second": {
- "type": "ModelReference",
- "keys": [
- {
- "type": "Submodel",
- "value": "http://acplt.org/Test_Submodel"
- },
- {
- "type": "Property",
- "value": "ExampleProperty"
- }
- ]
- }
- },
- {
- "idShort": "ExampleOperation",
- "modelType": "Operation"
- },
- {
- "idShort": "ExampleCapability",
- "modelType": "Capability"
- },
- {
- "idShort": "ExampleBasicEventElement",
- "modelType": "BasicEventElement",
- "observed": {
- "type": "ModelReference",
- "keys": [
- {
- "type": "Submodel",
- "value": "http://acplt.org/Test_Submodel"
- },
- {
- "type": "Property",
- "value": "ExampleProperty"
- }
- ]
- },
- "direction": "input",
- "state": "off"
- },
- {
- "idShort": "ExampleSubmodelList",
- "typeValueListElement": "SubmodelElementCollection",
- "modelType": "SubmodelElementList",
- "value": [
- {
- "modelType": "SubmodelElementCollection",
- "value": [
- {
- "idShort": "ExampleBlob",
- "modelType": "Blob",
- "contentType": "application/pdf"
- },
- {
- "idShort": "ExampleFile",
- "modelType": "File",
- "contentType": "application/pdf"
- },
- {
- "idShort": "ExampleMultiLanguageProperty",
- "category": "PARAMETER",
- "modelType": "MultiLanguageProperty"
- },
- {
- "idShort": "ExampleProperty",
- "category": "PARAMETER",
- "modelType": "Property",
- "valueType": "xs:string"
- },
- {
- "idShort": "ExampleRange",
- "category": "PARAMETER",
- "modelType": "Range",
- "valueType": "xs:int"
- },
- {
- "idShort": "ExampleReferenceElement",
- "category": "PARAMETER",
- "modelType": "ReferenceElement"
- }
- ]
- },
- {
- "modelType": "SubmodelElementCollection"
- }
- ]
- },
- {
- "idShort": "ExampleSubmodelList2",
- "typeValueListElement": "Capability",
- "modelType": "SubmodelElementList"
- }
- ]
- },
- {
- "modelType": "Submodel",
- "id": "https://acplt.org/Test_Submodel2_Mandatory"
- },
- {
- "idShort": "TestSubmodel",
- "description": [
- {
- "language": "en-US",
- "text": "An example submodel for the test application"
- },
- {
- "language": "de",
- "text": "Ein Beispiel-Teilmodell f\u00fcr eine Test-Anwendung"
- }
- ],
- "modelType": "Submodel",
- "id": "https://acplt.org/Test_Submodel_Missing",
- "administration": {
- "version": "9",
- "revision": "0"
- },
- "semanticId": {
- "type": "ExternalReference",
- "keys": [
- {
- "type": "GlobalReference",
- "value": "http://acplt.org/SubmodelTemplates/ExampleSubmodel"
- }
- ]
- },
- "submodelElements": [
- {
- "idShort": "ExampleRelationshipElement",
- "category": "PARAMETER",
- "description": [
- {
- "language": "en-US",
- "text": "Example RelationshipElement object"
- },
- {
- "language": "de",
- "text": "Beispiel RelationshipElement Element"
- }
- ],
- "modelType": "RelationshipElement",
- "semanticId": {
- "type": "ExternalReference",
- "keys": [
- {
- "type": "GlobalReference",
- "value": "http://acplt.org/RelationshipElements/ExampleRelationshipElement"
- }
- ]
- },
- "first": {
- "type": "ModelReference",
- "keys": [
- {
- "type": "Submodel",
- "value": "http://acplt.org/Test_Submodel"
- },
- {
- "type": "Property",
- "value": "ExampleProperty"
- }
- ]
- },
- "second": {
- "type": "ModelReference",
- "keys": [
- {
- "type": "Submodel",
- "value": "http://acplt.org/Test_Submodel"
- },
- {
- "type": "Property",
- "value": "ExampleProperty"
- }
- ]
- }
- },
- {
- "idShort": "ExampleAnnotatedRelationshipElement",
- "category": "PARAMETER",
- "description": [
- {
- "language": "en-US",
- "text": "Example AnnotatedRelationshipElement object"
- },
- {
- "language": "de",
- "text": "Beispiel AnnotatedRelationshipElement Element"
- }
- ],
- "modelType": "AnnotatedRelationshipElement",
- "semanticId": {
- "type": "ExternalReference",
- "keys": [
- {
- "type": "GlobalReference",
- "value": "http://acplt.org/RelationshipElements/ExampleAnnotatedRelationshipElement"
- }
- ]
- },
- "first": {
- "type": "ModelReference",
- "keys": [
- {
- "type": "Submodel",
- "value": "http://acplt.org/Test_Submodel"
- },
- {
- "type": "Property",
- "value": "ExampleProperty"
- }
- ]
- },
- "second": {
- "type": "ModelReference",
- "keys": [
- {
- "type": "Submodel",
- "value": "http://acplt.org/Test_Submodel"
- },
- {
- "type": "Property",
- "value": "ExampleProperty"
- }
- ]
- },
- "annotations": [
- {
- "idShort": "ExampleAnnotatedRange",
- "category": "PARAMETER",
- "modelType": "Range",
- "valueType": "xs:integer",
- "min": "1",
- "max": "5"
- },
- {
- "idShort": "ExampleAnnotatedProperty",
- "category": "PARAMETER",
- "modelType": "Property",
- "value": "exampleValue",
- "valueType": "xs:string"
- }
- ]
- },
- {
- "idShort": "ExampleOperation",
- "category": "PARAMETER",
- "description": [
- {
- "language": "en-US",
- "text": "Example Operation object"
- },
- {
- "language": "de",
- "text": "Beispiel Operation Element"
- }
- ],
- "modelType": "Operation",
- "semanticId": {
- "type": "ExternalReference",
- "keys": [
- {
- "type": "GlobalReference",
- "value": "http://acplt.org/Operations/ExampleOperation"
- }
- ]
- },
- "inputVariables": [
- {
- "value": {
- "idShort": "ExamplePropertyInput",
- "displayName": [
- {
- "language": "en-US",
- "text": "ExampleProperty"
- },
- {
- "language": "de",
- "text": "BeispielProperty"
- }
- ],
- "category": "CONSTANT",
- "description": [
- {
- "language": "en-US",
- "text": "Example Property object"
- },
- {
- "language": "de",
- "text": "Beispiel Property Element"
- }
- ],
- "modelType": "Property",
- "semanticId": {
- "type": "ExternalReference",
- "keys": [
- {
- "type": "GlobalReference",
- "value": "http://acplt.org/Properties/ExamplePropertyInput"
- }
- ]
- },
- "kind": "Template",
- "value": "exampleValue",
- "valueId": {
- "type": "ExternalReference",
- "keys": [
- {
- "type": "GlobalReference",
- "value": "http://acplt.org/ValueId/ExampleValueId"
- }
- ]
- },
- "valueType": "xs:string"
- }
- }
- ],
- "outputVariables": [
- {
- "value": {
- "idShort": "ExamplePropertyOutput",
- "displayName": [
- {
- "language": "en-US",
- "text": "ExampleProperty"
- },
- {
- "language": "de",
- "text": "BeispielProperty"
- }
- ],
- "category": "CONSTANT",
- "description": [
- {
- "language": "en-US",
- "text": "Example Property object"
- },
- {
- "language": "de",
- "text": "Beispiel Property Element"
- }
- ],
- "modelType": "Property",
- "semanticId": {
- "type": "ExternalReference",
- "keys": [
- {
- "type": "GlobalReference",
- "value": "http://acplt.org/Properties/ExamplePropertyOutput"
- }
- ]
- },
- "kind": "Template",
- "value": "exampleValue",
- "valueId": {
- "type": "ExternalReference",
- "keys": [
- {
- "type": "GlobalReference",
- "value": "http://acplt.org/ValueId/ExampleValueId"
- }
- ]
- },
- "valueType": "xs:string"
- }
- }
- ],
- "inoutputVariables": [
- {
- "value": {
- "idShort": "ExamplePropertyInOutput",
- "displayName": [
- {
- "language": "en-US",
- "text": "ExampleProperty"
- },
- {
- "language": "de",
- "text": "BeispielProperty"
- }
- ],
- "category": "CONSTANT",
- "description": [
- {
- "language": "en-US",
- "text": "Example Property object"
- },
- {
- "language": "de",
- "text": "Beispiel Property Element"
- }
- ],
- "modelType": "Property",
- "semanticId": {
- "type": "ExternalReference",
- "keys": [
- {
- "type": "GlobalReference",
- "value": "http://acplt.org/Properties/ExamplePropertyInOutput"
- }
- ]
- },
- "kind": "Template",
- "value": "exampleValue",
- "valueId": {
- "type": "ExternalReference",
- "keys": [
- {
- "type": "GlobalReference",
- "value": "http://acplt.org/ValueId/ExampleValueId"
- }
- ]
- },
- "valueType": "xs:string"
- }
- }
- ]
- },
- {
- "idShort": "ExampleCapability",
- "category": "PARAMETER",
- "description": [
- {
- "language": "en-US",
- "text": "Example Capability object"
- },
- {
- "language": "de",
- "text": "Beispiel Capability Element"
- }
- ],
- "modelType": "Capability",
- "semanticId": {
- "type": "ExternalReference",
- "keys": [
- {
- "type": "GlobalReference",
- "value": "http://acplt.org/Capabilities/ExampleCapability"
- }
- ]
- }
- },
- {
- "idShort": "ExampleBasicEventElement",
- "category": "PARAMETER",
- "description": [
- {
- "language": "en-US",
- "text": "Example BasicEventElement object"
- },
- {
- "language": "de",
- "text": "Beispiel BasicEventElement Element"
- }
- ],
- "modelType": "BasicEventElement",
- "semanticId": {
- "type": "ExternalReference",
- "keys": [
- {
- "type": "GlobalReference",
- "value": "http://acplt.org/Events/ExampleBasicEventElement"
- }
- ]
- },
- "observed": {
- "type": "ModelReference",
- "keys": [
- {
- "type": "Submodel",
- "value": "http://acplt.org/Test_Submodel"
- },
- {
- "type": "Property",
- "value": "ExampleProperty"
- }
- ]
- },
- "direction": "output",
- "state": "on",
- "messageTopic": "ExampleTopic",
- "messageBroker": {
- "type": "ModelReference",
- "keys": [
- {
- "type": "Submodel",
- "value": "http://acplt.org/ExampleMessageBroker"
- }
- ]
- },
- "lastUpdate": "2022-11-12T23:50:23.123456+00:00",
- "minInterval": "PT0.000001S",
- "maxInterval": "P1Y2M3DT4H5M6.123456S"
- },
- {
- "idShort": "ExampleSubmodelCollection",
- "category": "PARAMETER",
- "description": [
- {
- "language": "en-US",
- "text": "Example SubmodelElementCollection object"
- },
- {
- "language": "de",
- "text": "Beispiel SubmodelElementCollection Element"
- }
- ],
- "modelType": "SubmodelElementCollection",
- "semanticId": {
- "type": "ExternalReference",
- "keys": [
- {
- "type": "GlobalReference",
- "value": "http://acplt.org/SubmodelElementCollections/ExampleSubmodelElementCollection"
- }
- ]
- },
- "value": [
- {
- "idShort": "ExampleBlob",
- "category": "PARAMETER",
- "description": [
- {
- "language": "en-US",
- "text": "Example Blob object"
- },
- {
- "language": "de",
- "text": "Beispiel Blob Element"
- }
- ],
- "modelType": "Blob",
- "semanticId": {
- "type": "ExternalReference",
- "keys": [
- {
- "type": "GlobalReference",
- "value": "http://acplt.org/Blobs/ExampleBlob"
- }
- ]
- },
- "contentType": "application/pdf",
- "value": "AQIDBAU="
- },
- {
- "idShort": "ExampleFile",
- "category": "PARAMETER",
- "description": [
- {
- "language": "en-US",
- "text": "Example File object"
- },
- {
- "language": "de",
- "text": "Beispiel File Element"
- }
- ],
- "modelType": "File",
- "semanticId": {
- "type": "ExternalReference",
- "keys": [
- {
- "type": "GlobalReference",
- "value": "http://acplt.org/Files/ExampleFile"
- }
- ]
- },
- "value": "/TestFile.pdf",
- "contentType": "application/pdf"
- },
- {
- "idShort": "ExampleMultiLanguageProperty",
- "category": "CONSTANT",
- "description": [
- {
- "language": "en-US",
- "text": "Example MultiLanguageProperty object"
- },
- {
- "language": "de",
- "text": "Beispiel MultiLanguageProperty Element"
- }
- ],
- "modelType": "MultiLanguageProperty",
- "semanticId": {
- "type": "ExternalReference",
- "keys": [
- {
- "type": "GlobalReference",
- "value": "http://acplt.org/MultiLanguageProperties/ExampleMultiLanguageProperty"
- }
- ]
- },
- "value": [
- {
- "language": "en-US",
- "text": "Example value of a MultiLanguageProperty element"
- },
- {
- "language": "de",
- "text": "Beispielwert f\u00fcr ein MultiLanguageProperty-Element"
- }
- ]
- },
- {
- "idShort": "ExampleProperty",
- "category": "CONSTANT",
- "description": [
- {
- "language": "en-US",
- "text": "Example Property object"
- },
- {
- "language": "de",
- "text": "Beispiel Property Element"
- }
- ],
- "modelType": "Property",
- "semanticId": {
- "type": "ExternalReference",
- "keys": [
- {
- "type": "GlobalReference",
- "value": "http://acplt.org/Properties/ExampleProperty"
- }
- ]
- },
- "qualifiers": [
- {
- "valueType": "xs:string",
- "type": "http://acplt.org/Qualifier/ExampleQualifier"
- }
- ],
- "value": "exampleValue",
- "valueType": "xs:string"
- },
- {
- "idShort": "ExampleRange",
- "category": "PARAMETER",
- "description": [
- {
- "language": "en-US",
- "text": "Example Range object"
- },
- {
- "language": "de",
- "text": "Beispiel Range Element"
- }
- ],
- "modelType": "Range",
- "semanticId": {
- "type": "ExternalReference",
- "keys": [
- {
- "type": "GlobalReference",
- "value": "http://acplt.org/Ranges/ExampleRange"
- }
- ]
- },
- "valueType": "xs:int",
- "min": "0",
- "max": "100"
- },
- {
- "idShort": "ExampleReferenceElement",
- "category": "PARAMETER",
- "description": [
- {
- "language": "en-US",
- "text": "Example Reference Element object"
- },
- {
- "language": "de",
- "text": "Beispiel Reference Element Element"
- }
- ],
- "modelType": "ReferenceElement",
- "semanticId": {
- "type": "ExternalReference",
- "keys": [
- {
- "type": "GlobalReference",
- "value": "http://acplt.org/ReferenceElements/ExampleReferenceElement"
- }
- ]
- },
- "value": {
- "type": "ModelReference",
- "keys": [
- {
- "type": "Submodel",
- "value": "http://acplt.org/Test_Submodel"
- },
- {
- "type": "Property",
- "value": "ExampleProperty"
- }
- ]
- }
- }
- ]
- }
- ]
- },
- {
- "idShort": "TestSubmodel",
- "description": [
- {
- "language": "en-US",
- "text": "An example submodel for the test application"
- },
- {
- "language": "de",
- "text": "Ein Beispiel-Teilmodell f\u00fcr eine Test-Anwendung"
- }
- ],
- "modelType": "Submodel",
- "id": "https://acplt.org/Test_Submodel_Template",
- "administration": {
- "version": "9",
- "revision": "0"
- },
- "semanticId": {
- "type": "ExternalReference",
- "keys": [
- {
- "type": "GlobalReference",
- "value": "http://acplt.org/SubmodelTemplates/ExampleSubmodel"
- }
- ]
- },
- "kind": "Template",
- "submodelElements": [
- {
- "idShort": "ExampleRelationshipElement",
- "category": "PARAMETER",
- "description": [
- {
- "language": "en-US",
- "text": "Example RelationshipElement object"
- },
- {
- "language": "de",
- "text": "Beispiel RelationshipElement Element"
- }
- ],
- "modelType": "RelationshipElement",
- "semanticId": {
- "type": "ExternalReference",
- "keys": [
- {
- "type": "GlobalReference",
- "value": "http://acplt.org/RelationshipElements/ExampleRelationshipElement"
- }
- ]
- },
- "kind": "Template",
- "first": {
- "type": "ModelReference",
- "keys": [
- {
- "type": "Submodel",
- "value": "http://acplt.org/Test_Submodel"
- },
- {
- "type": "Property",
- "value": "ExampleProperty"
- }
- ]
- },
- "second": {
- "type": "ModelReference",
- "keys": [
- {
- "type": "Submodel",
- "value": "http://acplt.org/Test_Submodel"
- },
- {
- "type": "Property",
- "value": "ExampleProperty"
- }
- ]
- }
- },
- {
- "idShort": "ExampleAnnotatedRelationshipElement",
- "category": "PARAMETER",
- "description": [
- {
- "language": "en-US",
- "text": "Example AnnotatedRelationshipElement object"
- },
- {
- "language": "de",
- "text": "Beispiel AnnotatedRelationshipElement Element"
- }
- ],
- "modelType": "AnnotatedRelationshipElement",
- "semanticId": {
- "type": "ExternalReference",
- "keys": [
- {
- "type": "GlobalReference",
- "value": "http://acplt.org/RelationshipElements/ExampleAnnotatedRelationshipElement"
- }
- ]
- },
- "kind": "Template",
- "first": {
- "type": "ModelReference",
- "keys": [
- {
- "type": "Submodel",
- "value": "http://acplt.org/Test_Submodel"
- },
- {
- "type": "Property",
- "value": "ExampleProperty"
- }
- ]
- },
- "second": {
- "type": "ModelReference",
- "keys": [
- {
- "type": "Submodel",
- "value": "http://acplt.org/Test_Submodel"
- },
- {
- "type": "Property",
- "value": "ExampleProperty"
- }
- ]
- }
- },
- {
- "idShort": "ExampleOperation",
- "category": "PARAMETER",
- "description": [
- {
- "language": "en-US",
- "text": "Example Operation object"
- },
- {
- "language": "de",
- "text": "Beispiel Operation Element"
- }
- ],
- "modelType": "Operation",
- "semanticId": {
- "type": "ExternalReference",
- "keys": [
- {
- "type": "GlobalReference",
- "value": "http://acplt.org/Operations/ExampleOperation"
- }
- ]
- },
- "kind": "Template",
- "inputVariables": [
- {
- "value": {
- "idShort": "ExamplePropertyInput",
- "category": "CONSTANT",
- "description": [
- {
- "language": "en-US",
- "text": "Example Property object"
- },
- {
- "language": "de",
- "text": "Beispiel Property Element"
- }
- ],
- "modelType": "Property",
- "semanticId": {
- "type": "ExternalReference",
- "keys": [
- {
- "type": "GlobalReference",
- "value": "http://acplt.org/Properties/ExamplePropertyInput"
- }
- ]
- },
- "kind": "Template",
- "valueType": "xs:string"
- }
- }
- ],
- "outputVariables": [
- {
- "value": {
- "idShort": "ExamplePropertyOutput",
- "category": "CONSTANT",
- "description": [
- {
- "language": "en-US",
- "text": "Example Property object"
- },
- {
- "language": "de",
- "text": "Beispiel Property Element"
- }
- ],
- "modelType": "Property",
- "semanticId": {
- "type": "ExternalReference",
- "keys": [
- {
- "type": "GlobalReference",
- "value": "http://acplt.org/Properties/ExamplePropertyOutput"
- }
- ]
- },
- "kind": "Template",
- "valueType": "xs:string"
- }
- }
- ],
- "inoutputVariables": [
- {
- "value": {
- "idShort": "ExamplePropertyInOutput",
- "category": "CONSTANT",
- "description": [
- {
- "language": "en-US",
- "text": "Example Property object"
- },
- {
- "language": "de",
- "text": "Beispiel Property Element"
- }
- ],
- "modelType": "Property",
- "semanticId": {
- "type": "ExternalReference",
- "keys": [
- {
- "type": "GlobalReference",
- "value": "http://acplt.org/Properties/ExamplePropertyInOutput"
- }
- ]
- },
- "kind": "Template",
- "valueType": "xs:string"
- }
- }
- ]
- },
- {
- "idShort": "ExampleCapability",
- "category": "PARAMETER",
- "description": [
- {
- "language": "en-US",
- "text": "Example Capability object"
- },
- {
- "language": "de",
- "text": "Beispiel Capability Element"
- }
- ],
- "modelType": "Capability",
- "semanticId": {
- "type": "ExternalReference",
- "keys": [
- {
- "type": "GlobalReference",
- "value": "http://acplt.org/Capabilities/ExampleCapability"
- }
- ]
- },
- "kind": "Template"
- },
- {
- "idShort": "ExampleBasicEventElement",
- "category": "PARAMETER",
- "description": [
- {
- "language": "en-US",
- "text": "Example BasicEventElement object"
- },
- {
- "language": "de",
- "text": "Beispiel BasicEventElement Element"
- }
- ],
- "modelType": "BasicEventElement",
- "semanticId": {
- "type": "ExternalReference",
- "keys": [
- {
- "type": "GlobalReference",
- "value": "http://acplt.org/Events/ExampleBasicEventElement"
- }
- ]
- },
- "kind": "Template",
- "observed": {
- "type": "ModelReference",
- "keys": [
- {
- "type": "Submodel",
- "value": "http://acplt.org/Test_Submodel"
- },
- {
- "type": "Property",
- "value": "ExampleProperty"
- }
- ]
- },
- "direction": "output",
- "state": "on",
- "messageTopic": "ExampleTopic",
- "messageBroker": {
- "type": "ModelReference",
- "keys": [
- {
- "type": "Submodel",
- "value": "http://acplt.org/ExampleMessageBroker"
- }
- ]
- },
- "lastUpdate": "2022-11-12T23:50:23.123456+00:00",
- "minInterval": "PT0.000001S",
- "maxInterval": "P1Y2M3DT4H5M6.123456S"
- },
- {
- "idShort": "ExampleSubmodelList",
- "typeValueListElement": "SubmodelElementCollection",
- "semanticIdListElement": {
- "type": "ExternalReference",
- "keys": [
- {
- "type": "GlobalReference",
- "value": "http://acplt.org/SubmodelElementCollections/ExampleSubmodelElementCollection"
- }
- ]
- },
- "orderRelevant": true,
- "category": "PARAMETER",
- "description": [
- {
- "language": "en-US",
- "text": "Example SubmodelElementList object"
- },
- {
- "language": "de",
- "text": "Beispiel SubmodelElementList Element"
- }
- ],
- "modelType": "SubmodelElementList",
- "semanticId": {
- "type": "ExternalReference",
- "keys": [
- {
- "type": "GlobalReference",
- "value": "http://acplt.org/SubmodelElementLists/ExampleSubmodelElementList"
- }
- ]
- },
- "kind": "Template",
- "value": [
- {
- "category": "PARAMETER",
- "description": [
- {
- "language": "en-US",
- "text": "Example SubmodelElementCollection object"
- },
- {
- "language": "de",
- "text": "Beispiel SubmodelElementCollection Element"
- }
- ],
- "modelType": "SubmodelElementCollection",
- "semanticId": {
- "type": "ExternalReference",
- "keys": [
- {
- "type": "GlobalReference",
- "value": "http://acplt.org/SubmodelElementCollections/ExampleSubmodelElementCollection"
- }
- ]
- },
- "kind": "Template",
- "value": [
- {
- "idShort": "ExampleProperty",
- "category": "CONSTANT",
- "description": [
- {
- "language": "en-US",
- "text": "Example Property object"
- },
- {
- "language": "de",
- "text": "Beispiel Property Element"
- }
- ],
- "modelType": "Property",
- "semanticId": {
- "type": "ExternalReference",
- "keys": [
- {
- "type": "GlobalReference",
- "value": "http://acplt.org/Properties/ExampleProperty"
- }
- ]
- },
- "kind": "Template",
- "valueType": "xs:string"
- },
- {
- "idShort": "ExampleMultiLanguageProperty",
- "category": "CONSTANT",
- "description": [
- {
- "language": "en-US",
- "text": "Example MultiLanguageProperty object"
- },
- {
- "language": "de",
- "text": "Beispiel MultiLanguageProperty Element"
- }
- ],
- "modelType": "MultiLanguageProperty",
- "semanticId": {
- "type": "ExternalReference",
- "keys": [
- {
- "type": "GlobalReference",
- "value": "http://acplt.org/MultiLanguageProperties/ExampleMultiLanguageProperty"
- }
- ]
- },
- "kind": "Template"
- },
- {
- "idShort": "ExampleRange",
- "category": "PARAMETER",
- "description": [
- {
- "language": "en-US",
- "text": "Example Range object"
- },
- {
- "language": "de",
- "text": "Beispiel Range Element"
- }
- ],
- "modelType": "Range",
- "semanticId": {
- "type": "ExternalReference",
- "keys": [
- {
- "type": "GlobalReference",
- "value": "http://acplt.org/Ranges/ExampleRange"
- }
- ]
- },
- "kind": "Template",
- "valueType": "xs:int",
- "max": "100"
- },
- {
- "idShort": "ExampleRange2",
- "category": "PARAMETER",
- "description": [
- {
- "language": "en-US",
- "text": "Example Range object"
- },
- {
- "language": "de",
- "text": "Beispiel Range Element"
- }
- ],
- "modelType": "Range",
- "semanticId": {
- "type": "ExternalReference",
- "keys": [
- {
- "type": "GlobalReference",
- "value": "http://acplt.org/Ranges/ExampleRange"
- }
- ]
- },
- "kind": "Template",
- "valueType": "xs:int",
- "min": "0"
- },
- {
- "idShort": "ExampleBlob",
- "category": "PARAMETER",
- "description": [
- {
- "language": "en-US",
- "text": "Example Blob object"
- },
- {
- "language": "de",
- "text": "Beispiel Blob Element"
- }
- ],
- "modelType": "Blob",
- "semanticId": {
- "type": "ExternalReference",
- "keys": [
- {
- "type": "GlobalReference",
- "value": "http://acplt.org/Blobs/ExampleBlob"
- }
- ]
- },
- "kind": "Template",
- "contentType": "application/pdf"
- },
- {
- "idShort": "ExampleFile",
- "category": "PARAMETER",
- "description": [
- {
- "language": "en-US",
- "text": "Example File object"
- },
- {
- "language": "de",
- "text": "Beispiel File Element"
- }
- ],
- "modelType": "File",
- "semanticId": {
- "type": "ExternalReference",
- "keys": [
- {
- "type": "GlobalReference",
- "value": "http://acplt.org/Files/ExampleFile"
- }
- ]
- },
- "kind": "Template",
- "contentType": "application/pdf"
- },
- {
- "idShort": "ExampleReferenceElement",
- "category": "PARAMETER",
- "description": [
- {
- "language": "en-US",
- "text": "Example Reference Element object"
- },
- {
- "language": "de",
- "text": "Beispiel Reference Element Element"
- }
- ],
- "modelType": "ReferenceElement",
- "semanticId": {
- "type": "ExternalReference",
- "keys": [
- {
- "type": "GlobalReference",
- "value": "http://acplt.org/ReferenceElements/ExampleReferenceElement"
- }
- ]
- },
- "kind": "Template"
- }
- ]
- },
- {
- "category": "PARAMETER",
- "description": [
- {
- "language": "en-US",
- "text": "Example SubmodelElementCollection object"
- },
- {
- "language": "de",
- "text": "Beispiel SubmodelElementCollection Element"
- }
- ],
- "modelType": "SubmodelElementCollection",
- "semanticId": {
- "type": "ExternalReference",
- "keys": [
- {
- "type": "GlobalReference",
- "value": "http://acplt.org/SubmodelElementCollections/ExampleSubmodelElementCollection"
- }
- ]
- },
- "kind": "Template"
- }
- ]
- },
- {
- "idShort": "ExampleSubmodelList2",
- "typeValueListElement": "Capability",
- "semanticIdListElement": {
- "type": "ExternalReference",
- "keys": [
- {
- "type": "GlobalReference",
- "value": "http://acplt.org/SubmodelElementCollections/ExampleSubmodelElementCollection"
- }
- ]
- },
- "orderRelevant": true,
- "category": "PARAMETER",
- "description": [
- {
- "language": "en-US",
- "text": "Example SubmodelElementList object"
- },
- {
- "language": "de",
- "text": "Beispiel SubmodelElementList Element"
- }
- ],
- "modelType": "SubmodelElementList",
- "semanticId": {
- "type": "ExternalReference",
- "keys": [
- {
- "type": "GlobalReference",
- "value": "http://acplt.org/SubmodelElementLists/ExampleSubmodelElementList"
- }
- ]
- },
- "kind": "Template"
- }
- ]
- }
- ],
- "conceptDescriptions": [
- {
- "idShort": "TestConceptDescription",
- "description": [
- {
- "language": "en-US",
- "text": "An example concept description for the test application"
- },
- {
- "language": "de",
- "text": "Ein Beispiel-ConceptDescription f\u00fcr eine Test-Anwendung"
- }
- ],
- "modelType": "ConceptDescription",
- "id": "https://acplt.org/Test_ConceptDescription",
- "administration": {
- "version": "9",
- "revision": "0",
- "creator": {
- "type": "ExternalReference",
- "keys": [
- {
- "type": "GlobalReference",
- "value": "http://acplt.org/AdministrativeInformation/Test_ConceptDescription"
- }
- ]
- },
- "templateId": "http://acplt.org/AdministrativeInformationTemplates/Test_ConceptDescription",
- "embeddedDataSpecifications": [
- {
- "dataSpecification": {
- "type": "ExternalReference",
- "keys": [
- {
- "type": "GlobalReference",
- "value": "https://admin-shell.io/DataSpecificationTemplates/DataSpecificationIEC61360/3/0"
- }
- ]
- },
- "dataSpecificationContent": {
- "modelType": "DataSpecificationIec61360",
- "preferredName": [
- {
- "language": "de",
- "text": "Test Specification"
- },
- {
- "language": "en-US",
- "text": "TestSpecification"
- }
- ],
- "dataType": "REAL_MEASURE",
- "definition": [
- {
- "language": "de",
- "text": "Dies ist eine Data Specification f\u00fcr Testzwecke"
- },
- {
- "language": "en-US",
- "text": "This is a DataSpecification for testing purposes"
- }
- ],
- "shortName": [
- {
- "language": "de",
- "text": "Test Spec"
- },
- {
- "language": "en-US",
- "text": "TestSpec"
- }
- ],
- "unit": "SpaceUnit",
- "unitId": {
- "type": "ExternalReference",
- "keys": [
- {
- "type": "GlobalReference",
- "value": "http://acplt.org/Units/SpaceUnit"
- }
- ]
- },
- "sourceOfDefinition": "http://acplt.org/DataSpec/ExampleDef",
- "symbol": "SU",
- "valueFormat": "M",
- "valueList": {
- "valueReferencePairs": [
- {
- "value": "exampleValue",
- "valueId": {
- "type": "ExternalReference",
- "keys": [
- {
- "type": "GlobalReference",
- "value": "http://acplt.org/ValueId/ExampleValueId"
- }
- ]
- }
- },
- {
- "value": "exampleValue2",
- "valueId": {
- "type": "ExternalReference",
- "keys": [
- {
- "type": "GlobalReference",
- "value": "http://acplt.org/ValueId/ExampleValueId2"
- }
- ]
- }
- }
- ]
- },
- "value": "TEST",
- "valueId": {
- "type": "ExternalReference",
- "keys": [
- {
- "type": "GlobalReference",
- "value": "http://acplt.org/Values/TestValueId"
- }
- ]
- },
- "levelType": {
- "min": true,
- "max": true,
- "nom": false,
- "typ": false
- }
- }
- }
- ]
- },
- "isCaseOf": [
- {
- "type": "ExternalReference",
- "keys": [
- {
- "type": "GlobalReference",
- "value": "http://acplt.org/DataSpecifications/ConceptDescriptions/TestConceptDescription"
- }
- ]
- }
- ]
- },
- {
- "modelType": "ConceptDescription",
- "id": "https://acplt.org/Test_ConceptDescription_Mandatory"
- },
- {
- "idShort": "TestConceptDescription",
- "description": [
- {
- "language": "en-US",
- "text": "An example concept description for the test application"
- },
- {
- "language": "de",
- "text": "Ein Beispiel-ConceptDescription f\u00fcr eine Test-Anwendung"
- }
- ],
- "modelType": "ConceptDescription",
- "id": "https://acplt.org/Test_ConceptDescription_Missing",
- "administration": {
- "version": "9",
- "revision": "0"
- }
- }
- ]
-}
\ No newline at end of file
diff --git a/compliance_tool/test/files/test_demo_full_example_json_aasx/docProps/core.xml b/compliance_tool/test/files/test_demo_full_example_json_aasx/docProps/core.xml
deleted file mode 100644
index 5f0e65331..000000000
--- a/compliance_tool/test/files/test_demo_full_example_json_aasx/docProps/core.xml
+++ /dev/null
@@ -1 +0,0 @@
-2020-01-01T00:00:00Eclipse BaSyx Python Testing FrameworkTest_DescriptionEclipse BaSyx Python Testing Framework Compliance Tool2020-01-01T00:00:011.0Test Title2.0.1
\ No newline at end of file
diff --git a/compliance_tool/test/files/test_demo_full_example_wrong_attribute.json b/compliance_tool/test/files/test_demo_full_example_wrong_attribute.json
deleted file mode 100644
index d748e7908..000000000
--- a/compliance_tool/test/files/test_demo_full_example_wrong_attribute.json
+++ /dev/null
@@ -1,3210 +0,0 @@
-{
- "assetAdministrationShells": [
- {
- "idShort": "TestAssetAdministrationShell123",
- "description": [
- {
- "language": "en-US",
- "text": "An Example Asset Administration Shell for the test application"
- },
- {
- "language": "de",
- "text": "Ein Beispiel-Verwaltungsschale f\u00fcr eine Test-Anwendung"
- }
- ],
- "modelType": "AssetAdministrationShell",
- "id": "https://acplt.org/Test_AssetAdministrationShell",
- "administration": {
- "version": "9",
- "revision": "0",
- "creator": {
- "type": "ExternalReference",
- "keys": [
- {
- "type": "GlobalReference",
- "value": "http://acplt.org/AdministrativeInformation/Test_AssetAdministrationShell"
- }
- ]
- },
- "templateId": "http://acplt.org/AdministrativeInformationTemplates/Test_AssetAdministrationShell"
- },
- "derivedFrom": {
- "type": "ModelReference",
- "keys": [
- {
- "type": "AssetAdministrationShell",
- "value": "https://acplt.org/TestAssetAdministrationShell2"
- }
- ]
- },
- "assetInformation": {
- "assetKind": "Instance",
- "globalAssetId": "http://acplt.org/TestAsset/",
- "specificAssetIds": [
- {
- "name": "TestKey",
- "value": "TestValue",
- "externalSubjectId": {
- "type": "ExternalReference",
- "keys": [
- {
- "type": "GlobalReference",
- "value": "http://acplt.org/SpecificAssetId/"
- }
- ]
- },
- "semanticId": {
- "type": "ExternalReference",
- "keys": [
- {
- "type": "GlobalReference",
- "value": "http://acplt.org/SpecificAssetId/"
- }
- ]
- }
- }
- ],
- "assetType": "http://acplt.org/TestAssetType/",
- "defaultThumbnail": {
- "path": "file:///path/to/thumbnail.png",
- "contentType": "image/png"
- }
- },
- "submodels": [
- {
- "type": "ModelReference",
- "keys": [
- {
- "type": "Submodel",
- "value": "https://acplt.org/Test_Submodel"
- }
- ],
- "referredSemanticId": {
- "type": "ExternalReference",
- "keys": [
- {
- "type": "GlobalReference",
- "value": "http://acplt.org/SubmodelTemplates/ExampleSubmodel"
- }
- ]
- }
- },
- {
- "type": "ModelReference",
- "keys": [
- {
- "type": "Submodel",
- "value": "http://acplt.org/Submodels/Assets/TestAsset/BillOfMaterial"
- }
- ]
- },
- {
- "type": "ModelReference",
- "keys": [
- {
- "type": "Submodel",
- "value": "http://acplt.org/Submodels/Assets/TestAsset/Identification"
- }
- ],
- "referredSemanticId": {
- "type": "ModelReference",
- "keys": [
- {
- "type": "Submodel",
- "value": "http://acplt.org/SubmodelTemplates/AssetIdentification"
- }
- ]
- }
- }
- ],
- "embeddedDataSpecifications": [
- {
- "dataSpecification": {
- "type": "ExternalReference",
- "keys": [
- {
- "type": "GlobalReference",
- "value": "https://admin-shell.io/DataSpecificationTemplates/DataSpecificationIEC61360/3/0"
- }
- ]
- },
- "dataSpecificationContent": {
- "modelType": "DataSpecificationIec61360",
- "preferredName": [
- {
- "language": "de",
- "text": "Test Specification"
- },
- {
- "language": "en-US",
- "text": "TestSpecification"
- }
- ],
- "dataType": "REAL_MEASURE",
- "definition": [
- {
- "language": "de",
- "text": "Dies ist eine Data Specification f\u00fcr Testzwecke"
- },
- {
- "language": "en-US",
- "text": "This is a DataSpecification for testing purposes"
- }
- ],
- "shortName": [
- {
- "language": "de",
- "text": "Test Spec"
- },
- {
- "language": "en-US",
- "text": "TestSpec"
- }
- ],
- "unit": "SpaceUnit",
- "unitId": {
- "type": "ExternalReference",
- "keys": [
- {
- "type": "GlobalReference",
- "value": "http://acplt.org/Units/SpaceUnit"
- }
- ]
- },
- "sourceOfDefinition": "http://acplt.org/DataSpec/ExampleDef",
- "symbol": "SU",
- "valueFormat": "M",
- "valueList": {
- "valueReferencePairs": [
- {
- "value": "exampleValue",
- "valueId": {
- "type": "ExternalReference",
- "keys": [
- {
- "type": "GlobalReference",
- "value": "http://acplt.org/ValueId/ExampleValueId"
- }
- ]
- }
- },
- {
- "value": "exampleValue2",
- "valueId": {
- "type": "ExternalReference",
- "keys": [
- {
- "type": "GlobalReference",
- "value": "http://acplt.org/ValueId/ExampleValueId2"
- }
- ]
- }
- }
- ]
- },
- "value": "TEST",
- "valueId": {
- "type": "ExternalReference",
- "keys": [
- {
- "type": "GlobalReference",
- "value": "http://acplt.org/Values/TestValueId"
- }
- ]
- },
- "levelType": {
- "min": true,
- "max": true,
- "nom": false,
- "typ": false
- }
- }
- }
- ]
- },
- {
- "modelType": "AssetAdministrationShell",
- "id": "https://acplt.org/Test_AssetAdministrationShell_Mandatory",
- "assetInformation": {
- "assetKind": "Instance",
- "globalAssetId": "http://acplt.org/Test_Asset_Mandatory/"
- },
- "submodels": [
- {
- "type": "ModelReference",
- "keys": [
- {
- "type": "Submodel",
- "value": "https://acplt.org/Test_Submodel2_Mandatory"
- }
- ]
- },
- {
- "type": "ModelReference",
- "keys": [
- {
- "type": "Submodel",
- "value": "https://acplt.org/Test_Submodel_Mandatory"
- }
- ]
- }
- ]
- },
- {
- "modelType": "AssetAdministrationShell",
- "id": "https://acplt.org/Test_AssetAdministrationShell2_Mandatory",
- "assetInformation": {
- "assetKind": "Instance",
- "globalAssetId": "http://acplt.org/TestAsset2_Mandatory/"
- }
- },
- {
- "idShort": "TestAssetAdministrationShell",
- "description": [
- {
- "language": "en-US",
- "text": "An Example Asset Administration Shell for the test application"
- },
- {
- "language": "de",
- "text": "Ein Beispiel-Verwaltungsschale f\u00fcr eine Test-Anwendung"
- }
- ],
- "modelType": "AssetAdministrationShell",
- "id": "https://acplt.org/Test_AssetAdministrationShell_Missing",
- "administration": {
- "version": "9",
- "revision": "0"
- },
- "assetInformation": {
- "assetKind": "Instance",
- "globalAssetId": "http://acplt.org/Test_Asset_Missing/",
- "specificAssetIds": [
- {
- "name": "TestKey",
- "value": "TestValue",
- "externalSubjectId": {
- "type": "ExternalReference",
- "keys": [
- {
- "type": "GlobalReference",
- "value": "http://acplt.org/SpecificAssetId/"
- }
- ]
- }
- }
- ],
- "defaultThumbnail": {
- "path": "file:///TestFile.pdf",
- "contentType": "application/pdf"
- }
- },
- "submodels": [
- {
- "type": "ModelReference",
- "keys": [
- {
- "type": "Submodel",
- "value": "https://acplt.org/Test_Submodel_Missing"
- }
- ]
- }
- ]
- }
- ],
- "submodels": [
- {
- "idShort": "Identification",
- "description": [
- {
- "language": "en-US",
- "text": "An example asset identification submodel for the test application"
- },
- {
- "language": "de",
- "text": "Ein Beispiel-Identifikations-Submodel f\u00fcr eine Test-Anwendung"
- }
- ],
- "modelType": "Submodel",
- "id": "http://acplt.org/Submodels/Assets/TestAsset/Identification",
- "administration": {
- "version": "9",
- "revision": "0",
- "creator": {
- "type": "ExternalReference",
- "keys": [
- {
- "type": "GlobalReference",
- "value": "http://acplt.org/AdministrativeInformation/TestAsset/Identification"
- }
- ]
- },
- "templateId": "http://acplt.org/AdministrativeInformationTemplates/TestAsset/Identification"
- },
- "semanticId": {
- "type": "ModelReference",
- "keys": [
- {
- "type": "Submodel",
- "value": "http://acplt.org/SubmodelTemplates/AssetIdentification"
- }
- ]
- },
- "submodelElements": [
- {
- "extensions": [
- {
- "value": "ExampleExtensionValue",
- "refersTo": [
- {
- "type": "ModelReference",
- "keys": [
- {
- "type": "AssetAdministrationShell",
- "value": "http://acplt.org/RefersTo/ExampleRefersTo"
- }
- ]
- }
- ],
- "valueType": "xs:string",
- "name": "ExampleExtension"
- }
- ],
- "idShort": "ManufacturerName",
- "category": "PARAMETER",
- "description": [
- {
- "language": "en-US",
- "text": "Legally valid designation of the natural or judicial person which is directly responsible for the design, production, packaging and labeling of a product in respect to its being brought into circulation."
- },
- {
- "language": "de",
- "text": "Bezeichnung f\u00fcr eine nat\u00fcrliche oder juristische Person, die f\u00fcr die Auslegung, Herstellung und Verpackung sowie die Etikettierung eines Produkts im Hinblick auf das 'Inverkehrbringen' im eigenen Namen verantwortlich ist"
- }
- ],
- "modelType": "Property",
- "semanticId": {
- "type": "ExternalReference",
- "keys": [
- {
- "type": "GlobalReference",
- "value": "0173-1#02-AAO677#002"
- }
- ]
- },
- "qualifiers": [
- {
- "value": "50",
- "valueId": {
- "type": "ExternalReference",
- "keys": [
- {
- "type": "GlobalReference",
- "value": "http://acplt.org/ValueId/ExampleValueId"
- }
- ]
- },
- "valueType": "xs:int",
- "type": "http://acplt.org/Qualifier/ExampleQualifier2",
- "kind": "TemplateQualifier"
- },
- {
- "value": "100",
- "valueId": {
- "type": "ExternalReference",
- "keys": [
- {
- "type": "GlobalReference",
- "value": "http://acplt.org/ValueId/ExampleValueId"
- }
- ]
- },
- "valueType": "xs:int",
- "type": "http://acplt.org/Qualifier/ExampleQualifier",
- "kind": "ConceptQualifier"
- }
- ],
- "value": "ACPLT",
- "valueId": {
- "type": "ExternalReference",
- "keys": [
- {
- "type": "GlobalReference",
- "value": "http://acplt.org/ValueId/ExampleValueId"
- }
- ]
- },
- "valueType": "xs:string"
- },
- {
- "idShort": "InstanceId",
- "category": "PARAMETER",
- "description": [
- {
- "language": "en-US",
- "text": "Legally valid designation of the natural or judicial person which is directly responsible for the design, production, packaging and labeling of a product in respect to its being brought into circulation."
- },
- {
- "language": "de",
- "text": "Bezeichnung f\u00fcr eine nat\u00fcrliche oder juristische Person, die f\u00fcr die Auslegung, Herstellung und Verpackung sowie die Etikettierung eines Produkts im Hinblick auf das 'Inverkehrbringen' im eigenen Namen verantwortlich ist"
- }
- ],
- "modelType": "Property",
- "semanticId": {
- "type": "ExternalReference",
- "keys": [
- {
- "type": "GlobalReference",
- "value": "http://opcfoundation.org/UA/DI/1.1/DeviceType/Serialnumber"
- }
- ]
- },
- "qualifiers": [
- {
- "value": "2023-04-07T16:59:54.870123",
- "valueId": {
- "type": "ExternalReference",
- "keys": [
- {
- "type": "GlobalReference",
- "value": "http://acplt.org/ValueId/ExampleValueId"
- }
- ]
- },
- "valueType": "xs:dateTime",
- "type": "http://acplt.org/Qualifier/ExampleQualifier3",
- "kind": "ValueQualifier"
- }
- ],
- "value": "978-8234-234-342",
- "valueId": {
- "type": "ExternalReference",
- "keys": [
- {
- "type": "GlobalReference",
- "value": "http://acplt.org/ValueId/ExampleValueId"
- }
- ]
- },
- "valueType": "xs:string"
- }
- ]
- },
- {
- "idShort": "BillOfMaterial",
- "description": [
- {
- "language": "en-US",
- "text": "An example bill of material submodel for the test application"
- },
- {
- "language": "de",
- "text": "Ein Beispiel-BillOfMaterial-Submodel f\u00fcr eine Test-Anwendung"
- }
- ],
- "modelType": "Submodel",
- "id": "http://acplt.org/Submodels/Assets/TestAsset/BillOfMaterial",
- "administration": {
- "version": "9",
- "templateId": "http://acplt.org/AdministrativeInformationTemplates/TestAsset/BillOfMaterial"
- },
- "semanticId": {
- "type": "ModelReference",
- "keys": [
- {
- "type": "Submodel",
- "value": "http://acplt.org/SubmodelTemplates/BillOfMaterial"
- }
- ]
- },
- "submodelElements": [
- {
- "idShort": "ExampleEntity",
- "category": "PARAMETER",
- "description": [
- {
- "language": "en-US",
- "text": "Legally valid designation of the natural or judicial person which is directly responsible for the design, production, packaging and labeling of a product in respect to its being brought into circulation."
- },
- {
- "language": "de",
- "text": "Bezeichnung f\u00fcr eine nat\u00fcrliche oder juristische Person, die f\u00fcr die Auslegung, Herstellung und Verpackung sowie die Etikettierung eines Produkts im Hinblick auf das 'Inverkehrbringen' im eigenen Namen verantwortlich ist"
- }
- ],
- "modelType": "Entity",
- "semanticId": {
- "type": "ExternalReference",
- "keys": [
- {
- "type": "GlobalReference",
- "value": "http://opcfoundation.org/UA/DI/1.1/DeviceType/Serialnumber"
- }
- ]
- },
- "statements": [
- {
- "idShort": "ExampleProperty2",
- "category": "CONSTANT",
- "description": [
- {
- "language": "en-US",
- "text": "Example Property object"
- },
- {
- "language": "de",
- "text": "Beispiel Property Element"
- }
- ],
- "modelType": "Property",
- "semanticId": {
- "type": "ExternalReference",
- "keys": [
- {
- "type": "GlobalReference",
- "value": "http://acplt.org/Properties/ExampleProperty"
- }
- ]
- },
- "value": "exampleValue2",
- "valueId": {
- "type": "ExternalReference",
- "keys": [
- {
- "type": "GlobalReference",
- "value": "http://acplt.org/ValueId/ExampleValueId"
- }
- ]
- },
- "valueType": "xs:string"
- },
- {
- "idShort": "ExampleProperty",
- "category": "CONSTANT",
- "description": [
- {
- "language": "en-US",
- "text": "Example Property object"
- },
- {
- "language": "de",
- "text": "Beispiel Property Element"
- }
- ],
- "modelType": "Property",
- "semanticId": {
- "type": "ExternalReference",
- "keys": [
- {
- "type": "GlobalReference",
- "value": "http://acplt.org/Properties/ExampleProperty"
- }
- ]
- },
- "value": "exampleValue",
- "valueId": {
- "type": "ExternalReference",
- "keys": [
- {
- "type": "GlobalReference",
- "value": "http://acplt.org/ValueId/ExampleValueId"
- }
- ]
- },
- "valueType": "xs:string",
- "embeddedDataSpecifications": [
- {
- "dataSpecification": {
- "type": "ExternalReference",
- "keys": [
- {
- "type": "GlobalReference",
- "value": "https://admin-shell.io/DataSpecificationTemplates/DataSpecificationIEC61360/3/0"
- }
- ]
- },
- "dataSpecificationContent": {
- "modelType": "DataSpecificationIec61360",
- "preferredName": [
- {
- "language": "de",
- "text": "Test Specification"
- },
- {
- "language": "en-US",
- "text": "TestSpecification"
- }
- ],
- "dataType": "REAL_MEASURE",
- "definition": [
- {
- "language": "de",
- "text": "Dies ist eine Data Specification f\u00fcr Testzwecke"
- },
- {
- "language": "en-US",
- "text": "This is a DataSpecification for testing purposes"
- }
- ],
- "shortName": [
- {
- "language": "de",
- "text": "Test Spec"
- },
- {
- "language": "en-US",
- "text": "TestSpec"
- }
- ],
- "unit": "SpaceUnit",
- "unitId": {
- "type": "ExternalReference",
- "keys": [
- {
- "type": "GlobalReference",
- "value": "http://acplt.org/Units/SpaceUnit"
- }
- ]
- },
- "sourceOfDefinition": "http://acplt.org/DataSpec/ExampleDef",
- "symbol": "SU",
- "valueFormat": "M",
- "valueList": {
- "valueReferencePairs": [
- {
- "value": "exampleValue",
- "valueId": {
- "type": "ExternalReference",
- "keys": [
- {
- "type": "GlobalReference",
- "value": "http://acplt.org/ValueId/ExampleValueId"
- }
- ]
- },
- "valueType": "xs:string"
- },
- {
- "value": "exampleValue2",
- "valueId": {
- "type": "ExternalReference",
- "keys": [
- {
- "type": "GlobalReference",
- "value": "http://acplt.org/ValueId/ExampleValueId2"
- }
- ]
- },
- "valueType": "xs:string"
- }
- ]
- },
- "value": "TEST",
- "valueId": {
- "type": "ExternalReference",
- "keys": [
- {
- "type": "GlobalReference",
- "value": "http://acplt.org/Values/TestValueId"
- }
- ]
- },
- "levelType": {
- "min": true,
- "max": true,
- "nom": false,
- "typ": false
- }
- }
- }
- ]
- }
- ],
- "entityType": "SelfManagedEntity",
- "globalAssetId": "http://acplt.org/TestAsset/",
- "specificAssetIds": [
- {
- "name": "TestKey",
- "value": "TestValue",
- "externalSubjectId": {
- "type": "ExternalReference",
- "keys": [
- {
- "type": "GlobalReference",
- "value": "http://acplt.org/SpecificAssetId/"
- }
- ]
- }
- }
- ]
- },
- {
- "idShort": "ExampleEntity2",
- "category": "PARAMETER",
- "description": [
- {
- "language": "en-US",
- "text": "Legally valid designation of the natural or judicial person which is directly responsible for the design, production, packaging and labeling of a product in respect to its being brought into circulation."
- },
- {
- "language": "de",
- "text": "Bezeichnung f\u00fcr eine nat\u00fcrliche oder juristische Person, die f\u00fcr die Auslegung, Herstellung und Verpackung sowie die Etikettierung eines Produkts im Hinblick auf das 'Inverkehrbringen' im eigenen Namen verantwortlich ist"
- }
- ],
- "modelType": "Entity",
- "semanticId": {
- "type": "ExternalReference",
- "keys": [
- {
- "type": "GlobalReference",
- "value": "http://opcfoundation.org/UA/DI/1.1/DeviceType/Serialnumber"
- }
- ]
- },
- "entityType": "CoManagedEntity"
- }
- ]
- },
- {
- "idShort": "TestSubmodel",
- "description": [
- {
- "language": "en-US",
- "text": "An example submodel for the test application"
- },
- {
- "language": "de",
- "text": "Ein Beispiel-Teilmodell f\u00fcr eine Test-Anwendung"
- }
- ],
- "modelType": "Submodel",
- "id": "https://acplt.org/Test_Submodel",
- "administration": {
- "version": "9",
- "revision": "0",
- "creator": {
- "type": "ExternalReference",
- "keys": [
- {
- "type": "GlobalReference",
- "value": "http://acplt.org/AdministrativeInformation/Test_Submodel"
- }
- ]
- }
- },
- "semanticId": {
- "type": "ExternalReference",
- "keys": [
- {
- "type": "GlobalReference",
- "value": "http://acplt.org/SubmodelTemplates/ExampleSubmodel"
- }
- ]
- },
- "submodelElements": [
- {
- "idShort": "ExampleRelationshipElement",
- "category": "PARAMETER",
- "description": [
- {
- "language": "en-US",
- "text": "Example RelationshipElement object"
- },
- {
- "language": "de",
- "text": "Beispiel RelationshipElement Element"
- }
- ],
- "modelType": "RelationshipElement",
- "semanticId": {
- "type": "ModelReference",
- "keys": [
- {
- "type": "ConceptDescription",
- "value": "https://acplt.org/Test_ConceptDescription"
- }
- ]
- },
- "first": {
- "type": "ModelReference",
- "keys": [
- {
- "type": "Submodel",
- "value": "http://acplt.org/Test_Submodel"
- },
- {
- "type": "Property",
- "value": "ExampleProperty"
- }
- ]
- },
- "second": {
- "type": "ModelReference",
- "keys": [
- {
- "type": "Submodel",
- "value": "http://acplt.org/Test_Submodel"
- },
- {
- "type": "Property",
- "value": "ExampleProperty2"
- }
- ]
- }
- },
- {
- "idShort": "ExampleAnnotatedRelationshipElement",
- "category": "PARAMETER",
- "description": [
- {
- "language": "en-US",
- "text": "Example AnnotatedRelationshipElement object"
- },
- {
- "language": "de",
- "text": "Beispiel AnnotatedRelationshipElement Element"
- }
- ],
- "modelType": "AnnotatedRelationshipElement",
- "semanticId": {
- "type": "ExternalReference",
- "keys": [
- {
- "type": "GlobalReference",
- "value": "http://acplt.org/RelationshipElements/ExampleAnnotatedRelationshipElement"
- }
- ]
- },
- "first": {
- "type": "ModelReference",
- "keys": [
- {
- "type": "Submodel",
- "value": "http://acplt.org/Test_Submodel"
- },
- {
- "type": "Property",
- "value": "ExampleProperty"
- }
- ]
- },
- "second": {
- "type": "ModelReference",
- "keys": [
- {
- "type": "Submodel",
- "value": "http://acplt.org/Test_Submodel"
- },
- {
- "type": "Property",
- "value": "ExampleProperty2"
- }
- ]
- },
- "annotations": [
- {
- "idShort": "ExampleAnnotatedProperty",
- "category": "PARAMETER",
- "modelType": "Property",
- "value": "exampleValue",
- "valueType": "xs:string"
- },
- {
- "idShort": "ExampleAnnotatedRange",
- "category": "PARAMETER",
- "modelType": "Range",
- "valueType": "xs:integer",
- "min": "1",
- "max": "5"
- }
- ]
- },
- {
- "idShort": "ExampleOperation",
- "category": "PARAMETER",
- "description": [
- {
- "language": "en-US",
- "text": "Example Operation object"
- },
- {
- "language": "de",
- "text": "Beispiel Operation Element"
- }
- ],
- "modelType": "Operation",
- "semanticId": {
- "type": "ExternalReference",
- "keys": [
- {
- "type": "GlobalReference",
- "value": "http://acplt.org/Operations/ExampleOperation"
- }
- ]
- },
- "inputVariables": [
- {
- "value": {
- "idShort": "ExamplePropertyInput",
- "displayName": [
- {
- "language": "en-US",
- "text": "ExampleProperty"
- },
- {
- "language": "de",
- "text": "BeispielProperty"
- }
- ],
- "category": "CONSTANT",
- "description": [
- {
- "language": "en-US",
- "text": "Example Property object"
- },
- {
- "language": "de",
- "text": "Beispiel Property Element"
- }
- ],
- "modelType": "Property",
- "semanticId": {
- "type": "ExternalReference",
- "keys": [
- {
- "type": "GlobalReference",
- "value": "http://acplt.org/Properties/ExamplePropertyInput"
- }
- ]
- },
- "kind": "Template",
- "value": "exampleValue",
- "valueId": {
- "type": "ExternalReference",
- "keys": [
- {
- "type": "GlobalReference",
- "value": "http://acplt.org/ValueId/ExampleValueId"
- }
- ]
- },
- "valueType": "xs:string"
- }
- }
- ],
- "outputVariables": [
- {
- "value": {
- "idShort": "ExamplePropertyOutput",
- "displayName": [
- {
- "language": "en-US",
- "text": "ExampleProperty"
- },
- {
- "language": "de",
- "text": "BeispielProperty"
- }
- ],
- "category": "CONSTANT",
- "description": [
- {
- "language": "en-US",
- "text": "Example Property object"
- },
- {
- "language": "de",
- "text": "Beispiel Property Element"
- }
- ],
- "modelType": "Property",
- "semanticId": {
- "type": "ExternalReference",
- "keys": [
- {
- "type": "GlobalReference",
- "value": "http://acplt.org/Properties/ExamplePropertyOutput"
- }
- ]
- },
- "kind": "Template",
- "value": "exampleValue",
- "valueId": {
- "type": "ExternalReference",
- "keys": [
- {
- "type": "GlobalReference",
- "value": "http://acplt.org/ValueId/ExampleValueId"
- }
- ]
- },
- "valueType": "xs:string"
- }
- }
- ],
- "inoutputVariables": [
- {
- "value": {
- "idShort": "ExamplePropertyInOutput",
- "displayName": [
- {
- "language": "en-US",
- "text": "ExampleProperty"
- },
- {
- "language": "de",
- "text": "BeispielProperty"
- }
- ],
- "category": "CONSTANT",
- "description": [
- {
- "language": "en-US",
- "text": "Example Property object"
- },
- {
- "language": "de",
- "text": "Beispiel Property Element"
- }
- ],
- "modelType": "Property",
- "semanticId": {
- "type": "ExternalReference",
- "keys": [
- {
- "type": "GlobalReference",
- "value": "http://acplt.org/Properties/ExamplePropertyInOutput"
- }
- ]
- },
- "kind": "Template",
- "value": "exampleValue",
- "valueId": {
- "type": "ExternalReference",
- "keys": [
- {
- "type": "GlobalReference",
- "value": "http://acplt.org/ValueId/ExampleValueId"
- }
- ]
- },
- "valueType": "xs:string"
- }
- }
- ]
- },
- {
- "idShort": "ExampleCapability",
- "category": "PARAMETER",
- "description": [
- {
- "language": "en-US",
- "text": "Example Capability object"
- },
- {
- "language": "de",
- "text": "Beispiel Capability Element"
- }
- ],
- "modelType": "Capability",
- "semanticId": {
- "type": "ExternalReference",
- "keys": [
- {
- "type": "GlobalReference",
- "value": "http://acplt.org/Capabilities/ExampleCapability"
- }
- ]
- }
- },
- {
- "idShort": "ExampleBasicEventElement",
- "category": "PARAMETER",
- "description": [
- {
- "language": "en-US",
- "text": "Example BasicEventElement object"
- },
- {
- "language": "de",
- "text": "Beispiel BasicEventElement Element"
- }
- ],
- "modelType": "BasicEventElement",
- "semanticId": {
- "type": "ExternalReference",
- "keys": [
- {
- "type": "GlobalReference",
- "value": "http://acplt.org/Events/ExampleBasicEventElement"
- }
- ]
- },
- "observed": {
- "type": "ModelReference",
- "keys": [
- {
- "type": "Submodel",
- "value": "http://acplt.org/Test_Submodel"
- },
- {
- "type": "Property",
- "value": "ExampleProperty"
- }
- ]
- },
- "direction": "output",
- "state": "on",
- "messageTopic": "ExampleTopic",
- "messageBroker": {
- "type": "ModelReference",
- "keys": [
- {
- "type": "Submodel",
- "value": "http://acplt.org/ExampleMessageBroker"
- }
- ]
- },
- "lastUpdate": "2022-11-12T23:50:23.123456+00:00",
- "minInterval": "PT0.000001S",
- "maxInterval": "P1Y2M3DT4H5M6.123456S"
- },
- {
- "idShort": "ExampleSubmodelCollection",
- "category": "PARAMETER",
- "description": [
- {
- "language": "en-US",
- "text": "Example SubmodelElementCollection object"
- },
- {
- "language": "de",
- "text": "Beispiel SubmodelElementCollection Element"
- }
- ],
- "modelType": "SubmodelElementCollection",
- "semanticId": {
- "type": "ExternalReference",
- "keys": [
- {
- "type": "GlobalReference",
- "value": "http://acplt.org/SubmodelElementCollections/ExampleSubmodelElementCollection"
- }
- ]
- },
- "value": [
- {
- "idShort": "ExampleBlob",
- "category": "PARAMETER",
- "description": [
- {
- "language": "en-US",
- "text": "Example Blob object"
- },
- {
- "language": "de",
- "text": "Beispiel Blob Element"
- }
- ],
- "modelType": "Blob",
- "semanticId": {
- "type": "ExternalReference",
- "keys": [
- {
- "type": "GlobalReference",
- "value": "http://acplt.org/Blobs/ExampleBlob"
- }
- ]
- },
- "contentType": "application/pdf",
- "value": "AQIDBAU="
- },
- {
- "idShort": "ExampleFile",
- "category": "PARAMETER",
- "description": [
- {
- "language": "en-US",
- "text": "Example File object"
- },
- {
- "language": "de",
- "text": "Beispiel File Element"
- }
- ],
- "modelType": "File",
- "semanticId": {
- "type": "ExternalReference",
- "keys": [
- {
- "type": "GlobalReference",
- "value": "http://acplt.org/Files/ExampleFile"
- }
- ]
- },
- "value": "/TestFile.pdf",
- "contentType": "application/pdf"
- },
- {
- "idShort": "ExampleFileURI",
- "category": "CONSTANT",
- "description": [
- {
- "language": "en-US",
- "text": "Details of the Asset Administration Shell \u2014 An example for an external file reference"
- },
- {
- "language": "de",
- "text": "Details of the Asset Administration Shell \u2013 Ein Beispiel f\u00fcr eine extern referenzierte Datei"
- }
- ],
- "modelType": "File",
- "semanticId": {
- "type": "ExternalReference",
- "keys": [
- {
- "type": "GlobalReference",
- "value": "http://acplt.org/Files/ExampleFile"
- }
- ]
- },
- "value": "https://www.plattform-i40.de/PI40/Redaktion/DE/Downloads/Publikation/Details-of-the-Asset-Administration-Shell-Part1.pdf?__blob=publicationFile&v=5",
- "contentType": "application/pdf"
- },
- {
- "idShort": "ExampleMultiLanguageProperty",
- "category": "CONSTANT",
- "description": [
- {
- "language": "en-US",
- "text": "Example MultiLanguageProperty object"
- },
- {
- "language": "de",
- "text": "Beispiel MultiLanguageProperty Element"
- }
- ],
- "modelType": "MultiLanguageProperty",
- "semanticId": {
- "type": "ExternalReference",
- "keys": [
- {
- "type": "GlobalReference",
- "value": "http://acplt.org/MultiLanguageProperties/ExampleMultiLanguageProperty"
- }
- ],
- "referredSemanticId": {
- "type": "ExternalReference",
- "keys": [
- {
- "type": "GlobalReference",
- "value": "http://acplt.org/Properties/ExampleProperty/Referred"
- }
- ]
- }
- },
- "value": [
- {
- "language": "en-US",
- "text": "Example value of a MultiLanguageProperty element"
- },
- {
- "language": "de",
- "text": "Beispielwert f\u00fcr ein MultiLanguageProperty-Element"
- }
- ],
- "valueId": {
- "type": "ExternalReference",
- "keys": [
- {
- "type": "GlobalReference",
- "value": "http://acplt.org/ValueId/ExampleMultiLanguageValueId"
- }
- ]
- }
- },
- {
- "idShort": "ExampleRange",
- "category": "PARAMETER",
- "description": [
- {
- "language": "en-US",
- "text": "Example Range object"
- },
- {
- "language": "de",
- "text": "Beispiel Range Element"
- }
- ],
- "modelType": "Range",
- "semanticId": {
- "type": "ExternalReference",
- "keys": [
- {
- "type": "GlobalReference",
- "value": "http://acplt.org/Ranges/ExampleRange"
- }
- ]
- },
- "valueType": "xs:int",
- "min": "0",
- "max": "100"
- },
- {
- "idShort": "ExampleReferenceElement",
- "category": "PARAMETER",
- "description": [
- {
- "language": "en-US",
- "text": "Example Reference Element object"
- },
- {
- "language": "de",
- "text": "Beispiel Reference Element Element"
- }
- ],
- "modelType": "ReferenceElement",
- "semanticId": {
- "type": "ExternalReference",
- "keys": [
- {
- "type": "GlobalReference",
- "value": "http://acplt.org/ReferenceElements/ExampleReferenceElement"
- }
- ]
- },
- "value": {
- "type": "ModelReference",
- "keys": [
- {
- "type": "Submodel",
- "value": "http://acplt.org/Test_Submodel"
- },
- {
- "type": "Property",
- "value": "ExampleProperty"
- }
- ]
- }
- },
- {
- "idShort": "ExampleSubmodelList",
- "typeValueListElement": "Property",
- "valueTypeListElement": "xs:string",
- "semanticIdListElement": {
- "type": "ExternalReference",
- "keys": [
- {
- "type": "GlobalReference",
- "value": "http://acplt.org/Properties/ExampleProperty"
- }
- ]
- },
- "orderRelevant": true,
- "category": "PARAMETER",
- "description": [
- {
- "language": "en-US",
- "text": "Example SubmodelElementList object"
- },
- {
- "language": "de",
- "text": "Beispiel SubmodelElementList Element"
- }
- ],
- "modelType": "SubmodelElementList",
- "semanticId": {
- "type": "ExternalReference",
- "keys": [
- {
- "type": "GlobalReference",
- "value": "http://acplt.org/SubmodelElementLists/ExampleSubmodelElementList"
- }
- ]
- },
- "value": [
- {
- "displayName": [
- {
- "language": "en-US",
- "text": "ExampleProperty"
- },
- {
- "language": "de",
- "text": "BeispielProperty"
- }
- ],
- "category": "CONSTANT",
- "description": [
- {
- "language": "en-US",
- "text": "Example Property object"
- },
- {
- "language": "de",
- "text": "Beispiel Property Element"
- }
- ],
- "modelType": "Property",
- "semanticId": {
- "type": "ExternalReference",
- "keys": [
- {
- "type": "GlobalReference",
- "value": "http://acplt.org/Properties/ExampleProperty"
- }
- ]
- },
- "supplementalSemanticIds": [
- {
- "type": "ExternalReference",
- "keys": [
- {
- "type": "GlobalReference",
- "value": "http://acplt.org/Properties/ExampleProperty/SupplementalId1"
- }
- ]
- },
- {
- "type": "ExternalReference",
- "keys": [
- {
- "type": "GlobalReference",
- "value": "http://acplt.org/Properties/ExampleProperty/SupplementalId2"
- }
- ]
- }
- ],
- "value": "exampleValue",
- "valueId": {
- "type": "ExternalReference",
- "keys": [
- {
- "type": "GlobalReference",
- "value": "http://acplt.org/ValueId/ExampleValueId"
- }
- ]
- },
- "valueType": "xs:string",
- "embeddedDataSpecifications": [
- {
- "dataSpecification": {
- "type": "ExternalReference",
- "keys": [
- {
- "type": "GlobalReference",
- "value": "https://admin-shell.io/DataSpecificationTemplates/DataSpecificationIEC61360/3/0"
- }
- ]
- },
- "dataSpecificationContent": {
- "modelType": "DataSpecificationIec61360",
- "preferredName": [
- {
- "language": "de",
- "text": "Test Specification"
- },
- {
- "language": "en-US",
- "text": "TestSpecification"
- }
- ],
- "dataType": "REAL_MEASURE",
- "definition": [
- {
- "language": "de",
- "text": "Dies ist eine Data Specification f\u00fcr Testzwecke"
- },
- {
- "language": "en-US",
- "text": "This is a DataSpecification for testing purposes"
- }
- ],
- "shortName": [
- {
- "language": "de",
- "text": "Test Spec"
- },
- {
- "language": "en-US",
- "text": "TestSpec"
- }
- ],
- "unit": "SpaceUnit",
- "unitId": {
- "type": "ExternalReference",
- "keys": [
- {
- "type": "GlobalReference",
- "value": "http://acplt.org/Units/SpaceUnit"
- }
- ]
- },
- "sourceOfDefinition": "http://acplt.org/DataSpec/ExampleDef",
- "symbol": "SU",
- "valueFormat": "M",
- "valueList": {
- "valueReferencePairs": [
- {
- "value": "exampleValue",
- "valueId": {
- "type": "ExternalReference",
- "keys": [
- {
- "type": "GlobalReference",
- "value": "http://acplt.org/ValueId/ExampleValueId"
- }
- ]
- }
- },
- {
- "value": "exampleValue2",
- "valueId": {
- "type": "ExternalReference",
- "keys": [
- {
- "type": "GlobalReference",
- "value": "http://acplt.org/ValueId/ExampleValueId2"
- }
- ]
- }
- }
- ]
- },
- "value": "TEST",
- "valueId": {
- "type": "ExternalReference",
- "keys": [
- {
- "type": "GlobalReference",
- "value": "http://acplt.org/Values/TestValueId"
- }
- ]
- },
- "levelType": {
- "min": true,
- "max": true,
- "nom": false,
- "typ": false
- }
- }
- }
- ]
- },
- {
- "displayName": [
- {
- "language": "en-US",
- "text": "ExampleProperty"
- },
- {
- "language": "de",
- "text": "BeispielProperty"
- }
- ],
- "category": "CONSTANT",
- "description": [
- {
- "language": "en-US",
- "text": "Example Property object"
- },
- {
- "language": "de",
- "text": "Beispiel Property Element"
- }
- ],
- "modelType": "Property",
- "semanticId": {
- "type": "ExternalReference",
- "keys": [
- {
- "type": "GlobalReference",
- "value": "http://acplt.org/Properties/ExampleProperty"
- }
- ]
- },
- "supplementalSemanticIds": [
- {
- "type": "ExternalReference",
- "keys": [
- {
- "type": "GlobalReference",
- "value": "http://acplt.org/Properties/ExampleProperty2/SupplementalId"
- }
- ]
- }
- ],
- "value": "exampleValue",
- "valueId": {
- "type": "ExternalReference",
- "keys": [
- {
- "type": "GlobalReference",
- "value": "http://acplt.org/ValueId/ExampleValueId"
- }
- ]
- },
- "valueType": "xs:string"
- }
- ]
- }
- ]
- }
- ]
- },
- {
- "modelType": "Submodel",
- "id": "https://acplt.org/Test_Submodel_Mandatory",
- "submodelElements": [
- {
- "idShort": "ExampleRelationshipElement",
- "modelType": "RelationshipElement",
- "first": {
- "type": "ModelReference",
- "keys": [
- {
- "type": "Submodel",
- "value": "http://acplt.org/Test_Submodel"
- },
- {
- "type": "Property",
- "value": "ExampleProperty"
- }
- ]
- },
- "second": {
- "type": "ModelReference",
- "keys": [
- {
- "type": "Submodel",
- "value": "http://acplt.org/Test_Submodel"
- },
- {
- "type": "Property",
- "value": "ExampleProperty"
- }
- ]
- }
- },
- {
- "idShort": "ExampleAnnotatedRelationshipElement",
- "modelType": "AnnotatedRelationshipElement",
- "first": {
- "type": "ModelReference",
- "keys": [
- {
- "type": "Submodel",
- "value": "http://acplt.org/Test_Submodel"
- },
- {
- "type": "Property",
- "value": "ExampleProperty"
- }
- ]
- },
- "second": {
- "type": "ModelReference",
- "keys": [
- {
- "type": "Submodel",
- "value": "http://acplt.org/Test_Submodel"
- },
- {
- "type": "Property",
- "value": "ExampleProperty"
- }
- ]
- }
- },
- {
- "idShort": "ExampleOperation",
- "modelType": "Operation"
- },
- {
- "idShort": "ExampleCapability",
- "modelType": "Capability"
- },
- {
- "idShort": "ExampleBasicEventElement",
- "modelType": "BasicEventElement",
- "observed": {
- "type": "ModelReference",
- "keys": [
- {
- "type": "Submodel",
- "value": "http://acplt.org/Test_Submodel"
- },
- {
- "type": "Property",
- "value": "ExampleProperty"
- }
- ]
- },
- "direction": "input",
- "state": "off"
- },
- {
- "idShort": "ExampleSubmodelList",
- "typeValueListElement": "SubmodelElementCollection",
- "modelType": "SubmodelElementList",
- "value": [
- {
- "modelType": "SubmodelElementCollection",
- "value": [
- {
- "idShort": "ExampleBlob",
- "modelType": "Blob",
- "contentType": "application/pdf"
- },
- {
- "idShort": "ExampleFile",
- "modelType": "File",
- "contentType": "application/pdf"
- },
- {
- "idShort": "ExampleMultiLanguageProperty",
- "category": "PARAMETER",
- "modelType": "MultiLanguageProperty"
- },
- {
- "idShort": "ExampleProperty",
- "category": "PARAMETER",
- "modelType": "Property",
- "valueType": "xs:string"
- },
- {
- "idShort": "ExampleRange",
- "category": "PARAMETER",
- "modelType": "Range",
- "valueType": "xs:int"
- },
- {
- "idShort": "ExampleReferenceElement",
- "category": "PARAMETER",
- "modelType": "ReferenceElement"
- }
- ]
- },
- {
- "modelType": "SubmodelElementCollection"
- }
- ]
- },
- {
- "idShort": "ExampleSubmodelList2",
- "typeValueListElement": "Capability",
- "modelType": "SubmodelElementList"
- }
- ]
- },
- {
- "modelType": "Submodel",
- "id": "https://acplt.org/Test_Submodel2_Mandatory"
- },
- {
- "idShort": "TestSubmodel",
- "description": [
- {
- "language": "en-US",
- "text": "An example submodel for the test application"
- },
- {
- "language": "de",
- "text": "Ein Beispiel-Teilmodell f\u00fcr eine Test-Anwendung"
- }
- ],
- "modelType": "Submodel",
- "id": "https://acplt.org/Test_Submodel_Missing",
- "administration": {
- "version": "9",
- "revision": "0"
- },
- "semanticId": {
- "type": "ExternalReference",
- "keys": [
- {
- "type": "GlobalReference",
- "value": "http://acplt.org/SubmodelTemplates/ExampleSubmodel"
- }
- ]
- },
- "submodelElements": [
- {
- "idShort": "ExampleRelationshipElement",
- "category": "PARAMETER",
- "description": [
- {
- "language": "en-US",
- "text": "Example RelationshipElement object"
- },
- {
- "language": "de",
- "text": "Beispiel RelationshipElement Element"
- }
- ],
- "modelType": "RelationshipElement",
- "semanticId": {
- "type": "ExternalReference",
- "keys": [
- {
- "type": "GlobalReference",
- "value": "http://acplt.org/RelationshipElements/ExampleRelationshipElement"
- }
- ]
- },
- "first": {
- "type": "ModelReference",
- "keys": [
- {
- "type": "Submodel",
- "value": "http://acplt.org/Test_Submodel"
- },
- {
- "type": "Property",
- "value": "ExampleProperty"
- }
- ]
- },
- "second": {
- "type": "ModelReference",
- "keys": [
- {
- "type": "Submodel",
- "value": "http://acplt.org/Test_Submodel"
- },
- {
- "type": "Property",
- "value": "ExampleProperty"
- }
- ]
- }
- },
- {
- "idShort": "ExampleAnnotatedRelationshipElement",
- "category": "PARAMETER",
- "description": [
- {
- "language": "en-US",
- "text": "Example AnnotatedRelationshipElement object"
- },
- {
- "language": "de",
- "text": "Beispiel AnnotatedRelationshipElement Element"
- }
- ],
- "modelType": "AnnotatedRelationshipElement",
- "semanticId": {
- "type": "ExternalReference",
- "keys": [
- {
- "type": "GlobalReference",
- "value": "http://acplt.org/RelationshipElements/ExampleAnnotatedRelationshipElement"
- }
- ]
- },
- "first": {
- "type": "ModelReference",
- "keys": [
- {
- "type": "Submodel",
- "value": "http://acplt.org/Test_Submodel"
- },
- {
- "type": "Property",
- "value": "ExampleProperty"
- }
- ]
- },
- "second": {
- "type": "ModelReference",
- "keys": [
- {
- "type": "Submodel",
- "value": "http://acplt.org/Test_Submodel"
- },
- {
- "type": "Property",
- "value": "ExampleProperty"
- }
- ]
- },
- "annotations": [
- {
- "idShort": "ExampleAnnotatedRange",
- "category": "PARAMETER",
- "modelType": "Range",
- "valueType": "xs:integer",
- "min": "1",
- "max": "5"
- },
- {
- "idShort": "ExampleAnnotatedProperty",
- "category": "PARAMETER",
- "modelType": "Property",
- "value": "exampleValue",
- "valueType": "xs:string"
- }
- ]
- },
- {
- "idShort": "ExampleOperation",
- "category": "PARAMETER",
- "description": [
- {
- "language": "en-US",
- "text": "Example Operation object"
- },
- {
- "language": "de",
- "text": "Beispiel Operation Element"
- }
- ],
- "modelType": "Operation",
- "semanticId": {
- "type": "ExternalReference",
- "keys": [
- {
- "type": "GlobalReference",
- "value": "http://acplt.org/Operations/ExampleOperation"
- }
- ]
- },
- "inputVariables": [
- {
- "value": {
- "idShort": "ExamplePropertyInput",
- "displayName": [
- {
- "language": "en-US",
- "text": "ExampleProperty"
- },
- {
- "language": "de",
- "text": "BeispielProperty"
- }
- ],
- "category": "CONSTANT",
- "description": [
- {
- "language": "en-US",
- "text": "Example Property object"
- },
- {
- "language": "de",
- "text": "Beispiel Property Element"
- }
- ],
- "modelType": "Property",
- "semanticId": {
- "type": "ExternalReference",
- "keys": [
- {
- "type": "GlobalReference",
- "value": "http://acplt.org/Properties/ExamplePropertyInput"
- }
- ]
- },
- "kind": "Template",
- "value": "exampleValue",
- "valueId": {
- "type": "ExternalReference",
- "keys": [
- {
- "type": "GlobalReference",
- "value": "http://acplt.org/ValueId/ExampleValueId"
- }
- ]
- },
- "valueType": "xs:string"
- }
- }
- ],
- "outputVariables": [
- {
- "value": {
- "idShort": "ExamplePropertyOutput",
- "displayName": [
- {
- "language": "en-US",
- "text": "ExampleProperty"
- },
- {
- "language": "de",
- "text": "BeispielProperty"
- }
- ],
- "category": "CONSTANT",
- "description": [
- {
- "language": "en-US",
- "text": "Example Property object"
- },
- {
- "language": "de",
- "text": "Beispiel Property Element"
- }
- ],
- "modelType": "Property",
- "semanticId": {
- "type": "ExternalReference",
- "keys": [
- {
- "type": "GlobalReference",
- "value": "http://acplt.org/Properties/ExamplePropertyOutput"
- }
- ]
- },
- "kind": "Template",
- "value": "exampleValue",
- "valueId": {
- "type": "ExternalReference",
- "keys": [
- {
- "type": "GlobalReference",
- "value": "http://acplt.org/ValueId/ExampleValueId"
- }
- ]
- },
- "valueType": "xs:string"
- }
- }
- ],
- "inoutputVariables": [
- {
- "value": {
- "idShort": "ExamplePropertyInOutput",
- "displayName": [
- {
- "language": "en-US",
- "text": "ExampleProperty"
- },
- {
- "language": "de",
- "text": "BeispielProperty"
- }
- ],
- "category": "CONSTANT",
- "description": [
- {
- "language": "en-US",
- "text": "Example Property object"
- },
- {
- "language": "de",
- "text": "Beispiel Property Element"
- }
- ],
- "modelType": "Property",
- "semanticId": {
- "type": "ExternalReference",
- "keys": [
- {
- "type": "GlobalReference",
- "value": "http://acplt.org/Properties/ExamplePropertyInOutput"
- }
- ]
- },
- "kind": "Template",
- "value": "exampleValue",
- "valueId": {
- "type": "ExternalReference",
- "keys": [
- {
- "type": "GlobalReference",
- "value": "http://acplt.org/ValueId/ExampleValueId"
- }
- ]
- },
- "valueType": "xs:string"
- }
- }
- ]
- },
- {
- "idShort": "ExampleCapability",
- "category": "PARAMETER",
- "description": [
- {
- "language": "en-US",
- "text": "Example Capability object"
- },
- {
- "language": "de",
- "text": "Beispiel Capability Element"
- }
- ],
- "modelType": "Capability",
- "semanticId": {
- "type": "ExternalReference",
- "keys": [
- {
- "type": "GlobalReference",
- "value": "http://acplt.org/Capabilities/ExampleCapability"
- }
- ]
- }
- },
- {
- "idShort": "ExampleBasicEventElement",
- "category": "PARAMETER",
- "description": [
- {
- "language": "en-US",
- "text": "Example BasicEventElement object"
- },
- {
- "language": "de",
- "text": "Beispiel BasicEventElement Element"
- }
- ],
- "modelType": "BasicEventElement",
- "semanticId": {
- "type": "ExternalReference",
- "keys": [
- {
- "type": "GlobalReference",
- "value": "http://acplt.org/Events/ExampleBasicEventElement"
- }
- ]
- },
- "observed": {
- "type": "ModelReference",
- "keys": [
- {
- "type": "Submodel",
- "value": "http://acplt.org/Test_Submodel"
- },
- {
- "type": "Property",
- "value": "ExampleProperty"
- }
- ]
- },
- "direction": "output",
- "state": "on",
- "messageTopic": "ExampleTopic",
- "messageBroker": {
- "type": "ModelReference",
- "keys": [
- {
- "type": "Submodel",
- "value": "http://acplt.org/ExampleMessageBroker"
- }
- ]
- },
- "lastUpdate": "2022-11-12T23:50:23.123456+00:00",
- "minInterval": "PT0.000001S",
- "maxInterval": "P1Y2M3DT4H5M6.123456S"
- },
- {
- "idShort": "ExampleSubmodelCollection",
- "category": "PARAMETER",
- "description": [
- {
- "language": "en-US",
- "text": "Example SubmodelElementCollection object"
- },
- {
- "language": "de",
- "text": "Beispiel SubmodelElementCollection Element"
- }
- ],
- "modelType": "SubmodelElementCollection",
- "semanticId": {
- "type": "ExternalReference",
- "keys": [
- {
- "type": "GlobalReference",
- "value": "http://acplt.org/SubmodelElementCollections/ExampleSubmodelElementCollection"
- }
- ]
- },
- "value": [
- {
- "idShort": "ExampleBlob",
- "category": "PARAMETER",
- "description": [
- {
- "language": "en-US",
- "text": "Example Blob object"
- },
- {
- "language": "de",
- "text": "Beispiel Blob Element"
- }
- ],
- "modelType": "Blob",
- "semanticId": {
- "type": "ExternalReference",
- "keys": [
- {
- "type": "GlobalReference",
- "value": "http://acplt.org/Blobs/ExampleBlob"
- }
- ]
- },
- "contentType": "application/pdf",
- "value": "AQIDBAU="
- },
- {
- "idShort": "ExampleFile",
- "category": "PARAMETER",
- "description": [
- {
- "language": "en-US",
- "text": "Example File object"
- },
- {
- "language": "de",
- "text": "Beispiel File Element"
- }
- ],
- "modelType": "File",
- "semanticId": {
- "type": "ExternalReference",
- "keys": [
- {
- "type": "GlobalReference",
- "value": "http://acplt.org/Files/ExampleFile"
- }
- ]
- },
- "value": "/TestFile.pdf",
- "contentType": "application/pdf"
- },
- {
- "idShort": "ExampleMultiLanguageProperty",
- "category": "CONSTANT",
- "description": [
- {
- "language": "en-US",
- "text": "Example MultiLanguageProperty object"
- },
- {
- "language": "de",
- "text": "Beispiel MultiLanguageProperty Element"
- }
- ],
- "modelType": "MultiLanguageProperty",
- "semanticId": {
- "type": "ExternalReference",
- "keys": [
- {
- "type": "GlobalReference",
- "value": "http://acplt.org/MultiLanguageProperties/ExampleMultiLanguageProperty"
- }
- ]
- },
- "value": [
- {
- "language": "en-US",
- "text": "Example value of a MultiLanguageProperty element"
- },
- {
- "language": "de",
- "text": "Beispielwert f\u00fcr ein MultiLanguageProperty-Element"
- }
- ]
- },
- {
- "idShort": "ExampleProperty",
- "category": "CONSTANT",
- "description": [
- {
- "language": "en-US",
- "text": "Example Property object"
- },
- {
- "language": "de",
- "text": "Beispiel Property Element"
- }
- ],
- "modelType": "Property",
- "semanticId": {
- "type": "ExternalReference",
- "keys": [
- {
- "type": "GlobalReference",
- "value": "http://acplt.org/Properties/ExampleProperty"
- }
- ]
- },
- "qualifiers": [
- {
- "valueType": "xs:string",
- "type": "http://acplt.org/Qualifier/ExampleQualifier"
- }
- ],
- "value": "exampleValue",
- "valueType": "xs:string"
- },
- {
- "idShort": "ExampleRange",
- "category": "PARAMETER",
- "description": [
- {
- "language": "en-US",
- "text": "Example Range object"
- },
- {
- "language": "de",
- "text": "Beispiel Range Element"
- }
- ],
- "modelType": "Range",
- "semanticId": {
- "type": "ExternalReference",
- "keys": [
- {
- "type": "GlobalReference",
- "value": "http://acplt.org/Ranges/ExampleRange"
- }
- ]
- },
- "valueType": "xs:int",
- "min": "0",
- "max": "100"
- },
- {
- "idShort": "ExampleReferenceElement",
- "category": "PARAMETER",
- "description": [
- {
- "language": "en-US",
- "text": "Example Reference Element object"
- },
- {
- "language": "de",
- "text": "Beispiel Reference Element Element"
- }
- ],
- "modelType": "ReferenceElement",
- "semanticId": {
- "type": "ExternalReference",
- "keys": [
- {
- "type": "GlobalReference",
- "value": "http://acplt.org/ReferenceElements/ExampleReferenceElement"
- }
- ]
- },
- "value": {
- "type": "ModelReference",
- "keys": [
- {
- "type": "Submodel",
- "value": "http://acplt.org/Test_Submodel"
- },
- {
- "type": "Property",
- "value": "ExampleProperty"
- }
- ]
- }
- }
- ]
- }
- ]
- },
- {
- "idShort": "TestSubmodel",
- "description": [
- {
- "language": "en-US",
- "text": "An example submodel for the test application"
- },
- {
- "language": "de",
- "text": "Ein Beispiel-Teilmodell f\u00fcr eine Test-Anwendung"
- }
- ],
- "modelType": "Submodel",
- "id": "https://acplt.org/Test_Submodel_Template",
- "administration": {
- "version": "9",
- "revision": "0"
- },
- "semanticId": {
- "type": "ExternalReference",
- "keys": [
- {
- "type": "GlobalReference",
- "value": "http://acplt.org/SubmodelTemplates/ExampleSubmodel"
- }
- ]
- },
- "kind": "Template",
- "submodelElements": [
- {
- "idShort": "ExampleRelationshipElement",
- "category": "PARAMETER",
- "description": [
- {
- "language": "en-US",
- "text": "Example RelationshipElement object"
- },
- {
- "language": "de",
- "text": "Beispiel RelationshipElement Element"
- }
- ],
- "modelType": "RelationshipElement",
- "semanticId": {
- "type": "ExternalReference",
- "keys": [
- {
- "type": "GlobalReference",
- "value": "http://acplt.org/RelationshipElements/ExampleRelationshipElement"
- }
- ]
- },
- "kind": "Template",
- "first": {
- "type": "ModelReference",
- "keys": [
- {
- "type": "Submodel",
- "value": "http://acplt.org/Test_Submodel"
- },
- {
- "type": "Property",
- "value": "ExampleProperty"
- }
- ]
- },
- "second": {
- "type": "ModelReference",
- "keys": [
- {
- "type": "Submodel",
- "value": "http://acplt.org/Test_Submodel"
- },
- {
- "type": "Property",
- "value": "ExampleProperty"
- }
- ]
- }
- },
- {
- "idShort": "ExampleAnnotatedRelationshipElement",
- "category": "PARAMETER",
- "description": [
- {
- "language": "en-US",
- "text": "Example AnnotatedRelationshipElement object"
- },
- {
- "language": "de",
- "text": "Beispiel AnnotatedRelationshipElement Element"
- }
- ],
- "modelType": "AnnotatedRelationshipElement",
- "semanticId": {
- "type": "ExternalReference",
- "keys": [
- {
- "type": "GlobalReference",
- "value": "http://acplt.org/RelationshipElements/ExampleAnnotatedRelationshipElement"
- }
- ]
- },
- "kind": "Template",
- "first": {
- "type": "ModelReference",
- "keys": [
- {
- "type": "Submodel",
- "value": "http://acplt.org/Test_Submodel"
- },
- {
- "type": "Property",
- "value": "ExampleProperty"
- }
- ]
- },
- "second": {
- "type": "ModelReference",
- "keys": [
- {
- "type": "Submodel",
- "value": "http://acplt.org/Test_Submodel"
- },
- {
- "type": "Property",
- "value": "ExampleProperty"
- }
- ]
- }
- },
- {
- "idShort": "ExampleOperation",
- "category": "PARAMETER",
- "description": [
- {
- "language": "en-US",
- "text": "Example Operation object"
- },
- {
- "language": "de",
- "text": "Beispiel Operation Element"
- }
- ],
- "modelType": "Operation",
- "semanticId": {
- "type": "ExternalReference",
- "keys": [
- {
- "type": "GlobalReference",
- "value": "http://acplt.org/Operations/ExampleOperation"
- }
- ]
- },
- "kind": "Template",
- "inputVariables": [
- {
- "value": {
- "idShort": "ExamplePropertyInput",
- "category": "CONSTANT",
- "description": [
- {
- "language": "en-US",
- "text": "Example Property object"
- },
- {
- "language": "de",
- "text": "Beispiel Property Element"
- }
- ],
- "modelType": "Property",
- "semanticId": {
- "type": "ExternalReference",
- "keys": [
- {
- "type": "GlobalReference",
- "value": "http://acplt.org/Properties/ExamplePropertyInput"
- }
- ]
- },
- "kind": "Template",
- "valueType": "xs:string"
- }
- }
- ],
- "outputVariables": [
- {
- "value": {
- "idShort": "ExamplePropertyOutput",
- "category": "CONSTANT",
- "description": [
- {
- "language": "en-US",
- "text": "Example Property object"
- },
- {
- "language": "de",
- "text": "Beispiel Property Element"
- }
- ],
- "modelType": "Property",
- "semanticId": {
- "type": "ExternalReference",
- "keys": [
- {
- "type": "GlobalReference",
- "value": "http://acplt.org/Properties/ExamplePropertyOutput"
- }
- ]
- },
- "kind": "Template",
- "valueType": "xs:string"
- }
- }
- ],
- "inoutputVariables": [
- {
- "value": {
- "idShort": "ExamplePropertyInOutput",
- "category": "CONSTANT",
- "description": [
- {
- "language": "en-US",
- "text": "Example Property object"
- },
- {
- "language": "de",
- "text": "Beispiel Property Element"
- }
- ],
- "modelType": "Property",
- "semanticId": {
- "type": "ExternalReference",
- "keys": [
- {
- "type": "GlobalReference",
- "value": "http://acplt.org/Properties/ExamplePropertyInOutput"
- }
- ]
- },
- "kind": "Template",
- "valueType": "xs:string"
- }
- }
- ]
- },
- {
- "idShort": "ExampleCapability",
- "category": "PARAMETER",
- "description": [
- {
- "language": "en-US",
- "text": "Example Capability object"
- },
- {
- "language": "de",
- "text": "Beispiel Capability Element"
- }
- ],
- "modelType": "Capability",
- "semanticId": {
- "type": "ExternalReference",
- "keys": [
- {
- "type": "GlobalReference",
- "value": "http://acplt.org/Capabilities/ExampleCapability"
- }
- ]
- },
- "kind": "Template"
- },
- {
- "idShort": "ExampleBasicEventElement",
- "category": "PARAMETER",
- "description": [
- {
- "language": "en-US",
- "text": "Example BasicEventElement object"
- },
- {
- "language": "de",
- "text": "Beispiel BasicEventElement Element"
- }
- ],
- "modelType": "BasicEventElement",
- "semanticId": {
- "type": "ExternalReference",
- "keys": [
- {
- "type": "GlobalReference",
- "value": "http://acplt.org/Events/ExampleBasicEventElement"
- }
- ]
- },
- "kind": "Template",
- "observed": {
- "type": "ModelReference",
- "keys": [
- {
- "type": "Submodel",
- "value": "http://acplt.org/Test_Submodel"
- },
- {
- "type": "Property",
- "value": "ExampleProperty"
- }
- ]
- },
- "direction": "output",
- "state": "on",
- "messageTopic": "ExampleTopic",
- "messageBroker": {
- "type": "ModelReference",
- "keys": [
- {
- "type": "Submodel",
- "value": "http://acplt.org/ExampleMessageBroker"
- }
- ]
- },
- "lastUpdate": "2022-11-12T23:50:23.123456+00:00",
- "minInterval": "PT0.000001S",
- "maxInterval": "P1Y2M3DT4H5M6.123456S"
- },
- {
- "idShort": "ExampleSubmodelList",
- "typeValueListElement": "SubmodelElementCollection",
- "semanticIdListElement": {
- "type": "ExternalReference",
- "keys": [
- {
- "type": "GlobalReference",
- "value": "http://acplt.org/SubmodelElementCollections/ExampleSubmodelElementCollection"
- }
- ]
- },
- "orderRelevant": true,
- "category": "PARAMETER",
- "description": [
- {
- "language": "en-US",
- "text": "Example SubmodelElementList object"
- },
- {
- "language": "de",
- "text": "Beispiel SubmodelElementList Element"
- }
- ],
- "modelType": "SubmodelElementList",
- "semanticId": {
- "type": "ExternalReference",
- "keys": [
- {
- "type": "GlobalReference",
- "value": "http://acplt.org/SubmodelElementLists/ExampleSubmodelElementList"
- }
- ]
- },
- "kind": "Template",
- "value": [
- {
- "category": "PARAMETER",
- "description": [
- {
- "language": "en-US",
- "text": "Example SubmodelElementCollection object"
- },
- {
- "language": "de",
- "text": "Beispiel SubmodelElementCollection Element"
- }
- ],
- "modelType": "SubmodelElementCollection",
- "semanticId": {
- "type": "ExternalReference",
- "keys": [
- {
- "type": "GlobalReference",
- "value": "http://acplt.org/SubmodelElementCollections/ExampleSubmodelElementCollection"
- }
- ]
- },
- "kind": "Template",
- "value": [
- {
- "idShort": "ExampleProperty",
- "category": "CONSTANT",
- "description": [
- {
- "language": "en-US",
- "text": "Example Property object"
- },
- {
- "language": "de",
- "text": "Beispiel Property Element"
- }
- ],
- "modelType": "Property",
- "semanticId": {
- "type": "ExternalReference",
- "keys": [
- {
- "type": "GlobalReference",
- "value": "http://acplt.org/Properties/ExampleProperty"
- }
- ]
- },
- "kind": "Template",
- "valueType": "xs:string"
- },
- {
- "idShort": "ExampleMultiLanguageProperty",
- "category": "CONSTANT",
- "description": [
- {
- "language": "en-US",
- "text": "Example MultiLanguageProperty object"
- },
- {
- "language": "de",
- "text": "Beispiel MultiLanguageProperty Element"
- }
- ],
- "modelType": "MultiLanguageProperty",
- "semanticId": {
- "type": "ExternalReference",
- "keys": [
- {
- "type": "GlobalReference",
- "value": "http://acplt.org/MultiLanguageProperties/ExampleMultiLanguageProperty"
- }
- ]
- },
- "kind": "Template"
- },
- {
- "idShort": "ExampleRange",
- "category": "PARAMETER",
- "description": [
- {
- "language": "en-US",
- "text": "Example Range object"
- },
- {
- "language": "de",
- "text": "Beispiel Range Element"
- }
- ],
- "modelType": "Range",
- "semanticId": {
- "type": "ExternalReference",
- "keys": [
- {
- "type": "GlobalReference",
- "value": "http://acplt.org/Ranges/ExampleRange"
- }
- ]
- },
- "kind": "Template",
- "valueType": "xs:int",
- "max": "100"
- },
- {
- "idShort": "ExampleRange2",
- "category": "PARAMETER",
- "description": [
- {
- "language": "en-US",
- "text": "Example Range object"
- },
- {
- "language": "de",
- "text": "Beispiel Range Element"
- }
- ],
- "modelType": "Range",
- "semanticId": {
- "type": "ExternalReference",
- "keys": [
- {
- "type": "GlobalReference",
- "value": "http://acplt.org/Ranges/ExampleRange"
- }
- ]
- },
- "kind": "Template",
- "valueType": "xs:int",
- "min": "0"
- },
- {
- "idShort": "ExampleBlob",
- "category": "PARAMETER",
- "description": [
- {
- "language": "en-US",
- "text": "Example Blob object"
- },
- {
- "language": "de",
- "text": "Beispiel Blob Element"
- }
- ],
- "modelType": "Blob",
- "semanticId": {
- "type": "ExternalReference",
- "keys": [
- {
- "type": "GlobalReference",
- "value": "http://acplt.org/Blobs/ExampleBlob"
- }
- ]
- },
- "kind": "Template",
- "contentType": "application/pdf"
- },
- {
- "idShort": "ExampleFile",
- "category": "PARAMETER",
- "description": [
- {
- "language": "en-US",
- "text": "Example File object"
- },
- {
- "language": "de",
- "text": "Beispiel File Element"
- }
- ],
- "modelType": "File",
- "semanticId": {
- "type": "ExternalReference",
- "keys": [
- {
- "type": "GlobalReference",
- "value": "http://acplt.org/Files/ExampleFile"
- }
- ]
- },
- "kind": "Template",
- "contentType": "application/pdf"
- },
- {
- "idShort": "ExampleReferenceElement",
- "category": "PARAMETER",
- "description": [
- {
- "language": "en-US",
- "text": "Example Reference Element object"
- },
- {
- "language": "de",
- "text": "Beispiel Reference Element Element"
- }
- ],
- "modelType": "ReferenceElement",
- "semanticId": {
- "type": "ExternalReference",
- "keys": [
- {
- "type": "GlobalReference",
- "value": "http://acplt.org/ReferenceElements/ExampleReferenceElement"
- }
- ]
- },
- "kind": "Template"
- }
- ]
- },
- {
- "category": "PARAMETER",
- "description": [
- {
- "language": "en-US",
- "text": "Example SubmodelElementCollection object"
- },
- {
- "language": "de",
- "text": "Beispiel SubmodelElementCollection Element"
- }
- ],
- "modelType": "SubmodelElementCollection",
- "semanticId": {
- "type": "ExternalReference",
- "keys": [
- {
- "type": "GlobalReference",
- "value": "http://acplt.org/SubmodelElementCollections/ExampleSubmodelElementCollection"
- }
- ]
- },
- "kind": "Template"
- }
- ]
- },
- {
- "idShort": "ExampleSubmodelList2",
- "typeValueListElement": "Capability",
- "semanticIdListElement": {
- "type": "ExternalReference",
- "keys": [
- {
- "type": "GlobalReference",
- "value": "http://acplt.org/SubmodelElementCollections/ExampleSubmodelElementCollection"
- }
- ]
- },
- "orderRelevant": true,
- "category": "PARAMETER",
- "description": [
- {
- "language": "en-US",
- "text": "Example SubmodelElementList object"
- },
- {
- "language": "de",
- "text": "Beispiel SubmodelElementList Element"
- }
- ],
- "modelType": "SubmodelElementList",
- "semanticId": {
- "type": "ExternalReference",
- "keys": [
- {
- "type": "GlobalReference",
- "value": "http://acplt.org/SubmodelElementLists/ExampleSubmodelElementList"
- }
- ]
- },
- "kind": "Template"
- }
- ]
- }
- ],
- "conceptDescriptions": [
- {
- "idShort": "TestConceptDescription",
- "description": [
- {
- "language": "en-US",
- "text": "An example concept description for the test application"
- },
- {
- "language": "de",
- "text": "Ein Beispiel-ConceptDescription f\u00fcr eine Test-Anwendung"
- }
- ],
- "modelType": "ConceptDescription",
- "id": "https://acplt.org/Test_ConceptDescription",
- "administration": {
- "version": "9",
- "revision": "0",
- "creator": {
- "type": "ExternalReference",
- "keys": [
- {
- "type": "GlobalReference",
- "value": "http://acplt.org/AdministrativeInformation/Test_ConceptDescription"
- }
- ]
- },
- "templateId": "http://acplt.org/AdministrativeInformationTemplates/Test_ConceptDescription",
- "embeddedDataSpecifications": [
- {
- "dataSpecification": {
- "type": "ExternalReference",
- "keys": [
- {
- "type": "GlobalReference",
- "value": "https://admin-shell.io/DataSpecificationTemplates/DataSpecificationIEC61360/3/0"
- }
- ]
- },
- "dataSpecificationContent": {
- "modelType": "DataSpecificationIec61360",
- "preferredName": [
- {
- "language": "de",
- "text": "Test Specification"
- },
- {
- "language": "en-US",
- "text": "TestSpecification"
- }
- ],
- "dataType": "REAL_MEASURE",
- "definition": [
- {
- "language": "de",
- "text": "Dies ist eine Data Specification f\u00fcr Testzwecke"
- },
- {
- "language": "en-US",
- "text": "This is a DataSpecification for testing purposes"
- }
- ],
- "shortName": [
- {
- "language": "de",
- "text": "Test Spec"
- },
- {
- "language": "en-US",
- "text": "TestSpec"
- }
- ],
- "unit": "SpaceUnit",
- "unitId": {
- "type": "ExternalReference",
- "keys": [
- {
- "type": "GlobalReference",
- "value": "http://acplt.org/Units/SpaceUnit"
- }
- ]
- },
- "sourceOfDefinition": "http://acplt.org/DataSpec/ExampleDef",
- "symbol": "SU",
- "valueFormat": "M",
- "valueList": {
- "valueReferencePairs": [
- {
- "value": "exampleValue",
- "valueId": {
- "type": "ExternalReference",
- "keys": [
- {
- "type": "GlobalReference",
- "value": "http://acplt.org/ValueId/ExampleValueId"
- }
- ]
- }
- },
- {
- "value": "exampleValue2",
- "valueId": {
- "type": "ExternalReference",
- "keys": [
- {
- "type": "GlobalReference",
- "value": "http://acplt.org/ValueId/ExampleValueId2"
- }
- ]
- }
- }
- ]
- },
- "value": "TEST",
- "valueId": {
- "type": "ExternalReference",
- "keys": [
- {
- "type": "GlobalReference",
- "value": "http://acplt.org/Values/TestValueId"
- }
- ]
- },
- "levelType": {
- "min": true,
- "max": true,
- "nom": false,
- "typ": false
- }
- }
- }
- ]
- },
- "isCaseOf": [
- {
- "type": "ExternalReference",
- "keys": [
- {
- "type": "GlobalReference",
- "value": "http://acplt.org/DataSpecifications/ConceptDescriptions/TestConceptDescription"
- }
- ]
- }
- ]
- },
- {
- "modelType": "ConceptDescription",
- "id": "https://acplt.org/Test_ConceptDescription_Mandatory"
- },
- {
- "idShort": "TestConceptDescription",
- "description": [
- {
- "language": "en-US",
- "text": "An example concept description for the test application"
- },
- {
- "language": "de",
- "text": "Ein Beispiel-ConceptDescription f\u00fcr eine Test-Anwendung"
- }
- ],
- "modelType": "ConceptDescription",
- "id": "https://acplt.org/Test_ConceptDescription_Missing",
- "administration": {
- "version": "9",
- "revision": "0"
- }
- }
- ]
-}
\ No newline at end of file
diff --git a/compliance_tool/test/files/test_demo_full_example_wrong_attribute.xml b/compliance_tool/test/files/test_demo_full_example_wrong_attribute.xml
deleted file mode 100644
index 94c47d36b..000000000
--- a/compliance_tool/test/files/test_demo_full_example_wrong_attribute.xml
+++ /dev/null
@@ -1,2991 +0,0 @@
-
-
-
-
- TestAssetAdministrationShell123
-
-
- en-US
- An Example Asset Administration Shell for the test application
-
-
- de
- Ein Beispiel-Verwaltungsschale für eine Test-Anwendung
-
-
-
- 9
- 0
-
- ExternalReference
-
-
- GlobalReference
- http://acplt.org/AdministrativeInformation/Test_AssetAdministrationShell
-
-
-
- http://acplt.org/AdministrativeInformationTemplates/Test_AssetAdministrationShell
-
- https://acplt.org/Test_AssetAdministrationShell
-
-
-
- ExternalReference
-
-
- GlobalReference
- https://admin-shell.io/DataSpecificationTemplates/DataSpecificationIEC61360/3/0
-
-
-
-
-
-
-
- de
- Test Specification
-
-
- en-US
- TestSpecification
-
-
-
-
- de
- Test Spec
-
-
- en-US
- TestSpec
-
-
- SpaceUnit
-
- ExternalReference
-
-
- GlobalReference
- http://acplt.org/Units/SpaceUnit
-
-
-
- http://acplt.org/DataSpec/ExampleDef
- SU
- REAL_MEASURE
-
-
- de
- Dies ist eine Data Specification für Testzwecke
-
-
- en-US
- This is a DataSpecification for testing purposes
-
-
- M
-
-
-
- exampleValue
-
- ExternalReference
-
-
- GlobalReference
- http://acplt.org/ValueId/ExampleValueId
-
-
-
-
-
- exampleValue2
-
- ExternalReference
-
-
- GlobalReference
- http://acplt.org/ValueId/ExampleValueId2
-
-
-
-
-
-
- TEST
-
- true
- false
- false
- true
-
-
-
-
-
-
- ModelReference
-
-
- AssetAdministrationShell
- https://acplt.org/TestAssetAdministrationShell2
-
-
-
-
- Instance
- http://acplt.org/TestAsset/
-
-
-
- ExternalReference
-
-
- GlobalReference
- http://acplt.org/SpecificAssetId/
-
-
-
- TestKey
- TestValue
-
- ExternalReference
-
-
- GlobalReference
- http://acplt.org/SpecificAssetId/
-
-
-
-
-
- http://acplt.org/TestAssetType/
-
- file:///path/to/thumbnail.png
- image/png
-
-
-
-
- ModelReference
-
- ModelReference
-
-
- Submodel
- http://acplt.org/SubmodelTemplates/AssetIdentification
-
-
-
-
-
- Submodel
- http://acplt.org/Submodels/Assets/TestAsset/Identification
-
-
-
-
- ModelReference
-
- ExternalReference
-
-
- GlobalReference
- http://acplt.org/SubmodelTemplates/ExampleSubmodel
-
-
-
-
-
- Submodel
- https://acplt.org/Test_Submodel
-
-
-
-
- ModelReference
-
-
- Submodel
- http://acplt.org/Submodels/Assets/TestAsset/BillOfMaterial
-
-
-
-
-
-
- https://acplt.org/Test_AssetAdministrationShell_Mandatory
-
- Instance
- http://acplt.org/Test_Asset_Mandatory/
-
-
-
- ModelReference
-
-
- Submodel
- https://acplt.org/Test_Submodel2_Mandatory
-
-
-
-
- ModelReference
-
-
- Submodel
- https://acplt.org/Test_Submodel_Mandatory
-
-
-
-
-
-
- https://acplt.org/Test_AssetAdministrationShell2_Mandatory
-
- Instance
- http://acplt.org/TestAsset2_Mandatory/
-
-
-
- TestAssetAdministrationShell
-
-
- en-US
- An Example Asset Administration Shell for the test application
-
-
- de
- Ein Beispiel-Verwaltungsschale für eine Test-Anwendung
-
-
-
- 9
- 0
-
- https://acplt.org/Test_AssetAdministrationShell_Missing
-
- Instance
- http://acplt.org/Test_Asset_Missing/
-
-
- TestKey
- TestValue
-
- ExternalReference
-
-
- GlobalReference
- http://acplt.org/SpecificAssetId/
-
-
-
-
-
-
- file:///TestFile.pdf
- application/pdf
-
-
-
-
- ModelReference
-
-
- Submodel
- https://acplt.org/Test_Submodel_Missing
-
-
-
-
-
-
-
-
- Identification
-
-
- en-US
- An example asset identification submodel for the test application
-
-
- de
- Ein Beispiel-Identifikations-Submodel für eine Test-Anwendung
-
-
-
- 9
- 0
-
- ExternalReference
-
-
- GlobalReference
- http://acplt.org/AdministrativeInformation/TestAsset/Identification
-
-
-
- http://acplt.org/AdministrativeInformationTemplates/TestAsset/Identification
-
- http://acplt.org/Submodels/Assets/TestAsset/Identification
- Instance
-
- ModelReference
-
-
- Submodel
- http://acplt.org/SubmodelTemplates/AssetIdentification
-
-
-
-
-
-
-
- ExampleExtension
- xs:string
- ExampleExtensionValue
-
-
- ModelReference
-
-
- AssetAdministrationShell
- http://acplt.org/RefersTo/ExampleRefersTo
-
-
-
-
-
-
- PARAMETER
- ManufacturerName
-
-
- en-US
- Legally valid designation of the natural or judicial person which is directly responsible for the design, production, packaging and labeling of a product in respect to its being brought into circulation.
-
-
- de
- Bezeichnung für eine natürliche oder juristische Person, die für die Auslegung, Herstellung und Verpackung sowie die Etikettierung eines Produkts im Hinblick auf das 'Inverkehrbringen' im eigenen Namen verantwortlich ist
-
-
-
- ExternalReference
-
-
- GlobalReference
- 0173-1#02-AAO677#002
-
-
-
-
-
- ConceptQualifier
- http://acplt.org/Qualifier/ExampleQualifier
- xs:int
- 100
-
- ExternalReference
-
-
- GlobalReference
- http://acplt.org/ValueId/ExampleValueId
-
-
-
-
-
- TemplateQualifier
- http://acplt.org/Qualifier/ExampleQualifier2
- xs:int
- 50
-
- ExternalReference
-
-
- GlobalReference
- http://acplt.org/ValueId/ExampleValueId
-
-
-
-
-
- xs:string
- ACPLT
-
- ExternalReference
-
-
- GlobalReference
- http://acplt.org/ValueId/ExampleValueId
-
-
-
-
-
- PARAMETER
- InstanceId
-
-
- en-US
- Legally valid designation of the natural or judicial person which is directly responsible for the design, production, packaging and labeling of a product in respect to its being brought into circulation.
-
-
- de
- Bezeichnung für eine natürliche oder juristische Person, die für die Auslegung, Herstellung und Verpackung sowie die Etikettierung eines Produkts im Hinblick auf das 'Inverkehrbringen' im eigenen Namen verantwortlich ist
-
-
-
- ExternalReference
-
-
- GlobalReference
- http://opcfoundation.org/UA/DI/1.1/DeviceType/Serialnumber
-
-
-
-
-
- ValueQualifier
- http://acplt.org/Qualifier/ExampleQualifier3
- xs:dateTime
- 2023-04-07T16:59:54.870123
-
- ExternalReference
-
-
- GlobalReference
- http://acplt.org/ValueId/ExampleValueId
-
-
-
-
-
- xs:string
- 978-8234-234-342
-
- ExternalReference
-
-
- GlobalReference
- http://acplt.org/ValueId/ExampleValueId
-
-
-
-
-
-
-
- BillOfMaterial
-
-
- en-US
- An example bill of material submodel for the test application
-
-
- de
- Ein Beispiel-BillOfMaterial-Submodel für eine Test-Anwendung
-
-
-
- 9
- http://acplt.org/AdministrativeInformationTemplates/TestAsset/BillOfMaterial
-
- http://acplt.org/Submodels/Assets/TestAsset/BillOfMaterial
- Instance
-
- ModelReference
-
-
- Submodel
- http://acplt.org/SubmodelTemplates/BillOfMaterial
-
-
-
-
-
- PARAMETER
- ExampleEntity
-
-
- en-US
- Legally valid designation of the natural or judicial person which is directly responsible for the design, production, packaging and labeling of a product in respect to its being brought into circulation.
-
-
- de
- Bezeichnung für eine natürliche oder juristische Person, die für die Auslegung, Herstellung und Verpackung sowie die Etikettierung eines Produkts im Hinblick auf das 'Inverkehrbringen' im eigenen Namen verantwortlich ist
-
-
-
- ExternalReference
-
-
- GlobalReference
- http://opcfoundation.org/UA/DI/1.1/DeviceType/Serialnumber
-
-
-
-
-
- CONSTANT
- ExampleProperty2
-
-
- en-US
- Example Property object
-
-
- de
- Beispiel Property Element
-
-
-
- ExternalReference
-
-
- GlobalReference
- http://acplt.org/Properties/ExampleProperty
-
-
-
- xs:string
- exampleValue2
-
- ExternalReference
-
-
- GlobalReference
- http://acplt.org/ValueId/ExampleValueId
-
-
-
-
-
- CONSTANT
- ExampleProperty
-
-
- en-US
- Example Property object
-
-
- de
- Beispiel Property Element
-
-
-
- ExternalReference
-
-
- GlobalReference
- http://acplt.org/Properties/ExampleProperty
-
-
-
- xs:string
- exampleValue
-
- ExternalReference
-
-
- GlobalReference
- http://acplt.org/ValueId/ExampleValueId
-
-
-
-
-
- SelfManagedEntity
- http://acplt.org/TestAsset/
-
-
- TestKey
- TestValue
-
- ExternalReference
-
-
- GlobalReference
- http://acplt.org/SpecificAssetId/
-
-
-
-
-
-
-
- PARAMETER
- ExampleEntity2
-
-
- en-US
- Legally valid designation of the natural or judicial person which is directly responsible for the design, production, packaging and labeling of a product in respect to its being brought into circulation.
-
-
- de
- Bezeichnung für eine natürliche oder juristische Person, die für die Auslegung, Herstellung und Verpackung sowie die Etikettierung eines Produkts im Hinblick auf das 'Inverkehrbringen' im eigenen Namen verantwortlich ist
-
-
-
- ExternalReference
-
-
- GlobalReference
- http://opcfoundation.org/UA/DI/1.1/DeviceType/Serialnumber
-
-
-
- CoManagedEntity
-
-
-
-
- TestSubmodel
-
-
- en-US
- An example submodel for the test application
-
-
- de
- Ein Beispiel-Teilmodell für eine Test-Anwendung
-
-
-
- 9
- 0
-
- ExternalReference
-
-
- GlobalReference
- http://acplt.org/AdministrativeInformation/Test_Submodel
-
-
-
-
- https://acplt.org/Test_Submodel
- Instance
-
- ExternalReference
-
-
- GlobalReference
- http://acplt.org/SubmodelTemplates/ExampleSubmodel
-
-
-
-
-
- PARAMETER
- ExampleRelationshipElement
-
-
- en-US
- Example RelationshipElement object
-
-
- de
- Beispiel RelationshipElement Element
-
-
-
- ModelReference
-
-
- ConceptDescription
- https://acplt.org/Test_ConceptDescription
-
-
-
-
- ModelReference
-
-
- Submodel
- http://acplt.org/Test_Submodel
-
-
- Property
- ExampleProperty
-
-
-
-
- ModelReference
-
-
- Submodel
- http://acplt.org/Test_Submodel
-
-
- Property
- ExampleProperty2
-
-
-
-
-
- PARAMETER
- ExampleAnnotatedRelationshipElement
-
-
- en-US
- Example AnnotatedRelationshipElement object
-
-
- de
- Beispiel AnnotatedRelationshipElement Element
-
-
-
- ExternalReference
-
-
- GlobalReference
- http://acplt.org/RelationshipElements/ExampleAnnotatedRelationshipElement
-
-
-
-
- ModelReference
-
-
- Submodel
- http://acplt.org/Test_Submodel
-
-
- Property
- ExampleProperty
-
-
-
-
- ModelReference
-
-
- Submodel
- http://acplt.org/Test_Submodel
-
-
- Property
- ExampleProperty2
-
-
-
-
-
- PARAMETER
- ExampleAnnotatedProperty
- xs:string
- exampleValue
-
-
- PARAMETER
- ExampleAnnotatedRange
- xs:integer
- 1
- 5
-
-
-
-
- PARAMETER
- ExampleOperation
-
-
- en-US
- Example Operation object
-
-
- de
- Beispiel Operation Element
-
-
-
- ExternalReference
-
-
- GlobalReference
- http://acplt.org/Operations/ExampleOperation
-
-
-
-
-
-
-
- CONSTANT
- ExamplePropertyInput
-
-
- en-US
- ExampleProperty
-
-
- de
- BeispielProperty
-
-
-
-
- en-US
- Example Property object
-
-
- de
- Beispiel Property Element
-
-
-
- ExternalReference
-
-
- GlobalReference
- http://acplt.org/Properties/ExamplePropertyInput
-
-
-
- xs:string
- exampleValue
-
- ExternalReference
-
-
- GlobalReference
- http://acplt.org/ValueId/ExampleValueId
-
-
-
-
-
-
-
-
-
-
-
- CONSTANT
- ExamplePropertyOutput
-
-
- en-US
- ExampleProperty
-
-
- de
- BeispielProperty
-
-
-
-
- en-US
- Example Property object
-
-
- de
- Beispiel Property Element
-
-
-
- ExternalReference
-
-
- GlobalReference
- http://acplt.org/Properties/ExamplePropertyOutput
-
-
-
- xs:string
- exampleValue
-
- ExternalReference
-
-
- GlobalReference
- http://acplt.org/ValueId/ExampleValueId
-
-
-
-
-
-
-
-
-
-
-
- CONSTANT
- ExamplePropertyInOutput
-
-
- en-US
- ExampleProperty
-
-
- de
- BeispielProperty
-
-
-
-
- en-US
- Example Property object
-
-
- de
- Beispiel Property Element
-
-
-
- ExternalReference
-
-
- GlobalReference
- http://acplt.org/Properties/ExamplePropertyInOutput
-
-
-
- xs:string
- exampleValue
-
- ExternalReference
-
-
- GlobalReference
- http://acplt.org/ValueId/ExampleValueId
-
-
-
-
-
-
-
-
-
- PARAMETER
- ExampleCapability
-
-
- en-US
- Example Capability object
-
-
- de
- Beispiel Capability Element
-
-
-
- ExternalReference
-
-
- GlobalReference
- http://acplt.org/Capabilities/ExampleCapability
-
-
-
-
-
- PARAMETER
- ExampleBasicEventElement
-
-
- en-US
- Example BasicEventElement object
-
-
- de
- Beispiel BasicEventElement Element
-
-
-
- ExternalReference
-
-
- GlobalReference
- http://acplt.org/Events/ExampleBasicEventElement
-
-
-
-
- ModelReference
-
-
- Submodel
- http://acplt.org/Test_Submodel
-
-
- Property
- ExampleProperty
-
-
-
- output
- on
- ExampleTopic
-
- ModelReference
-
-
- Submodel
- http://acplt.org/ExampleMessageBroker
-
-
-
- 2022-11-12T23:50:23.123456+00:00
- PT0.000001S
- P1Y2M3DT4H5M6.123456S
-
-
- PARAMETER
- ExampleSubmodelCollection
-
-
- en-US
- Example SubmodelElementCollection object
-
-
- de
- Beispiel SubmodelElementCollection Element
-
-
-
- ExternalReference
-
-
- GlobalReference
- http://acplt.org/SubmodelElementCollections/ExampleSubmodelElementCollection
-
-
-
-
-
- PARAMETER
- ExampleBlob
-
-
- en-US
- Example Blob object
-
-
- de
- Beispiel Blob Element
-
-
-
- ExternalReference
-
-
- GlobalReference
- http://acplt.org/Blobs/ExampleBlob
-
-
-
- AQIDBAU=
- application/pdf
-
-
- PARAMETER
- ExampleFile
-
-
- en-US
- Example File object
-
-
- de
- Beispiel File Element
-
-
-
- ExternalReference
-
-
- GlobalReference
- http://acplt.org/Files/ExampleFile
-
-
-
- /TestFile.pdf
- application/pdf
-
-
- CONSTANT
- ExampleFileURI
-
-
- en-US
- Details of the Asset Administration Shell — An example for an external file reference
-
-
- de
- Details of the Asset Administration Shell – Ein Beispiel für eine extern referenzierte Datei
-
-
-
- ExternalReference
-
-
- GlobalReference
- http://acplt.org/Files/ExampleFile
-
-
-
- https://www.plattform-i40.de/PI40/Redaktion/DE/Downloads/Publikation/Details-of-the-Asset-Administration-Shell-Part1.pdf?__blob=publicationFile&v=5
- application/pdf
-
-
- PARAMETER
- ExampleSubmodelList
-
-
- en-US
- Example SubmodelElementList object
-
-
- de
- Beispiel SubmodelElementList Element
-
-
-
- ExternalReference
-
-
- GlobalReference
- http://acplt.org/SubmodelElementLists/ExampleSubmodelElementList
-
-
-
- true
-
- ExternalReference
-
-
- GlobalReference
- http://acplt.org/Properties/ExampleProperty
-
-
-
- Property
- xs:string
-
-
- CONSTANT
-
-
- en-US
- ExampleProperty
-
-
- de
- BeispielProperty
-
-
-
-
- en-US
- Example Property object
-
-
- de
- Beispiel Property Element
-
-
-
- ExternalReference
-
-
- GlobalReference
- http://acplt.org/Properties/ExampleProperty
-
-
-
-
-
- ExternalReference
-
-
- GlobalReference
- http://acplt.org/Properties/ExampleProperty/SupplementalId1
-
-
-
-
- ExternalReference
-
-
- GlobalReference
- http://acplt.org/Properties/ExampleProperty/SupplementalId2
-
-
-
-
-
-
-
- ExternalReference
-
-
- GlobalReference
- https://admin-shell.io/DataSpecificationTemplates/DataSpecificationIEC61360/3/0
-
-
-
-
-
-
-
- de
- Test Specification
-
-
- en-US
- TestSpecification
-
-
-
-
- de
- Test Spec
-
-
- en-US
- TestSpec
-
-
- SpaceUnit
-
- ExternalReference
-
-
- GlobalReference
- http://acplt.org/Units/SpaceUnit
-
-
-
- http://acplt.org/DataSpec/ExampleDef
- SU
- REAL_MEASURE
-
-
- de
- Dies ist eine Data Specification für Testzwecke
-
-
- en-US
- This is a DataSpecification for testing purposes
-
-
- M
-
-
-
- exampleValue
-
- ExternalReference
-
-
- GlobalReference
- http://acplt.org/ValueId/ExampleValueId
-
-
-
-
-
- exampleValue2
-
- ExternalReference
-
-
- GlobalReference
- http://acplt.org/ValueId/ExampleValueId2
-
-
-
-
-
-
- TEST
-
- true
- false
- false
- true
-
-
-
-
-
- xs:string
- exampleValue
-
- ExternalReference
-
-
- GlobalReference
- http://acplt.org/ValueId/ExampleValueId
-
-
-
-
-
- CONSTANT
-
-
- en-US
- ExampleProperty
-
-
- de
- BeispielProperty
-
-
-
-
- en-US
- Example Property object
-
-
- de
- Beispiel Property Element
-
-
-
- ExternalReference
-
-
- GlobalReference
- http://acplt.org/Properties/ExampleProperty
-
-
-
-
-
- ExternalReference
-
-
- GlobalReference
- http://acplt.org/Properties/ExampleProperty2/SupplementalId
-
-
-
-
- xs:string
- exampleValue
-
- ExternalReference
-
-
- GlobalReference
- http://acplt.org/ValueId/ExampleValueId
-
-
-
-
-
-
-
- CONSTANT
- ExampleMultiLanguageProperty
-
-
- en-US
- Example MultiLanguageProperty object
-
-
- de
- Beispiel MultiLanguageProperty Element
-
-
-
- ExternalReference
-
- ExternalReference
-
-
- GlobalReference
- http://acplt.org/Properties/ExampleProperty/Referred
-
-
-
-
-
- GlobalReference
- http://acplt.org/MultiLanguageProperties/ExampleMultiLanguageProperty
-
-
-
-
-
- en-US
- Example value of a MultiLanguageProperty element
-
-
- de
- Beispielwert für ein MultiLanguageProperty-Element
-
-
-
- ExternalReference
-
-
- GlobalReference
- http://acplt.org/ValueId/ExampleMultiLanguageValueId
-
-
-
-
-
- PARAMETER
- ExampleRange
-
-
- en-US
- Example Range object
-
-
- de
- Beispiel Range Element
-
-
-
- ExternalReference
-
-
- GlobalReference
- http://acplt.org/Ranges/ExampleRange
-
-
-
- xs:int
- 0
- 100
-
-
- PARAMETER
- ExampleReferenceElement
-
-
- en-US
- Example Reference Element object
-
-
- de
- Beispiel Reference Element Element
-
-
-
- ExternalReference
-
-
- GlobalReference
- http://acplt.org/ReferenceElements/ExampleReferenceElement
-
-
-
-
- ModelReference
-
-
- Submodel
- http://acplt.org/Test_Submodel
-
-
- Property
- ExampleProperty
-
-
-
-
-
-
-
-
-
- https://acplt.org/Test_Submodel_Mandatory
- Instance
-
-
- ExampleRelationshipElement
-
- ModelReference
-
-
- Submodel
- http://acplt.org/Test_Submodel
-
-
- Property
- ExampleProperty
-
-
-
-
- ModelReference
-
-
- Submodel
- http://acplt.org/Test_Submodel
-
-
- Property
- ExampleProperty
-
-
-
-
-
- ExampleAnnotatedRelationshipElement
-
- ModelReference
-
-
- Submodel
- http://acplt.org/Test_Submodel
-
-
- Property
- ExampleProperty
-
-
-
-
- ModelReference
-
-
- Submodel
- http://acplt.org/Test_Submodel
-
-
- Property
- ExampleProperty
-
-
-
-
-
- ExampleOperation
-
-
- ExampleCapability
-
-
- ExampleBasicEventElement
-
- ModelReference
-
-
- Submodel
- http://acplt.org/Test_Submodel
-
-
- Property
- ExampleProperty
-
-
-
- input
- off
-
-
- ExampleSubmodelList
- SubmodelElementCollection
-
-
-
-
- ExampleBlob
-
- application/pdf
-
-
- ExampleFile
- application/pdf
-
-
- PARAMETER
- ExampleMultiLanguageProperty
-
-
- PARAMETER
- ExampleProperty
- xs:string
-
-
- PARAMETER
- ExampleRange
- xs:int
-
-
- PARAMETER
- ExampleReferenceElement
-
-
-
-
-
-
-
-
- ExampleSubmodelList2
- Capability
-
-
-
-
- https://acplt.org/Test_Submodel2_Mandatory
- Instance
-
-
- TestSubmodel
-
-
- en-US
- An example submodel for the test application
-
-
- de
- Ein Beispiel-Teilmodell für eine Test-Anwendung
-
-
-
- 9
- 0
-
- https://acplt.org/Test_Submodel_Missing
- Instance
-
- ExternalReference
-
-
- GlobalReference
- http://acplt.org/SubmodelTemplates/ExampleSubmodel
-
-
-
-
-
- PARAMETER
- ExampleRelationshipElement
-
-
- en-US
- Example RelationshipElement object
-
-
- de
- Beispiel RelationshipElement Element
-
-
-
- ExternalReference
-
-
- GlobalReference
- http://acplt.org/RelationshipElements/ExampleRelationshipElement
-
-
-
-
- ModelReference
-
-
- Submodel
- http://acplt.org/Test_Submodel
-
-
- Property
- ExampleProperty
-
-
-
-
- ModelReference
-
-
- Submodel
- http://acplt.org/Test_Submodel
-
-
- Property
- ExampleProperty
-
-
-
-
-
- PARAMETER
- ExampleAnnotatedRelationshipElement
-
-
- en-US
- Example AnnotatedRelationshipElement object
-
-
- de
- Beispiel AnnotatedRelationshipElement Element
-
-
-
- ExternalReference
-
-
- GlobalReference
- http://acplt.org/RelationshipElements/ExampleAnnotatedRelationshipElement
-
-
-
-
- ModelReference
-
-
- Submodel
- http://acplt.org/Test_Submodel
-
-
- Property
- ExampleProperty
-
-
-
-
- ModelReference
-
-
- Submodel
- http://acplt.org/Test_Submodel
-
-
- Property
- ExampleProperty
-
-
-
-
-
- PARAMETER
- ExampleAnnotatedRange
- xs:integer
- 1
- 5
-
-
- PARAMETER
- ExampleAnnotatedProperty
- xs:string
- exampleValue
-
-
-
-
- PARAMETER
- ExampleOperation
-
-
- en-US
- Example Operation object
-
-
- de
- Beispiel Operation Element
-
-
-
- ExternalReference
-
-
- GlobalReference
- http://acplt.org/Operations/ExampleOperation
-
-
-
-
-
-
-
- CONSTANT
- ExamplePropertyInput
-
-
- en-US
- ExampleProperty
-
-
- de
- BeispielProperty
-
-
-
-
- en-US
- Example Property object
-
-
- de
- Beispiel Property Element
-
-
-
- ExternalReference
-
-
- GlobalReference
- http://acplt.org/Properties/ExamplePropertyInput
-
-
-
- xs:string
- exampleValue
-
- ExternalReference
-
-
- GlobalReference
- http://acplt.org/ValueId/ExampleValueId
-
-
-
-
-
-
-
-
-
-
-
- CONSTANT
- ExamplePropertyOutput
-
-
- en-US
- ExampleProperty
-
-
- de
- BeispielProperty
-
-
-
-
- en-US
- Example Property object
-
-
- de
- Beispiel Property Element
-
-
-
- ExternalReference
-
-
- GlobalReference
- http://acplt.org/Properties/ExamplePropertyOutput
-
-
-
- xs:string
- exampleValue
-
- ExternalReference
-
-
- GlobalReference
- http://acplt.org/ValueId/ExampleValueId
-
-
-
-
-
-
-
-
-
-
-
- CONSTANT
- ExamplePropertyInOutput
-
-
- en-US
- ExampleProperty
-
-
- de
- BeispielProperty
-
-
-
-
- en-US
- Example Property object
-
-
- de
- Beispiel Property Element
-
-
-
- ExternalReference
-
-
- GlobalReference
- http://acplt.org/Properties/ExamplePropertyInOutput
-
-
-
- xs:string
- exampleValue
-
- ExternalReference
-
-
- GlobalReference
- http://acplt.org/ValueId/ExampleValueId
-
-
-
-
-
-
-
-
-
- PARAMETER
- ExampleCapability
-
-
- en-US
- Example Capability object
-
-
- de
- Beispiel Capability Element
-
-
-
- ExternalReference
-
-
- GlobalReference
- http://acplt.org/Capabilities/ExampleCapability
-
-
-
-
-
- PARAMETER
- ExampleBasicEventElement
-
-
- en-US
- Example BasicEventElement object
-
-
- de
- Beispiel BasicEventElement Element
-
-
-
- ExternalReference
-
-
- GlobalReference
- http://acplt.org/Events/ExampleBasicEventElement
-
-
-
-
- ModelReference
-
-
- Submodel
- http://acplt.org/Test_Submodel
-
-
- Property
- ExampleProperty
-
-
-
- output
- on
- ExampleTopic
-
- ModelReference
-
-
- Submodel
- http://acplt.org/ExampleMessageBroker
-
-
-
- 2022-11-12T23:50:23.123456+00:00
- PT0.000001S
- P1Y2M3DT4H5M6.123456S
-
-
- PARAMETER
- ExampleSubmodelCollection
-
-
- en-US
- Example SubmodelElementCollection object
-
-
- de
- Beispiel SubmodelElementCollection Element
-
-
-
- ExternalReference
-
-
- GlobalReference
- http://acplt.org/SubmodelElementCollections/ExampleSubmodelElementCollection
-
-
-
-
-
- PARAMETER
- ExampleBlob
-
-
- en-US
- Example Blob object
-
-
- de
- Beispiel Blob Element
-
-
-
- ExternalReference
-
-
- GlobalReference
- http://acplt.org/Blobs/ExampleBlob
-
-
-
- AQIDBAU=
- application/pdf
-
-
- PARAMETER
- ExampleFile
-
-
- en-US
- Example File object
-
-
- de
- Beispiel File Element
-
-
-
- ExternalReference
-
-
- GlobalReference
- http://acplt.org/Files/ExampleFile
-
-
-
- /TestFile.pdf
- application/pdf
-
-
- CONSTANT
- ExampleMultiLanguageProperty
-
-
- en-US
- Example MultiLanguageProperty object
-
-
- de
- Beispiel MultiLanguageProperty Element
-
-
-
- ExternalReference
-
-
- GlobalReference
- http://acplt.org/MultiLanguageProperties/ExampleMultiLanguageProperty
-
-
-
-
-
- en-US
- Example value of a MultiLanguageProperty element
-
-
- de
- Beispielwert für ein MultiLanguageProperty-Element
-
-
-
-
- CONSTANT
- ExampleProperty
-
-
- en-US
- Example Property object
-
-
- de
- Beispiel Property Element
-
-
-
- ExternalReference
-
-
- GlobalReference
- http://acplt.org/Properties/ExampleProperty
-
-
-
-
-
- http://acplt.org/Qualifier/ExampleQualifier
- xs:string
-
-
- xs:string
- exampleValue
-
-
- PARAMETER
- ExampleRange
-
-
- en-US
- Example Range object
-
-
- de
- Beispiel Range Element
-
-
-
- ExternalReference
-
-
- GlobalReference
- http://acplt.org/Ranges/ExampleRange
-
-
-
- xs:int
- 0
- 100
-
-
- PARAMETER
- ExampleReferenceElement
-
-
- en-US
- Example Reference Element object
-
-
- de
- Beispiel Reference Element Element
-
-
-
- ExternalReference
-
-
- GlobalReference
- http://acplt.org/ReferenceElements/ExampleReferenceElement
-
-
-
-
- ModelReference
-
-
- Submodel
- http://acplt.org/Test_Submodel
-
-
- Property
- ExampleProperty
-
-
-
-
-
-
-
-
-
- TestSubmodel
-
-
- en-US
- An example submodel for the test application
-
-
- de
- Ein Beispiel-Teilmodell für eine Test-Anwendung
-
-
-
- 9
- 0
-
- https://acplt.org/Test_Submodel_Template
- Template
-
- ExternalReference
-
-
- GlobalReference
- http://acplt.org/SubmodelTemplates/ExampleSubmodel
-
-
-
-
-
- PARAMETER
- ExampleRelationshipElement
-
-
- en-US
- Example RelationshipElement object
-
-
- de
- Beispiel RelationshipElement Element
-
-
-
- ExternalReference
-
-
- GlobalReference
- http://acplt.org/RelationshipElements/ExampleRelationshipElement
-
-
-
-
- ModelReference
-
-
- Submodel
- http://acplt.org/Test_Submodel
-
-
- Property
- ExampleProperty
-
-
-
-
- ModelReference
-
-
- Submodel
- http://acplt.org/Test_Submodel
-
-
- Property
- ExampleProperty
-
-
-
-
-
- PARAMETER
- ExampleAnnotatedRelationshipElement
-
-
- en-US
- Example AnnotatedRelationshipElement object
-
-
- de
- Beispiel AnnotatedRelationshipElement Element
-
-
-
- ExternalReference
-
-
- GlobalReference
- http://acplt.org/RelationshipElements/ExampleAnnotatedRelationshipElement
-
-
-
-
- ModelReference
-
-
- Submodel
- http://acplt.org/Test_Submodel
-
-
- Property
- ExampleProperty
-
-
-
-
- ModelReference
-
-
- Submodel
- http://acplt.org/Test_Submodel
-
-
- Property
- ExampleProperty
-
-
-
-
-
- PARAMETER
- ExampleOperation
-
-
- en-US
- Example Operation object
-
-
- de
- Beispiel Operation Element
-
-
-
- ExternalReference
-
-
- GlobalReference
- http://acplt.org/Operations/ExampleOperation
-
-
-
-
-
-
-
- CONSTANT
- ExamplePropertyInput
-
-
- en-US
- Example Property object
-
-
- de
- Beispiel Property Element
-
-
-
- ExternalReference
-
-
- GlobalReference
- http://acplt.org/Properties/ExamplePropertyInput
-
-
-
- xs:string
-
-
-
-
-
-
-
-
- CONSTANT
- ExamplePropertyOutput
-
-
- en-US
- Example Property object
-
-
- de
- Beispiel Property Element
-
-
-
- ExternalReference
-
-
- GlobalReference
- http://acplt.org/Properties/ExamplePropertyOutput
-
-
-
- xs:string
-
-
-
-
-
-
-
-
- CONSTANT
- ExamplePropertyInOutput
-
-
- en-US
- Example Property object
-
-
- de
- Beispiel Property Element
-
-
-
- ExternalReference
-
-
- GlobalReference
- http://acplt.org/Properties/ExamplePropertyInOutput
-
-
-
- xs:string
-
-
-
-
-
-
- PARAMETER
- ExampleCapability
-
-
- en-US
- Example Capability object
-
-
- de
- Beispiel Capability Element
-
-
-
- ExternalReference
-
-
- GlobalReference
- http://acplt.org/Capabilities/ExampleCapability
-
-
-
-
-
- PARAMETER
- ExampleBasicEventElement
-
-
- en-US
- Example BasicEventElement object
-
-
- de
- Beispiel BasicEventElement Element
-
-
-
- ExternalReference
-
-
- GlobalReference
- http://acplt.org/Events/ExampleBasicEventElement
-
-
-
-
- ModelReference
-
-
- Submodel
- http://acplt.org/Test_Submodel
-
-
- Property
- ExampleProperty
-
-
-
- output
- on
- ExampleTopic
-
- ModelReference
-
-
- Submodel
- http://acplt.org/ExampleMessageBroker
-
-
-
- 2022-11-12T23:50:23.123456+00:00
- PT0.000001S
- P1Y2M3DT4H5M6.123456S
-
-
- PARAMETER
- ExampleSubmodelList
-
-
- en-US
- Example SubmodelElementList object
-
-
- de
- Beispiel SubmodelElementList Element
-
-
-
- ExternalReference
-
-
- GlobalReference
- http://acplt.org/SubmodelElementLists/ExampleSubmodelElementList
-
-
-
- true
-
- ExternalReference
-
-
- GlobalReference
- http://acplt.org/SubmodelElementCollections/ExampleSubmodelElementCollection
-
-
-
- SubmodelElementCollection
-
-
- PARAMETER
-
-
- en-US
- Example SubmodelElementCollection object
-
-
- de
- Beispiel SubmodelElementCollection Element
-
-
-
- ExternalReference
-
-
- GlobalReference
- http://acplt.org/SubmodelElementCollections/ExampleSubmodelElementCollection
-
-
-
-
-
- CONSTANT
- ExampleProperty
-
-
- en-US
- Example Property object
-
-
- de
- Beispiel Property Element
-
-
-
- ExternalReference
-
-
- GlobalReference
- http://acplt.org/Properties/ExampleProperty
-
-
-
- xs:string
-
-
- CONSTANT
- ExampleMultiLanguageProperty
-
-
- en-US
- Example MultiLanguageProperty object
-
-
- de
- Beispiel MultiLanguageProperty Element
-
-
-
- ExternalReference
-
-
- GlobalReference
- http://acplt.org/MultiLanguageProperties/ExampleMultiLanguageProperty
-
-
-
-
-
- PARAMETER
- ExampleRange
-
-
- en-US
- Example Range object
-
-
- de
- Beispiel Range Element
-
-
-
- ExternalReference
-
-
- GlobalReference
- http://acplt.org/Ranges/ExampleRange
-
-
-
- xs:int
- 100
-
-
- PARAMETER
- ExampleRange2
-
-
- en-US
- Example Range object
-
-
- de
- Beispiel Range Element
-
-
-
- ExternalReference
-
-
- GlobalReference
- http://acplt.org/Ranges/ExampleRange
-
-
-
- xs:int
- 0
-
-
- PARAMETER
- ExampleBlob
-
-
- en-US
- Example Blob object
-
-
- de
- Beispiel Blob Element
-
-
-
- ExternalReference
-
-
- GlobalReference
- http://acplt.org/Blobs/ExampleBlob
-
-
-
-
- application/pdf
-
-
- PARAMETER
- ExampleFile
-
-
- en-US
- Example File object
-
-
- de
- Beispiel File Element
-
-
-
- ExternalReference
-
-
- GlobalReference
- http://acplt.org/Files/ExampleFile
-
-
-
- application/pdf
-
-
- PARAMETER
- ExampleReferenceElement
-
-
- en-US
- Example Reference Element object
-
-
- de
- Beispiel Reference Element Element
-
-
-
- ExternalReference
-
-
- GlobalReference
- http://acplt.org/ReferenceElements/ExampleReferenceElement
-
-
-
-
-
-
-
- PARAMETER
-
-
- en-US
- Example SubmodelElementCollection object
-
-
- de
- Beispiel SubmodelElementCollection Element
-
-
-
- ExternalReference
-
-
- GlobalReference
- http://acplt.org/SubmodelElementCollections/ExampleSubmodelElementCollection
-
-
-
-
-
-
-
- PARAMETER
- ExampleSubmodelList2
-
-
- en-US
- Example SubmodelElementList object
-
-
- de
- Beispiel SubmodelElementList Element
-
-
-
- ExternalReference
-
-
- GlobalReference
- http://acplt.org/SubmodelElementLists/ExampleSubmodelElementList
-
-
-
- true
-
- ExternalReference
-
-
- GlobalReference
- http://acplt.org/SubmodelElementCollections/ExampleSubmodelElementCollection
-
-
-
- Capability
-
-
-
-
-
-
- TestConceptDescription
-
-
- en-US
- An example concept description for the test application
-
-
- de
- Ein Beispiel-ConceptDescription für eine Test-Anwendung
-
-
-
-
-
-
- ExternalReference
-
-
- GlobalReference
- https://admin-shell.io/DataSpecificationTemplates/DataSpecificationIEC61360/3/0
-
-
-
-
-
-
-
- de
- Test Specification
-
-
- en-US
- TestSpecification
-
-
-
-
- de
- Test Spec
-
-
- en-US
- TestSpec
-
-
- SpaceUnit
-
- ExternalReference
-
-
- GlobalReference
- http://acplt.org/Units/SpaceUnit
-
-
-
- http://acplt.org/DataSpec/ExampleDef
- SU
- REAL_MEASURE
-
-
- de
- Dies ist eine Data Specification für Testzwecke
-
-
- en-US
- This is a DataSpecification for testing purposes
-
-
- M
-
-
-
- exampleValue
-
- ExternalReference
-
-
- GlobalReference
- http://acplt.org/ValueId/ExampleValueId
-
-
-
-
-
- exampleValue2
-
- ExternalReference
-
-
- GlobalReference
- http://acplt.org/ValueId/ExampleValueId2
-
-
-
-
-
-
- TEST
-
- true
- false
- false
- true
-
-
-
-
-
- 9
- 0
-
- ExternalReference
-
-
- GlobalReference
- http://acplt.org/AdministrativeInformation/Test_ConceptDescription
-
-
-
- http://acplt.org/AdministrativeInformationTemplates/Test_ConceptDescription
-
- https://acplt.org/Test_ConceptDescription
-
-
- ExternalReference
-
-
- GlobalReference
- http://acplt.org/DataSpecifications/ConceptDescriptions/TestConceptDescription
-
-
-
-
-
-
- https://acplt.org/Test_ConceptDescription_Mandatory
-
-
- TestConceptDescription
-
-
- en-US
- An example concept description for the test application
-
-
- de
- Ein Beispiel-ConceptDescription für eine Test-Anwendung
-
-
-
- 9
- 0
-
- https://acplt.org/Test_ConceptDescription_Missing
-
-
-
diff --git a/compliance_tool/test/files/test_demo_full_example_xml_aasx/TestFile.pdf b/compliance_tool/test/files/test_demo_full_example_xml_aasx/TestFile.pdf
deleted file mode 100644
index 2bccbec5f..000000000
Binary files a/compliance_tool/test/files/test_demo_full_example_xml_aasx/TestFile.pdf and /dev/null differ
diff --git a/compliance_tool/test/files/test_demo_full_example_xml_aasx/[Content_Types].xml b/compliance_tool/test/files/test_demo_full_example_xml_aasx/[Content_Types].xml
deleted file mode 100644
index efab09eb1..000000000
--- a/compliance_tool/test/files/test_demo_full_example_xml_aasx/[Content_Types].xml
+++ /dev/null
@@ -1,2 +0,0 @@
-
-
\ No newline at end of file
diff --git a/compliance_tool/test/files/test_demo_full_example_xml_aasx/_rels/.rels b/compliance_tool/test/files/test_demo_full_example_xml_aasx/_rels/.rels
deleted file mode 100644
index 9758543f3..000000000
--- a/compliance_tool/test/files/test_demo_full_example_xml_aasx/_rels/.rels
+++ /dev/null
@@ -1,2 +0,0 @@
-
-
\ No newline at end of file
diff --git a/compliance_tool/test/files/test_demo_full_example_xml_aasx/aasx/_rels/aasx-origin.rels b/compliance_tool/test/files/test_demo_full_example_xml_aasx/aasx/_rels/aasx-origin.rels
deleted file mode 100644
index fc764b657..000000000
--- a/compliance_tool/test/files/test_demo_full_example_xml_aasx/aasx/_rels/aasx-origin.rels
+++ /dev/null
@@ -1,2 +0,0 @@
-
-
\ No newline at end of file
diff --git a/compliance_tool/test/files/test_demo_full_example_xml_aasx/aasx/_rels/data.xml.rels b/compliance_tool/test/files/test_demo_full_example_xml_aasx/aasx/_rels/data.xml.rels
deleted file mode 100644
index 43350edd0..000000000
--- a/compliance_tool/test/files/test_demo_full_example_xml_aasx/aasx/_rels/data.xml.rels
+++ /dev/null
@@ -1,2 +0,0 @@
-
-
\ No newline at end of file
diff --git a/compliance_tool/test/files/test_demo_full_example_xml_aasx/aasx/data.xml b/compliance_tool/test/files/test_demo_full_example_xml_aasx/aasx/data.xml
deleted file mode 100644
index cb203c9d8..000000000
--- a/compliance_tool/test/files/test_demo_full_example_xml_aasx/aasx/data.xml
+++ /dev/null
@@ -1,2999 +0,0 @@
-
-
-
-
- TestAssetAdministrationShell
-
-
- en-US
- An Example Asset Administration Shell for the test application
-
-
- de
- Ein Beispiel-Verwaltungsschale für eine Test-Anwendung
-
-
-
- 9
- 0
-
- ExternalReference
-
-
- GlobalReference
- http://acplt.org/AdministrativeInformation/Test_AssetAdministrationShell
-
-
-
- http://acplt.org/AdministrativeInformationTemplates/Test_AssetAdministrationShell
-
- https://acplt.org/Test_AssetAdministrationShell
-
-
-
- ExternalReference
-
-
- GlobalReference
- https://admin-shell.io/DataSpecificationTemplates/DataSpecificationIEC61360/3/0
-
-
-
-
-
-
-
- de
- Test Specification
-
-
- en-US
- TestSpecification
-
-
-
-
- de
- Test Spec
-
-
- en-US
- TestSpec
-
-
- SpaceUnit
-
- ExternalReference
-
-
- GlobalReference
- http://acplt.org/Units/SpaceUnit
-
-
-
- http://acplt.org/DataSpec/ExampleDef
- SU
- REAL_MEASURE
-
-
- de
- Dies ist eine Data Specification für Testzwecke
-
-
- en-US
- This is a DataSpecification for testing purposes
-
-
- M
-
-
-
- exampleValue
-
- ExternalReference
-
-
- GlobalReference
- http://acplt.org/ValueId/ExampleValueId
-
-
-
-
-
- exampleValue2
-
- ExternalReference
-
-
- GlobalReference
- http://acplt.org/ValueId/ExampleValueId2
-
-
-
-
-
-
- TEST
-
- true
- false
- false
- true
-
-
-
-
-
-
- ModelReference
-
-
- AssetAdministrationShell
- https://acplt.org/TestAssetAdministrationShell2
-
-
-
-
- Instance
- http://acplt.org/TestAsset/
-
-
-
- ExternalReference
-
-
- GlobalReference
- http://acplt.org/SpecificAssetId/
-
-
-
- TestKey
- TestValue
-
- ExternalReference
-
-
- GlobalReference
- http://acplt.org/SpecificAssetId/
-
-
-
-
-
-
- file:///path/to/thumbnail.png
- image/png
-
-
-
-
- ModelReference
-
- ModelReference
-
-
- Submodel
- http://acplt.org/SubmodelTemplates/AssetIdentification
-
-
-
-
-
- Submodel
- http://acplt.org/Submodels/Assets/TestAsset/Identification
-
-
-
-
- ModelReference
-
- ExternalReference
-
-
- GlobalReference
- http://acplt.org/SubmodelTemplates/ExampleSubmodel
-
-
-
-
-
- Submodel
- https://acplt.org/Test_Submodel
-
-
-
-
- ModelReference
-
-
- Submodel
- http://acplt.org/Submodels/Assets/TestAsset/BillOfMaterial
-
-
-
-
- ModelReference
-
-
- Submodel
- https://acplt.org/Test_Submodel_Template
-
-
-
-
-
-
- https://acplt.org/Test_AssetAdministrationShell_Mandatory
-
- Instance
- http://acplt.org/Test_Asset_Mandatory/
-
-
-
- ModelReference
-
-
- Submodel
- https://acplt.org/Test_Submodel2_Mandatory
-
-
-
-
- ModelReference
-
-
- Submodel
- https://acplt.org/Test_Submodel_Mandatory
-
-
-
-
-
-
- https://acplt.org/Test_AssetAdministrationShell2_Mandatory
-
- Instance
- http://acplt.org/TestAsset2_Mandatory/
-
-
-
- TestAssetAdministrationShell
-
-
- en-US
- An Example Asset Administration Shell for the test application
-
-
- de
- Ein Beispiel-Verwaltungsschale für eine Test-Anwendung
-
-
-
- 9
- 0
-
- https://acplt.org/Test_AssetAdministrationShell_Missing
-
- Instance
- http://acplt.org/Test_Asset_Missing/
-
-
- TestKey
- TestValue
-
- ExternalReference
-
-
- GlobalReference
- http://acplt.org/SpecificAssetId/
-
-
-
-
-
-
- file:///TestFile.pdf
- application/pdf
-
-
-
-
- ModelReference
-
-
- Submodel
- https://acplt.org/Test_Submodel_Missing
-
-
-
-
-
-
-
-
- Identification
-
-
- en-US
- An example asset identification submodel for the test application
-
-
- de
- Ein Beispiel-Identifikations-Submodel für eine Test-Anwendung
-
-
-
- 9
- 0
-
- ExternalReference
-
-
- GlobalReference
- http://acplt.org/AdministrativeInformation/TestAsset/Identification
-
-
-
- http://acplt.org/AdministrativeInformationTemplates/TestAsset/Identification
-
- http://acplt.org/Submodels/Assets/TestAsset/Identification
- Instance
-
- ModelReference
-
-
- Submodel
- http://acplt.org/SubmodelTemplates/AssetIdentification
-
-
-
-
-
-
-
- ExampleExtension
- xs:string
- ExampleExtensionValue
-
-
- ModelReference
-
-
- AssetAdministrationShell
- http://acplt.org/RefersTo/ExampleRefersTo
-
-
-
-
-
-
- PARAMETER
- ManufacturerName
-
-
- en-US
- Legally valid designation of the natural or judicial person which is directly responsible for the design, production, packaging and labeling of a product in respect to its being brought into circulation.
-
-
- de
- Bezeichnung für eine natürliche oder juristische Person, die für die Auslegung, Herstellung und Verpackung sowie die Etikettierung eines Produkts im Hinblick auf das 'Inverkehrbringen' im eigenen Namen verantwortlich ist
-
-
-
- ExternalReference
-
-
- GlobalReference
- 0173-1#02-AAO677#002
-
-
-
-
-
- ConceptQualifier
- http://acplt.org/Qualifier/ExampleQualifier
- xs:int
- 100
-
- ExternalReference
-
-
- GlobalReference
- http://acplt.org/ValueId/ExampleValueId
-
-
-
-
-
- TemplateQualifier
- http://acplt.org/Qualifier/ExampleQualifier2
- xs:int
- 50
-
- ExternalReference
-
-
- GlobalReference
- http://acplt.org/ValueId/ExampleValueId
-
-
-
-
-
- xs:string
- ACPLT
-
- ExternalReference
-
-
- GlobalReference
- http://acplt.org/ValueId/ExampleValueId
-
-
-
-
-
- PARAMETER
- InstanceId
-
-
- en-US
- Legally valid designation of the natural or judicial person which is directly responsible for the design, production, packaging and labeling of a product in respect to its being brought into circulation.
-
-
- de
- Bezeichnung für eine natürliche oder juristische Person, die für die Auslegung, Herstellung und Verpackung sowie die Etikettierung eines Produkts im Hinblick auf das 'Inverkehrbringen' im eigenen Namen verantwortlich ist
-
-
-
- ExternalReference
-
-
- GlobalReference
- http://opcfoundation.org/UA/DI/1.1/DeviceType/Serialnumber
-
-
-
-
-
- ValueQualifier
- http://acplt.org/Qualifier/ExampleQualifier3
- xs:dateTime
- 2023-04-07T16:59:54.870123
-
- ExternalReference
-
-
- GlobalReference
- http://acplt.org/ValueId/ExampleValueId
-
-
-
-
-
- xs:string
- 978-8234-234-342
-
- ExternalReference
-
-
- GlobalReference
- http://acplt.org/ValueId/ExampleValueId
-
-
-
-
-
-
-
- BillOfMaterial
-
-
- en-US
- An example bill of material submodel for the test application
-
-
- de
- Ein Beispiel-BillOfMaterial-Submodel für eine Test-Anwendung
-
-
-
- 9
- http://acplt.org/AdministrativeInformationTemplates/TestAsset/BillOfMaterial
-
- http://acplt.org/Submodels/Assets/TestAsset/BillOfMaterial
- Instance
-
- ModelReference
-
-
- Submodel
- http://acplt.org/SubmodelTemplates/BillOfMaterial
-
-
-
-
-
- PARAMETER
- ExampleEntity
-
-
- en-US
- Legally valid designation of the natural or judicial person which is directly responsible for the design, production, packaging and labeling of a product in respect to its being brought into circulation.
-
-
- de
- Bezeichnung für eine natürliche oder juristische Person, die für die Auslegung, Herstellung und Verpackung sowie die Etikettierung eines Produkts im Hinblick auf das 'Inverkehrbringen' im eigenen Namen verantwortlich ist
-
-
-
- ExternalReference
-
-
- GlobalReference
- http://opcfoundation.org/UA/DI/1.1/DeviceType/Serialnumber
-
-
-
-
-
- CONSTANT
- ExampleProperty2
-
-
- en-US
- Example Property object
-
-
- de
- Beispiel Property Element
-
-
-
- ExternalReference
-
-
- GlobalReference
- http://acplt.org/Properties/ExampleProperty
-
-
-
- xs:string
- exampleValue2
-
- ExternalReference
-
-
- GlobalReference
- http://acplt.org/ValueId/ExampleValueId
-
-
-
-
-
- CONSTANT
- ExampleProperty
-
-
- en-US
- Example Property object
-
-
- de
- Beispiel Property Element
-
-
-
- ExternalReference
-
-
- GlobalReference
- http://acplt.org/Properties/ExampleProperty
-
-
-
- xs:string
- exampleValue
-
- ExternalReference
-
-
- GlobalReference
- http://acplt.org/ValueId/ExampleValueId
-
-
-
-
-
- SelfManagedEntity
- http://acplt.org/TestAsset/
-
-
- TestKey
- TestValue
-
- ExternalReference
-
-
- GlobalReference
- http://acplt.org/SpecificAssetId/
-
-
-
-
-
-
-
- PARAMETER
- ExampleEntity2
-
-
- en-US
- Legally valid designation of the natural or judicial person which is directly responsible for the design, production, packaging and labeling of a product in respect to its being brought into circulation.
-
-
- de
- Bezeichnung für eine natürliche oder juristische Person, die für die Auslegung, Herstellung und Verpackung sowie die Etikettierung eines Produkts im Hinblick auf das 'Inverkehrbringen' im eigenen Namen verantwortlich ist
-
-
-
- ExternalReference
-
-
- GlobalReference
- http://opcfoundation.org/UA/DI/1.1/DeviceType/Serialnumber
-
-
-
- CoManagedEntity
-
-
-
-
- TestSubmodel
-
-
- en-US
- An example submodel for the test application
-
-
- de
- Ein Beispiel-Teilmodell für eine Test-Anwendung
-
-
-
- 9
- 0
-
- ExternalReference
-
-
- GlobalReference
- http://acplt.org/AdministrativeInformation/Test_Submodel
-
-
-
-
- https://acplt.org/Test_Submodel
- Instance
-
- ExternalReference
-
-
- GlobalReference
- http://acplt.org/SubmodelTemplates/ExampleSubmodel
-
-
-
-
-
- PARAMETER
- ExampleRelationshipElement
-
-
- en-US
- Example RelationshipElement object
-
-
- de
- Beispiel RelationshipElement Element
-
-
-
- ModelReference
-
-
- ConceptDescription
- https://acplt.org/Test_ConceptDescription
-
-
-
-
- ModelReference
-
-
- Submodel
- http://acplt.org/Test_Submodel
-
-
- Property
- ExampleProperty
-
-
-
-
- ModelReference
-
-
- Submodel
- http://acplt.org/Test_Submodel
-
-
- Property
- ExampleProperty2
-
-
-
-
-
- PARAMETER
- ExampleAnnotatedRelationshipElement
-
-
- en-US
- Example AnnotatedRelationshipElement object
-
-
- de
- Beispiel AnnotatedRelationshipElement Element
-
-
-
- ExternalReference
-
-
- GlobalReference
- http://acplt.org/RelationshipElements/ExampleAnnotatedRelationshipElement
-
-
-
-
- ModelReference
-
-
- Submodel
- http://acplt.org/Test_Submodel
-
-
- Property
- ExampleProperty
-
-
-
-
- ModelReference
-
-
- Submodel
- http://acplt.org/Test_Submodel
-
-
- Property
- ExampleProperty2
-
-
-
-
-
- PARAMETER
- ExampleAnnotatedProperty
- xs:string
- exampleValue
-
-
- PARAMETER
- ExampleAnnotatedRange
- xs:integer
- 1
- 5
-
-
-
-
- PARAMETER
- ExampleOperation
-
-
- en-US
- Example Operation object
-
-
- de
- Beispiel Operation Element
-
-
-
- ExternalReference
-
-
- GlobalReference
- http://acplt.org/Operations/ExampleOperation
-
-
-
-
-
-
-
- CONSTANT
- ExamplePropertyInput
-
-
- en-US
- ExampleProperty
-
-
- de
- BeispielProperty
-
-
-
-
- en-US
- Example Property object
-
-
- de
- Beispiel Property Element
-
-
-
- ExternalReference
-
-
- GlobalReference
- http://acplt.org/Properties/ExamplePropertyInput
-
-
-
- xs:string
- exampleValue
-
- ExternalReference
-
-
- GlobalReference
- http://acplt.org/ValueId/ExampleValueId
-
-
-
-
-
-
-
-
-
-
-
- CONSTANT
- ExamplePropertyOutput
-
-
- en-US
- ExampleProperty
-
-
- de
- BeispielProperty
-
-
-
-
- en-US
- Example Property object
-
-
- de
- Beispiel Property Element
-
-
-
- ExternalReference
-
-
- GlobalReference
- http://acplt.org/Properties/ExamplePropertyOutput
-
-
-
- xs:string
- exampleValue
-
- ExternalReference
-
-
- GlobalReference
- http://acplt.org/ValueId/ExampleValueId
-
-
-
-
-
-
-
-
-
-
-
- CONSTANT
- ExamplePropertyInOutput
-
-
- en-US
- ExampleProperty
-
-
- de
- BeispielProperty
-
-
-
-
- en-US
- Example Property object
-
-
- de
- Beispiel Property Element
-
-
-
- ExternalReference
-
-
- GlobalReference
- http://acplt.org/Properties/ExamplePropertyInOutput
-
-
-
- xs:string
- exampleValue
-
- ExternalReference
-
-
- GlobalReference
- http://acplt.org/ValueId/ExampleValueId
-
-
-
-
-
-
-
-
-
- PARAMETER
- ExampleCapability
-
-
- en-US
- Example Capability object
-
-
- de
- Beispiel Capability Element
-
-
-
- ExternalReference
-
-
- GlobalReference
- http://acplt.org/Capabilities/ExampleCapability
-
-
-
-
-
- PARAMETER
- ExampleBasicEventElement
-
-
- en-US
- Example BasicEventElement object
-
-
- de
- Beispiel BasicEventElement Element
-
-
-
- ExternalReference
-
-
- GlobalReference
- http://acplt.org/Events/ExampleBasicEventElement
-
-
-
-
- ModelReference
-
-
- Submodel
- http://acplt.org/Test_Submodel
-
-
- Property
- ExampleProperty
-
-
-
- output
- on
- ExampleTopic
-
- ModelReference
-
-
- Submodel
- http://acplt.org/ExampleMessageBroker
-
-
-
- 2022-11-12T23:50:23.123456+00:00
- PT0.000001S
- P1Y2M3DT4H5M6.123456S
-
-
- PARAMETER
- ExampleSubmodelCollection
-
-
- en-US
- Example SubmodelElementCollection object
-
-
- de
- Beispiel SubmodelElementCollection Element
-
-
-
- ExternalReference
-
-
- GlobalReference
- http://acplt.org/SubmodelElementCollections/ExampleSubmodelElementCollection
-
-
-
-
-
- PARAMETER
- ExampleBlob
-
-
- en-US
- Example Blob object
-
-
- de
- Beispiel Blob Element
-
-
-
- ExternalReference
-
-
- GlobalReference
- http://acplt.org/Blobs/ExampleBlob
-
-
-
- AQIDBAU=
- application/pdf
-
-
- PARAMETER
- ExampleFile
-
-
- en-US
- Example File object
-
-
- de
- Beispiel File Element
-
-
-
- ExternalReference
-
-
- GlobalReference
- http://acplt.org/Files/ExampleFile
-
-
-
- /TestFile.pdf
- application/pdf
-
-
- CONSTANT
- ExampleFileURI
-
-
- en-US
- Details of the Asset Administration Shell — An example for an external file reference
-
-
- de
- Details of the Asset Administration Shell – Ein Beispiel für eine extern referenzierte Datei
-
-
-
- ExternalReference
-
-
- GlobalReference
- http://acplt.org/Files/ExampleFile
-
-
-
- https://www.plattform-i40.de/PI40/Redaktion/DE/Downloads/Publikation/Details-of-the-Asset-Administration-Shell-Part1.pdf?__blob=publicationFile&v=5
- application/pdf
-
-
- PARAMETER
- ExampleSubmodelList
-
-
- en-US
- Example SubmodelElementList object
-
-
- de
- Beispiel SubmodelElementList Element
-
-
-
- ExternalReference
-
-
- GlobalReference
- http://acplt.org/SubmodelElementLists/ExampleSubmodelElementList
-
-
-
- true
-
- ExternalReference
-
-
- GlobalReference
- http://acplt.org/Properties/ExampleProperty
-
-
-
- Property
- xs:string
-
-
- CONSTANT
-
-
- en-US
- ExampleProperty
-
-
- de
- BeispielProperty
-
-
-
-
- en-US
- Example Property object
-
-
- de
- Beispiel Property Element
-
-
-
- ExternalReference
-
-
- GlobalReference
- http://acplt.org/Properties/ExampleProperty
-
-
-
-
-
- ExternalReference
-
-
- GlobalReference
- http://acplt.org/Properties/ExampleProperty/SupplementalId1
-
-
-
-
- ExternalReference
-
-
- GlobalReference
- http://acplt.org/Properties/ExampleProperty/SupplementalId2
-
-
-
-
-
-
-
- ExternalReference
-
-
- GlobalReference
- https://admin-shell.io/DataSpecificationTemplates/DataSpecificationIEC61360/3/0
-
-
-
-
-
-
-
- de
- Test Specification
-
-
- en-US
- TestSpecification
-
-
-
-
- de
- Test Spec
-
-
- en-US
- TestSpec
-
-
- SpaceUnit
-
- ExternalReference
-
-
- GlobalReference
- http://acplt.org/Units/SpaceUnit
-
-
-
- http://acplt.org/DataSpec/ExampleDef
- SU
- REAL_MEASURE
-
-
- de
- Dies ist eine Data Specification für Testzwecke
-
-
- en-US
- This is a DataSpecification for testing purposes
-
-
- M
-
-
-
- exampleValue
-
- ExternalReference
-
-
- GlobalReference
- http://acplt.org/ValueId/ExampleValueId
-
-
-
-
-
- exampleValue2
-
- ExternalReference
-
-
- GlobalReference
- http://acplt.org/ValueId/ExampleValueId2
-
-
-
-
-
-
- TEST
-
- true
- false
- false
- true
-
-
-
-
-
- xs:string
- exampleValue
-
- ExternalReference
-
-
- GlobalReference
- http://acplt.org/ValueId/ExampleValueId
-
-
-
-
-
- CONSTANT
-
-
- en-US
- ExampleProperty
-
-
- de
- BeispielProperty
-
-
-
-
- en-US
- Example Property object
-
-
- de
- Beispiel Property Element
-
-
-
- ExternalReference
-
-
- GlobalReference
- http://acplt.org/Properties/ExampleProperty
-
-
-
-
-
- ExternalReference
-
-
- GlobalReference
- http://acplt.org/Properties/ExampleProperty2/SupplementalId
-
-
-
-
- xs:string
- exampleValue
-
- ExternalReference
-
-
- GlobalReference
- http://acplt.org/ValueId/ExampleValueId
-
-
-
-
-
-
-
- CONSTANT
- ExampleMultiLanguageProperty
-
-
- en-US
- Example MultiLanguageProperty object
-
-
- de
- Beispiel MultiLanguageProperty Element
-
-
-
- ExternalReference
-
- ExternalReference
-
-
- GlobalReference
- http://acplt.org/Properties/ExampleProperty/Referred
-
-
-
-
-
- GlobalReference
- http://acplt.org/MultiLanguageProperties/ExampleMultiLanguageProperty
-
-
-
-
-
- en-US
- Example value of a MultiLanguageProperty element
-
-
- de
- Beispielwert für ein MultiLanguageProperty-Element
-
-
-
- ExternalReference
-
-
- GlobalReference
- http://acplt.org/ValueId/ExampleMultiLanguageValueId
-
-
-
-
-
- PARAMETER
- ExampleRange
-
-
- en-US
- Example Range object
-
-
- de
- Beispiel Range Element
-
-
-
- ExternalReference
-
-
- GlobalReference
- http://acplt.org/Ranges/ExampleRange
-
-
-
- xs:int
- 0
- 100
-
-
- PARAMETER
- ExampleReferenceElement
-
-
- en-US
- Example Reference Element object
-
-
- de
- Beispiel Reference Element Element
-
-
-
- ExternalReference
-
-
- GlobalReference
- http://acplt.org/ReferenceElements/ExampleReferenceElement
-
-
-
-
- ModelReference
-
-
- Submodel
- http://acplt.org/Test_Submodel
-
-
- Property
- ExampleProperty
-
-
-
-
-
-
-
-
-
- https://acplt.org/Test_Submodel_Mandatory
- Instance
-
-
- ExampleRelationshipElement
-
- ModelReference
-
-
- Submodel
- http://acplt.org/Test_Submodel
-
-
- Property
- ExampleProperty
-
-
-
-
- ModelReference
-
-
- Submodel
- http://acplt.org/Test_Submodel
-
-
- Property
- ExampleProperty
-
-
-
-
-
- ExampleAnnotatedRelationshipElement
-
- ModelReference
-
-
- Submodel
- http://acplt.org/Test_Submodel
-
-
- Property
- ExampleProperty
-
-
-
-
- ModelReference
-
-
- Submodel
- http://acplt.org/Test_Submodel
-
-
- Property
- ExampleProperty
-
-
-
-
-
- ExampleOperation
-
-
- ExampleCapability
-
-
- ExampleBasicEventElement
-
- ModelReference
-
-
- Submodel
- http://acplt.org/Test_Submodel
-
-
- Property
- ExampleProperty
-
-
-
- input
- off
-
-
- ExampleSubmodelList
- SubmodelElementCollection
-
-
-
-
- ExampleBlob
-
- application/pdf
-
-
- ExampleFile
- application/pdf
-
-
- PARAMETER
- ExampleMultiLanguageProperty
-
-
- PARAMETER
- ExampleProperty
- xs:string
-
-
- PARAMETER
- ExampleRange
- xs:int
-
-
- PARAMETER
- ExampleReferenceElement
-
-
-
-
-
-
-
-
- ExampleSubmodelList2
- Capability
-
-
-
-
- https://acplt.org/Test_Submodel2_Mandatory
- Instance
-
-
- TestSubmodel
-
-
- en-US
- An example submodel for the test application
-
-
- de
- Ein Beispiel-Teilmodell für eine Test-Anwendung
-
-
-
- 9
- 0
-
- https://acplt.org/Test_Submodel_Missing
- Instance
-
- ExternalReference
-
-
- GlobalReference
- http://acplt.org/SubmodelTemplates/ExampleSubmodel
-
-
-
-
-
- PARAMETER
- ExampleRelationshipElement
-
-
- en-US
- Example RelationshipElement object
-
-
- de
- Beispiel RelationshipElement Element
-
-
-
- ExternalReference
-
-
- GlobalReference
- http://acplt.org/RelationshipElements/ExampleRelationshipElement
-
-
-
-
- ModelReference
-
-
- Submodel
- http://acplt.org/Test_Submodel
-
-
- Property
- ExampleProperty
-
-
-
-
- ModelReference
-
-
- Submodel
- http://acplt.org/Test_Submodel
-
-
- Property
- ExampleProperty
-
-
-
-
-
- PARAMETER
- ExampleAnnotatedRelationshipElement
-
-
- en-US
- Example AnnotatedRelationshipElement object
-
-
- de
- Beispiel AnnotatedRelationshipElement Element
-
-
-
- ExternalReference
-
-
- GlobalReference
- http://acplt.org/RelationshipElements/ExampleAnnotatedRelationshipElement
-
-
-
-
- ModelReference
-
-
- Submodel
- http://acplt.org/Test_Submodel
-
-
- Property
- ExampleProperty
-
-
-
-
- ModelReference
-
-
- Submodel
- http://acplt.org/Test_Submodel
-
-
- Property
- ExampleProperty
-
-
-
-
-
- PARAMETER
- ExampleAnnotatedRange
- xs:integer
- 1
- 5
-
-
- PARAMETER
- ExampleAnnotatedProperty
- xs:string
- exampleValue
-
-
-
-
- PARAMETER
- ExampleOperation
-
-
- en-US
- Example Operation object
-
-
- de
- Beispiel Operation Element
-
-
-
- ExternalReference
-
-
- GlobalReference
- http://acplt.org/Operations/ExampleOperation
-
-
-
-
-
-
-
- CONSTANT
- ExamplePropertyInput
-
-
- en-US
- ExampleProperty
-
-
- de
- BeispielProperty
-
-
-
-
- en-US
- Example Property object
-
-
- de
- Beispiel Property Element
-
-
-
- ExternalReference
-
-
- GlobalReference
- http://acplt.org/Properties/ExamplePropertyInput
-
-
-
- xs:string
- exampleValue
-
- ExternalReference
-
-
- GlobalReference
- http://acplt.org/ValueId/ExampleValueId
-
-
-
-
-
-
-
-
-
-
-
- CONSTANT
- ExamplePropertyOutput
-
-
- en-US
- ExampleProperty
-
-
- de
- BeispielProperty
-
-
-
-
- en-US
- Example Property object
-
-
- de
- Beispiel Property Element
-
-
-
- ExternalReference
-
-
- GlobalReference
- http://acplt.org/Properties/ExamplePropertyOutput
-
-
-
- xs:string
- exampleValue
-
- ExternalReference
-
-
- GlobalReference
- http://acplt.org/ValueId/ExampleValueId
-
-
-
-
-
-
-
-
-
-
-
- CONSTANT
- ExamplePropertyInOutput
-
-
- en-US
- ExampleProperty
-
-
- de
- BeispielProperty
-
-
-
-
- en-US
- Example Property object
-
-
- de
- Beispiel Property Element
-
-
-
- ExternalReference
-
-
- GlobalReference
- http://acplt.org/Properties/ExamplePropertyInOutput
-
-
-
- xs:string
- exampleValue
-
- ExternalReference
-
-
- GlobalReference
- http://acplt.org/ValueId/ExampleValueId
-
-
-
-
-
-
-
-
-
- PARAMETER
- ExampleCapability
-
-
- en-US
- Example Capability object
-
-
- de
- Beispiel Capability Element
-
-
-
- ExternalReference
-
-
- GlobalReference
- http://acplt.org/Capabilities/ExampleCapability
-
-
-
-
-
- PARAMETER
- ExampleBasicEventElement
-
-
- en-US
- Example BasicEventElement object
-
-
- de
- Beispiel BasicEventElement Element
-
-
-
- ExternalReference
-
-
- GlobalReference
- http://acplt.org/Events/ExampleBasicEventElement
-
-
-
-
- ModelReference
-
-
- Submodel
- http://acplt.org/Test_Submodel
-
-
- Property
- ExampleProperty
-
-
-
- output
- on
- ExampleTopic
-
- ModelReference
-
-
- Submodel
- http://acplt.org/ExampleMessageBroker
-
-
-
- 2022-11-12T23:50:23.123456+00:00
- PT0.000001S
- P1Y2M3DT4H5M6.123456S
-
-
- PARAMETER
- ExampleSubmodelCollection
-
-
- en-US
- Example SubmodelElementCollection object
-
-
- de
- Beispiel SubmodelElementCollection Element
-
-
-
- ExternalReference
-
-
- GlobalReference
- http://acplt.org/SubmodelElementCollections/ExampleSubmodelElementCollection
-
-
-
-
-
- PARAMETER
- ExampleBlob
-
-
- en-US
- Example Blob object
-
-
- de
- Beispiel Blob Element
-
-
-
- ExternalReference
-
-
- GlobalReference
- http://acplt.org/Blobs/ExampleBlob
-
-
-
- AQIDBAU=
- application/pdf
-
-
- PARAMETER
- ExampleFile
-
-
- en-US
- Example File object
-
-
- de
- Beispiel File Element
-
-
-
- ExternalReference
-
-
- GlobalReference
- http://acplt.org/Files/ExampleFile
-
-
-
- /TestFile.pdf
- application/pdf
-
-
- CONSTANT
- ExampleMultiLanguageProperty
-
-
- en-US
- Example MultiLanguageProperty object
-
-
- de
- Beispiel MultiLanguageProperty Element
-
-
-
- ExternalReference
-
-
- GlobalReference
- http://acplt.org/MultiLanguageProperties/ExampleMultiLanguageProperty
-
-
-
-
-
- en-US
- Example value of a MultiLanguageProperty element
-
-
- de
- Beispielwert für ein MultiLanguageProperty-Element
-
-
-
-
- CONSTANT
- ExampleProperty
-
-
- en-US
- Example Property object
-
-
- de
- Beispiel Property Element
-
-
-
- ExternalReference
-
-
- GlobalReference
- http://acplt.org/Properties/ExampleProperty
-
-
-
-
-
- http://acplt.org/Qualifier/ExampleQualifier
- xs:string
-
-
- xs:string
- exampleValue
-
-
- PARAMETER
- ExampleRange
-
-
- en-US
- Example Range object
-
-
- de
- Beispiel Range Element
-
-
-
- ExternalReference
-
-
- GlobalReference
- http://acplt.org/Ranges/ExampleRange
-
-
-
- xs:int
- 0
- 100
-
-
- PARAMETER
- ExampleReferenceElement
-
-
- en-US
- Example Reference Element object
-
-
- de
- Beispiel Reference Element Element
-
-
-
- ExternalReference
-
-
- GlobalReference
- http://acplt.org/ReferenceElements/ExampleReferenceElement
-
-
-
-
- ModelReference
-
-
- Submodel
- http://acplt.org/Test_Submodel
-
-
- Property
- ExampleProperty
-
-
-
-
-
-
-
-
-
- TestSubmodel
-
-
- en-US
- An example submodel for the test application
-
-
- de
- Ein Beispiel-Teilmodell für eine Test-Anwendung
-
-
-
- 9
- 0
-
- https://acplt.org/Test_Submodel_Template
- Template
-
- ExternalReference
-
-
- GlobalReference
- http://acplt.org/SubmodelTemplates/ExampleSubmodel
-
-
-
-
-
- PARAMETER
- ExampleRelationshipElement
-
-
- en-US
- Example RelationshipElement object
-
-
- de
- Beispiel RelationshipElement Element
-
-
-
- ExternalReference
-
-
- GlobalReference
- http://acplt.org/RelationshipElements/ExampleRelationshipElement
-
-
-
-
- ModelReference
-
-
- Submodel
- http://acplt.org/Test_Submodel
-
-
- Property
- ExampleProperty
-
-
-
-
- ModelReference
-
-
- Submodel
- http://acplt.org/Test_Submodel
-
-
- Property
- ExampleProperty
-
-
-
-
-
- PARAMETER
- ExampleAnnotatedRelationshipElement
-
-
- en-US
- Example AnnotatedRelationshipElement object
-
-
- de
- Beispiel AnnotatedRelationshipElement Element
-
-
-
- ExternalReference
-
-
- GlobalReference
- http://acplt.org/RelationshipElements/ExampleAnnotatedRelationshipElement
-
-
-
-
- ModelReference
-
-
- Submodel
- http://acplt.org/Test_Submodel
-
-
- Property
- ExampleProperty
-
-
-
-
- ModelReference
-
-
- Submodel
- http://acplt.org/Test_Submodel
-
-
- Property
- ExampleProperty
-
-
-
-
-
- PARAMETER
- ExampleOperation
-
-
- en-US
- Example Operation object
-
-
- de
- Beispiel Operation Element
-
-
-
- ExternalReference
-
-
- GlobalReference
- http://acplt.org/Operations/ExampleOperation
-
-
-
-
-
-
-
- CONSTANT
- ExamplePropertyInput
-
-
- en-US
- Example Property object
-
-
- de
- Beispiel Property Element
-
-
-
- ExternalReference
-
-
- GlobalReference
- http://acplt.org/Properties/ExamplePropertyInput
-
-
-
- xs:string
-
-
-
-
-
-
-
-
- CONSTANT
- ExamplePropertyOutput
-
-
- en-US
- Example Property object
-
-
- de
- Beispiel Property Element
-
-
-
- ExternalReference
-
-
- GlobalReference
- http://acplt.org/Properties/ExamplePropertyOutput
-
-
-
- xs:string
-
-
-
-
-
-
-
-
- CONSTANT
- ExamplePropertyInOutput
-
-
- en-US
- Example Property object
-
-
- de
- Beispiel Property Element
-
-
-
- ExternalReference
-
-
- GlobalReference
- http://acplt.org/Properties/ExamplePropertyInOutput
-
-
-
- xs:string
-
-
-
-
-
-
- PARAMETER
- ExampleCapability
-
-
- en-US
- Example Capability object
-
-
- de
- Beispiel Capability Element
-
-
-
- ExternalReference
-
-
- GlobalReference
- http://acplt.org/Capabilities/ExampleCapability
-
-
-
-
-
- PARAMETER
- ExampleBasicEventElement
-
-
- en-US
- Example BasicEventElement object
-
-
- de
- Beispiel BasicEventElement Element
-
-
-
- ExternalReference
-
-
- GlobalReference
- http://acplt.org/Events/ExampleBasicEventElement
-
-
-
-
- ModelReference
-
-
- Submodel
- http://acplt.org/Test_Submodel
-
-
- Property
- ExampleProperty
-
-
-
- output
- on
- ExampleTopic
-
- ModelReference
-
-
- Submodel
- http://acplt.org/ExampleMessageBroker
-
-
-
- 2022-11-12T23:50:23.123456+00:00
- PT0.000001S
- P1Y2M3DT4H5M6.123456S
-
-
- PARAMETER
- ExampleSubmodelList
-
-
- en-US
- Example SubmodelElementList object
-
-
- de
- Beispiel SubmodelElementList Element
-
-
-
- ExternalReference
-
-
- GlobalReference
- http://acplt.org/SubmodelElementLists/ExampleSubmodelElementList
-
-
-
- true
-
- ExternalReference
-
-
- GlobalReference
- http://acplt.org/SubmodelElementCollections/ExampleSubmodelElementCollection
-
-
-
- SubmodelElementCollection
-
-
- PARAMETER
-
-
- en-US
- Example SubmodelElementCollection object
-
-
- de
- Beispiel SubmodelElementCollection Element
-
-
-
- ExternalReference
-
-
- GlobalReference
- http://acplt.org/SubmodelElementCollections/ExampleSubmodelElementCollection
-
-
-
-
-
- CONSTANT
- ExampleProperty
-
-
- en-US
- Example Property object
-
-
- de
- Beispiel Property Element
-
-
-
- ExternalReference
-
-
- GlobalReference
- http://acplt.org/Properties/ExampleProperty
-
-
-
- xs:string
-
-
- CONSTANT
- ExampleMultiLanguageProperty
-
-
- en-US
- Example MultiLanguageProperty object
-
-
- de
- Beispiel MultiLanguageProperty Element
-
-
-
- ExternalReference
-
-
- GlobalReference
- http://acplt.org/MultiLanguageProperties/ExampleMultiLanguageProperty
-
-
-
-
-
- PARAMETER
- ExampleRange
-
-
- en-US
- Example Range object
-
-
- de
- Beispiel Range Element
-
-
-
- ExternalReference
-
-
- GlobalReference
- http://acplt.org/Ranges/ExampleRange
-
-
-
- xs:int
- 100
-
-
- PARAMETER
- ExampleRange2
-
-
- en-US
- Example Range object
-
-
- de
- Beispiel Range Element
-
-
-
- ExternalReference
-
-
- GlobalReference
- http://acplt.org/Ranges/ExampleRange
-
-
-
- xs:int
- 0
-
-
- PARAMETER
- ExampleBlob
-
-
- en-US
- Example Blob object
-
-
- de
- Beispiel Blob Element
-
-
-
- ExternalReference
-
-
- GlobalReference
- http://acplt.org/Blobs/ExampleBlob
-
-
-
-
- application/pdf
-
-
- PARAMETER
- ExampleFile
-
-
- en-US
- Example File object
-
-
- de
- Beispiel File Element
-
-
-
- ExternalReference
-
-
- GlobalReference
- http://acplt.org/Files/ExampleFile
-
-
-
- application/pdf
-
-
- PARAMETER
- ExampleReferenceElement
-
-
- en-US
- Example Reference Element object
-
-
- de
- Beispiel Reference Element Element
-
-
-
- ExternalReference
-
-
- GlobalReference
- http://acplt.org/ReferenceElements/ExampleReferenceElement
-
-
-
-
-
-
-
- PARAMETER
-
-
- en-US
- Example SubmodelElementCollection object
-
-
- de
- Beispiel SubmodelElementCollection Element
-
-
-
- ExternalReference
-
-
- GlobalReference
- http://acplt.org/SubmodelElementCollections/ExampleSubmodelElementCollection
-
-
-
-
-
-
-
- PARAMETER
- ExampleSubmodelList2
-
-
- en-US
- Example SubmodelElementList object
-
-
- de
- Beispiel SubmodelElementList Element
-
-
-
- ExternalReference
-
-
- GlobalReference
- http://acplt.org/SubmodelElementLists/ExampleSubmodelElementList
-
-
-
- true
-
- ExternalReference
-
-
- GlobalReference
- http://acplt.org/SubmodelElementCollections/ExampleSubmodelElementCollection
-
-
-
- Capability
-
-
-
-
-
-
- TestConceptDescription
-
-
- en-US
- An example concept description for the test application
-
-
- de
- Ein Beispiel-ConceptDescription für eine Test-Anwendung
-
-
-
-
-
-
- ExternalReference
-
-
- GlobalReference
- https://admin-shell.io/DataSpecificationTemplates/DataSpecificationIEC61360/3/0
-
-
-
-
-
-
-
- de
- Test Specification
-
-
- en-US
- TestSpecification
-
-
-
-
- de
- Test Spec
-
-
- en-US
- TestSpec
-
-
- SpaceUnit
-
- ExternalReference
-
-
- GlobalReference
- http://acplt.org/Units/SpaceUnit
-
-
-
- http://acplt.org/DataSpec/ExampleDef
- SU
- REAL_MEASURE
-
-
- de
- Dies ist eine Data Specification für Testzwecke
-
-
- en-US
- This is a DataSpecification for testing purposes
-
-
- M
-
-
-
- exampleValue
-
- ExternalReference
-
-
- GlobalReference
- http://acplt.org/ValueId/ExampleValueId
-
-
-
-
-
- exampleValue2
-
- ExternalReference
-
-
- GlobalReference
- http://acplt.org/ValueId/ExampleValueId2
-
-
-
-
-
-
- TEST
-
- true
- false
- false
- true
-
-
-
-
-
- 9
- 0
-
- ExternalReference
-
-
- GlobalReference
- http://acplt.org/AdministrativeInformation/Test_ConceptDescription
-
-
-
- http://acplt.org/AdministrativeInformationTemplates/Test_ConceptDescription
-
- https://acplt.org/Test_ConceptDescription
-
-
- ExternalReference
-
-
- GlobalReference
- http://acplt.org/DataSpecifications/ConceptDescriptions/TestConceptDescription
-
-
-
-
-
-
- https://acplt.org/Test_ConceptDescription_Mandatory
-
-
- TestConceptDescription
-
-
- en-US
- An example concept description for the test application
-
-
- de
- Ein Beispiel-ConceptDescription für eine Test-Anwendung
-
-
-
- 9
- 0
-
- https://acplt.org/Test_ConceptDescription_Missing
-
-
-
diff --git a/compliance_tool/test/files/test_demo_full_example_xml_aasx/docProps/core.xml b/compliance_tool/test/files/test_demo_full_example_xml_aasx/docProps/core.xml
deleted file mode 100644
index 5f0e65331..000000000
--- a/compliance_tool/test/files/test_demo_full_example_xml_aasx/docProps/core.xml
+++ /dev/null
@@ -1 +0,0 @@
-2020-01-01T00:00:00Eclipse BaSyx Python Testing FrameworkTest_DescriptionEclipse BaSyx Python Testing Framework Compliance Tool2020-01-01T00:00:011.0Test Title2.0.1
\ No newline at end of file
diff --git a/compliance_tool/test/files/test_demo_full_example_xml_wrong_attribute_aasx/TestFile.pdf b/compliance_tool/test/files/test_demo_full_example_xml_wrong_attribute_aasx/TestFile.pdf
deleted file mode 100644
index 2bccbec5f..000000000
Binary files a/compliance_tool/test/files/test_demo_full_example_xml_wrong_attribute_aasx/TestFile.pdf and /dev/null differ
diff --git a/compliance_tool/test/files/test_demo_full_example_xml_wrong_attribute_aasx/[Content_Types].xml b/compliance_tool/test/files/test_demo_full_example_xml_wrong_attribute_aasx/[Content_Types].xml
deleted file mode 100644
index efab09eb1..000000000
--- a/compliance_tool/test/files/test_demo_full_example_xml_wrong_attribute_aasx/[Content_Types].xml
+++ /dev/null
@@ -1,2 +0,0 @@
-
-
\ No newline at end of file
diff --git a/compliance_tool/test/files/test_demo_full_example_xml_wrong_attribute_aasx/_rels/.rels b/compliance_tool/test/files/test_demo_full_example_xml_wrong_attribute_aasx/_rels/.rels
deleted file mode 100644
index 9758543f3..000000000
--- a/compliance_tool/test/files/test_demo_full_example_xml_wrong_attribute_aasx/_rels/.rels
+++ /dev/null
@@ -1,2 +0,0 @@
-
-
\ No newline at end of file
diff --git a/compliance_tool/test/files/test_demo_full_example_xml_wrong_attribute_aasx/aasx/_rels/aasx-origin.rels b/compliance_tool/test/files/test_demo_full_example_xml_wrong_attribute_aasx/aasx/_rels/aasx-origin.rels
deleted file mode 100644
index fc764b657..000000000
--- a/compliance_tool/test/files/test_demo_full_example_xml_wrong_attribute_aasx/aasx/_rels/aasx-origin.rels
+++ /dev/null
@@ -1,2 +0,0 @@
-
-
\ No newline at end of file
diff --git a/compliance_tool/test/files/test_demo_full_example_xml_wrong_attribute_aasx/aasx/_rels/data.xml.rels b/compliance_tool/test/files/test_demo_full_example_xml_wrong_attribute_aasx/aasx/_rels/data.xml.rels
deleted file mode 100644
index 43350edd0..000000000
--- a/compliance_tool/test/files/test_demo_full_example_xml_wrong_attribute_aasx/aasx/_rels/data.xml.rels
+++ /dev/null
@@ -1,2 +0,0 @@
-
-
\ No newline at end of file
diff --git a/compliance_tool/test/files/test_demo_full_example_xml_wrong_attribute_aasx/aasx/data.xml b/compliance_tool/test/files/test_demo_full_example_xml_wrong_attribute_aasx/aasx/data.xml
deleted file mode 100644
index 7f2531f6c..000000000
--- a/compliance_tool/test/files/test_demo_full_example_xml_wrong_attribute_aasx/aasx/data.xml
+++ /dev/null
@@ -1,2999 +0,0 @@
-
-
-
-
- TestAssetAdministrationShell123
-
-
- en-US
- An Example Asset Administration Shell for the test application
-
-
- de
- Ein Beispiel-Verwaltungsschale für eine Test-Anwendung
-
-
-
- 9
- 0
-
- ExternalReference
-
-
- GlobalReference
- http://acplt.org/AdministrativeInformation/Test_AssetAdministrationShell
-
-
-
- http://acplt.org/AdministrativeInformationTemplates/Test_AssetAdministrationShell
-
- https://acplt.org/Test_AssetAdministrationShell
-
-
-
- ExternalReference
-
-
- GlobalReference
- https://admin-shell.io/DataSpecificationTemplates/DataSpecificationIEC61360/3/0
-
-
-
-
-
-
-
- de
- Test Specification
-
-
- en-US
- TestSpecification
-
-
-
-
- de
- Test Spec
-
-
- en-US
- TestSpec
-
-
- SpaceUnit
-
- ExternalReference
-
-
- GlobalReference
- http://acplt.org/Units/SpaceUnit
-
-
-
- http://acplt.org/DataSpec/ExampleDef
- SU
- REAL_MEASURE
-
-
- de
- Dies ist eine Data Specification für Testzwecke
-
-
- en-US
- This is a DataSpecification for testing purposes
-
-
- M
-
-
-
- exampleValue
-
- ExternalReference
-
-
- GlobalReference
- http://acplt.org/ValueId/ExampleValueId
-
-
-
-
-
- exampleValue2
-
- ExternalReference
-
-
- GlobalReference
- http://acplt.org/ValueId/ExampleValueId2
-
-
-
-
-
-
- TEST
-
- true
- false
- false
- true
-
-
-
-
-
-
- ModelReference
-
-
- AssetAdministrationShell
- https://acplt.org/TestAssetAdministrationShell2
-
-
-
-
- Instance
- http://acplt.org/TestAsset/
-
-
-
- ExternalReference
-
-
- GlobalReference
- http://acplt.org/SpecificAssetId/
-
-
-
- TestKey
- TestValue
-
- ExternalReference
-
-
- GlobalReference
- http://acplt.org/SpecificAssetId/
-
-
-
-
-
-
- file:///path/to/thumbnail.png
- image/png
-
-
-
-
- ModelReference
-
- ModelReference
-
-
- Submodel
- http://acplt.org/SubmodelTemplates/AssetIdentification
-
-
-
-
-
- Submodel
- http://acplt.org/Submodels/Assets/TestAsset/Identification
-
-
-
-
- ModelReference
-
- ExternalReference
-
-
- GlobalReference
- http://acplt.org/SubmodelTemplates/ExampleSubmodel
-
-
-
-
-
- Submodel
- https://acplt.org/Test_Submodel
-
-
-
-
- ModelReference
-
-
- Submodel
- http://acplt.org/Submodels/Assets/TestAsset/BillOfMaterial
-
-
-
-
- ModelReference
-
-
- Submodel
- https://acplt.org/Test_Submodel_Template
-
-
-
-
-
-
- https://acplt.org/Test_AssetAdministrationShell_Mandatory
-
- Instance
- http://acplt.org/Test_Asset_Mandatory/
-
-
-
- ModelReference
-
-
- Submodel
- https://acplt.org/Test_Submodel2_Mandatory
-
-
-
-
- ModelReference
-
-
- Submodel
- https://acplt.org/Test_Submodel_Mandatory
-
-
-
-
-
-
- https://acplt.org/Test_AssetAdministrationShell2_Mandatory
-
- Instance
- http://acplt.org/TestAsset2_Mandatory/
-
-
-
- TestAssetAdministrationShell
-
-
- en-US
- An Example Asset Administration Shell for the test application
-
-
- de
- Ein Beispiel-Verwaltungsschale für eine Test-Anwendung
-
-
-
- 9
- 0
-
- https://acplt.org/Test_AssetAdministrationShell_Missing
-
- Instance
- http://acplt.org/Test_Asset_Missing/
-
-
- TestKey
- TestValue
-
- ExternalReference
-
-
- GlobalReference
- http://acplt.org/SpecificAssetId/
-
-
-
-
-
-
- file:///TestFile.pdf
- application/pdf
-
-
-
-
- ModelReference
-
-
- Submodel
- https://acplt.org/Test_Submodel_Missing
-
-
-
-
-
-
-
-
- Identification
-
-
- en-US
- An example asset identification submodel for the test application
-
-
- de
- Ein Beispiel-Identifikations-Submodel für eine Test-Anwendung
-
-
-
- 9
- 0
-
- ExternalReference
-
-
- GlobalReference
- http://acplt.org/AdministrativeInformation/TestAsset/Identification
-
-
-
- http://acplt.org/AdministrativeInformationTemplates/TestAsset/Identification
-
- http://acplt.org/Submodels/Assets/TestAsset/Identification
- Instance
-
- ModelReference
-
-
- Submodel
- http://acplt.org/SubmodelTemplates/AssetIdentification
-
-
-
-
-
-
-
- ExampleExtension
- xs:string
- ExampleExtensionValue
-
-
- ModelReference
-
-
- AssetAdministrationShell
- http://acplt.org/RefersTo/ExampleRefersTo
-
-
-
-
-
-
- PARAMETER
- ManufacturerName
-
-
- en-US
- Legally valid designation of the natural or judicial person which is directly responsible for the design, production, packaging and labeling of a product in respect to its being brought into circulation.
-
-
- de
- Bezeichnung für eine natürliche oder juristische Person, die für die Auslegung, Herstellung und Verpackung sowie die Etikettierung eines Produkts im Hinblick auf das 'Inverkehrbringen' im eigenen Namen verantwortlich ist
-
-
-
- ExternalReference
-
-
- GlobalReference
- 0173-1#02-AAO677#002
-
-
-
-
-
- ConceptQualifier
- http://acplt.org/Qualifier/ExampleQualifier
- xs:int
- 100
-
- ExternalReference
-
-
- GlobalReference
- http://acplt.org/ValueId/ExampleValueId
-
-
-
-
-
- TemplateQualifier
- http://acplt.org/Qualifier/ExampleQualifier2
- xs:int
- 50
-
- ExternalReference
-
-
- GlobalReference
- http://acplt.org/ValueId/ExampleValueId
-
-
-
-
-
- xs:string
- ACPLT
-
- ExternalReference
-
-
- GlobalReference
- http://acplt.org/ValueId/ExampleValueId
-
-
-
-
-
- PARAMETER
- InstanceId
-
-
- en-US
- Legally valid designation of the natural or judicial person which is directly responsible for the design, production, packaging and labeling of a product in respect to its being brought into circulation.
-
-
- de
- Bezeichnung für eine natürliche oder juristische Person, die für die Auslegung, Herstellung und Verpackung sowie die Etikettierung eines Produkts im Hinblick auf das 'Inverkehrbringen' im eigenen Namen verantwortlich ist
-
-
-
- ExternalReference
-
-
- GlobalReference
- http://opcfoundation.org/UA/DI/1.1/DeviceType/Serialnumber
-
-
-
-
-
- ValueQualifier
- http://acplt.org/Qualifier/ExampleQualifier3
- xs:dateTime
- 2023-04-07T16:59:54.870123
-
- ExternalReference
-
-
- GlobalReference
- http://acplt.org/ValueId/ExampleValueId
-
-
-
-
-
- xs:string
- 978-8234-234-342
-
- ExternalReference
-
-
- GlobalReference
- http://acplt.org/ValueId/ExampleValueId
-
-
-
-
-
-
-
- BillOfMaterial
-
-
- en-US
- An example bill of material submodel for the test application
-
-
- de
- Ein Beispiel-BillOfMaterial-Submodel für eine Test-Anwendung
-
-
-
- 9
- http://acplt.org/AdministrativeInformationTemplates/TestAsset/BillOfMaterial
-
- http://acplt.org/Submodels/Assets/TestAsset/BillOfMaterial
- Instance
-
- ModelReference
-
-
- Submodel
- http://acplt.org/SubmodelTemplates/BillOfMaterial
-
-
-
-
-
- PARAMETER
- ExampleEntity
-
-
- en-US
- Legally valid designation of the natural or judicial person which is directly responsible for the design, production, packaging and labeling of a product in respect to its being brought into circulation.
-
-
- de
- Bezeichnung für eine natürliche oder juristische Person, die für die Auslegung, Herstellung und Verpackung sowie die Etikettierung eines Produkts im Hinblick auf das 'Inverkehrbringen' im eigenen Namen verantwortlich ist
-
-
-
- ExternalReference
-
-
- GlobalReference
- http://opcfoundation.org/UA/DI/1.1/DeviceType/Serialnumber
-
-
-
-
-
- CONSTANT
- ExampleProperty2
-
-
- en-US
- Example Property object
-
-
- de
- Beispiel Property Element
-
-
-
- ExternalReference
-
-
- GlobalReference
- http://acplt.org/Properties/ExampleProperty
-
-
-
- xs:string
- exampleValue2
-
- ExternalReference
-
-
- GlobalReference
- http://acplt.org/ValueId/ExampleValueId
-
-
-
-
-
- CONSTANT
- ExampleProperty
-
-
- en-US
- Example Property object
-
-
- de
- Beispiel Property Element
-
-
-
- ExternalReference
-
-
- GlobalReference
- http://acplt.org/Properties/ExampleProperty
-
-
-
- xs:string
- exampleValue
-
- ExternalReference
-
-
- GlobalReference
- http://acplt.org/ValueId/ExampleValueId
-
-
-
-
-
- SelfManagedEntity
- http://acplt.org/TestAsset/
-
-
- TestKey
- TestValue
-
- ExternalReference
-
-
- GlobalReference
- http://acplt.org/SpecificAssetId/
-
-
-
-
-
-
-
- PARAMETER
- ExampleEntity2
-
-
- en-US
- Legally valid designation of the natural or judicial person which is directly responsible for the design, production, packaging and labeling of a product in respect to its being brought into circulation.
-
-
- de
- Bezeichnung für eine natürliche oder juristische Person, die für die Auslegung, Herstellung und Verpackung sowie die Etikettierung eines Produkts im Hinblick auf das 'Inverkehrbringen' im eigenen Namen verantwortlich ist
-
-
-
- ExternalReference
-
-
- GlobalReference
- http://opcfoundation.org/UA/DI/1.1/DeviceType/Serialnumber
-
-
-
- CoManagedEntity
-
-
-
-
- TestSubmodel
-
-
- en-US
- An example submodel for the test application
-
-
- de
- Ein Beispiel-Teilmodell für eine Test-Anwendung
-
-
-
- 9
- 0
-
- ExternalReference
-
-
- GlobalReference
- http://acplt.org/AdministrativeInformation/Test_Submodel
-
-
-
-
- https://acplt.org/Test_Submodel
- Instance
-
- ExternalReference
-
-
- GlobalReference
- http://acplt.org/SubmodelTemplates/ExampleSubmodel
-
-
-
-
-
- PARAMETER
- ExampleRelationshipElement
-
-
- en-US
- Example RelationshipElement object
-
-
- de
- Beispiel RelationshipElement Element
-
-
-
- ModelReference
-
-
- ConceptDescription
- https://acplt.org/Test_ConceptDescription
-
-
-
-
- ModelReference
-
-
- Submodel
- http://acplt.org/Test_Submodel
-
-
- Property
- ExampleProperty
-
-
-
-
- ModelReference
-
-
- Submodel
- http://acplt.org/Test_Submodel
-
-
- Property
- ExampleProperty2
-
-
-
-
-
- PARAMETER
- ExampleAnnotatedRelationshipElement
-
-
- en-US
- Example AnnotatedRelationshipElement object
-
-
- de
- Beispiel AnnotatedRelationshipElement Element
-
-
-
- ExternalReference
-
-
- GlobalReference
- http://acplt.org/RelationshipElements/ExampleAnnotatedRelationshipElement
-
-
-
-
- ModelReference
-
-
- Submodel
- http://acplt.org/Test_Submodel
-
-
- Property
- ExampleProperty
-
-
-
-
- ModelReference
-
-
- Submodel
- http://acplt.org/Test_Submodel
-
-
- Property
- ExampleProperty2
-
-
-
-
-
- PARAMETER
- ExampleAnnotatedProperty
- xs:string
- exampleValue
-
-
- PARAMETER
- ExampleAnnotatedRange
- xs:integer
- 1
- 5
-
-
-
-
- PARAMETER
- ExampleOperation
-
-
- en-US
- Example Operation object
-
-
- de
- Beispiel Operation Element
-
-
-
- ExternalReference
-
-
- GlobalReference
- http://acplt.org/Operations/ExampleOperation
-
-
-
-
-
-
-
- CONSTANT
- ExamplePropertyInput
-
-
- en-US
- ExampleProperty
-
-
- de
- BeispielProperty
-
-
-
-
- en-US
- Example Property object
-
-
- de
- Beispiel Property Element
-
-
-
- ExternalReference
-
-
- GlobalReference
- http://acplt.org/Properties/ExamplePropertyInput
-
-
-
- xs:string
- exampleValue
-
- ExternalReference
-
-
- GlobalReference
- http://acplt.org/ValueId/ExampleValueId
-
-
-
-
-
-
-
-
-
-
-
- CONSTANT
- ExamplePropertyOutput
-
-
- en-US
- ExampleProperty
-
-
- de
- BeispielProperty
-
-
-
-
- en-US
- Example Property object
-
-
- de
- Beispiel Property Element
-
-
-
- ExternalReference
-
-
- GlobalReference
- http://acplt.org/Properties/ExamplePropertyOutput
-
-
-
- xs:string
- exampleValue
-
- ExternalReference
-
-
- GlobalReference
- http://acplt.org/ValueId/ExampleValueId
-
-
-
-
-
-
-
-
-
-
-
- CONSTANT
- ExamplePropertyInOutput
-
-
- en-US
- ExampleProperty
-
-
- de
- BeispielProperty
-
-
-
-
- en-US
- Example Property object
-
-
- de
- Beispiel Property Element
-
-
-
- ExternalReference
-
-
- GlobalReference
- http://acplt.org/Properties/ExamplePropertyInOutput
-
-
-
- xs:string
- exampleValue
-
- ExternalReference
-
-
- GlobalReference
- http://acplt.org/ValueId/ExampleValueId
-
-
-
-
-
-
-
-
-
- PARAMETER
- ExampleCapability
-
-
- en-US
- Example Capability object
-
-
- de
- Beispiel Capability Element
-
-
-
- ExternalReference
-
-
- GlobalReference
- http://acplt.org/Capabilities/ExampleCapability
-
-
-
-
-
- PARAMETER
- ExampleBasicEventElement
-
-
- en-US
- Example BasicEventElement object
-
-
- de
- Beispiel BasicEventElement Element
-
-
-
- ExternalReference
-
-
- GlobalReference
- http://acplt.org/Events/ExampleBasicEventElement
-
-
-
-
- ModelReference
-
-
- Submodel
- http://acplt.org/Test_Submodel
-
-
- Property
- ExampleProperty
-
-
-
- output
- on
- ExampleTopic
-
- ModelReference
-
-
- Submodel
- http://acplt.org/ExampleMessageBroker
-
-
-
- 2022-11-12T23:50:23.123456+00:00
- PT0.000001S
- P1Y2M3DT4H5M6.123456S
-
-
- PARAMETER
- ExampleSubmodelCollection
-
-
- en-US
- Example SubmodelElementCollection object
-
-
- de
- Beispiel SubmodelElementCollection Element
-
-
-
- ExternalReference
-
-
- GlobalReference
- http://acplt.org/SubmodelElementCollections/ExampleSubmodelElementCollection
-
-
-
-
-
- PARAMETER
- ExampleBlob
-
-
- en-US
- Example Blob object
-
-
- de
- Beispiel Blob Element
-
-
-
- ExternalReference
-
-
- GlobalReference
- http://acplt.org/Blobs/ExampleBlob
-
-
-
- AQIDBAU=
- application/pdf
-
-
- PARAMETER
- ExampleFile
-
-
- en-US
- Example File object
-
-
- de
- Beispiel File Element
-
-
-
- ExternalReference
-
-
- GlobalReference
- http://acplt.org/Files/ExampleFile
-
-
-
- /TestFile.pdf
- application/pdf
-
-
- CONSTANT
- ExampleFileURI
-
-
- en-US
- Details of the Asset Administration Shell — An example for an external file reference
-
-
- de
- Details of the Asset Administration Shell – Ein Beispiel für eine extern referenzierte Datei
-
-
-
- ExternalReference
-
-
- GlobalReference
- http://acplt.org/Files/ExampleFile
-
-
-
- https://www.plattform-i40.de/PI40/Redaktion/DE/Downloads/Publikation/Details-of-the-Asset-Administration-Shell-Part1.pdf?__blob=publicationFile&v=5
- application/pdf
-
-
- PARAMETER
- ExampleSubmodelList
-
-
- en-US
- Example SubmodelElementList object
-
-
- de
- Beispiel SubmodelElementList Element
-
-
-
- ExternalReference
-
-
- GlobalReference
- http://acplt.org/SubmodelElementLists/ExampleSubmodelElementList
-
-
-
- true
-
- ExternalReference
-
-
- GlobalReference
- http://acplt.org/Properties/ExampleProperty
-
-
-
- Property
- xs:string
-
-
- CONSTANT
-
-
- en-US
- ExampleProperty
-
-
- de
- BeispielProperty
-
-
-
-
- en-US
- Example Property object
-
-
- de
- Beispiel Property Element
-
-
-
- ExternalReference
-
-
- GlobalReference
- http://acplt.org/Properties/ExampleProperty
-
-
-
-
-
- ExternalReference
-
-
- GlobalReference
- http://acplt.org/Properties/ExampleProperty/SupplementalId1
-
-
-
-
- ExternalReference
-
-
- GlobalReference
- http://acplt.org/Properties/ExampleProperty/SupplementalId2
-
-
-
-
-
-
-
- ExternalReference
-
-
- GlobalReference
- https://admin-shell.io/DataSpecificationTemplates/DataSpecificationIEC61360/3/0
-
-
-
-
-
-
-
- de
- Test Specification
-
-
- en-US
- TestSpecification
-
-
-
-
- de
- Test Spec
-
-
- en-US
- TestSpec
-
-
- SpaceUnit
-
- ExternalReference
-
-
- GlobalReference
- http://acplt.org/Units/SpaceUnit
-
-
-
- http://acplt.org/DataSpec/ExampleDef
- SU
- REAL_MEASURE
-
-
- de
- Dies ist eine Data Specification für Testzwecke
-
-
- en-US
- This is a DataSpecification for testing purposes
-
-
- M
-
-
-
- exampleValue
-
- ExternalReference
-
-
- GlobalReference
- http://acplt.org/ValueId/ExampleValueId
-
-
-
-
-
- exampleValue2
-
- ExternalReference
-
-
- GlobalReference
- http://acplt.org/ValueId/ExampleValueId2
-
-
-
-
-
-
- TEST
-
- true
- false
- false
- true
-
-
-
-
-
- xs:string
- exampleValue
-
- ExternalReference
-
-
- GlobalReference
- http://acplt.org/ValueId/ExampleValueId
-
-
-
-
-
- CONSTANT
-
-
- en-US
- ExampleProperty
-
-
- de
- BeispielProperty
-
-
-
-
- en-US
- Example Property object
-
-
- de
- Beispiel Property Element
-
-
-
- ExternalReference
-
-
- GlobalReference
- http://acplt.org/Properties/ExampleProperty
-
-
-
-
-
- ExternalReference
-
-
- GlobalReference
- http://acplt.org/Properties/ExampleProperty2/SupplementalId
-
-
-
-
- xs:string
- exampleValue
-
- ExternalReference
-
-
- GlobalReference
- http://acplt.org/ValueId/ExampleValueId
-
-
-
-
-
-
-
- CONSTANT
- ExampleMultiLanguageProperty
-
-
- en-US
- Example MultiLanguageProperty object
-
-
- de
- Beispiel MultiLanguageProperty Element
-
-
-
- ExternalReference
-
- ExternalReference
-
-
- GlobalReference
- http://acplt.org/Properties/ExampleProperty/Referred
-
-
-
-
-
- GlobalReference
- http://acplt.org/MultiLanguageProperties/ExampleMultiLanguageProperty
-
-
-
-
-
- en-US
- Example value of a MultiLanguageProperty element
-
-
- de
- Beispielwert für ein MultiLanguageProperty-Element
-
-
-
- ExternalReference
-
-
- GlobalReference
- http://acplt.org/ValueId/ExampleMultiLanguageValueId
-
-
-
-
-
- PARAMETER
- ExampleRange
-
-
- en-US
- Example Range object
-
-
- de
- Beispiel Range Element
-
-
-
- ExternalReference
-
-
- GlobalReference
- http://acplt.org/Ranges/ExampleRange
-
-
-
- xs:int
- 0
- 100
-
-
- PARAMETER
- ExampleReferenceElement
-
-
- en-US
- Example Reference Element object
-
-
- de
- Beispiel Reference Element Element
-
-
-
- ExternalReference
-
-
- GlobalReference
- http://acplt.org/ReferenceElements/ExampleReferenceElement
-
-
-
-
- ModelReference
-
-
- Submodel
- http://acplt.org/Test_Submodel
-
-
- Property
- ExampleProperty
-
-
-
-
-
-
-
-
-
- https://acplt.org/Test_Submodel_Mandatory
- Instance
-
-
- ExampleRelationshipElement
-
- ModelReference
-
-
- Submodel
- http://acplt.org/Test_Submodel
-
-
- Property
- ExampleProperty
-
-
-
-
- ModelReference
-
-
- Submodel
- http://acplt.org/Test_Submodel
-
-
- Property
- ExampleProperty
-
-
-
-
-
- ExampleAnnotatedRelationshipElement
-
- ModelReference
-
-
- Submodel
- http://acplt.org/Test_Submodel
-
-
- Property
- ExampleProperty
-
-
-
-
- ModelReference
-
-
- Submodel
- http://acplt.org/Test_Submodel
-
-
- Property
- ExampleProperty
-
-
-
-
-
- ExampleOperation
-
-
- ExampleCapability
-
-
- ExampleBasicEventElement
-
- ModelReference
-
-
- Submodel
- http://acplt.org/Test_Submodel
-
-
- Property
- ExampleProperty
-
-
-
- input
- off
-
-
- ExampleSubmodelList
- SubmodelElementCollection
-
-
-
-
- ExampleBlob
-
- application/pdf
-
-
- ExampleFile
- application/pdf
-
-
- PARAMETER
- ExampleMultiLanguageProperty
-
-
- PARAMETER
- ExampleProperty
- xs:string
-
-
- PARAMETER
- ExampleRange
- xs:int
-
-
- PARAMETER
- ExampleReferenceElement
-
-
-
-
-
-
-
-
- ExampleSubmodelList2
- Capability
-
-
-
-
- https://acplt.org/Test_Submodel2_Mandatory
- Instance
-
-
- TestSubmodel
-
-
- en-US
- An example submodel for the test application
-
-
- de
- Ein Beispiel-Teilmodell für eine Test-Anwendung
-
-
-
- 9
- 0
-
- https://acplt.org/Test_Submodel_Missing
- Instance
-
- ExternalReference
-
-
- GlobalReference
- http://acplt.org/SubmodelTemplates/ExampleSubmodel
-
-
-
-
-
- PARAMETER
- ExampleRelationshipElement
-
-
- en-US
- Example RelationshipElement object
-
-
- de
- Beispiel RelationshipElement Element
-
-
-
- ExternalReference
-
-
- GlobalReference
- http://acplt.org/RelationshipElements/ExampleRelationshipElement
-
-
-
-
- ModelReference
-
-
- Submodel
- http://acplt.org/Test_Submodel
-
-
- Property
- ExampleProperty
-
-
-
-
- ModelReference
-
-
- Submodel
- http://acplt.org/Test_Submodel
-
-
- Property
- ExampleProperty
-
-
-
-
-
- PARAMETER
- ExampleAnnotatedRelationshipElement
-
-
- en-US
- Example AnnotatedRelationshipElement object
-
-
- de
- Beispiel AnnotatedRelationshipElement Element
-
-
-
- ExternalReference
-
-
- GlobalReference
- http://acplt.org/RelationshipElements/ExampleAnnotatedRelationshipElement
-
-
-
-
- ModelReference
-
-
- Submodel
- http://acplt.org/Test_Submodel
-
-
- Property
- ExampleProperty
-
-
-
-
- ModelReference
-
-
- Submodel
- http://acplt.org/Test_Submodel
-
-
- Property
- ExampleProperty
-
-
-
-
-
- PARAMETER
- ExampleAnnotatedRange
- xs:integer
- 1
- 5
-
-
- PARAMETER
- ExampleAnnotatedProperty
- xs:string
- exampleValue
-
-
-
-
- PARAMETER
- ExampleOperation
-
-
- en-US
- Example Operation object
-
-
- de
- Beispiel Operation Element
-
-
-
- ExternalReference
-
-
- GlobalReference
- http://acplt.org/Operations/ExampleOperation
-
-
-
-
-
-
-
- CONSTANT
- ExamplePropertyInput
-
-
- en-US
- ExampleProperty
-
-
- de
- BeispielProperty
-
-
-
-
- en-US
- Example Property object
-
-
- de
- Beispiel Property Element
-
-
-
- ExternalReference
-
-
- GlobalReference
- http://acplt.org/Properties/ExamplePropertyInput
-
-
-
- xs:string
- exampleValue
-
- ExternalReference
-
-
- GlobalReference
- http://acplt.org/ValueId/ExampleValueId
-
-
-
-
-
-
-
-
-
-
-
- CONSTANT
- ExamplePropertyOutput
-
-
- en-US
- ExampleProperty
-
-
- de
- BeispielProperty
-
-
-
-
- en-US
- Example Property object
-
-
- de
- Beispiel Property Element
-
-
-
- ExternalReference
-
-
- GlobalReference
- http://acplt.org/Properties/ExamplePropertyOutput
-
-
-
- xs:string
- exampleValue
-
- ExternalReference
-
-
- GlobalReference
- http://acplt.org/ValueId/ExampleValueId
-
-
-
-
-
-
-
-
-
-
-
- CONSTANT
- ExamplePropertyInOutput
-
-
- en-US
- ExampleProperty
-
-
- de
- BeispielProperty
-
-
-
-
- en-US
- Example Property object
-
-
- de
- Beispiel Property Element
-
-
-
- ExternalReference
-
-
- GlobalReference
- http://acplt.org/Properties/ExamplePropertyInOutput
-
-
-
- xs:string
- exampleValue
-
- ExternalReference
-
-
- GlobalReference
- http://acplt.org/ValueId/ExampleValueId
-
-
-
-
-
-
-
-
-
- PARAMETER
- ExampleCapability
-
-
- en-US
- Example Capability object
-
-
- de
- Beispiel Capability Element
-
-
-
- ExternalReference
-
-
- GlobalReference
- http://acplt.org/Capabilities/ExampleCapability
-
-
-
-
-
- PARAMETER
- ExampleBasicEventElement
-
-
- en-US
- Example BasicEventElement object
-
-
- de
- Beispiel BasicEventElement Element
-
-
-
- ExternalReference
-
-
- GlobalReference
- http://acplt.org/Events/ExampleBasicEventElement
-
-
-
-
- ModelReference
-
-
- Submodel
- http://acplt.org/Test_Submodel
-
-
- Property
- ExampleProperty
-
-
-
- output
- on
- ExampleTopic
-
- ModelReference
-
-
- Submodel
- http://acplt.org/ExampleMessageBroker
-
-
-
- 2022-11-12T23:50:23.123456+00:00
- PT0.000001S
- P1Y2M3DT4H5M6.123456S
-
-
- PARAMETER
- ExampleSubmodelCollection
-
-
- en-US
- Example SubmodelElementCollection object
-
-
- de
- Beispiel SubmodelElementCollection Element
-
-
-
- ExternalReference
-
-
- GlobalReference
- http://acplt.org/SubmodelElementCollections/ExampleSubmodelElementCollection
-
-
-
-
-
- PARAMETER
- ExampleBlob
-
-
- en-US
- Example Blob object
-
-
- de
- Beispiel Blob Element
-
-
-
- ExternalReference
-
-
- GlobalReference
- http://acplt.org/Blobs/ExampleBlob
-
-
-
- AQIDBAU=
- application/pdf
-
-
- PARAMETER
- ExampleFile
-
-
- en-US
- Example File object
-
-
- de
- Beispiel File Element
-
-
-
- ExternalReference
-
-
- GlobalReference
- http://acplt.org/Files/ExampleFile
-
-
-
- /TestFile.pdf
- application/pdf
-
-
- CONSTANT
- ExampleMultiLanguageProperty
-
-
- en-US
- Example MultiLanguageProperty object
-
-
- de
- Beispiel MultiLanguageProperty Element
-
-
-
- ExternalReference
-
-
- GlobalReference
- http://acplt.org/MultiLanguageProperties/ExampleMultiLanguageProperty
-
-
-
-
-
- en-US
- Example value of a MultiLanguageProperty element
-
-
- de
- Beispielwert für ein MultiLanguageProperty-Element
-
-
-
-
- CONSTANT
- ExampleProperty
-
-
- en-US
- Example Property object
-
-
- de
- Beispiel Property Element
-
-
-
- ExternalReference
-
-
- GlobalReference
- http://acplt.org/Properties/ExampleProperty
-
-
-
-
-
- http://acplt.org/Qualifier/ExampleQualifier
- xs:string
-
-
- xs:string
- exampleValue
-
-
- PARAMETER
- ExampleRange
-
-
- en-US
- Example Range object
-
-
- de
- Beispiel Range Element
-
-
-
- ExternalReference
-
-
- GlobalReference
- http://acplt.org/Ranges/ExampleRange
-
-
-
- xs:int
- 0
- 100
-
-
- PARAMETER
- ExampleReferenceElement
-
-
- en-US
- Example Reference Element object
-
-
- de
- Beispiel Reference Element Element
-
-
-
- ExternalReference
-
-
- GlobalReference
- http://acplt.org/ReferenceElements/ExampleReferenceElement
-
-
-
-
- ModelReference
-
-
- Submodel
- http://acplt.org/Test_Submodel
-
-
- Property
- ExampleProperty
-
-
-
-
-
-
-
-
-
- TestSubmodel
-
-
- en-US
- An example submodel for the test application
-
-
- de
- Ein Beispiel-Teilmodell für eine Test-Anwendung
-
-
-
- 9
- 0
-
- https://acplt.org/Test_Submodel_Template
- Template
-
- ExternalReference
-
-
- GlobalReference
- http://acplt.org/SubmodelTemplates/ExampleSubmodel
-
-
-
-
-
- PARAMETER
- ExampleRelationshipElement
-
-
- en-US
- Example RelationshipElement object
-
-
- de
- Beispiel RelationshipElement Element
-
-
-
- ExternalReference
-
-
- GlobalReference
- http://acplt.org/RelationshipElements/ExampleRelationshipElement
-
-
-
-
- ModelReference
-
-
- Submodel
- http://acplt.org/Test_Submodel
-
-
- Property
- ExampleProperty
-
-
-
-
- ModelReference
-
-
- Submodel
- http://acplt.org/Test_Submodel
-
-
- Property
- ExampleProperty
-
-
-
-
-
- PARAMETER
- ExampleAnnotatedRelationshipElement
-
-
- en-US
- Example AnnotatedRelationshipElement object
-
-
- de
- Beispiel AnnotatedRelationshipElement Element
-
-
-
- ExternalReference
-
-
- GlobalReference
- http://acplt.org/RelationshipElements/ExampleAnnotatedRelationshipElement
-
-
-
-
- ModelReference
-
-
- Submodel
- http://acplt.org/Test_Submodel
-
-
- Property
- ExampleProperty
-
-
-
-
- ModelReference
-
-
- Submodel
- http://acplt.org/Test_Submodel
-
-
- Property
- ExampleProperty
-
-
-
-
-
- PARAMETER
- ExampleOperation
-
-
- en-US
- Example Operation object
-
-
- de
- Beispiel Operation Element
-
-
-
- ExternalReference
-
-
- GlobalReference
- http://acplt.org/Operations/ExampleOperation
-
-
-
-
-
-
-
- CONSTANT
- ExamplePropertyInput
-
-
- en-US
- Example Property object
-
-
- de
- Beispiel Property Element
-
-
-
- ExternalReference
-
-
- GlobalReference
- http://acplt.org/Properties/ExamplePropertyInput
-
-
-
- xs:string
-
-
-
-
-
-
-
-
- CONSTANT
- ExamplePropertyOutput
-
-
- en-US
- Example Property object
-
-
- de
- Beispiel Property Element
-
-
-
- ExternalReference
-
-
- GlobalReference
- http://acplt.org/Properties/ExamplePropertyOutput
-
-
-
- xs:string
-
-
-
-
-
-
-
-
- CONSTANT
- ExamplePropertyInOutput
-
-
- en-US
- Example Property object
-
-
- de
- Beispiel Property Element
-
-
-
- ExternalReference
-
-
- GlobalReference
- http://acplt.org/Properties/ExamplePropertyInOutput
-
-
-
- xs:string
-
-
-
-
-
-
- PARAMETER
- ExampleCapability
-
-
- en-US
- Example Capability object
-
-
- de
- Beispiel Capability Element
-
-
-
- ExternalReference
-
-
- GlobalReference
- http://acplt.org/Capabilities/ExampleCapability
-
-
-
-
-
- PARAMETER
- ExampleBasicEventElement
-
-
- en-US
- Example BasicEventElement object
-
-
- de
- Beispiel BasicEventElement Element
-
-
-
- ExternalReference
-
-
- GlobalReference
- http://acplt.org/Events/ExampleBasicEventElement
-
-
-
-
- ModelReference
-
-
- Submodel
- http://acplt.org/Test_Submodel
-
-
- Property
- ExampleProperty
-
-
-
- output
- on
- ExampleTopic
-
- ModelReference
-
-
- Submodel
- http://acplt.org/ExampleMessageBroker
-
-
-
- 2022-11-12T23:50:23.123456+00:00
- PT0.000001S
- P1Y2M3DT4H5M6.123456S
-
-
- PARAMETER
- ExampleSubmodelList
-
-
- en-US
- Example SubmodelElementList object
-
-
- de
- Beispiel SubmodelElementList Element
-
-
-
- ExternalReference
-
-
- GlobalReference
- http://acplt.org/SubmodelElementLists/ExampleSubmodelElementList
-
-
-
- true
-
- ExternalReference
-
-
- GlobalReference
- http://acplt.org/SubmodelElementCollections/ExampleSubmodelElementCollection
-
-
-
- SubmodelElementCollection
-
-
- PARAMETER
-
-
- en-US
- Example SubmodelElementCollection object
-
-
- de
- Beispiel SubmodelElementCollection Element
-
-
-
- ExternalReference
-
-
- GlobalReference
- http://acplt.org/SubmodelElementCollections/ExampleSubmodelElementCollection
-
-
-
-
-
- CONSTANT
- ExampleProperty
-
-
- en-US
- Example Property object
-
-
- de
- Beispiel Property Element
-
-
-
- ExternalReference
-
-
- GlobalReference
- http://acplt.org/Properties/ExampleProperty
-
-
-
- xs:string
-
-
- CONSTANT
- ExampleMultiLanguageProperty
-
-
- en-US
- Example MultiLanguageProperty object
-
-
- de
- Beispiel MultiLanguageProperty Element
-
-
-
- ExternalReference
-
-
- GlobalReference
- http://acplt.org/MultiLanguageProperties/ExampleMultiLanguageProperty
-
-
-
-
-
- PARAMETER
- ExampleRange
-
-
- en-US
- Example Range object
-
-
- de
- Beispiel Range Element
-
-
-
- ExternalReference
-
-
- GlobalReference
- http://acplt.org/Ranges/ExampleRange
-
-
-
- xs:int
- 100
-
-
- PARAMETER
- ExampleRange2
-
-
- en-US
- Example Range object
-
-
- de
- Beispiel Range Element
-
-
-
- ExternalReference
-
-
- GlobalReference
- http://acplt.org/Ranges/ExampleRange
-
-
-
- xs:int
- 0
-
-
- PARAMETER
- ExampleBlob
-
-
- en-US
- Example Blob object
-
-
- de
- Beispiel Blob Element
-
-
-
- ExternalReference
-
-
- GlobalReference
- http://acplt.org/Blobs/ExampleBlob
-
-
-
-
- application/pdf
-
-
- PARAMETER
- ExampleFile
-
-
- en-US
- Example File object
-
-
- de
- Beispiel File Element
-
-
-
- ExternalReference
-
-
- GlobalReference
- http://acplt.org/Files/ExampleFile
-
-
-
- application/pdf
-
-
- PARAMETER
- ExampleReferenceElement
-
-
- en-US
- Example Reference Element object
-
-
- de
- Beispiel Reference Element Element
-
-
-
- ExternalReference
-
-
- GlobalReference
- http://acplt.org/ReferenceElements/ExampleReferenceElement
-
-
-
-
-
-
-
- PARAMETER
-
-
- en-US
- Example SubmodelElementCollection object
-
-
- de
- Beispiel SubmodelElementCollection Element
-
-
-
- ExternalReference
-
-
- GlobalReference
- http://acplt.org/SubmodelElementCollections/ExampleSubmodelElementCollection
-
-
-
-
-
-
-
- PARAMETER
- ExampleSubmodelList2
-
-
- en-US
- Example SubmodelElementList object
-
-
- de
- Beispiel SubmodelElementList Element
-
-
-
- ExternalReference
-
-
- GlobalReference
- http://acplt.org/SubmodelElementLists/ExampleSubmodelElementList
-
-
-
- true
-
- ExternalReference
-
-
- GlobalReference
- http://acplt.org/SubmodelElementCollections/ExampleSubmodelElementCollection
-
-
-
- Capability
-
-
-
-
-
-
- TestConceptDescription
-
-
- en-US
- An example concept description for the test application
-
-
- de
- Ein Beispiel-ConceptDescription für eine Test-Anwendung
-
-
-
-
-
-
- ExternalReference
-
-
- GlobalReference
- https://admin-shell.io/DataSpecificationTemplates/DataSpecificationIEC61360/3/0
-
-
-
-
-
-
-
- de
- Test Specification
-
-
- en-US
- TestSpecification
-
-
-
-
- de
- Test Spec
-
-
- en-US
- TestSpec
-
-
- SpaceUnit
-
- ExternalReference
-
-
- GlobalReference
- http://acplt.org/Units/SpaceUnit
-
-
-
- http://acplt.org/DataSpec/ExampleDef
- SU
- REAL_MEASURE
-
-
- de
- Dies ist eine Data Specification für Testzwecke
-
-
- en-US
- This is a DataSpecification for testing purposes
-
-
- M
-
-
-
- exampleValue
-
- ExternalReference
-
-
- GlobalReference
- http://acplt.org/ValueId/ExampleValueId
-
-
-
-
-
- exampleValue2
-
- ExternalReference
-
-
- GlobalReference
- http://acplt.org/ValueId/ExampleValueId2
-
-
-
-
-
-
- TEST
-
- true
- false
- false
- true
-
-
-
-
-
- 9
- 0
-
- ExternalReference
-
-
- GlobalReference
- http://acplt.org/AdministrativeInformation/Test_ConceptDescription
-
-
-
- http://acplt.org/AdministrativeInformationTemplates/Test_ConceptDescription
-
- https://acplt.org/Test_ConceptDescription
-
-
- ExternalReference
-
-
- GlobalReference
- http://acplt.org/DataSpecifications/ConceptDescriptions/TestConceptDescription
-
-
-
-
-
-
- https://acplt.org/Test_ConceptDescription_Mandatory
-
-
- TestConceptDescription
-
-
- en-US
- An example concept description for the test application
-
-
- de
- Ein Beispiel-ConceptDescription für eine Test-Anwendung
-
-
-
- 9
- 0
-
- https://acplt.org/Test_ConceptDescription_Missing
-
-
-
diff --git a/compliance_tool/test/files/test_demo_full_example_xml_wrong_attribute_aasx/docProps/core.xml b/compliance_tool/test/files/test_demo_full_example_xml_wrong_attribute_aasx/docProps/core.xml
deleted file mode 100644
index 4dc0b87c5..000000000
--- a/compliance_tool/test/files/test_demo_full_example_xml_wrong_attribute_aasx/docProps/core.xml
+++ /dev/null
@@ -1 +0,0 @@
-2020-01-01T00:00:00PyI40AAS Testing FrameworkTest_DescriptionPyI40AAS Testing Framework Compliance Tool2020-01-01T00:00:011.0Test Title2.0.1
\ No newline at end of file
diff --git a/compliance_tool/test/files/test_deserializable_aas_warning.json b/compliance_tool/test/files/test_deserializable_aas_warning.json
deleted file mode 100644
index 35da52c24..000000000
--- a/compliance_tool/test/files/test_deserializable_aas_warning.json
+++ /dev/null
@@ -1,16 +0,0 @@
-{
- "assetAdministrationShells": [
- {
- "id": "https://acplt.org/Test_AssetAdministrationShell",
- "idShort": "TestAssetAdministrationShell",
- "administration": {
- "revision": "0"
- },
- "modelType": "AssetAdministrationShell",
- "assetInformation": {
- "assetKind": "Instance",
- "globalAssetId": "http://acplt.org/TestAsset/"
- }
- }
- ]
-}
\ No newline at end of file
diff --git a/compliance_tool/test/files/test_deserializable_aas_warning.xml b/compliance_tool/test/files/test_deserializable_aas_warning.xml
deleted file mode 100644
index 53f72ab71..000000000
--- a/compliance_tool/test/files/test_deserializable_aas_warning.xml
+++ /dev/null
@@ -1,16 +0,0 @@
-
-
-
-
- TestAssetAdministrationShell
-
- 0
-
- https://acplt.org/Test_AssetAdministrationShell
-
- Instance
- http://acplt.org/TestAsset/
-
-
-
-
\ No newline at end of file
diff --git a/compliance_tool/test/files/test_empty.json b/compliance_tool/test/files/test_empty.json
deleted file mode 100644
index 9e26dfeeb..000000000
--- a/compliance_tool/test/files/test_empty.json
+++ /dev/null
@@ -1 +0,0 @@
-{}
\ No newline at end of file
diff --git a/compliance_tool/test/files/test_empty.xml b/compliance_tool/test/files/test_empty.xml
deleted file mode 100644
index 0329f4b5a..000000000
--- a/compliance_tool/test/files/test_empty.xml
+++ /dev/null
@@ -1,3 +0,0 @@
-
-
-
\ No newline at end of file
diff --git a/compliance_tool/test/files/test_empty_aasx/[Content_Types].xml b/compliance_tool/test/files/test_empty_aasx/[Content_Types].xml
deleted file mode 100644
index 18520c7e8..000000000
--- a/compliance_tool/test/files/test_empty_aasx/[Content_Types].xml
+++ /dev/null
@@ -1 +0,0 @@
-
\ No newline at end of file
diff --git a/compliance_tool/test/files/test_empty_aasx/_rels/.rels b/compliance_tool/test/files/test_empty_aasx/_rels/.rels
deleted file mode 100644
index 9c5de6cf4..000000000
--- a/compliance_tool/test/files/test_empty_aasx/_rels/.rels
+++ /dev/null
@@ -1 +0,0 @@
-
\ No newline at end of file
diff --git a/compliance_tool/test/files/test_empty_aasx/aasx/_rels/aasx-origin.rels b/compliance_tool/test/files/test_empty_aasx/aasx/_rels/aasx-origin.rels
deleted file mode 100644
index 7b813240b..000000000
--- a/compliance_tool/test/files/test_empty_aasx/aasx/_rels/aasx-origin.rels
+++ /dev/null
@@ -1 +0,0 @@
-
\ No newline at end of file
diff --git a/compliance_tool/test/files/test_empty_aasx/aasx/aasx-origin b/compliance_tool/test/files/test_empty_aasx/aasx/aasx-origin
deleted file mode 100644
index e69de29bb..000000000
diff --git a/compliance_tool/test/files/test_empty_aasx/docProps/core.xml b/compliance_tool/test/files/test_empty_aasx/docProps/core.xml
deleted file mode 100644
index 344bc075a..000000000
--- a/compliance_tool/test/files/test_empty_aasx/docProps/core.xml
+++ /dev/null
@@ -1 +0,0 @@
-2020-09-25T16:07:16.936996PyI40AAS Testing Framework
\ No newline at end of file
diff --git a/compliance_tool/test/files/test_not_deserializable.json b/compliance_tool/test/files/test_not_deserializable.json
deleted file mode 100644
index 9a0c369d9..000000000
--- a/compliance_tool/test/files/test_not_deserializable.json
+++ /dev/null
@@ -1,5 +0,0 @@
-{
- "assetAdministrationShells": [],
- "submodels": []
- "conceptDescriptions": []
-}
\ No newline at end of file
diff --git a/compliance_tool/test/files/test_not_deserializable_aas.json b/compliance_tool/test/files/test_not_deserializable_aas.json
deleted file mode 100644
index cf239e8b7..000000000
--- a/compliance_tool/test/files/test_not_deserializable_aas.json
+++ /dev/null
@@ -1,15 +0,0 @@
-{
- "assetAdministrationShells": [
- {
- "id": "https://acplt.org/Test_AssetAdministrationShell",
- "idShort": "TestAssetAdministrationShell",
- "modelType": "Test",
- "assetInformation": {
- "assetKind": "Instance",
- "globalAssetId": "http://acplt.org/Test_Asset/"
- }
- }
- ],
- "submodels": [],
- "conceptDescriptions": []
-}
\ No newline at end of file
diff --git a/compliance_tool/test/files/test_not_deserializable_aas.xml b/compliance_tool/test/files/test_not_deserializable_aas.xml
deleted file mode 100644
index d673e80ec..000000000
--- a/compliance_tool/test/files/test_not_deserializable_aas.xml
+++ /dev/null
@@ -1,13 +0,0 @@
-
-
-
-
-
- https://acplt.org/Test_Submodel2_Mandatory
- Instance
-
-
-
-
-
-
\ No newline at end of file
diff --git a/compliance_tool/test/test_aas_compliance_tool.py b/compliance_tool/test/test_aas_compliance_tool.py
index cb631cf9c..df314888d 100644
--- a/compliance_tool/test/test_aas_compliance_tool.py
+++ b/compliance_tool/test/test_aas_compliance_tool.py
@@ -1,4 +1,4 @@
-# Copyright (c) 2025 the Eclipse BaSyx Authors
+# Copyright (c) 2026 the Eclipse BaSyx Authors
#
# This program and the accompanying materials are made available under the terms of the MIT License, available in
# the LICENSE file of this project.
@@ -6,13 +6,15 @@
# SPDX-License-Identifier: MIT
import datetime
import hashlib
+import io
import os
-import subprocess
-import sys
+import tempfile
import unittest
-import io
+from contextlib import redirect_stderr, redirect_stdout
+from io import StringIO
+from unittest.mock import patch, ANY
-import tempfile
+from aas_compliance_tool.cli import main, parse_cli_arguments
from basyx.aas import model
from basyx.aas.adapter import aasx
from basyx.aas.adapter.json import read_aas_json_file
@@ -21,310 +23,151 @@
from basyx.aas.examples.data._helper import AASDataChecker
-def _run_compliance_tool(*compliance_tool_args, **kwargs) -> subprocess.CompletedProcess:
- """
- This function runs the compliance tool using subprocess.run()
- and sets the stdout and stderr parameters of subprocess.run() to PIPE.
- Positional arguments are passed to the compliance tool, while keyword arguments are passed to subprocess.run().
- """
- env = os.environ.copy()
- parent_dir = os.path.join(
- os.path.dirname(os.path.dirname(__file__)),
- 'aas_compliance_tool'
- )
- env["PYTHONPATH"] = parent_dir + os.pathsep + env.get("PYTHONPATH", "")
- compliance_tool_path = os.path.join(
- os.path.dirname(os.path.dirname(__file__)),
- 'aas_compliance_tool',
- 'cli.py'
- )
- return subprocess.run([sys.executable, compliance_tool_path] + list(compliance_tool_args), stdout=subprocess.PIPE,
- stderr=subprocess.PIPE, env=env, **kwargs)
-
-
-class ComplianceToolTest(unittest.TestCase):
- def test_parse_args(self) -> None:
- test_file_path = os.path.join(os.path.dirname(__file__), 'files')
-
- # test schema check
- output = _run_compliance_tool("s")
- self.assertNotEqual(0, output.returncode)
- self.assertIn('error: the following arguments are required: file_1', str(output.stderr))
-
- output = _run_compliance_tool("s", os.path.join(test_file_path, "test_demo_full_example.json"))
- self.assertNotEqual(0, output.returncode)
- self.assertIn('error: one of the arguments --json --xml is required', str(output.stderr))
-
- # test deserialisation check
- output = _run_compliance_tool("d")
- self.assertNotEqual(0, output.returncode)
- self.assertIn('error: the following arguments are required: file_1', str(output.stderr))
-
- output = _run_compliance_tool("d", os.path.join(test_file_path, "test_demo_full_example.json"))
- self.assertNotEqual(0, output.returncode)
- self.assertIn('error: one of the arguments --json --xml is required', str(output.stderr))
-
- output = _run_compliance_tool("d", os.path.join(test_file_path, "test_demo_full_example.json"), "--aasx")
- self.assertNotEqual(0, output.returncode)
- self.assertIn('error: one of the arguments --json --xml is required', str(output.stderr))
-
- # test example check
- output = _run_compliance_tool("e")
- self.assertNotEqual(0, output.returncode)
- self.assertIn('error: the following arguments are required: file_1', str(output.stderr))
-
- output = _run_compliance_tool("e", os.path.join(test_file_path, "test_demo_full_example.json"))
- self.assertNotEqual(0, output.returncode)
- self.assertIn('error: one of the arguments --json --xml is required', str(output.stderr))
-
- output = _run_compliance_tool("e", os.path.join(test_file_path, "test_demo_full_example.json"), "--aasx")
- self.assertNotEqual(0, output.returncode)
- self.assertIn('error: one of the arguments --json --xml is required', str(output.stderr))
-
- # test file check
- output = _run_compliance_tool("f")
- self.assertNotEqual(0, output.returncode)
- self.assertIn('error: the following arguments are required: file_1', str(output.stderr))
-
- output = _run_compliance_tool("f", os.path.join(test_file_path, "test_demo_full_example.json"))
- self.assertNotEqual(0, output.returncode)
- self.assertIn('error: one of the arguments --json --xml is required', str(output.stderr))
-
- output = _run_compliance_tool("f", os.path.join(test_file_path, "test_demo_full_example.json"), "--aasx")
- self.assertNotEqual(0, output.returncode)
- self.assertIn('error: one of the arguments --json --xml is required', str(output.stderr))
-
- output = _run_compliance_tool("f", os.path.join(test_file_path, "test_demo_full_example.json"), "--json")
- self.assertNotEqual(0, output.returncode)
- self.assertIn('error: f or files requires two file path', str(output.stderr))
-
- output = _run_compliance_tool("f", os.path.join(test_file_path, "test_demo_full_example.json"),
- os.path.join(test_file_path, "test_demo_full_example.json"))
- self.assertNotEqual(0, output.returncode)
- self.assertIn('error: one of the arguments --json --xml is required', str(output.stderr))
-
- # test verbose
- output = _run_compliance_tool("e", os.path.join(test_file_path, "test_demo_full_example.json"), "--json", "-v")
- self.assertEqual(0, output.returncode)
- self.assertNotIn('ERROR', str(output.stderr))
- self.assertNotIn('INFO', str(output.stdout))
-
- output = _run_compliance_tool("e", os.path.join(test_file_path, "test_demo_full_example.json"), "--json", "-v",
- "-v")
- self.assertEqual(0, output.returncode)
- self.assertNotIn('ERROR', str(output.stderr))
- self.assertIn('INFO', str(output.stdout))
-
- # test quite
- output = _run_compliance_tool("e", os.path.join(test_file_path, "test_demo_full_example.json"), "--json", "-q")
- self.assertEqual(0, output.returncode)
- self.assertEqual("b''", str(output.stdout))
-
- # test logfile
- output = _run_compliance_tool("e", os.path.join(test_file_path, "test_demo_full_example.json"), "--json", "-l")
- self.assertNotEqual(0, output.returncode)
- self.assertIn('error: argument -l/--logfile: expected one argument', str(output.stderr))
-
- # todo: add test for correct logfile
-
- def test_json_create_example(self) -> None:
- file, filename = tempfile.mkstemp(suffix=".json")
- os.close(file)
- output = _run_compliance_tool("c", filename, "--json")
-
- self.assertEqual(0, output.returncode)
- self.assertIn('SUCCESS: Create example data', str(output.stdout))
- self.assertIn('SUCCESS: Open file', str(output.stdout))
- self.assertIn('SUCCESS: Write data to file', str(output.stdout))
-
- with open(filename, "r", encoding='utf-8-sig') as f:
- json_object_store = read_aas_json_file(f, failsafe=False)
- data = create_example()
+class ComplianceToolParserTest(unittest.TestCase):
+ def test_json_xml_mutually_exclusive(self):
+ parser = parse_cli_arguments()
+ with self.assertRaises(SystemExit) as cm:
+ with redirect_stderr(StringIO()):
+ parser.parse_args(["d", "f.json", "--json", "--xml"])
+ self.assertEqual(2, cm.exception.code)
+
+
+class ComplianceToolActionTest(unittest.TestCase):
+ def setUp(self):
+ self._json_patcher = patch('aas_compliance_tool.cli.compliance_tool_json')
+ self._xml_patcher = patch('aas_compliance_tool.cli.compliance_tool_xml')
+ self._aasx_patcher = patch('aas_compliance_tool.cli.compliance_tool_aasx')
+ self.mock_json = self._json_patcher.start()
+ self.mock_xml = self._xml_patcher.start()
+ self.mock_aasx = self._aasx_patcher.start()
+
+ def tearDown(self):
+ self._json_patcher.stop()
+ self._xml_patcher.stop()
+ self._aasx_patcher.stop()
+
+ def _call_main(self, args):
+ with patch('sys.argv', ['compliance_tool'] + args):
+ with redirect_stdout(StringIO()):
+ main()
+
+ def test_route_d_json(self):
+ self._call_main(['d', 'f.json', '--json'])
+ self.mock_json.check_deserialization.assert_called_once_with('f.json', ANY)
+
+ def test_route_d_xml(self):
+ self._call_main(['d', 'f.xml', '--xml'])
+ self.mock_xml.check_deserialization.assert_called_once_with('f.xml', ANY)
+
+ def test_route_d_aasx(self):
+ self._call_main(['d', 'f.aasx', '--json', '--aasx'])
+ self.mock_aasx.check_deserialization.assert_called_once_with('f.aasx', ANY)
+
+ def test_route_e_json(self):
+ self._call_main(['e', 'f.json', '--json'])
+ self.mock_json.check_aas_example.assert_called_once_with('f.json', ANY, check_extensions=True)
+
+ def test_route_e_xml(self):
+ self._call_main(['e', 'f.xml', '--xml'])
+ self.mock_xml.check_aas_example.assert_called_once_with('f.xml', ANY, check_extensions=True)
+
+ def test_route_e_aasx(self):
+ self._call_main(['e', 'f.aasx', '--json', '--aasx'])
+ self.mock_aasx.check_aas_example.assert_called_once_with('f.aasx', ANY, check_extensions=True)
+
+ def test_route_f_json(self):
+ self._call_main(['f', 'a.json', 'b.json', '--json'])
+ self.mock_json.check_json_files_equivalence.assert_called_once_with('a.json', 'b.json', ANY,
+ check_extensions=True)
+
+ def test_route_f_xml(self):
+ self._call_main(['f', 'a.xml', 'b.xml', '--xml'])
+ self.mock_xml.check_xml_files_equivalence.assert_called_once_with('a.xml', 'b.xml', ANY,
+ check_extensions=True)
+
+ def test_route_f_aasx(self):
+ self._call_main(['f', 'a.aasx', 'b.aasx', '--json', '--aasx'])
+ self.mock_aasx.check_aasx_files_equivalence.assert_called_once_with('a.aasx', 'b.aasx', ANY,
+ check_extensions=True)
+
+ def test_route_f_missing_file2(self):
+ with self.assertRaises(SystemExit) as cm:
+ with redirect_stderr(StringIO()):
+ self._call_main(['f', 'f.json', '--json'])
+
+ self.assertEqual(2, cm.exception.code)
+
+
+class ComplianceToolCreateTests(unittest.TestCase):
+ def _call_main(self, args) -> str:
+ buf = StringIO()
+ with patch('sys.argv', ['compliance_tool'] + args):
+ with redirect_stdout(buf):
+ main()
+ return buf.getvalue()
+
+ def test_create_json(self):
+ with tempfile.NamedTemporaryFile(suffix=".json") as tf:
+
+ output = self._call_main(['c', tf.name, '--json'])
+
+ self.assertIn('SUCCESS: Create example data', output)
+ self.assertIn('SUCCESS: Open file', output)
+ self.assertIn('SUCCESS: Write data to file', output)
+
+ json_store = read_aas_json_file(tf, failsafe=False)
checker = AASDataChecker(raise_immediately=True)
- checker.check_object_store(json_object_store, data)
- os.unlink(filename)
-
- def test_json_deserialization(self) -> None:
- test_file_path = os.path.join(os.path.dirname(__file__), 'files')
-
- output = _run_compliance_tool("d", os.path.join(test_file_path, "test_demo_full_example.json"), "--json")
- self.assertEqual(0, output.returncode)
- self.assertIn('SUCCESS: Open file', str(output.stdout))
- self.assertIn('SUCCESS: Read file and check if it is deserializable', str(output.stdout))
-
- def test_json_example(self) -> None:
- test_file_path = os.path.join(os.path.dirname(__file__), 'files')
-
- output = _run_compliance_tool("e", os.path.join(test_file_path, "test_demo_full_example.json"), "--json")
- self.assertEqual(0, output.returncode)
- self.assertIn('SUCCESS: Open file', str(output.stdout))
- self.assertIn('SUCCESS: Read file and check if it is deserializable', str(output.stdout))
- self.assertIn('SUCCESS: Check if data is equal to example data', str(output.stdout))
-
- def test_json_file(self) -> None:
- test_file_path = os.path.join(os.path.dirname(__file__), 'files')
-
- output = _run_compliance_tool("f", os.path.join(test_file_path, "test_demo_full_example.json"),
- os.path.join(test_file_path, "test_demo_full_example.json"), "--json")
-
- self.assertEqual(0, output.returncode)
- self.assertIn('SUCCESS: Open first file', str(output.stdout))
- self.assertIn('SUCCESS: Read file', str(output.stdout))
- self.assertIn('SUCCESS: Open second file', str(output.stdout))
- self.assertIn('SUCCESS: Read file', str(output.stdout))
- self.assertIn('SUCCESS: Check if data in files are equal', str(output.stdout))
-
- def test_xml_create_example(self) -> None:
- file, filename = tempfile.mkstemp(suffix=".xml")
- os.close(file)
- output = _run_compliance_tool("c", filename, "--xml")
- self.assertEqual(0, output.returncode)
- self.assertIn('SUCCESS: Create example data', str(output.stdout))
- self.assertIn('SUCCESS: Open file', str(output.stdout))
- self.assertIn('SUCCESS: Write data to file', str(output.stdout))
-
- with open(filename, "rb") as f:
- xml_object_store = read_aas_xml_file(f, failsafe=False)
- data = create_example()
+ checker.check_identifiable_store(json_store, create_example())
+
+ def test_create_xml(self):
+ with tempfile.NamedTemporaryFile(suffix=".json") as tf:
+ output = self._call_main(['c', tf.name, '--xml'])
+
+ self.assertIn('SUCCESS: Create example data', output)
+ self.assertIn('SUCCESS: Open file', output)
+ self.assertIn('SUCCESS: Write data to file', output)
+ xml_store = read_aas_xml_file(tf, failsafe=False)
checker = AASDataChecker(raise_immediately=True)
- checker.check_object_store(xml_object_store, data)
- os.unlink(filename)
-
- def test_xml_deseralization(self) -> None:
- test_file_path = os.path.join(os.path.dirname(__file__), 'files')
-
- output = _run_compliance_tool("d", os.path.join(test_file_path, "test_demo_full_example.xml"), "--xml")
- self.assertEqual(0, output.returncode)
- self.assertIn('SUCCESS: Open file', str(output.stdout))
- self.assertIn('SUCCESS: Read file and check if it is deserializable', str(output.stdout))
-
- def test_xml_example(self) -> None:
- test_file_path = os.path.join(os.path.dirname(__file__), 'files')
-
- output = _run_compliance_tool("e", os.path.join(test_file_path, "test_demo_full_example.xml"), "--xml")
- self.assertEqual(0, output.returncode)
- self.assertIn('SUCCESS: Open file', str(output.stdout))
- self.assertIn('SUCCESS: Read file and check if it is deserializable', str(output.stdout))
- self.assertIn('SUCCESS: Check if data is equal to example data', str(output.stdout))
-
- def test_xml_file(self) -> None:
- test_file_path = os.path.join(os.path.dirname(__file__), 'files')
-
- output = _run_compliance_tool("f", os.path.join(test_file_path, "test_demo_full_example.xml"),
- os.path.join(test_file_path, "test_demo_full_example.xml"), "--xml")
- self.assertEqual(0, output.returncode)
- self.assertIn('SUCCESS: Open first file', str(output.stdout))
- self.assertIn('SUCCESS: Read file', str(output.stdout))
- self.assertIn('SUCCESS: Open second file', str(output.stdout))
- self.assertIn('SUCCESS: Read file', str(output.stdout))
- self.assertIn('SUCCESS: Check if data in files are equal', str(output.stdout))
-
- def test_aasx_create_example(self) -> None:
- file, filename = tempfile.mkstemp(suffix=".aasx")
- os.close(file)
- output = _run_compliance_tool("c", filename, "--xml", "--aasx")
- self.assertEqual(0, output.returncode)
- self.assertIn('SUCCESS: Create example data', str(output.stdout))
- self.assertIn('SUCCESS: Open file', str(output.stdout))
- self.assertIn('SUCCESS: Write data to file', str(output.stdout))
-
- # Read AASX file
- new_data: model.DictObjectStore[model.Identifiable] = model.DictObjectStore()
- new_files = aasx.DictSupplementaryFileContainer()
- with aasx.AASXReader(filename) as reader:
- reader.read_into(new_data, new_files)
- new_cp = reader.get_core_properties()
-
- # Check AAS objects
- assert (isinstance(new_cp.created, datetime.datetime))
+ checker.check_identifiable_store(xml_store, create_example())
+
+ def test_create_aasx_xml(self):
+ with tempfile.NamedTemporaryFile(suffix=".json") as tf:
+ output = self._call_main(['c', tf.name, '--xml', '--aasx'])
+
+ self.assertIn('SUCCESS: Create example data', output)
+ self.assertIn('SUCCESS: Open file', output)
+ self.assertIn('SUCCESS: Write data to file', output)
+
+ new_data: model.DictIdentifiableStore = model.DictIdentifiableStore()
+ new_files = aasx.DictSupplementaryFileContainer()
+ with aasx.AASXReader(tf) as reader:
+ reader.read_into(new_data, new_files)
+ new_cp = reader.get_core_properties()
+
self.assertIsInstance(new_cp.created, datetime.datetime)
self.assertAlmostEqual(new_cp.created, datetime.datetime(2020, 1, 1, 0, 0, 0),
delta=datetime.timedelta(milliseconds=20))
self.assertEqual(new_cp.creator, "Eclipse BaSyx Python Testing Framework")
self.assertEqual(new_cp.description, "Test_Description")
self.assertEqual(new_cp.lastModifiedBy, "Eclipse BaSyx Python Testing Framework Compliance Tool")
- assert (isinstance(new_cp.modified, datetime.datetime))
+ self.assertIsInstance(new_cp.modified, datetime.datetime)
self.assertAlmostEqual(new_cp.modified, datetime.datetime(2020, 1, 1, 0, 0, 1),
delta=datetime.timedelta(milliseconds=20))
self.assertEqual(new_cp.revision, "1.0")
self.assertEqual(new_cp.version, "2.0.1")
self.assertEqual(new_cp.title, "Test Title")
-
- # Check files
self.assertEqual(new_files.get_content_type("/TestFile.pdf"), "application/pdf")
file_content = io.BytesIO()
new_files.write_file("/TestFile.pdf", file_content)
self.assertEqual(hashlib.sha1(file_content.getvalue()).hexdigest(),
"78450a66f59d74c073bf6858db340090ea72a8b1")
- os.unlink(filename)
-
- def test_aasx_deseralization_xml(self) -> None:
- test_file_path = os.path.join(os.path.dirname(__file__), 'files')
-
- output = _run_compliance_tool("d", os.path.join(test_file_path, "test_demo_full_example_xml.aasx"), "--xml",
- "--aasx")
- self.assertEqual(0, output.returncode)
- self.assertIn('SUCCESS: Open file', str(output.stdout))
- self.assertIn('SUCCESS: Read file', str(output.stdout))
-
- def test_aasx_example_xml(self) -> None:
- test_file_path = os.path.join(os.path.dirname(__file__), 'files')
-
- output = _run_compliance_tool("e", os.path.join(test_file_path, "test_demo_full_example_xml.aasx"), "--xml",
- "--aasx")
- self.assertEqual(0, output.returncode)
- self.assertIn('SUCCESS: Open file', str(output.stdout))
- self.assertIn('SUCCESS: Read file', str(output.stdout))
- self.assertIn('SUCCESS: Check if data is equal to example data', str(output.stdout))
-
- def test_aasx_deseralization_json(self) -> None:
- test_file_path = os.path.join(os.path.dirname(__file__), 'files')
-
- output = _run_compliance_tool("d", os.path.join(test_file_path, "test_demo_full_example_json.aasx"), "--json",
- "--aasx")
- self.assertEqual(0, output.returncode)
- self.assertIn('SUCCESS: Open file', str(output.stdout))
- self.assertIn('SUCCESS: Read file', str(output.stdout))
-
- def test_aasx_example_json(self) -> None:
- test_file_path = os.path.join(os.path.dirname(__file__), 'files')
-
- output = _run_compliance_tool("e", os.path.join(test_file_path, "test_demo_full_example_json.aasx"), "--json",
- "--aasx")
- self.assertEqual(0, output.returncode)
- self.assertIn('SUCCESS: Open file', str(output.stdout))
- self.assertIn('SUCCESS: Read file', str(output.stdout))
- self.assertIn('SUCCESS: Check if data is equal to example data', str(output.stdout))
-
- def test_aasx_file(self) -> None:
- test_file_path = os.path.join(os.path.dirname(__file__), 'files')
-
- output = _run_compliance_tool("f", os.path.join(test_file_path, "test_demo_full_example_xml.aasx"),
- os.path.join(test_file_path, "test_demo_full_example_xml.aasx"), "--xml",
- "--aasx")
- self.assertEqual(0, output.returncode)
- self.assertIn('SUCCESS: Open first file', str(output.stdout))
- self.assertIn('SUCCESS: Read file', str(output.stdout))
- self.assertIn('SUCCESS: Open second file', str(output.stdout))
- self.assertIn('SUCCESS: Read file', str(output.stdout))
- self.assertIn('SUCCESS: Check if data in files are equal', str(output.stdout))
-
- def test_logfile(self) -> None:
- file, filename = tempfile.mkstemp(suffix=".json")
- file2, filename2 = tempfile.mkstemp(suffix=".log")
- os.close(file)
- os.close(file2)
- output = _run_compliance_tool("c", filename, "--json", "-v", "-v", "-l", filename2)
- self.assertEqual(0, output.returncode)
- self.assertIn('SUCCESS: Create example data', str(output.stdout))
- self.assertIn('SUCCESS: Open file', str(output.stdout))
- self.assertIn('SUCCESS: Write data to file', str(output.stdout))
-
- with open(filename2, "r", encoding='utf-8-sig') as f:
- data = f.read()
- self.assertIn('SUCCESS: Create example data', data)
- self.assertIn('SUCCESS: Open file', data)
- self.assertIn('SUCCESS: Write data to file', data)
- os.unlink(filename)
- os.unlink(filename2)
+ def test_logfile(self):
+ with tempfile.NamedTemporaryFile(suffix=".json") as tf:
+ with tempfile.NamedTemporaryFile("w+", encoding='utf-8-sig', suffix=".log") as lf:
+
+ output = self._call_main(['c', tf.name, '--json', '-l', lf.name])
+
+ logfile_content = lf.read()
+ # print() appends a newline that file.write() does not
+ self.assertEqual(logfile_content, output.rstrip('\n'))
diff --git a/compliance_tool/test/test_compliance_check_aasx.py b/compliance_tool/test/test_compliance_check_aasx.py
index 953d4e35e..b6e3ffbeb 100644
--- a/compliance_tool/test/test_compliance_check_aasx.py
+++ b/compliance_tool/test/test_compliance_check_aasx.py
@@ -1,173 +1,369 @@
-# Copyright (c) 2025 the Eclipse BaSyx Authors
+# Copyright (c) 2026 the Eclipse BaSyx Authors
#
# This program and the accompanying materials are made available under the terms of the MIT License, available in
# the LICENSE file of this project.
#
# SPDX-License-Identifier: MIT
-import os
import unittest
+from unittest import mock
+from ._test_helper import create_example_aas_core_properties, create_read_into_mock
from aas_compliance_tool import compliance_check_aasx as compliance_tool
from aas_compliance_tool.state_manager import ComplianceToolStateManager, Status
+from basyx.aas.examples.data._helper import CheckResult
+
class ComplianceToolAASXTest(unittest.TestCase):
- def test_check_deserialization(self) -> None:
+
+ def test_check_deserialization_no_file(self) -> None:
+ manager = ComplianceToolStateManager()
+
+ compliance_tool.check_deserialization("", manager)
+ self.assertEqual(2, len(manager.steps))
+ self.assertEqual(Status.FAILED, manager.steps[0].status)
+ self.assertEqual(Status.NOT_EXECUTED, manager.steps[1].status)
+ self.assertIn("No such file or directory", manager.format_step(0, verbose_level=1))
+
+ @mock.patch("basyx.aas.adapter.aasx.AASXReader", autospec=True)
+ def test_check_deserialization_open_raises(self, mock_aasx_reader: mock.MagicMock) -> None:
manager = ComplianceToolStateManager()
- script_dir = os.path.dirname(__file__)
- file_path_1 = os.path.join(script_dir, 'files/test_not_found.aasx')
- compliance_tool.check_deserialization(file_path_1, manager)
+ mock_aasx_reader.side_effect = ValueError("Test error!")
+ compliance_tool.check_deserialization("", manager)
+
self.assertEqual(2, len(manager.steps))
self.assertEqual(Status.FAILED, manager.steps[0].status)
- # we should expect a FileNotFound error here since the file does not exist and that is the first error
- # aasx.py will throw if you try to open a file that does not exist.
- self.assertIn("No such file or directory:", manager.format_step(0, verbose_level=1))
self.assertEqual(Status.NOT_EXECUTED, manager.steps[1].status)
- # Todo add more tests for checking wrong aasx files
+ @mock.patch("basyx.aas.adapter.aasx.AASXReader", autospec=True)
+ def test_check_deserialization_read_raises(self, mock_aasx_reader: mock.MagicMock) -> None:
+ manager = ComplianceToolStateManager()
+
+ mock_aasx_reader.return_value.read_into.side_effect = ValueError("Test error!")
+ compliance_tool.check_deserialization("", manager)
- manager.steps = []
- file_path_5 = os.path.join(script_dir, 'files/test_demo_full_example_xml.aasx')
- compliance_tool.check_deserialization(file_path_5, manager)
self.assertEqual(2, len(manager.steps))
self.assertEqual(Status.SUCCESS, manager.steps[0].status)
- self.assertEqual(Status.SUCCESS, manager.steps[1].status)
+ self.assertEqual(Status.FAILED, manager.steps[1].status)
+
+ @mock.patch("basyx.aas.adapter.aasx.AASXReader", autospec=True)
+ def test_check_deserialization_success(self, mock_aasx_reader: mock.MagicMock) -> None:
+ manager = ComplianceToolStateManager()
+
+ compliance_tool.check_deserialization("", manager)
- manager.steps = []
- file_path_5 = os.path.join(script_dir, 'files/test_demo_full_example_json.aasx')
- compliance_tool.check_deserialization(file_path_5, manager)
self.assertEqual(2, len(manager.steps))
self.assertEqual(Status.SUCCESS, manager.steps[0].status)
self.assertEqual(Status.SUCCESS, manager.steps[1].status)
- def test_check_aas_example(self) -> None:
+ @mock.patch("basyx.aas.adapter.aasx.AASXReader", autospec=True)
+ @mock.patch("aas_compliance_tool.compliance_check_aasx.AASDataChecker", autospec=True)
+ def test_check_aas_example_fail_on_open(self, mock_data_checker: mock.MagicMock,
+ mock_aasx_reader: mock.MagicMock) -> None:
+ manager = ComplianceToolStateManager()
+
+ mock_aasx_reader.side_effect = ValueError("Test error!")
+ compliance_tool.check_aas_example("", manager)
+
+ self.assertEqual(5, len(manager.steps))
+ self.assertEqual(Status.FAILED, manager.steps[0].status)
+ self.assertEqual(Status.NOT_EXECUTED, manager.steps[1].status)
+ self.assertEqual(Status.NOT_EXECUTED, manager.steps[2].status)
+ self.assertEqual(Status.NOT_EXECUTED, manager.steps[3].status)
+ self.assertEqual(Status.NOT_EXECUTED, manager.steps[4].status)
+
+ @mock.patch("basyx.aas.adapter.aasx.AASXReader", autospec=True)
+ @mock.patch("aas_compliance_tool.compliance_check_aasx.AASDataChecker", autospec=True)
+ def test_check_aas_example_fail_on_read(self, mock_data_checker: mock.MagicMock,
+ mock_aasx_reader: mock.MagicMock) -> None:
+ manager = ComplianceToolStateManager()
+
+ mock_aasx_reader.return_value.read_into.side_effect = ValueError("Test error!")
+ compliance_tool.check_aas_example("", manager)
+
+ self.assertEqual(5, len(manager.steps))
+ self.assertEqual(Status.SUCCESS, manager.steps[0].status)
+ self.assertEqual(Status.FAILED, manager.steps[1].status)
+ self.assertEqual(Status.NOT_EXECUTED, manager.steps[2].status)
+ self.assertEqual(Status.NOT_EXECUTED, manager.steps[3].status)
+ self.assertEqual(Status.NOT_EXECUTED, manager.steps[4].status)
+
+ @mock.patch("basyx.aas.adapter.aasx.AASXReader", autospec=True)
+ @mock.patch("aas_compliance_tool.compliance_check_aasx.AASDataChecker", autospec=True)
+ def test_check_aas_example_fail_on_data_check(self, mock_data_checker: mock.MagicMock,
+ mock_aasx_reader: mock.MagicMock) -> None:
+ manager = ComplianceToolStateManager()
+
+ failed = [CheckResult("Expected Behavior", False, dict())]
+ mock_data_checker.return_value.checks = failed
+ type(mock_data_checker.return_value).failed_checks = mock.PropertyMock(side_effect=lambda: iter(failed))
+ compliance_tool.check_aas_example("", manager)
+
+ self.assertEqual(5, len(manager.steps))
+ self.assertEqual(Status.SUCCESS, manager.steps[0].status)
+ self.assertEqual(Status.SUCCESS, manager.steps[1].status)
+ self.assertEqual(Status.FAILED, manager.steps[2].status)
+ self.assertIn("Expected Behavior", manager.format_step(2, verbose_level=1))
+ self.assertEqual(Status.NOT_EXECUTED, manager.steps[3].status)
+ self.assertEqual(Status.NOT_EXECUTED, manager.steps[4].status)
+
+ @mock.patch("basyx.aas.adapter.aasx.AASXReader", autospec=True)
+ @mock.patch("aas_compliance_tool.compliance_check_aasx.AASDataChecker", autospec=True)
+ def test_check_aas_example_fail_on_core_properties(self, mock_data_checker: mock.MagicMock,
+ mock_aasx_reader: mock.MagicMock) -> None:
+ manager = ComplianceToolStateManager()
+
+ mock_data_checker.return_value.checks = []
+ type(mock_data_checker.return_value).failed_checks = mock.PropertyMock(side_effect=lambda: iter([]))
+ mock_aasx_reader.return_value.read_into.side_effect = create_read_into_mock(file='TestFile')
+ wrong_cp = create_example_aas_core_properties()
+ wrong_cp.creator = "Wrong Creator"
+ mock_aasx_reader.return_value.get_core_properties.return_value = wrong_cp
+ compliance_tool.check_aas_example("", manager)
+
+ self.assertEqual(5, len(manager.steps))
+ self.assertEqual(Status.SUCCESS, manager.steps[0].status)
+ self.assertEqual(Status.SUCCESS, manager.steps[1].status)
+ self.assertEqual(Status.SUCCESS, manager.steps[2].status)
+ self.assertEqual(Status.FAILED, manager.steps[3].status)
+ self.assertIn("Wrong Creator", manager.format_step(3, verbose_level=1))
+ self.assertEqual(Status.SUCCESS, manager.steps[4].status)
+
+ @mock.patch("basyx.aas.adapter.aasx.AASXReader", autospec=True)
+ @mock.patch("aas_compliance_tool.compliance_check_aasx.AASDataChecker", autospec=True)
+ def test_check_aas_example_fail_on_file_missing(self, mock_data_checker: mock.MagicMock,
+ mock_aasx_reader: mock.MagicMock) -> None:
+ manager = ComplianceToolStateManager()
+
+ mock_data_checker.return_value.checks = []
+ type(mock_data_checker.return_value).failed_checks = mock.PropertyMock(side_effect=lambda: iter([]))
+ mock_aasx_reader.return_value.read_into.side_effect = create_read_into_mock(file=None)
+ mock_aasx_reader.return_value.get_core_properties.return_value = create_example_aas_core_properties()
+ compliance_tool.check_aas_example("", manager)
+
+ self.assertEqual(5, len(manager.steps))
+ self.assertEqual(Status.SUCCESS, manager.steps[0].status)
+ self.assertEqual(Status.SUCCESS, manager.steps[1].status)
+ self.assertEqual(Status.SUCCESS, manager.steps[2].status)
+ self.assertEqual(Status.SUCCESS, manager.steps[3].status)
+ self.assertEqual(Status.FAILED, manager.steps[4].status)
+ self.assertIn("/TestFile.pdf", manager.format_step(4, verbose_level=1))
+
+ @mock.patch("basyx.aas.adapter.aasx.AASXReader", autospec=True)
+ @mock.patch("aas_compliance_tool.compliance_check_aasx.AASDataChecker", autospec=True)
+ def test_check_aas_example_fail_on_file_check(self, mock_data_checker: mock.MagicMock,
+ mock_aasx_reader: mock.MagicMock) -> None:
manager = ComplianceToolStateManager()
- script_dir = os.path.dirname(__file__)
- file_path_2 = os.path.join(script_dir, 'files/test_demo_full_example_xml.aasx')
- compliance_tool.check_aas_example(file_path_2, manager)
- self.assertEqual(4, len(manager.steps))
+ mock_data_checker.return_value.checks = []
+ type(mock_data_checker.return_value).failed_checks = mock.PropertyMock(side_effect=lambda: iter([]))
+ mock_aasx_reader.return_value.read_into.side_effect = create_read_into_mock(file='TestFileWrong')
+ mock_aasx_reader.return_value.get_core_properties.return_value = create_example_aas_core_properties()
+ compliance_tool.check_aas_example("", manager)
+
+ self.assertEqual(5, len(manager.steps))
self.assertEqual(Status.SUCCESS, manager.steps[0].status)
self.assertEqual(Status.SUCCESS, manager.steps[1].status)
self.assertEqual(Status.SUCCESS, manager.steps[2].status)
self.assertEqual(Status.SUCCESS, manager.steps[3].status)
+ self.assertEqual(Status.FAILED, manager.steps[4].status)
+ self.assertIn("/TestFile.pdf", manager.format_step(4, verbose_level=1))
+
+ @mock.patch("basyx.aas.adapter.aasx.AASXReader", autospec=True)
+ @mock.patch("aas_compliance_tool.compliance_check_aasx.AASDataChecker", autospec=True)
+ def test_check_aas_example_success(self, mock_data_checker: mock.MagicMock,
+ mock_aasx_reader: mock.MagicMock) -> None:
+ manager = ComplianceToolStateManager()
- manager.steps = []
- file_path_3 = os.path.join(script_dir, 'files/test_demo_full_example_json.aasx')
- compliance_tool.check_aas_example(file_path_3, manager)
- self.assertEqual(4, len(manager.steps))
+ mock_aasx_reader.return_value.read_into.side_effect = create_read_into_mock(file='TestFile')
+ mock_aasx_reader.return_value.get_core_properties.return_value = create_example_aas_core_properties()
+ mock_data_checker.return_value.checks = []
+ type(mock_data_checker.return_value).failed_checks = mock.PropertyMock(side_effect=lambda: iter([]))
+ compliance_tool.check_aas_example("", manager)
+
+ self.assertEqual(5, len(manager.steps))
self.assertEqual(Status.SUCCESS, manager.steps[0].status)
self.assertEqual(Status.SUCCESS, manager.steps[1].status)
self.assertEqual(Status.SUCCESS, manager.steps[2].status)
self.assertEqual(Status.SUCCESS, manager.steps[3].status)
+ self.assertEqual(Status.SUCCESS, manager.steps[4].status)
+
+ @mock.patch("basyx.aas.adapter.aasx.AASXReader", autospec=True)
+ @mock.patch("aas_compliance_tool.compliance_check_aasx.AASDataChecker", autospec=True)
+ def test_check_aasx_files_equivalence_file1_fail_on_open(self, mock_data_checker: mock.MagicMock,
+ mock_aasx_reader: mock.MagicMock) -> None:
+ manager = ComplianceToolStateManager()
+
+ mock_aasx_reader.side_effect = [ValueError("Test error!"), mock_aasx_reader.return_value]
+ mock_data_checker.return_value.checks = []
+ mock_aasx_reader.return_value.get_core_properties.return_value = create_example_aas_core_properties()
+ compliance_tool.check_aasx_files_equivalence("", "", manager)
- manager.steps = []
- file_path_4 = os.path.join(script_dir, 'files/test_demo_full_example_xml_wrong_attribute.aasx')
- compliance_tool.check_aas_example(file_path_4, manager)
- self.assertEqual(4, len(manager.steps))
+ self.assertEqual(7, len(manager.steps))
+ self.assertEqual(Status.FAILED, manager.steps[0].status)
+ self.assertIn("Test error!", manager.format_step(0, verbose_level=1))
+ self.assertEqual(Status.NOT_EXECUTED, manager.steps[1].status)
+ self.assertEqual(Status.SUCCESS, manager.steps[2].status)
+ self.assertEqual(Status.SUCCESS, manager.steps[3].status)
+ self.assertEqual(Status.NOT_EXECUTED, manager.steps[4].status)
+ self.assertEqual(Status.NOT_EXECUTED, manager.steps[5].status)
+ self.assertEqual(Status.NOT_EXECUTED, manager.steps[6].status)
+
+ @mock.patch("basyx.aas.adapter.aasx.AASXReader", autospec=True)
+ @mock.patch("aas_compliance_tool.compliance_check_aasx.AASDataChecker", autospec=True)
+ def test_check_aasx_files_equivalence_file2_fail_on_open(self, mock_data_checker: mock.MagicMock,
+ mock_aasx_reader: mock.MagicMock) -> None:
+ manager = ComplianceToolStateManager()
+
+ mock_aasx_reader.side_effect = [mock_aasx_reader.return_value, ValueError("Test error!")]
+ mock_data_checker.return_value.checks = []
+ mock_aasx_reader.return_value.get_core_properties.return_value = create_example_aas_core_properties()
+ compliance_tool.check_aasx_files_equivalence("", "", manager)
+
+ self.assertEqual(7, len(manager.steps))
self.assertEqual(Status.SUCCESS, manager.steps[0].status)
self.assertEqual(Status.SUCCESS, manager.steps[1].status)
self.assertEqual(Status.FAILED, manager.steps[2].status)
- self.assertEqual('FAILED: Check if data is equal to example data\n - ERROR: Attribute id_short of '
- 'AssetAdministrationShell[https://acplt.org/Test_AssetAdministrationShell] must be == '
- 'TestAssetAdministrationShell (value=\'TestAssetAdministrationShell123\')',
- manager.format_step(2, verbose_level=1))
+ self.assertIn("Test error!", manager.format_step(2, verbose_level=1))
self.assertEqual(Status.NOT_EXECUTED, manager.steps[3].status)
+ self.assertEqual(Status.NOT_EXECUTED, manager.steps[4].status)
+ self.assertEqual(Status.NOT_EXECUTED, manager.steps[5].status)
+ self.assertEqual(Status.NOT_EXECUTED, manager.steps[6].status)
- def test_check_aasx_files_equivalence(self) -> None:
+ @mock.patch("basyx.aas.adapter.aasx.AASXReader", autospec=True)
+ @mock.patch("aas_compliance_tool.compliance_check_aasx.AASDataChecker", autospec=True)
+ def test_check_aasx_files_equivalence_fail_on_data_check(self, mock_data_checker: mock.MagicMock,
+ mock_aasx_reader: mock.MagicMock) -> None:
manager = ComplianceToolStateManager()
- script_dir = os.path.dirname(__file__)
- file_path_1 = os.path.join(script_dir, 'files/test_demo_full_example_xml.aasx')
- file_path_2 = os.path.join(script_dir, 'files/test_empty.aasx')
- compliance_tool.check_aasx_files_equivalence(file_path_1, file_path_2, manager)
- self.assertEqual(6, len(manager.steps))
+ failed = [CheckResult("Expected Behavior", False, dict())]
+ mock_data_checker.return_value.checks = failed
+ type(mock_data_checker.return_value).failed_checks = mock.PropertyMock(side_effect=lambda: iter(failed))
+ mock_aasx_reader.return_value.get_core_properties.return_value = create_example_aas_core_properties()
+ compliance_tool.check_aasx_files_equivalence("", "", manager)
+
+ self.assertEqual(7, len(manager.steps))
self.assertEqual(Status.SUCCESS, manager.steps[0].status)
self.assertEqual(Status.SUCCESS, manager.steps[1].status)
self.assertEqual(Status.SUCCESS, manager.steps[2].status)
self.assertEqual(Status.SUCCESS, manager.steps[3].status)
self.assertEqual(Status.FAILED, manager.steps[4].status)
+ self.assertIn("Expected Behavior", manager.format_step(4, verbose_level=1))
self.assertEqual(Status.NOT_EXECUTED, manager.steps[5].status)
+ self.assertEqual(Status.NOT_EXECUTED, manager.steps[6].status)
- manager.steps = []
- compliance_tool.check_aasx_files_equivalence(file_path_2, file_path_1, manager)
- self.assertEqual(6, len(manager.steps))
+ @mock.patch("basyx.aas.adapter.aasx.AASXReader", autospec=True)
+ @mock.patch("aas_compliance_tool.compliance_check_aasx.AASDataChecker", autospec=True)
+ def test_check_aasx_files_equivalence_fail_on_core_properties(self, mock_data_checker: mock.MagicMock,
+ mock_aasx_reader: mock.MagicMock) -> None:
+ manager = ComplianceToolStateManager()
+
+ mock_aasx_reader.return_value.read_into.side_effect = create_read_into_mock(file='TestFile')
+ mock_data_checker.return_value.checks = []
+ mock_aasx_reader.return_value.get_core_properties.return_value = create_example_aas_core_properties()
+ type(mock_data_checker.return_value).failed_checks = mock.PropertyMock(side_effect=lambda: iter([]))
+
+ wrong_cp = create_example_aas_core_properties()
+ wrong_cp.creator = "Wrong Creator"
+ mock_aasx_reader.return_value.get_core_properties.side_effect = \
+ [create_example_aas_core_properties(), wrong_cp]
+
+ compliance_tool.check_aasx_files_equivalence("", "", manager)
+
+ self.assertEqual(7, len(manager.steps))
self.assertEqual(Status.SUCCESS, manager.steps[0].status)
self.assertEqual(Status.SUCCESS, manager.steps[1].status)
self.assertEqual(Status.SUCCESS, manager.steps[2].status)
self.assertEqual(Status.SUCCESS, manager.steps[3].status)
- self.assertEqual(Status.FAILED, manager.steps[4].status)
- self.assertEqual(Status.NOT_EXECUTED, manager.steps[5].status)
+ self.assertEqual(Status.SUCCESS, manager.steps[4].status)
+ self.assertEqual(Status.FAILED, manager.steps[5].status)
+ self.assertIn("Wrong Creator", manager.format_step(5, verbose_level=1))
+ self.assertEqual(Status.SUCCESS, manager.steps[6].status)
+
+ @mock.patch("basyx.aas.adapter.aasx.AASXReader", autospec=True)
+ @mock.patch("aas_compliance_tool.compliance_check_aasx.AASDataChecker", autospec=True)
+ def test_check_aasx_files_equivalence_fail_on_file_missing(self, mock_data_checker: mock.MagicMock,
+ mock_aasx_reader: mock.MagicMock) -> None:
+ manager = ComplianceToolStateManager()
+
+ mock_data_checker.return_value.checks = []
+ type(mock_data_checker.return_value).failed_checks = mock.PropertyMock(side_effect=lambda: iter([]))
+ mock_aasx_reader.return_value.get_core_properties.return_value = create_example_aas_core_properties()
+
+ call_count = [0]
+
+ def setup_file_stores(*args, **kwargs):
+ call_count[0] += 1
+ if call_count[0] == 1:
+ return create_read_into_mock(file='TestFile')(*args, **kwargs)
+ else:
+ return create_read_into_mock(file=None)(*args, **kwargs)
+
+ mock_aasx_reader.return_value.read_into.side_effect = setup_file_stores
+ compliance_tool.check_aasx_files_equivalence("", "", manager)
- manager.steps = []
- file_path_3 = os.path.join(script_dir, 'files/test_demo_full_example_xml.aasx')
- file_path_4 = os.path.join(script_dir, 'files/test_demo_full_example_json.aasx')
- compliance_tool.check_aasx_files_equivalence(file_path_3, file_path_4, manager)
- self.assertEqual(6, len(manager.steps))
+ self.assertEqual(7, len(manager.steps))
self.assertEqual(Status.SUCCESS, manager.steps[0].status)
self.assertEqual(Status.SUCCESS, manager.steps[1].status)
self.assertEqual(Status.SUCCESS, manager.steps[2].status)
self.assertEqual(Status.SUCCESS, manager.steps[3].status)
self.assertEqual(Status.SUCCESS, manager.steps[4].status)
self.assertEqual(Status.SUCCESS, manager.steps[5].status)
+ self.assertEqual(Status.FAILED, manager.steps[6].status)
+ self.assertIn("second file must contain supplementary file /TestFile.pdf",
+ manager.format_step(6, verbose_level=1))
+
+ @mock.patch("basyx.aas.adapter.aasx.AASXReader", autospec=True)
+ @mock.patch("aas_compliance_tool.compliance_check_aasx.AASDataChecker", autospec=True)
+ def test_check_aasx_files_equivalence_fail_on_file_check(self, mock_data_checker: mock.MagicMock,
+ mock_aasx_reader: mock.MagicMock) -> None:
+ manager = ComplianceToolStateManager()
+
+ mock_data_checker.return_value.checks = []
+ type(mock_data_checker.return_value).failed_checks = mock.PropertyMock(side_effect=lambda: iter([]))
+ mock_aasx_reader.return_value.get_core_properties.return_value = create_example_aas_core_properties()
+
+ call_count = [0]
+
+ def setup_file_stores(*args, **kwargs):
+ call_count[0] += 1
+ if call_count[0] == 1:
+ return create_read_into_mock(file='TestFile')(*args, **kwargs)
+ else:
+ return create_read_into_mock(file='TestFileWrong')(*args, **kwargs)
+
+ mock_aasx_reader.return_value.read_into.side_effect = setup_file_stores
+ compliance_tool.check_aasx_files_equivalence("", "", manager)
- manager.steps = []
- file_path_3 = os.path.join(script_dir, 'files/test_demo_full_example_xml.aasx')
- file_path_4 = os.path.join(script_dir, 'files/test_demo_full_example_xml_wrong_attribute.aasx')
- compliance_tool.check_aasx_files_equivalence(file_path_3, file_path_4, manager)
- self.assertEqual(6, len(manager.steps))
+ self.assertEqual(7, len(manager.steps))
self.assertEqual(Status.SUCCESS, manager.steps[0].status)
self.assertEqual(Status.SUCCESS, manager.steps[1].status)
self.assertEqual(Status.SUCCESS, manager.steps[2].status)
self.assertEqual(Status.SUCCESS, manager.steps[3].status)
- self.assertEqual(Status.FAILED, manager.steps[4].status)
- self.assertEqual('FAILED: Check if data in files are equal\n - ERROR: Attribute id_short of '
- 'AssetAdministrationShell[https://acplt.org/Test_AssetAdministrationShell] must be == '
- 'TestAssetAdministrationShell123 (value=\'TestAssetAdministrationShell\')',
- manager.format_step(4, verbose_level=1))
-
- manager.steps = []
- compliance_tool.check_aasx_files_equivalence(file_path_4, file_path_3, manager)
- self.assertEqual(6, len(manager.steps))
+ self.assertEqual(Status.SUCCESS, manager.steps[4].status)
+ self.assertEqual(Status.SUCCESS, manager.steps[5].status)
+ self.assertEqual(Status.FAILED, manager.steps[6].status)
+ self.assertIn("second file must contain supplementary file /TestFile.pdf with sha256",
+ manager.format_step(6, verbose_level=1))
+
+ @mock.patch("basyx.aas.adapter.aasx.AASXReader", autospec=True)
+ @mock.patch("aas_compliance_tool.compliance_check_aasx.AASDataChecker", autospec=True)
+ def test_check_aasx_files_equivalence_success(self, mock_data_checker: mock.MagicMock,
+ mock_aasx_reader: mock.MagicMock) -> None:
+ manager = ComplianceToolStateManager()
+
+ mock_aasx_reader.return_value.read_into.side_effect = create_read_into_mock(file='TestFile')
+ mock_aasx_reader.return_value.get_core_properties.return_value = create_example_aas_core_properties()
+ mock_data_checker.return_value.checks = []
+ type(mock_data_checker.return_value).failed_checks = mock.PropertyMock(side_effect=lambda: iter([]))
+ compliance_tool.check_aasx_files_equivalence("", "", manager)
+
+ self.assertEqual(7, len(manager.steps))
self.assertEqual(Status.SUCCESS, manager.steps[0].status)
self.assertEqual(Status.SUCCESS, manager.steps[1].status)
self.assertEqual(Status.SUCCESS, manager.steps[2].status)
self.assertEqual(Status.SUCCESS, manager.steps[3].status)
- self.assertEqual(Status.FAILED, manager.steps[4].status)
- self.assertEqual('FAILED: Check if data in files are equal\n - ERROR: Attribute id_short of '
- 'AssetAdministrationShell[https://acplt.org/Test_AssetAdministrationShell] must be == '
- 'TestAssetAdministrationShell (value=\'TestAssetAdministrationShell123\')',
- manager.format_step(4, verbose_level=1))
- self.assertEqual(Status.NOT_EXECUTED, manager.steps[5].status)
-
- def test_check_schema(self):
- manager = ComplianceToolStateManager()
- script_dir = os.path.dirname(__file__)
-
- file_path_2 = os.path.join(script_dir, 'files/test_demo_full_example_json.aasx')
- compliance_tool.check_schema(file_path_2, manager)
- self.assertEqual(4, len(manager.steps))
- for i in range(4):
- self.assertEqual(Status.SUCCESS, manager.steps[i].status)
-
- manager.steps = []
- file_path_3 = os.path.join(script_dir, 'files/test_demo_full_example_xml.aasx')
- compliance_tool.check_schema(file_path_3, manager)
- self.assertEqual(4, len(manager.steps))
- for i in range(4):
- self.assertEqual(Status.SUCCESS, manager.steps[i].status)
-
- manager.steps = []
- file_path_4 = os.path.join(script_dir, 'files/test_demo_full_example_xml_wrong_attribute.aasx')
- compliance_tool.check_schema(file_path_4, manager)
- self.assertEqual(4, len(manager.steps))
- for i in range(4):
- self.assertEqual(Status.SUCCESS, manager.steps[i].status)
-
- manager.steps = []
- file_path_5 = os.path.join(script_dir, 'files/test_empty.aasx')
- compliance_tool.check_schema(file_path_5, manager)
- self.assertEqual(2, len(manager.steps))
- for i in range(2):
- self.assertEqual(Status.SUCCESS, manager.steps[i].status)
+ self.assertEqual(Status.SUCCESS, manager.steps[4].status)
+ self.assertEqual(Status.SUCCESS, manager.steps[5].status)
+ self.assertEqual(Status.SUCCESS, manager.steps[6].status)
diff --git a/compliance_tool/test/test_compliance_check_json.py b/compliance_tool/test/test_compliance_check_json.py
index c738c0f13..e889bc394 100644
--- a/compliance_tool/test/test_compliance_check_json.py
+++ b/compliance_tool/test/test_compliance_check_json.py
@@ -1,164 +1,179 @@
-# Copyright (c) 2025 the Eclipse BaSyx Authors
+# Copyright (c) 2026 the Eclipse BaSyx Authors
#
# This program and the accompanying materials are made available under the terms of the MIT License, available in
# the LICENSE file of this project.
#
# SPDX-License-Identifier: MIT
-import os
import unittest
+from unittest import mock
+from ._test_helper import create_mock_effect
from aas_compliance_tool import compliance_check_json as compliance_tool
from aas_compliance_tool.state_manager import ComplianceToolStateManager, Status
+from basyx.aas.examples.data._helper import CheckResult
+
class ComplianceToolJsonTest(unittest.TestCase):
- def test_check_schema(self) -> None:
+
+ def test_check_deserialization_no_file(self) -> None:
manager = ComplianceToolStateManager()
- script_dir = os.path.dirname(__file__)
- file_path_1 = os.path.join(script_dir, 'files/test_not_found.json')
- compliance_tool.check_schema(file_path_1, manager)
- self.assertEqual(3, len(manager.steps))
+
+ compliance_tool.check_deserialization("", manager)
+ self.assertEqual(2, len(manager.steps))
self.assertEqual(Status.FAILED, manager.steps[0].status)
self.assertEqual(Status.NOT_EXECUTED, manager.steps[1].status)
- self.assertEqual(Status.NOT_EXECUTED, manager.steps[2].status)
self.assertIn("No such file or directory", manager.format_step(0, verbose_level=1))
- manager.steps = []
- file_path_2 = os.path.join(script_dir, 'files/test_not_deserializable.json')
- compliance_tool.check_schema(file_path_2, manager)
- self.assertEqual(3, len(manager.steps))
- self.assertEqual(Status.SUCCESS, manager.steps[0].status)
- self.assertEqual(Status.FAILED, manager.steps[1].status)
- self.assertEqual(Status.NOT_EXECUTED, manager.steps[2].status)
- self.assertIn("Expecting ',' delimiter: line 4 column 2 (char 54)", manager.format_step(1, verbose_level=1))
+ @mock.patch("builtins.open")
+ @mock.patch("basyx.aas.adapter.json.json_deserialization.read_aas_json_file", autospec=True)
+ def test_check_deserialization_fail_on_error(self, mock_read_json_file, mock_open) -> None:
+ manager = ComplianceToolStateManager()
- manager.steps = []
- file_path_3 = os.path.join(script_dir, 'files/test_empty.json')
- compliance_tool.check_schema(file_path_3, manager)
- self.assertEqual(3, len(manager.steps))
- self.assertEqual(Status.SUCCESS, manager.steps[0].status)
- self.assertEqual(Status.SUCCESS, manager.steps[1].status)
- self.assertEqual(Status.SUCCESS, manager.steps[2].status)
+ mock_read_json_file.side_effect = create_mock_effect('basyx.aas.adapter.json.json_deserialization', 'error')
+ compliance_tool.check_deserialization("", manager)
- manager.steps = []
- file_path_4 = os.path.join(script_dir, 'files/test_demo_full_example.json')
- compliance_tool.check_schema(file_path_4, manager)
- self.assertEqual(3, len(manager.steps))
- self.assertEqual(Status.SUCCESS, manager.steps[0].status)
- self.assertEqual(Status.SUCCESS, manager.steps[1].status)
- self.assertEqual(Status.SUCCESS, manager.steps[2].status)
-
- manager.steps = []
- file_path_5 = os.path.join(script_dir, 'files/test_demo_full_example_wrong_attribute.json')
- compliance_tool.check_schema(file_path_5, manager)
- self.assertEqual(3, len(manager.steps))
+ self.assertEqual(2, len(manager.steps))
self.assertEqual(Status.SUCCESS, manager.steps[0].status)
- self.assertEqual(Status.SUCCESS, manager.steps[1].status)
- self.assertEqual(Status.SUCCESS, manager.steps[2].status)
+ self.assertEqual(Status.FAILED, manager.steps[1].status)
+ self.assertIn("Test error!", manager.format_step(1, verbose_level=1))
- def test_check_deserialization(self) -> None:
+ @mock.patch("builtins.open")
+ @mock.patch("basyx.aas.adapter.json.json_deserialization.read_aas_json_file", autospec=True)
+ def test_check_deserialization_fail_on_warning(self, mock_read_json_file, mock_open) -> None:
manager = ComplianceToolStateManager()
- script_dir = os.path.dirname(__file__)
- file_path_1 = os.path.join(script_dir, 'files/test_not_found.json')
- compliance_tool.check_deserialization(file_path_1, manager)
- self.assertEqual(2, len(manager.steps))
- self.assertEqual(Status.FAILED, manager.steps[0].status)
- self.assertEqual(Status.NOT_EXECUTED, manager.steps[1].status)
- self.assertIn("No such file or directory", manager.format_step(0, verbose_level=1))
+ mock_read_json_file.side_effect = create_mock_effect('basyx.aas.adapter.json.json_deserialization', 'warning')
+ compliance_tool.check_deserialization("", manager)
- manager.steps = []
- file_path_2 = os.path.join(script_dir, 'files/test_not_deserializable_aas.json')
- compliance_tool.check_deserialization(file_path_2, manager)
self.assertEqual(2, len(manager.steps))
self.assertEqual(Status.SUCCESS, manager.steps[0].status)
self.assertEqual(Status.FAILED, manager.steps[1].status)
- self.assertIn('Found JSON object with modelType="Test", which is not a known AAS class',
- manager.format_step(1, verbose_level=1))
+ self.assertIn("Test warning!", manager.format_step(1, verbose_level=1))
- manager.steps = []
- file_path_3 = os.path.join(script_dir, 'files/test_deserializable_aas_warning.json')
- compliance_tool.check_deserialization(file_path_3, manager)
- self.assertEqual(2, len(manager.steps))
- self.assertEqual(Status.SUCCESS, manager.steps[0].status)
- self.assertEqual(Status.FAILED, manager.steps[1].status)
- self.assertIn("Ignoring 'revision' attribute of AdministrativeInformation object due to missing 'version'",
- manager.format_step(1, verbose_level=1))
+ @mock.patch("builtins.open")
+ @mock.patch("basyx.aas.adapter.json.json_deserialization.read_aas_json_file", autospec=True)
+ def test_check_deserialization_success(self, mock_read_json_file, mock_open) -> None:
+ manager = ComplianceToolStateManager()
- manager.steps = []
- file_path_4 = os.path.join(script_dir, 'files/test_empty.json')
- compliance_tool.check_deserialization(file_path_4, manager)
- self.assertEqual(2, len(manager.steps))
- self.assertEqual(Status.SUCCESS, manager.steps[0].status)
- self.assertEqual(Status.SUCCESS, manager.steps[1].status)
+ mock_read_json_file.side_effect = create_mock_effect('basyx.aas.adapter.json.json_deserialization', 'debug')
+ compliance_tool.check_deserialization("", manager)
- manager.steps = []
- file_path_4 = os.path.join(script_dir, 'files/test_empty.json')
- compliance_tool.check_deserialization(file_path_4, manager)
self.assertEqual(2, len(manager.steps))
self.assertEqual(Status.SUCCESS, manager.steps[0].status)
self.assertEqual(Status.SUCCESS, manager.steps[1].status)
- def test_check_aas_example(self) -> None:
+ @mock.patch("builtins.open")
+ @mock.patch("basyx.aas.adapter.json.json_deserialization.read_aas_json_file", autospec=True)
+ @mock.patch("aas_compliance_tool.compliance_check_json.AASDataChecker", autospec=True)
+ def test_check_example_success(self, mock_data_checker: mock.MagicMock, mock_read_json_file: mock.MagicMock,
+ mock_open: mock.MagicMock) -> None:
manager = ComplianceToolStateManager()
- script_dir = os.path.dirname(__file__)
- file_path_2 = os.path.join(script_dir, 'files/test_demo_full_example.json')
- compliance_tool.check_aas_example(file_path_2, manager)
+ mock_data_checker.return_value.checks = []
+ type(mock_data_checker.return_value).failed_checks = mock.PropertyMock(side_effect=lambda: iter([]))
+ compliance_tool.check_aas_example("", manager)
+
self.assertEqual(3, len(manager.steps))
self.assertEqual(Status.SUCCESS, manager.steps[0].status)
self.assertEqual(Status.SUCCESS, manager.steps[1].status)
self.assertEqual(Status.SUCCESS, manager.steps[2].status)
- manager.steps = []
- file_path_1 = os.path.join(script_dir, 'files/test_not_deserializable_aas.json')
- compliance_tool.check_aas_example(file_path_1, manager)
+ @mock.patch("builtins.open")
+ @mock.patch("basyx.aas.adapter.json.json_deserialization.read_aas_json_file", autospec=True)
+ @mock.patch("aas_compliance_tool.compliance_check_json.AASDataChecker", autospec=True)
+ def test_check_example_fail_on_read(self, mock_data_checker: mock.MagicMock, mock_read_json_file: mock.MagicMock,
+ mock_open: mock.MagicMock) -> None:
+ manager = ComplianceToolStateManager()
+
+ mock_read_json_file.side_effect = create_mock_effect('basyx.aas.adapter.json.json_deserialization', 'error',
+ error_msg="Error on reading aas json file!")
+ compliance_tool.check_aas_example("", manager)
+
self.assertEqual(3, len(manager.steps))
self.assertEqual(Status.SUCCESS, manager.steps[0].status)
self.assertEqual(Status.FAILED, manager.steps[1].status)
+ self.assertIn("Error on reading aas json file!", manager.format_step(1, verbose_level=1))
self.assertEqual(Status.NOT_EXECUTED, manager.steps[2].status)
- self.assertIn('Found JSON object with modelType="Test", which is not a known AAS class',
- manager.format_step(1, verbose_level=1))
- manager.steps = []
- file_path_3 = os.path.join(script_dir, 'files/test_demo_full_example_wrong_attribute.json')
- compliance_tool.check_aas_example(file_path_3, manager)
+ @mock.patch("builtins.open")
+ @mock.patch("basyx.aas.adapter.json.json_deserialization.read_aas_json_file", autospec=True)
+ @mock.patch("aas_compliance_tool.compliance_check_json.AASDataChecker", autospec=True)
+ def test_check_example_fail_on_check(self, mock_data_checker: mock.MagicMock, mock_read_json_file: mock.MagicMock,
+ mock_open: mock.MagicMock) -> None:
+ manager = ComplianceToolStateManager()
+ failed = [CheckResult("Expected Behavior", False, dict())]
+ mock_data_checker.return_value.checks = failed
+ type(mock_data_checker.return_value).failed_checks = mock.PropertyMock(side_effect=lambda: iter(failed))
+
+ compliance_tool.check_aas_example("", manager)
+
self.assertEqual(3, len(manager.steps))
self.assertEqual(Status.SUCCESS, manager.steps[0].status)
self.assertEqual(Status.SUCCESS, manager.steps[1].status)
self.assertEqual(Status.FAILED, manager.steps[2].status)
- self.assertEqual('FAILED: Check if data is equal to example data\n - ERROR: Attribute id_short of '
- 'AssetAdministrationShell[https://acplt.org/Test_AssetAdministrationShell] must be == '
- 'TestAssetAdministrationShell (value=\'TestAssetAdministrationShell123\')',
- manager.format_step(2, verbose_level=1))
+ self.assertIn("Expected Behavior", manager.format_step(2, verbose_level=1))
- def test_check_json_files_equivalence(self) -> None:
+ @mock.patch("builtins.open")
+ @mock.patch("basyx.aas.adapter.json.json_deserialization.read_aas_json_file", autospec=True)
+ @mock.patch("aas_compliance_tool.compliance_check_json.AASDataChecker", autospec=True)
+ def test_check_json_files_equivalence_file1_fail_on_deserialization(self, mock_data_checker, mock_read_json_file,
+ mock_open) -> None:
manager = ComplianceToolStateManager()
- script_dir = os.path.dirname(__file__)
- file_path_1 = os.path.join(script_dir, 'files/test_not_deserializable_aas.json')
- file_path_2 = os.path.join(script_dir, 'files/test_empty.json')
- compliance_tool.check_json_files_equivalence(file_path_1, file_path_2, manager)
+ call_count = [0]
+
+ def mock_first_fails(*args, **kwargs):
+ call_count[0] += 1
+ if call_count[0] == 1:
+ create_mock_effect('basyx.aas.adapter.json.json_deserialization', 'error')(*args, **kwargs)
+
+ mock_read_json_file.side_effect = mock_first_fails
+ compliance_tool.check_json_files_equivalence("", "", manager)
+
self.assertEqual(5, len(manager.steps))
self.assertEqual(Status.SUCCESS, manager.steps[0].status)
self.assertEqual(Status.FAILED, manager.steps[1].status)
+ self.assertIn("Test error!", manager.format_step(1, verbose_level=1))
self.assertEqual(Status.SUCCESS, manager.steps[2].status)
self.assertEqual(Status.SUCCESS, manager.steps[3].status)
self.assertEqual(Status.NOT_EXECUTED, manager.steps[4].status)
- manager.steps = []
- compliance_tool.check_json_files_equivalence(file_path_2, file_path_1, manager)
+ @mock.patch("builtins.open")
+ @mock.patch("basyx.aas.adapter.json.json_deserialization.read_aas_json_file", autospec=True)
+ @mock.patch("aas_compliance_tool.compliance_check_json.AASDataChecker", autospec=True)
+ def test_check_json_files_equivalence_file2_fail_on_deserialization(self, mock_data_checker, mock_read_json_file,
+ mock_open) -> None:
+ manager = ComplianceToolStateManager()
+
+ call_count = [0]
+
+ def mock_second_fails(*args, **kwargs):
+ call_count[0] += 1
+ if call_count[0] == 2:
+ create_mock_effect('basyx.aas.adapter.json.json_deserialization', 'error')(*args, **kwargs)
+
+ mock_read_json_file.side_effect = mock_second_fails
+ compliance_tool.check_json_files_equivalence("", "", manager)
+
self.assertEqual(5, len(manager.steps))
self.assertEqual(Status.SUCCESS, manager.steps[0].status)
self.assertEqual(Status.SUCCESS, manager.steps[1].status)
self.assertEqual(Status.SUCCESS, manager.steps[2].status)
self.assertEqual(Status.FAILED, manager.steps[3].status)
+ self.assertIn("Test error!", manager.format_step(3, verbose_level=1))
self.assertEqual(Status.NOT_EXECUTED, manager.steps[4].status)
- manager.steps = []
- file_path_3 = os.path.join(script_dir, 'files/test_demo_full_example.json')
- file_path_4 = os.path.join(script_dir, 'files/test_demo_full_example.json')
- compliance_tool.check_json_files_equivalence(file_path_3, file_path_4, manager)
+ @mock.patch("builtins.open")
+ @mock.patch("basyx.aas.adapter.json.json_deserialization.read_aas_json_file", autospec=True)
+ @mock.patch("aas_compliance_tool.compliance_check_json.AASDataChecker", autospec=True)
+ def test_check_json_files_equivalence_success(self, mock_data_checker, mock_read_json_file, mock_open) -> None:
+ manager = ComplianceToolStateManager()
+
+ mock_data_checker.return_value.checks = []
+ type(mock_data_checker.return_value).failed_checks = mock.PropertyMock(side_effect=lambda: iter([]))
+ compliance_tool.check_json_files_equivalence("", "", manager)
+
self.assertEqual(5, len(manager.steps))
self.assertEqual(Status.SUCCESS, manager.steps[0].status)
self.assertEqual(Status.SUCCESS, manager.steps[1].status)
@@ -166,30 +181,22 @@ def test_check_json_files_equivalence(self) -> None:
self.assertEqual(Status.SUCCESS, manager.steps[3].status)
self.assertEqual(Status.SUCCESS, manager.steps[4].status)
- manager.steps = []
- file_path_3 = os.path.join(script_dir, 'files/test_demo_full_example.json')
- file_path_4 = os.path.join(script_dir, 'files/test_demo_full_example_wrong_attribute.json')
- compliance_tool.check_json_files_equivalence(file_path_3, file_path_4, manager)
- self.assertEqual(5, len(manager.steps))
- self.assertEqual(Status.SUCCESS, manager.steps[0].status)
- self.assertEqual(Status.SUCCESS, manager.steps[1].status)
- self.assertEqual(Status.SUCCESS, manager.steps[2].status)
- self.assertEqual(Status.SUCCESS, manager.steps[3].status)
- self.assertEqual(Status.FAILED, manager.steps[4].status)
- self.assertEqual('FAILED: Check if data in files are equal\n - ERROR: Attribute id_short of '
- 'AssetAdministrationShell[https://acplt.org/Test_AssetAdministrationShell] must be == '
- 'TestAssetAdministrationShell123 (value=\'TestAssetAdministrationShell\')',
- manager.format_step(4, verbose_level=1))
+ @mock.patch("builtins.open")
+ @mock.patch("basyx.aas.adapter.json.json_deserialization.read_aas_json_file", autospec=True)
+ @mock.patch("aas_compliance_tool.compliance_check_json.AASDataChecker", autospec=True)
+ def test_check_json_files_equivalence_fail_on_check(self, mock_data_checker: mock.MagicMock, mock_read_json_file,
+ mock_open) -> None:
+ manager = ComplianceToolStateManager()
+
+ failed = [CheckResult("Expected Behavior", False, dict())]
+ mock_data_checker.return_value.checks = failed
+ type(mock_data_checker.return_value).failed_checks = mock.PropertyMock(side_effect=lambda: iter(failed))
+ compliance_tool.check_json_files_equivalence("", "", manager)
- manager.steps = []
- compliance_tool.check_json_files_equivalence(file_path_4, file_path_3, manager)
self.assertEqual(5, len(manager.steps))
self.assertEqual(Status.SUCCESS, manager.steps[0].status)
self.assertEqual(Status.SUCCESS, manager.steps[1].status)
self.assertEqual(Status.SUCCESS, manager.steps[2].status)
self.assertEqual(Status.SUCCESS, manager.steps[3].status)
self.assertEqual(Status.FAILED, manager.steps[4].status)
- self.assertEqual('FAILED: Check if data in files are equal\n - ERROR: Attribute id_short of '
- 'AssetAdministrationShell[https://acplt.org/Test_AssetAdministrationShell] must be == '
- 'TestAssetAdministrationShell (value=\'TestAssetAdministrationShell123\')',
- manager.format_step(4, verbose_level=1))
+ self.assertIn("Expected Behavior", manager.format_step(4, verbose_level=1))
diff --git a/compliance_tool/test/test_compliance_check_xml.py b/compliance_tool/test/test_compliance_check_xml.py
index 63089e186..0a85e330a 100644
--- a/compliance_tool/test/test_compliance_check_xml.py
+++ b/compliance_tool/test/test_compliance_check_xml.py
@@ -1,156 +1,179 @@
-# Copyright (c) 2025 the Eclipse BaSyx Authors
+# Copyright (c) 2026 the Eclipse BaSyx Authors
#
# This program and the accompanying materials are made available under the terms of the MIT License, available in
# the LICENSE file of this project.
#
# SPDX-License-Identifier: MIT
-import os
import unittest
+from unittest import mock
+from ._test_helper import create_mock_effect
from aas_compliance_tool import compliance_check_xml as compliance_tool
from aas_compliance_tool.state_manager import ComplianceToolStateManager, Status
+from basyx.aas.examples.data._helper import CheckResult
+
class ComplianceToolXmlTest(unittest.TestCase):
- def test_check_schema(self) -> None:
+
+ def test_check_deserialization_no_file(self) -> None:
manager = ComplianceToolStateManager()
- script_dir = os.path.dirname(__file__)
- file_path_1 = os.path.join(script_dir, 'files/test_not_found.xml')
- compliance_tool.check_schema(file_path_1, manager)
- self.assertEqual(3, len(manager.steps))
+
+ compliance_tool.check_deserialization("", manager)
+ self.assertEqual(2, len(manager.steps))
self.assertEqual(Status.FAILED, manager.steps[0].status)
self.assertEqual(Status.NOT_EXECUTED, manager.steps[1].status)
- self.assertEqual(Status.NOT_EXECUTED, manager.steps[2].status)
self.assertIn("No such file or directory", manager.format_step(0, verbose_level=1))
- manager.steps = []
- file_path_2 = os.path.join(script_dir, 'files/test_empty.xml')
- compliance_tool.check_schema(file_path_2, manager)
- self.assertEqual(3, len(manager.steps))
- self.assertEqual(Status.SUCCESS, manager.steps[0].status)
- self.assertEqual(Status.SUCCESS, manager.steps[1].status)
- self.assertEqual(Status.SUCCESS, manager.steps[2].status)
+ @mock.patch("builtins.open")
+ @mock.patch("basyx.aas.adapter.xml.xml_deserialization.read_aas_xml_file", autospec=True)
+ def test_check_deserialization_fail_on_error(self, mock_read_xml_file, mock_open) -> None:
+ manager = ComplianceToolStateManager()
- manager.steps = []
- file_path_3 = os.path.join(script_dir, 'files/test_demo_full_example.xml')
- compliance_tool.check_schema(file_path_3, manager)
- self.assertEqual(3, len(manager.steps))
- self.assertEqual(Status.SUCCESS, manager.steps[0].status)
- self.assertEqual(Status.SUCCESS, manager.steps[1].status)
- self.assertEqual(Status.SUCCESS, manager.steps[2].status)
+ mock_read_xml_file.side_effect = create_mock_effect('basyx.aas.adapter.xml.xml_deserialization', 'error')
+ compliance_tool.check_deserialization("", manager)
- manager.steps = []
- file_path_4 = os.path.join(script_dir, 'files/test_demo_full_example_wrong_attribute.xml')
- compliance_tool.check_schema(file_path_4, manager)
- self.assertEqual(3, len(manager.steps))
+ self.assertEqual(2, len(manager.steps))
self.assertEqual(Status.SUCCESS, manager.steps[0].status)
- self.assertEqual(Status.SUCCESS, manager.steps[1].status)
- self.assertEqual(Status.SUCCESS, manager.steps[2].status)
+ self.assertEqual(Status.FAILED, manager.steps[1].status)
+ self.assertIn("Test error!", manager.format_step(1, verbose_level=1))
- def test_check_deserialization(self) -> None:
+ @mock.patch("builtins.open")
+ @mock.patch("basyx.aas.adapter.xml.xml_deserialization.read_aas_xml_file", autospec=True)
+ def test_check_deserialization_fail_on_warning(self, mock_read_xml_file, mock_open) -> None:
manager = ComplianceToolStateManager()
- script_dir = os.path.dirname(__file__)
- file_path_1 = os.path.join(script_dir, 'files/test_not_found.xml')
- compliance_tool.check_deserialization(file_path_1, manager)
- self.assertEqual(2, len(manager.steps))
- self.assertEqual(Status.FAILED, manager.steps[0].status)
- self.assertEqual(Status.NOT_EXECUTED, manager.steps[1].status)
- self.assertIn("No such file or directory", manager.format_step(0, verbose_level=1))
+ mock_read_xml_file.side_effect = create_mock_effect('basyx.aas.adapter.xml.xml_deserialization', 'warning')
+ compliance_tool.check_deserialization("", manager)
- manager.steps = []
- file_path_2 = os.path.join(script_dir, 'files/test_not_deserializable_aas.xml')
- compliance_tool.check_deserialization(file_path_2, manager)
self.assertEqual(2, len(manager.steps))
self.assertEqual(Status.SUCCESS, manager.steps[0].status)
self.assertEqual(Status.FAILED, manager.steps[1].status)
- self.assertIn("child of aas:assetAdministrationShells", manager.format_step(1, verbose_level=1))
- self.assertIn("doesn't match the expected tag aas:assetAdministrationShell",
- manager.format_step(1, verbose_level=1))
+ self.assertIn("Test warning!", manager.format_step(1, verbose_level=1))
- manager.steps = []
- file_path_3 = os.path.join(script_dir, 'files/test_deserializable_aas_warning.xml')
- compliance_tool.check_deserialization(file_path_3, manager)
- self.assertEqual(2, len(manager.steps))
- self.assertEqual(Status.SUCCESS, manager.steps[0].status)
- self.assertEqual(Status.FAILED, manager.steps[1].status)
- self.assertIn("AASConstraintViolation: A revision requires a version", manager.format_step(1, verbose_level=1))
+ @mock.patch("builtins.open")
+ @mock.patch("basyx.aas.adapter.xml.xml_deserialization.read_aas_xml_file", autospec=True)
+ def test_check_deserialization_success(self, mock_read_xml_file, mock_open) -> None:
+ manager = ComplianceToolStateManager()
- manager.steps = []
- file_path_4 = os.path.join(script_dir, 'files/test_empty.xml')
- compliance_tool.check_deserialization(file_path_4, manager)
- self.assertEqual(2, len(manager.steps))
- self.assertEqual(Status.SUCCESS, manager.steps[0].status)
- self.assertEqual(Status.SUCCESS, manager.steps[1].status)
+ mock_read_xml_file.side_effect = create_mock_effect('basyx.aas.adapter.xml.xml_deserialization', 'debug')
+ compliance_tool.check_deserialization("", manager)
- manager.steps = []
- file_path_4 = os.path.join(script_dir, 'files/test_empty.xml')
- compliance_tool.check_deserialization(file_path_4, manager)
self.assertEqual(2, len(manager.steps))
self.assertEqual(Status.SUCCESS, manager.steps[0].status)
self.assertEqual(Status.SUCCESS, manager.steps[1].status)
- def test_check_aas_example(self) -> None:
+ @mock.patch("builtins.open")
+ @mock.patch("basyx.aas.adapter.xml.xml_deserialization.read_aas_xml_file", autospec=True)
+ @mock.patch("aas_compliance_tool.compliance_check_xml.AASDataChecker", autospec=True)
+ def test_check_example_success(self, mock_data_checker: mock.MagicMock, mock_read_xml_file: mock.MagicMock,
+ mock_open: mock.MagicMock) -> None:
manager = ComplianceToolStateManager()
- script_dir = os.path.dirname(__file__)
- file_path_2 = os.path.join(script_dir, 'files/test_demo_full_example.xml')
- compliance_tool.check_aas_example(file_path_2, manager)
+ mock_data_checker.return_value.checks = []
+ type(mock_data_checker.return_value).failed_checks = mock.PropertyMock(side_effect=lambda: iter([]))
+ compliance_tool.check_aas_example("", manager)
+
self.assertEqual(3, len(manager.steps))
self.assertEqual(Status.SUCCESS, manager.steps[0].status)
self.assertEqual(Status.SUCCESS, manager.steps[1].status)
self.assertEqual(Status.SUCCESS, manager.steps[2].status)
- manager.steps = []
- file_path_1 = os.path.join(script_dir, 'files/test_not_deserializable_aas.xml')
- compliance_tool.check_aas_example(file_path_1, manager)
+ @mock.patch("builtins.open")
+ @mock.patch("basyx.aas.adapter.xml.xml_deserialization.read_aas_xml_file", autospec=True)
+ @mock.patch("aas_compliance_tool.compliance_check_xml.AASDataChecker", autospec=True)
+ def test_check_example_fail_on_read(self, mock_data_checker: mock.MagicMock, mock_read_xml_file: mock.MagicMock,
+ mock_open: mock.MagicMock) -> None:
+ manager = ComplianceToolStateManager()
+
+ mock_read_xml_file.side_effect = create_mock_effect('basyx.aas.adapter.xml.xml_deserialization', 'error',
+ error_msg="Error on reading aas xml file!")
+ compliance_tool.check_aas_example("", manager)
+
self.assertEqual(3, len(manager.steps))
self.assertEqual(Status.SUCCESS, manager.steps[0].status)
self.assertEqual(Status.FAILED, manager.steps[1].status)
+ self.assertIn("Error on reading aas xml file!", manager.format_step(1, verbose_level=1))
self.assertEqual(Status.NOT_EXECUTED, manager.steps[2].status)
- self.assertIn("child of aas:assetAdministrationShells", manager.format_step(1, verbose_level=1))
- self.assertIn("doesn't match the expected tag aas:assetAdministrationShell",
- manager.format_step(1, verbose_level=1))
- manager.steps = []
- file_path_3 = os.path.join(script_dir, 'files/test_demo_full_example_wrong_attribute.xml')
- compliance_tool.check_aas_example(file_path_3, manager)
+ @mock.patch("builtins.open")
+ @mock.patch("basyx.aas.adapter.xml.xml_deserialization.read_aas_xml_file", autospec=True)
+ @mock.patch("aas_compliance_tool.compliance_check_xml.AASDataChecker", autospec=True)
+ def test_check_example_fail_on_check(self, mock_data_checker: mock.MagicMock, mock_read_xml_file: mock.MagicMock,
+ mock_open: mock.MagicMock) -> None:
+ manager = ComplianceToolStateManager()
+ failed = [CheckResult("Expected Behavior", False, dict())]
+ mock_data_checker.return_value.checks = failed
+ type(mock_data_checker.return_value).failed_checks = mock.PropertyMock(side_effect=lambda: iter(failed))
+
+ compliance_tool.check_aas_example("", manager)
+
self.assertEqual(3, len(manager.steps))
self.assertEqual(Status.SUCCESS, manager.steps[0].status)
self.assertEqual(Status.SUCCESS, manager.steps[1].status)
self.assertEqual(Status.FAILED, manager.steps[2].status)
- self.assertEqual('FAILED: Check if data is equal to example data\n - ERROR: Attribute id_short of '
- 'AssetAdministrationShell[https://acplt.org/Test_AssetAdministrationShell] must be == '
- 'TestAssetAdministrationShell (value=\'TestAssetAdministrationShell123\')',
- manager.format_step(2, verbose_level=1))
+ self.assertIn("Expected Behavior", manager.format_step(2, verbose_level=1))
- def test_check_xml_files_equivalence(self) -> None:
+ @mock.patch("builtins.open")
+ @mock.patch("basyx.aas.adapter.xml.xml_deserialization.read_aas_xml_file", autospec=True)
+ @mock.patch("aas_compliance_tool.compliance_check_xml.AASDataChecker", autospec=True)
+ def test_check_xml_files_equivalence_file1_fail_on_deserialization(self, mock_data_checker, mock_read_xml_file,
+ mock_open) -> None:
manager = ComplianceToolStateManager()
- script_dir = os.path.dirname(__file__)
- file_path_1 = os.path.join(script_dir, 'files/test_not_deserializable_aas.xml')
- file_path_2 = os.path.join(script_dir, 'files/test_empty.xml')
- compliance_tool.check_xml_files_equivalence(file_path_1, file_path_2, manager)
+ call_count = [0]
+
+ def mock_first_fails(*args, **kwargs):
+ call_count[0] += 1
+ if call_count[0] == 1:
+ create_mock_effect('basyx.aas.adapter.xml.xml_deserialization', 'error')(*args, **kwargs)
+
+ mock_read_xml_file.side_effect = mock_first_fails
+ compliance_tool.check_xml_files_equivalence("", "", manager)
+
self.assertEqual(5, len(manager.steps))
self.assertEqual(Status.SUCCESS, manager.steps[0].status)
self.assertEqual(Status.FAILED, manager.steps[1].status)
+ self.assertIn("Test error!", manager.format_step(1, verbose_level=1))
self.assertEqual(Status.SUCCESS, manager.steps[2].status)
self.assertEqual(Status.SUCCESS, manager.steps[3].status)
self.assertEqual(Status.NOT_EXECUTED, manager.steps[4].status)
- manager.steps = []
- compliance_tool.check_xml_files_equivalence(file_path_2, file_path_1, manager)
+ @mock.patch("builtins.open")
+ @mock.patch("basyx.aas.adapter.xml.xml_deserialization.read_aas_xml_file", autospec=True)
+ @mock.patch("aas_compliance_tool.compliance_check_xml.AASDataChecker", autospec=True)
+ def test_check_xml_files_equivalence_file2_fail_on_deserialization(self, mock_data_checker, mock_read_xml_file,
+ mock_open) -> None:
+ manager = ComplianceToolStateManager()
+
+ call_count = [0]
+
+ def mock_second_fails(*args, **kwargs):
+ call_count[0] += 1
+ if call_count[0] == 2:
+ create_mock_effect('basyx.aas.adapter.xml.xml_deserialization', 'error')(*args, **kwargs)
+
+ mock_read_xml_file.side_effect = mock_second_fails
+ compliance_tool.check_xml_files_equivalence("", "", manager)
+
self.assertEqual(5, len(manager.steps))
self.assertEqual(Status.SUCCESS, manager.steps[0].status)
self.assertEqual(Status.SUCCESS, manager.steps[1].status)
self.assertEqual(Status.SUCCESS, manager.steps[2].status)
self.assertEqual(Status.FAILED, manager.steps[3].status)
+ self.assertIn("Test error!", manager.format_step(3, verbose_level=1))
self.assertEqual(Status.NOT_EXECUTED, manager.steps[4].status)
- manager.steps = []
- file_path_3 = os.path.join(script_dir, 'files/test_demo_full_example.xml')
- file_path_4 = os.path.join(script_dir, 'files/test_demo_full_example.xml')
- compliance_tool.check_xml_files_equivalence(file_path_3, file_path_4, manager)
+ @mock.patch("builtins.open")
+ @mock.patch("basyx.aas.adapter.xml.xml_deserialization.read_aas_xml_file", autospec=True)
+ @mock.patch("aas_compliance_tool.compliance_check_xml.AASDataChecker", autospec=True)
+ def test_check_xml_files_equivalence_success(self, mock_data_checker, mock_read_xml_file, mock_open) -> None:
+ manager = ComplianceToolStateManager()
+
+ mock_data_checker.return_value.checks = []
+ type(mock_data_checker.return_value).failed_checks = mock.PropertyMock(side_effect=lambda: iter([]))
+ compliance_tool.check_xml_files_equivalence("", "", manager)
+
self.assertEqual(5, len(manager.steps))
self.assertEqual(Status.SUCCESS, manager.steps[0].status)
self.assertEqual(Status.SUCCESS, manager.steps[1].status)
@@ -158,30 +181,22 @@ def test_check_xml_files_equivalence(self) -> None:
self.assertEqual(Status.SUCCESS, manager.steps[3].status)
self.assertEqual(Status.SUCCESS, manager.steps[4].status)
- manager.steps = []
- file_path_3 = os.path.join(script_dir, 'files/test_demo_full_example.xml')
- file_path_4 = os.path.join(script_dir, 'files/test_demo_full_example_wrong_attribute.xml')
- compliance_tool.check_xml_files_equivalence(file_path_3, file_path_4, manager)
- self.assertEqual(5, len(manager.steps))
- self.assertEqual(Status.SUCCESS, manager.steps[0].status)
- self.assertEqual(Status.SUCCESS, manager.steps[1].status)
- self.assertEqual(Status.SUCCESS, manager.steps[2].status)
- self.assertEqual(Status.SUCCESS, manager.steps[3].status)
- self.assertEqual(Status.FAILED, manager.steps[4].status)
- self.assertEqual('FAILED: Check if data in files are equal\n - ERROR: Attribute id_short of '
- 'AssetAdministrationShell[https://acplt.org/Test_AssetAdministrationShell] must be == '
- 'TestAssetAdministrationShell123 (value=\'TestAssetAdministrationShell\')',
- manager.format_step(4, verbose_level=1))
+ @mock.patch("builtins.open")
+ @mock.patch("basyx.aas.adapter.xml.xml_deserialization.read_aas_xml_file", autospec=True)
+ @mock.patch("aas_compliance_tool.compliance_check_xml.AASDataChecker", autospec=True)
+ def test_check_xml_files_equivalence_fail_on_check(self, mock_data_checker: mock.MagicMock, mock_read_xml_file,
+ mock_open) -> None:
+ manager = ComplianceToolStateManager()
+
+ failed = [CheckResult("Expected Behavior", False, dict())]
+ mock_data_checker.return_value.checks = failed
+ type(mock_data_checker.return_value).failed_checks = mock.PropertyMock(side_effect=lambda: iter(failed))
+ compliance_tool.check_xml_files_equivalence("", "", manager)
- manager.steps = []
- compliance_tool.check_xml_files_equivalence(file_path_4, file_path_3, manager)
self.assertEqual(5, len(manager.steps))
self.assertEqual(Status.SUCCESS, manager.steps[0].status)
self.assertEqual(Status.SUCCESS, manager.steps[1].status)
self.assertEqual(Status.SUCCESS, manager.steps[2].status)
self.assertEqual(Status.SUCCESS, manager.steps[3].status)
self.assertEqual(Status.FAILED, manager.steps[4].status)
- self.assertEqual('FAILED: Check if data in files are equal\n - ERROR: Attribute id_short of '
- 'AssetAdministrationShell[https://acplt.org/Test_AssetAdministrationShell] must be == '
- 'TestAssetAdministrationShell (value=\'TestAssetAdministrationShell123\')',
- manager.format_step(4, verbose_level=1))
+ self.assertIn("Expected Behavior", manager.format_step(4, verbose_level=1))
diff --git a/compliance_tool/test/test_compliance_tool_integration.py b/compliance_tool/test/test_compliance_tool_integration.py
new file mode 100644
index 000000000..512c312e8
--- /dev/null
+++ b/compliance_tool/test/test_compliance_tool_integration.py
@@ -0,0 +1,132 @@
+# Copyright (c) 2026 the Eclipse BaSyx Authors
+#
+# This program and the accompanying materials are made available under the terms of the MIT License, available in
+# the LICENSE file of this project.
+#
+# SPDX-License-Identifier: MIT
+"""
+Integration tests that exercise the full compliance check pipeline without mocking AASDataChecker.
+Each test creates a real file on disk and runs the compliance tool CLI against it.
+"""
+import tempfile
+import unittest
+from contextlib import redirect_stdout
+from io import StringIO
+from typing import cast
+from unittest.mock import patch
+
+from basyx.aas import model
+from basyx.aas.adapter import aasx
+from basyx.aas.adapter.json import write_aas_json_file
+from basyx.aas.adapter.xml import write_aas_xml_file
+from basyx.aas.examples.data.example_aas import create_full_example
+
+from aas_compliance_tool.cli import main
+
+
+class ComplianceToolIntegrationTest(unittest.TestCase):
+
+ def _call_main(self, args) -> str:
+ buf = StringIO()
+ with patch('sys.argv', ['compliance_tool'] + args):
+ with redirect_stdout(buf):
+ main()
+ return buf.getvalue()
+
+ def _modified_example(self) -> model.DictIdentifiableStore:
+ modified_example = create_full_example()
+ submodel = cast(model.Submodel, modified_example.get_item("https://example.org/Test_Submodel"))
+ submodel.submodel_element.remove(next(iter(submodel.submodel_element)))
+ return modified_example
+
+ # --- JSON ---
+
+ def test_json_example_success(self) -> None:
+ with tempfile.NamedTemporaryFile(suffix=".json") as tf:
+ self._call_main(['c', tf.name, '--json'])
+ output = self._call_main(['e', tf.name, '--json'])
+ self.assertNotIn('FAILED:', output)
+
+ def test_json_example_fail(self) -> None:
+ with tempfile.NamedTemporaryFile(suffix=".json", mode='w', encoding='utf-8-sig') as tf:
+ write_aas_json_file(tf, self._modified_example())
+ tf.flush()
+ output = self._call_main(['e', tf.name, '--json'])
+ self.assertIn('FAILED:', output)
+
+ def test_json_files_equivalence_success(self) -> None:
+ with tempfile.NamedTemporaryFile(suffix=".json") as tf1, \
+ tempfile.NamedTemporaryFile(suffix=".json") as tf2:
+ self._call_main(['c', tf1.name, '--json'])
+ self._call_main(['c', tf2.name, '--json'])
+ output = self._call_main(['f', tf1.name, tf2.name, '--json'])
+ self.assertNotIn('FAILED:', output)
+
+ def test_json_files_equivalence_fail(self) -> None:
+ with tempfile.NamedTemporaryFile(suffix=".json") as tf1, \
+ tempfile.NamedTemporaryFile(suffix=".json", mode='w', encoding='utf-8-sig') as tf2:
+ self._call_main(['c', tf1.name, '--json'])
+ write_aas_json_file(tf2, self._modified_example())
+ tf2.flush()
+ output = self._call_main(['f', tf1.name, tf2.name, '--json'])
+ self.assertIn('FAILED:', output)
+
+ # --- XML ---
+
+ def test_xml_example_success(self) -> None:
+ with tempfile.NamedTemporaryFile(suffix=".xml") as tf:
+ self._call_main(['c', tf.name, '--xml'])
+ output = self._call_main(['e', tf.name, '--xml'])
+ self.assertNotIn('FAILED:', output)
+
+ def test_xml_example_fail(self) -> None:
+ with tempfile.NamedTemporaryFile(suffix=".xml") as tf:
+ write_aas_xml_file(tf, self._modified_example())
+ tf.flush()
+ output = self._call_main(['e', tf.name, '--xml'])
+ self.assertIn('FAILED:', output)
+
+ def test_xml_files_equivalence_success(self) -> None:
+ with tempfile.NamedTemporaryFile(suffix=".xml") as tf1, \
+ tempfile.NamedTemporaryFile(suffix=".xml") as tf2:
+ self._call_main(['c', tf1.name, '--xml'])
+ self._call_main(['c', tf2.name, '--xml'])
+ output = self._call_main(['f', tf1.name, tf2.name, '--xml'])
+ self.assertNotIn('FAILED:', output)
+
+ def test_xml_files_equivalence_fail(self) -> None:
+ with tempfile.NamedTemporaryFile(suffix=".xml") as tf1, \
+ tempfile.NamedTemporaryFile(suffix=".xml") as tf2:
+ self._call_main(['c', tf1.name, '--xml'])
+ write_aas_xml_file(tf2, self._modified_example())
+ tf2.flush()
+ output = self._call_main(['f', tf1.name, tf2.name, '--xml'])
+ self.assertIn('FAILED:', output)
+
+ # --- AASX ---
+
+ def test_aasx_example_success(self) -> None:
+ with tempfile.NamedTemporaryFile(suffix=".aasx") as tf:
+ self._call_main(['c', tf.name, '--xml', '--aasx'])
+ output = self._call_main(['e', tf.name, '--xml', '--aasx'])
+ self.assertNotIn('FAILED:', output)
+
+ def test_aasx_files_equivalence_success(self) -> None:
+ with tempfile.NamedTemporaryFile(suffix=".aasx") as tf1, \
+ tempfile.NamedTemporaryFile(suffix=".aasx") as tf2:
+ self._call_main(['c', tf1.name, '--xml', '--aasx'])
+ self._call_main(['c', tf2.name, '--xml', '--aasx'])
+ output = self._call_main(['f', tf1.name, tf2.name, '--xml', '--aasx'])
+ self.assertNotIn('FAILED:', output)
+
+ def test_aasx_files_equivalence_fail(self) -> None:
+ with tempfile.NamedTemporaryFile(suffix=".aasx") as tf1, \
+ tempfile.NamedTemporaryFile(suffix=".aasx") as tf2:
+ self._call_main(['c', tf1.name, '--xml', '--aasx'])
+
+ with aasx.AASXWriter(tf2.name) as writer:
+ writer.write_aas([], model.DictIdentifiableStore(), aasx.DictSupplementaryFileContainer())
+
+ output = self._call_main(['f', tf1.name, tf2.name, '--xml', '--aasx'])
+
+ self.assertIn('FAILED:', output)
diff --git a/etc/scripts/check_python_versions_supported.py b/etc/scripts/check_python_versions_supported.py
index 5aad31cca..5980102f6 100644
--- a/etc/scripts/check_python_versions_supported.py
+++ b/etc/scripts/check_python_versions_supported.py
@@ -14,7 +14,9 @@ def main(min_version: str, max_version: str) -> None:
response = requests.get("https://endoflife.date/api/python.json")
response.raise_for_status()
eol_data = response.json()
- eol_versions = {entry["cycle"]: {"eol": entry["eol"], "releaseDate": entry["releaseDate"]} for entry in eol_data}
+ eol_versions = {
+ entry["cycle"]: {"eol": entry["eol"], "releaseDate": entry["releaseDate"]} for entry in eol_data
+ }
# Get current date to compare with EoL and release dates
current_date = datetime.now().date()
diff --git a/etc/scripts/set_copyright_year.sh b/etc/scripts/set_copyright_year.sh
index de6c97eea..a69e66ef3 100755
--- a/etc/scripts/set_copyright_year.sh
+++ b/etc/scripts/set_copyright_year.sh
@@ -2,43 +2,75 @@
# Usage: ./set_copyright_year.sh [PATHS]
#
# This is a small script for setting the correct copyright year
-# for each given file (i.e. the year the file was last changed).
-# Instead of file paths you can also specify directories, in which
-# case the script will attempt to set the copyright year for all
-# files in the given directories. Globbing is also possible.
+# for each given source file (i.e. the year the file was last
+# changed) and LICENSE file (i.e. the latest modification year
+# across all files). Instead of file paths you can also specify
+# directories, in which case the script will attempt to set the
+# copyright year for all files in the given directories.
+# Globbing is also possible.
#
-# The script will check the first two lines for a copyright
-# notice (in case the first line is a shebang).
+# In source files, the script checks the first two lines for a
+# copyright notice (in case the first line is a shebang).
+# In the LICENSE file, it checks the first three lines for a
+# copyright notice containing a year range.
#
# Run this script with --check to have it raise an error if it
# would change anything.
+# Initialise a variable to track if any error occurred
+EXIT_CODE=0
# Set CHECK_MODE based on whether --check is passed
- CHECK_MODE=false
- if [[ "$1" == "--check" ]]; then
- CHECK_MODE=true
- shift # Remove --check from the arguments
- fi
+CHECK_MODE=false
+if [[ "$1" == "--check" ]]; then
+ CHECK_MODE=true
+ shift # Remove --check from the arguments
+fi
+
+# Initialise a variable to track the latest modification year across all files
+max_year=""
+# Validate the copyright year of each source file
while read -rd $'\0' year file; do
+ # Extract the current modification year and update variable
+ if [[ -z "$max_year" || "$year" -gt "$max_year" ]]; then
+ max_year="$year"
+ fi
+
# Extract the first year from the copyright notice
current_year=$(sed -n '1,2s/^\(# Copyright (c) \)\([[:digit:]]\{4,\}\).*/\2/p' "$file")
- # Skip the file if no year is found
+ # Skip the source file if no year is found
if [[ -z "$current_year" ]]; then
continue
fi
+ # If in check mode, report the incorrect copyright year
if $CHECK_MODE && [[ "$current_year" != "$year" ]]; then
echo "Error: Copyright year mismatch in file $file. Expected $year, found $current_year."
- exit 1
+ # Set EXIT_CODE to 1 to indicate mismatch
+ EXIT_CODE=1
fi
+ # Otherwise rewrite the incorrect copyright year
if ! $CHECK_MODE && [[ "$current_year" != "$year" ]]; then
sed -i "1,2s/^\(# Copyright (c) \)[[:digit:]]\{4,\}/\1$year/" "$file"
echo "Updated copyright year in $file"
fi
done < <(git ls-files -z "$@" | xargs -0I{} git log -1 -z --format="%cd {}" --date="format:%Y" -- "{}")
+# Validate the copyright year of the LICENSE file
+license_current_year=$(sed -n '1,3{s/^Copyright (c) [[:digit:]]\{4\}-\([[:digit:]]\{4\}\).*/\1/p}' LICENSE)
+
+if $CHECK_MODE && [[ -n "$license_current_year" && "$license_current_year" != "$max_year" ]]; then
+ echo "Error: Copyright year mismatch in file LICENSE. Expected $max_year, found $license_current_year."
+ EXIT_CODE=1
+fi
+
+if ! $CHECK_MODE && [[ -n "$license_current_year" && "$license_current_year" != "$max_year" ]]; then
+ sed -i "1,3s/^\(Copyright (c) [[:digit:]]\{4\}-\)[[:digit:]]\{4\}/\1$max_year/" LICENSE
+ echo "Updated copyright year in LICENSE"
+fi
+
+exit $EXIT_CODE
diff --git a/sdk/README.md b/sdk/README.md
index 74d63b8f3..4490ea883 100644
--- a/sdk/README.md
+++ b/sdk/README.md
@@ -8,7 +8,6 @@ for Industry 4.0 Systems.
## Features
* Modelling of AASs as Python objects
- * **except for**: *HasDataSpecification*
* Reading and writing of AASX package files
* (De-)serialization of AAS objects into/from JSON and XML
* Storing of AAS objects in CouchDB, Backend infrastructure for easy expansion
@@ -17,7 +16,7 @@ for Industry 4.0 Systems.
### Project Structure
-The BaSyx Python SDK project provides the `basax.aas` Python package with 6 submodules:
+The BaSyx Python SDK project provides the `basyx.aas` Python package with 5 submodules:
* `basyx.aas.model`: The AAS metamodel implemented in python
* `basyx.aas.adapter`: Adapters for various file formats
@@ -55,7 +54,7 @@ Development/testing/documentation/example dependencies:
* `lxml-stubs` (Apache License)
* `types-python-dateutil` (Apache License v2.0)
-Dependencies for building the documentation (see `docs/add-requirements.txt`):
+Dependencies for building the documentation (see the `docs` extra in `pyproject.toml`):
* `Sphinx` and its dependencies (BSD 2-clause License, MIT License, Apache License)
* `sphinx-rtd-theme` and its dependencies (MIT License, PSF License)
* `sphinx-argparse` (MIT License)
@@ -87,7 +86,7 @@ Create a `Submodel`:
```python
from basyx.aas import model # Import all BaSyx Python SDK classes from the model package
-identifier = 'https://acplt.org/Simple_Submodel'
+identifier = 'https://example.org/Simple_Submodel'
submodel = model.Submodel(identifier)
```
@@ -97,7 +96,7 @@ Create a `Property` and add it to the `Submodel`:
semantic_reference = model.ExternalReference(
(model.Key(
type_=model.KeyTypes.GLOBAL_REFERENCE,
- value='http://acplt.org/Properties/SimpleProperty'
+ value='http://example.org/Properties/SimpleProperty'
),)
)
property = model.Property(
@@ -113,7 +112,7 @@ Serialize the `Submodel` to XML:
```python
from basyx.aas.adapter.xml import write_aas_xml_file
-data: model.DictObjectStore[model.Identifiable] = model.DictObjectStore()
+data: model.DictIdentifiableStore[model.Identifiable] = model.DictIdentifiableStore()
data.add(submodel)
write_aas_xml_file(file='Simple_Submodel.xml', data=data)
```
@@ -124,10 +123,11 @@ 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_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 *CouchDBObjectStore* to manage and retrieve AAS objects in a CouchDB document database
+* [`tutorial_backend_couchdb`](./basyx/aas/examples/tutorial_backend_couchdb.py): Use the *CouchDBIdentifiableStore* to manage and retrieve AAS objects in a CouchDB document database
### Documentation
diff --git a/sdk/basyx/aas/adapter/__init__.py b/sdk/basyx/aas/adapter/__init__.py
index 0fca01291..166b6b90f 100644
--- a/sdk/basyx/aas/adapter/__init__.py
+++ b/sdk/basyx/aas/adapter/__init__.py
@@ -11,24 +11,24 @@
from basyx.aas.adapter.aasx import AASXReader, DictSupplementaryFileContainer
from basyx.aas.adapter.json import read_aas_json_file_into
from basyx.aas.adapter.xml import read_aas_xml_file_into
-from basyx.aas.model.provider import DictObjectStore
+from basyx.aas.model.provider import DictIdentifiableStore
from pathlib import Path
from typing import Union
-def load_directory(directory: Union[Path, str]) -> tuple[DictObjectStore, DictSupplementaryFileContainer]:
+def load_directory(directory: Union[Path, str]) -> tuple[DictIdentifiableStore, DictSupplementaryFileContainer]:
"""
- Create a new :class:`~basyx.aas.model.provider.DictObjectStore` and use it to load Asset Administration Shell and
- Submodel files in ``AASX``, ``JSON`` and ``XML`` format from a given directory into memory. Additionally, load all
- embedded supplementary files into a new :class:`~basyx.aas.adapter.aasx.DictSupplementaryFileContainer`.
+ Create a new :class:`~basyx.aas.model.provider.DictIdentifiableStore` and use it to load Asset Administration Shell
+ and Submodel files in ``AASX``, ``JSON`` and ``XML`` format from a given directory into memory. Additionally, load
+ all embedded supplementary files into a new :class:`~basyx.aas.adapter.aasx.DictSupplementaryFileContainer`.
:param directory: :class:`~pathlib.Path` or ``str`` pointing to the directory containing all Asset Administration
Shell and Submodel files to load
- :return: Tuple consisting of a :class:`~basyx.aas.model.provider.DictObjectStore` and a
+ :return: Tuple consisting of a :class:`~basyx.aas.model.provider.DictIdentifiableStore` and a
:class:`~basyx.aas.adapter.aasx.DictSupplementaryFileContainer` containing all loaded data
"""
- dict_object_store: DictObjectStore = DictObjectStore()
+ dict_identifiable_store: DictIdentifiableStore = DictIdentifiableStore()
file_container: DictSupplementaryFileContainer = DictSupplementaryFileContainer()
directory = Path(directory)
@@ -40,12 +40,12 @@ def load_directory(directory: Union[Path, str]) -> tuple[DictObjectStore, DictSu
suffix = file.suffix.lower()
if suffix == ".json":
with open(file) as f:
- read_aas_json_file_into(dict_object_store, f)
+ read_aas_json_file_into(dict_identifiable_store, f)
elif suffix == ".xml":
with open(file) as f:
- read_aas_xml_file_into(dict_object_store, f)
+ read_aas_xml_file_into(dict_identifiable_store, f)
elif suffix == ".aasx":
with AASXReader(file) as reader:
- reader.read_into(object_store=dict_object_store, file_store=file_container)
+ reader.read_into(object_store=dict_identifiable_store, file_store=file_container)
- return dict_object_store, file_container
+ return dict_identifiable_store, file_container
diff --git a/sdk/basyx/aas/adapter/_generic.py b/sdk/basyx/aas/adapter/_generic.py
index 65d14d8d3..aa4f9d692 100644
--- a/sdk/basyx/aas/adapter/_generic.py
+++ b/sdk/basyx/aas/adapter/_generic.py
@@ -1,4 +1,4 @@
-# Copyright (c) 2025 the Eclipse BaSyx Authors
+# Copyright (c) 2026 the Eclipse BaSyx Authors
#
# This program and the accompanying materials are made available under the terms of the MIT License, available in
# the LICENSE file of this project.
@@ -27,7 +27,7 @@
)
# XML Namespace definition
-XML_NS_MAP = {"aas": "https://admin-shell.io/aas/3/0"}
+XML_NS_MAP = {"aas": "https://admin-shell.io/aas/3/1"}
XML_NS_AAS = "{" + XML_NS_MAP["aas"] + "}"
MODELLING_KIND: Dict[model.ModellingKind, str] = {
@@ -37,7 +37,8 @@
ASSET_KIND: Dict[model.AssetKind, str] = {
model.AssetKind.TYPE: 'Type',
model.AssetKind.INSTANCE: 'Instance',
- model.AssetKind.NOT_APPLICABLE: 'NotApplicable'}
+ model.AssetKind.NOT_APPLICABLE: 'NotApplicable',
+ model.AssetKind.ROLE: 'Role'}
QUALIFIER_KIND: Dict[model.QualifierKind, str] = {
model.QualifierKind.CONCEPT_QUALIFIER: 'ConceptQualifier',
diff --git a/sdk/basyx/aas/adapter/aasx.py b/sdk/basyx/aas/adapter/aasx.py
index 8bb5958f6..82fe4b76f 100644
--- a/sdk/basyx/aas/adapter/aasx.py
+++ b/sdk/basyx/aas/adapter/aasx.py
@@ -1,4 +1,4 @@
-# Copyright (c) 2025 the Eclipse BaSyx Authors
+# Copyright (c) 2026 the Eclipse BaSyx Authors
#
# This program and the accompanying materials are made available under the terms of the MIT License, available in
# the LICENSE file of this project.
@@ -52,7 +52,7 @@ class AASXReader:
.. code-block:: python
- objects = DictObjectStore()
+ identifiables = DictIdentifiableStore()
files = DictSupplementaryFileContainer()
with AASXReader("filename.aasx") as reader:
meta_data = reader.get_core_properties()
@@ -140,23 +140,31 @@ def read_into(self, object_store: model.AbstractObjectStore,
:return: A set of the :class:`Identifiers ` of all
:class:`~basyx.aas.model.base.Identifiable` objects parsed from the AASX file
"""
+ # Format of supported and deprecated AASX relationship URL
+ AASX_REL_BASE = "http://admin-shell.io/aasx/relationships"
+ AASX_REL_BASE_DEPRECATED = "http://www.admin-shell.io/aasx/relationships"
+ RELATIONSHIP_TYPE_AASX_ORIGIN = f"{AASX_REL_BASE}/aasx-origin"
+ RELATIONSHIP_TYPE_AASX_ORIGIN_DEPRECATED = f"{AASX_REL_BASE_DEPRECATED}/aasx-origin"
+
# Find AASX-Origin part
core_rels = self.reader.get_related_parts_by_type()
try:
aasx_origin_part = core_rels[RELATIONSHIP_TYPE_AASX_ORIGIN][0]
except IndexError as e:
- if core_rels.get("http://www.admin-shell.io/aasx/relationships/aasx-origin"):
+ if core_rels.get(RELATIONSHIP_TYPE_AASX_ORIGIN_DEPRECATED):
# Since there are many AASX files with this (wrong) relationship URls in the wild, we make an exception
# and try to read it anyway. However, we notify the user that this may lead to data loss, since it is
# highly likely that the other relationship URLs are also wrong in that file.
# See also [#383](https://github.com/eclipse-basyx/basyx-python-sdk/issues/383) for the discussion.
- logger.warning("SPECIFICATION VIOLATED: The Relationship-URL in your AASX file "
- "('http://www.admin-shell.io/aasx/relationships/aasx-origin') "
- "is not valid, it should be 'http://admin-shell.io/aasx/relationships/aasx-origin'. "
- "We try to read the AASX file anyway, but this cannot guaranteed in the future,"
- "and the file may not be fully readable, so data losses may occur."
- "Please fix this and/or notify the source of the AASX.")
- aasx_origin_part = core_rels["http://www.admin-shell.io/aasx/relationships/aasx-origin"][0]
+ logger.warning(
+ "Deprecated AASX relationship URL format used: '%s'. "
+ "The supported AASX relationship URL format is: '%s'. "
+ "Support for the deprecated form is kept for compatibility, but data losses may occur. "
+ "Please fix the format and notify the author of the given AASX.",
+ RELATIONSHIP_TYPE_AASX_ORIGIN_DEPRECATED,
+ RELATIONSHIP_TYPE_AASX_ORIGIN,
+ )
+ aasx_origin_part = core_rels[RELATIONSHIP_TYPE_AASX_ORIGIN_DEPRECATED][0]
else:
raise ValueError("Not a valid AASX file: aasx-origin Relationship is missing.") from e
@@ -231,15 +239,17 @@ def _read_aas_part_into(self, part_name: str,
read_identifiables.add(obj.id)
if isinstance(obj, model.Submodel):
self._collect_supplementary_files(part_name, obj, file_store)
+ elif isinstance(obj, model.AssetAdministrationShell):
+ self._collect_supplementary_files(part_name, obj, file_store)
- def _parse_aas_part(self, part_name: str, **kwargs) -> model.DictObjectStore:
+ def _parse_aas_part(self, part_name: str, **kwargs) -> model.DictIdentifiableStore:
"""
Helper function to parse the AAS objects from a single JSON or XML part of the AASX package.
This method chooses and calls the correct parser.
:param part_name: The OPC part name of the part to be parsed
- :return: A DictObjectStore containing the parsed AAS objects
+ :return: A DictIdentifiableStore containing the parsed AAS objects
"""
content_type = self.reader.get_content_type(part_name)
extension = part_name.split("/")[-1].split(".")[-1]
@@ -259,35 +269,61 @@ def _parse_aas_part(self, part_name: str, **kwargs) -> model.DictObjectStore:
logger.error(error_message)
else:
raise ValueError(error_message)
- return model.DictObjectStore()
+ return model.DictIdentifiableStore()
- def _collect_supplementary_files(self, part_name: str, submodel: model.Submodel,
+ def _collect_supplementary_files(self, part_name: str,
+ root_element: Union[model.AssetAdministrationShell, model.Submodel],
file_store: "AbstractSupplementaryFileContainer") -> None:
"""
- Helper function to search File objects within a single parsed Submodel, extract the referenced supplementary
- files and update the File object's values with the absolute path.
+ Helper function to search File objects within a single parsed AssetAdministrationShell or Submodel.
+ Resolve their absolute paths, and update the corresponding File/Thumbnail objects with the absolute path.
- :param part_name: The OPC part name of the part the Submodel has been parsed from. This is used to resolve
+ :param part_name: The OPC part name of the part the root_element has been parsed from. This is used to resolve
relative file paths.
- :param submodel: The Submodel to process
+ :param root_element: The AssetAdministrationShell or Submodel to process
:param file_store: The SupplementaryFileContainer to add the extracted supplementary files to
"""
- for element in traversal.walk_submodel(submodel):
- if isinstance(element, model.File):
- if element.value is None:
- continue
- # Only absolute-path references and relative-path URI references (see RFC 3986, sec. 4.2) are considered
- # to refer to files within the AASX package. Thus, we must skip all other types of URIs (esp. absolute
- # URIs and network-path references)
- if element.value.startswith('//') or ':' in element.value.split('/')[0]:
- logger.info(f"Skipping supplementary file {element.value}, since it seems to be an absolute URI or "
- f"network-path URI reference")
- continue
- absolute_name = pyecma376_2.package_model.part_realpath(element.value, part_name)
- logger.debug(f"Reading supplementary file {absolute_name} from AASX package ...")
- with self.reader.open_part(absolute_name) as p:
- final_name = file_store.add_file(absolute_name, p, self.reader.get_content_type(absolute_name))
- element.value = final_name
+ if isinstance(root_element, model.AssetAdministrationShell):
+ if (root_element.asset_information.default_thumbnail and
+ root_element.asset_information.default_thumbnail.path):
+ file_name = self._add_supplementary_file(part_name,
+ root_element.asset_information.default_thumbnail.path,
+ file_store)
+ if file_name:
+ root_element.asset_information.default_thumbnail.path = file_name
+ if isinstance(root_element, model.Submodel):
+ for element in traversal.walk_submodel(root_element):
+ if isinstance(element, model.File):
+ if element.value is None:
+ continue
+ final_name = self._add_supplementary_file(part_name, element.value, file_store)
+ if final_name:
+ element.value = final_name
+
+ def _add_supplementary_file(self, part_name: str, file_path: str,
+ file_store: "AbstractSupplementaryFileContainer") -> Optional[str]:
+ """
+ Helper function to extract a single referenced supplementary file
+ and return the absolute path within the AASX package.
+
+ :param part_name: The OPC part name of the part the root_element has been parsed from. This is used to resolve
+ relative file paths.
+ :param file_path: The file path or URI reference of the supplementary file to be extracted
+ :param file_store: The SupplementaryFileContainer to add the extracted supplementary files to
+ :return: The stored file name as returned by *file_store*, or ``None`` if the reference was skipped.
+ """
+ # Only absolute-path references and relative-path URI references (see RFC 3986, sec. 4.2) are considered
+ # to refer to files within the AASX package. Thus, we must skip all other types of URIs (esp. absolute
+ # URIs and network-path references)
+ if file_path.startswith('//') or ':' in file_path.split('/')[0]:
+ logger.info(f"Skipping supplementary file {file_path}, since it seems to be an absolute URI or "
+ f"network-path URI reference")
+ return None
+ absolute_name = pyecma376_2.package_model.part_realpath(file_path, part_name)
+ logger.debug(f"Reading supplementary file {absolute_name} from AASX package ...")
+ with self.reader.open_part(absolute_name) as p:
+ final_name = file_store.add_file(absolute_name, p, self.reader.get_content_type(absolute_name))
+ return final_name
class AASXWriter:
@@ -304,10 +340,10 @@ class AASXWriter:
cp.created = datetime.datetime.now()
with AASXWriter("filename.aasx") as writer:
- writer.write_aas("https://acplt.org/AssetAdministrationShell",
+ writer.write_aas("https://example.org/AssetAdministrationShell",
object_store,
file_store)
- writer.write_aas("https://acplt.org/AssetAdministrationShell2",
+ writer.write_aas("https://example.org/AssetAdministrationShell2",
object_store,
file_store)
writer.write_core_properties(cp)
@@ -352,7 +388,7 @@ def __init__(self, file: Union[os.PathLike, str, IO], failsafe: bool = True):
def write_aas(self,
aas_ids: Union[model.Identifier, Iterable[model.Identifier]],
- object_store: model.AbstractObjectStore,
+ object_store: model.AbstractObjectStore[model.Identifier, model.Identifiable],
file_store: "AbstractSupplementaryFileContainer",
write_json: bool = False) -> None:
"""
@@ -402,10 +438,10 @@ def write_aas(self,
if isinstance(aas_ids, model.Identifier):
aas_ids = (aas_ids,)
- objects_to_be_written: model.DictObjectStore[model.Identifiable] = model.DictObjectStore()
+ objects_to_be_written: model.DictIdentifiableStore[model.Identifiable] = model.DictIdentifiableStore()
for aas_id in aas_ids:
try:
- aas = object_store.get_identifiable(aas_id)
+ aas = object_store.get_item(aas_id)
if not isinstance(aas, model.AssetAdministrationShell):
raise TypeError(f"Identifier {aas_id} does not belong to an AssetAdministrationShell object but to "
f"{aas!r}")
@@ -476,12 +512,13 @@ def write_aas_objects(self,
split_part: bool = False,
additional_relationships: Iterable[pyecma376_2.OPCRelationship] = ()) -> None:
"""
- A thin wrapper around :meth:`write_all_aas_objects` to ensure downwards compatibility
+ A thin wrapper around :meth:`write_all_aas_objects` to ensure backward compatibility
This method takes the AAS's :class:`~basyx.aas.model.base.Identifier` (as ``aas_id``) to retrieve it
- from the given object_store. If the list of written objects includes :class:`~basyx.aas.model.submodel.Submodel`
- objects, Supplementary files which are referenced by :class:`~basyx.aas.model.submodel.File` objects within
- those Submodels, are also added to the AASX package.
+ from the given object_store. If the list of written identifiables includes
+ :class:`~basyx.aas.model.submodel.Submodel` identifiables, Supplementary files which are referenced by
+ :class:`~basyx.aas.model.submodel.File` identifiables within those Submodels, are also added to the AASX
+ package.
.. attention::
@@ -491,14 +528,15 @@ def write_aas_objects(self,
:param part_name: Name of the Part within the AASX package to write the files to. Must be a valid ECMA376-2
part name and unique within the package. The extension of the part should match the data format (i.e.
'.json' if ``write_json`` else '.xml').
- :param object_ids: A list of :class:`Identifiers ` of the objects to be written
- to the AASX package. Only these :class:`~basyx.aas.model.base.Identifiable` objects (and included
- :class:`~basyx.aas.model.base.Referable` objects) are written to the package.
- :param object_store: The objects store to retrieve the :class:`~basyx.aas.model.base.Identifiable` objects from
+ :param object_ids: A list of :class:`Identifiers ` of the identifiables to be
+ written to the AASX package. Only these :class:`~basyx.aas.model.base.Identifiable` identifiables
+ (and included :class:`~basyx.aas.model.base.Referable` identifiables) are written to the package.
+ :param object_store: The identifiables store to retrieve the :class:`~basyx.aas.model.base.Identifiable`
+ identifiables from
:param file_store: The
:class:`SupplementaryFileContainer `
to retrieve supplementary files from (if there are any :class:`~basyx.aas.model.submodel.File`
- objects within the written objects.
+ identifiables within the written identifiables.
:param write_json: If ``True``, the part is written as a JSON file instead of an XML file. Defaults to
``False``.
:param split_part: If ``True``, no aas-spec relationship is added from the aasx-origin to this part. You must
@@ -506,29 +544,31 @@ def write_aas_objects(self,
:param additional_relationships: Optional OPC/ECMA376 relationships which should originate at the AAS object
part to be written, in addition to the aas-suppl relationships which are created automatically.
"""
- logger.debug(f"Writing AASX part {part_name} with AAS objects ...")
+ logger.debug(f"Writing AASX part {part_name} with AAS identifiables ...")
- objects: model.DictObjectStore[model.Identifiable] = model.DictObjectStore()
+ identifiables: model.DictIdentifiableStore[model.Identifiable] = model.DictIdentifiableStore()
- # Retrieve objects and scan for referenced supplementary files
+ # Retrieve identifiables and scan for referenced supplementary files
for identifier in object_ids:
try:
- the_object = object_store.get_identifiable(identifier)
+ the_identifiable = object_store.get_item(identifier)
except KeyError:
if self.failsafe:
- logger.error(f"Could not find object {identifier} in ObjectStore")
+ logger.error(f"Could not find identifiable {identifier} in IdentifiableStore")
continue
else:
- raise KeyError(f"Could not find object {identifier!r} in ObjectStore")
- objects.add(the_object)
+ raise KeyError(f"Could not find identifiable {identifier!r} in IdentifiableStore")
+ identifiables.add(the_identifiable)
- self.write_all_aas_objects(part_name, objects, file_store, write_json, split_part, additional_relationships)
+ self.write_all_aas_objects(
+ part_name, identifiables, file_store, write_json, split_part, additional_relationships
+ )
# TODO remove `split_part` parameter in future version.
# Not required anymore since changes from DotAAS version 2.0.1 to 3.0RC01
def write_all_aas_objects(self,
part_name: str,
- objects: model.AbstractObjectStore[model.Identifiable],
+ objects: model.AbstractObjectStore[model.Identifier, model.Identifiable],
file_store: "AbstractSupplementaryFileContainer",
write_json: bool = False,
split_part: bool = False,
@@ -541,7 +581,8 @@ def write_all_aas_objects(self,
contained objects into an ``aas_env`` part in the AASX package. If the ObjectStore includes
:class:`~basyx.aas.model.submodel.Submodel` objects, supplementary files which are referenced by
:class:`~basyx.aas.model.submodel.File` objects within those Submodels, are fetched from the ``file_store``
- and added to the AASX package.
+ and added to the AASX package. If the ObjectStore contains a thumbnail referenced by
+ ``default_thumbnail`` in :class:`~basyx.aas.model.aas.AssetInformation`, it is also added to the AASX package.
.. attention::
@@ -563,17 +604,24 @@ def write_all_aas_objects(self,
logger.debug(f"Writing AASX part {part_name} with AAS objects ...")
supplementary_files: List[str] = []
+ def _collect_supplementary_file(file_name: str) -> None:
+ # Skip File objects with empty value URI references that are considered to be no local file
+ # (absolute URIs or network-path URI references)
+ if file_name is None or file_name.startswith('//') or ':' in file_name.split('/')[0]:
+ return
+ supplementary_files.append(file_name)
+
# Retrieve objects and scan for referenced supplementary files
for the_object in objects:
+ if isinstance(the_object, model.AssetAdministrationShell):
+ if (the_object.asset_information.default_thumbnail and
+ the_object.asset_information.default_thumbnail.path):
+ _collect_supplementary_file(the_object.asset_information.default_thumbnail.path)
if isinstance(the_object, model.Submodel):
for element in traversal.walk_submodel(the_object):
if isinstance(element, model.File):
- file_name = element.value
- # Skip File objects with empty value URI references that are considered to be no local file
- # (absolute URIs or network-path URI references)
- if file_name is None or file_name.startswith('//') or ':' in file_name.split('/')[0]:
- continue
- supplementary_files.append(file_name)
+ if element.value:
+ _collect_supplementary_file(element.value)
# Add aas-spec relationship
if not split_part:
@@ -824,15 +872,27 @@ def add_file(self, name: str, file: IO[bytes], content_type: str) -> str:
if hash not in self._store:
self._store[hash] = data
self._store_refcount[hash] = 0
- name_map_data = (hash, content_type)
+ return self._assign_unique_name(name, hash, content_type)
+
+ def rename_file(self, old_name: str, new_name: str) -> str:
+ if old_name not in self._name_map:
+ raise KeyError(f"File with name {old_name} not found in SupplementaryFileContainer.")
+ if new_name == old_name:
+ return new_name
+ file_hash, file_content_type = self._name_map[old_name]
+ self._store_refcount[file_hash] -= 1
+ del self._name_map[old_name]
+ return self._assign_unique_name(new_name, file_hash, file_content_type)
+
+ def _assign_unique_name(self, name: str, sha: bytes, content_type: str) -> str:
new_name = name
i = 1
while True:
if new_name not in self._name_map:
- self._name_map[new_name] = name_map_data
- self._store_refcount[hash] += 1
+ self._name_map[new_name] = (sha, content_type)
+ self._store_refcount[sha] += 1
return new_name
- elif self._name_map[new_name] == name_map_data:
+ elif self._name_map[new_name] == (sha, content_type):
return new_name
new_name = self._append_counter(name, i)
i += 1
diff --git a/sdk/basyx/aas/adapter/json/json_deserialization.py b/sdk/basyx/aas/adapter/json/json_deserialization.py
index 84635703d..e6fc9b448 100644
--- a/sdk/basyx/aas/adapter/json/json_deserialization.py
+++ b/sdk/basyx/aas/adapter/json/json_deserialization.py
@@ -1,4 +1,4 @@
-# Copyright (c) 2025 the Eclipse BaSyx Authors
+# Copyright (c) 2026 the Eclipse BaSyx Authors
#
# This program and the accompanying materials are made available under the terms of the MIT License, available in
# the LICENSE file of this project.
@@ -21,7 +21,7 @@
takes a complete AAS JSON file, reads its contents and stores the objects in the provided
:class:`~basyx.aas.model.provider.AbstractObjectStore`. :meth:`read_aas_json_file` is a wrapper for this function.
Instead of storing the objects in a given :class:`~basyx.aas.model.provider.AbstractObjectStore`,
-it returns a :class:`~basyx.aas.model.provider.DictObjectStore` containing parsed objects.
+it returns a :class:`~basyx.aas.model.provider.DictIdentifiableStore` containing parsed objects.
The deserialization is performed in a bottom-up approach: The ``object_hook()`` method gets called for every parsed JSON
object (as dict) and checks for existence of the ``modelType`` attribute. If it is present, the ``AAS_CLASS_PARSERS``
@@ -278,7 +278,8 @@ def _amend_abstract_attributes(cls, obj: object, dct: Dict[str, object]) -> None
# TODO: remove the following type: ignore comment when mypy supports abstract types for Type[T]
# see https://github.com/python/mypy/issues/5374
model.EmbeddedDataSpecification(
- data_specification=cls._construct_reference(_get_ts(dspec, 'dataSpecification', dict)),
+ data_specification=cls._construct_external_reference(
+ _get_ts(dspec, 'dataSpecification', dict)),
data_specification_content=_get_ts(dspec, 'dataSpecificationContent',
model.DataSpecificationContent) # type: ignore
)
@@ -426,7 +427,8 @@ def _construct_value_list(cls, dct: Dict[str, object]) -> model.ValueList:
def _construct_value_reference_pair(cls, dct: Dict[str, object],
object_class=model.ValueReferencePair) -> model.ValueReferencePair:
return object_class(value=_get_ts(dct, 'value', str),
- value_id=cls._construct_reference(_get_ts(dct, 'valueId', dict)))
+ value_id=cls._construct_reference(_get_ts(dct, 'valueId', dict))
+ if 'valueId' in dct else None)
# #############################################################################
# Direct Constructor Methods (for classes with `modelType`) starting from here
@@ -511,8 +513,6 @@ def _construct_data_specification_iec61360(cls, dct: Dict[str, object],
ret.value_list = cls._construct_value_list(_get_ts(dct, 'valueList', dict))
if 'value' in dct:
ret.value = _get_ts(dct, 'value', str)
- if 'valueId' in dct:
- ret.value_id = cls._construct_reference(_get_ts(dct, 'valueId', dict))
if 'levelType' in dct:
for k, v in _get_ts(dct, 'levelType', dict).items():
if v:
@@ -528,9 +528,12 @@ def _construct_entity(cls, dct: Dict[str, object], object_class=model.Entity) ->
if 'specificAssetIds' in dct:
for desc_data in _get_ts(dct, "specificAssetIds", list):
specific_asset_id.add(cls._construct_specific_asset_id(desc_data, model.SpecificAssetId))
-
+ if 'entityType' in dct:
+ entity_type = ENTITY_TYPES_INVERSE[_get_ts(dct, 'entityType', str)]
+ else:
+ entity_type = None
ret = object_class(id_short=None,
- entity_type=ENTITY_TYPES_INVERSE[_get_ts(dct, "entityType", str)],
+ entity_type=entity_type,
global_asset_id=global_asset_id,
specific_asset_id=specific_asset_id)
cls._amend_abstract_attributes(ret, dct)
@@ -632,8 +635,8 @@ def _construct_operation(cls, dct: Dict[str, object], object_class=model.Operati
def _construct_relationship_element(
cls, dct: Dict[str, object], object_class=model.RelationshipElement) -> model.RelationshipElement:
ret = object_class(id_short=None,
- first=cls._construct_reference(_get_ts(dct, 'first', dict)),
- second=cls._construct_reference(_get_ts(dct, 'second', dict)))
+ first=cls._construct_reference(_get_ts(dct, 'first', dict)) if 'first' in dct else None,
+ second=cls._construct_reference(_get_ts(dct, 'second', dict)) if 'second' in dct else None)
cls._amend_abstract_attributes(ret, dct)
return ret
@@ -643,8 +646,8 @@ def _construct_annotated_relationship_element(
-> model.AnnotatedRelationshipElement:
ret = object_class(
id_short=None,
- first=cls._construct_reference(_get_ts(dct, 'first', dict)),
- second=cls._construct_reference(_get_ts(dct, 'second', dict)))
+ first=cls._construct_reference(_get_ts(dct, 'first', dict)) if 'first' in dct else None,
+ second=cls._construct_reference(_get_ts(dct, 'second', dict)) if 'second' in dct else None)
cls._amend_abstract_attributes(ret, dct)
if not cls.stripped and 'annotations' in dct:
for element in _get_ts(dct, 'annotations', list):
@@ -691,8 +694,10 @@ def _construct_submodel_element_list(cls, dct: Dict[str, object], object_class=m
@classmethod
def _construct_blob(cls, dct: Dict[str, object], object_class=model.Blob) -> model.Blob:
- ret = object_class(id_short=None,
- content_type=_get_ts(dct, "contentType", str))
+ ret = object_class(
+ id_short=None,
+ content_type=_get_ts(dct, "contentType", str) if 'contentType' in dct else None
+ )
cls._amend_abstract_attributes(ret, dct)
if 'value' in dct:
ret.value = base64.b64decode(_get_ts(dct, 'value', str))
@@ -700,9 +705,10 @@ def _construct_blob(cls, dct: Dict[str, object], object_class=model.Blob) -> mod
@classmethod
def _construct_file(cls, dct: Dict[str, object], object_class=model.File) -> model.File:
+ content_type = _get_ts(dct, "contentType", str) if 'contentType' in dct else None
ret = object_class(id_short=None,
value=None,
- content_type=_get_ts(dct, "contentType", str))
+ content_type=_get_ts(dct, "contentType", str) if 'contentType' in dct else None)
cls._amend_abstract_attributes(ret, dct)
if 'value' in dct and dct['value'] is not None:
ret.value = _get_ts(dct, 'value', str)
@@ -816,12 +822,12 @@ def read_aas_json_file_into(object_store: model.AbstractObjectStore, file: PathO
-> Set[model.Identifier]:
"""
Read an Asset Administration Shell JSON file according to 'Details of the Asset Administration Shell', chapter 5.5
- into a given object store.
+ into a given ObjectStore.
:param object_store: The :class:`ObjectStore ` in which the
identifiable objects should be stored
:param file: A filename or file-like object to read the JSON-serialized data from
- :param replace_existing: Whether to replace existing objects with the same identifier in the object store or not
+ :param replace_existing: Whether to replace existing objects with the same identifier in the ObjectStore or not
:param ignore_existing: Whether to ignore existing objects (e.g. log a message) or raise an error.
This parameter is ignored if replace_existing is ``True``.
:param failsafe: If ``True``, the document is parsed in a failsafe way: Missing attributes and elements are logged
@@ -898,11 +904,11 @@ def read_aas_json_file_into(object_store: model.AbstractObjectStore, file: PathO
return ret
-def read_aas_json_file(file: PathOrIO, failsafe: bool = True, **kwargs) -> model.DictObjectStore[model.Identifiable]:
+def read_aas_json_file(file: PathOrIO, failsafe: bool = True, **kwargs) -> model.DictIdentifiableStore:
"""
A wrapper of :meth:`~basyx.aas.adapter.json.json_deserialization.read_aas_json_file_into`, that reads all objects
- in an empty :class:`~basyx.aas.model.provider.DictObjectStore`. This function supports the same keyword arguments as
- :meth:`~basyx.aas.adapter.json.json_deserialization.read_aas_json_file_into`.
+ in an empty :class:`~basyx.aas.model.provider.DictIdentifiableStore`. This function supports the same keyword
+ arguments as :meth:`~basyx.aas.adapter.json.json_deserialization.read_aas_json_file_into`.
:param file: A filename or file-like object to read the JSON-serialized data from
:param failsafe: If ``True``, the document is parsed in a failsafe way: Missing attributes and elements are logged
@@ -913,8 +919,8 @@ def read_aas_json_file(file: PathOrIO, failsafe: bool = True, **kwargs) -> model
Errors during construction of the objects
:raises TypeError: **Non-failsafe**: Encountered an element in the wrong list
(e.g. an AssetAdministrationShell in ``submodels``)
- :return: A :class:`~basyx.aas.model.provider.DictObjectStore` containing all AAS objects from the JSON file
+ :return: A :class:`~basyx.aas.model.provider.DictIdentifiableStore` containing all AAS objects from the JSON file
"""
- object_store: model.DictObjectStore[model.Identifiable] = model.DictObjectStore()
- read_aas_json_file_into(object_store, file, failsafe=failsafe, **kwargs)
- return object_store
+ identifiable_store: model.DictIdentifiableStore = model.DictIdentifiableStore()
+ read_aas_json_file_into(identifiable_store, file, failsafe=failsafe, **kwargs)
+ return identifiable_store
diff --git a/sdk/basyx/aas/adapter/json/json_serialization.py b/sdk/basyx/aas/adapter/json/json_serialization.py
index 0b0df0164..d981f2283 100644
--- a/sdk/basyx/aas/adapter/json/json_serialization.py
+++ b/sdk/basyx/aas/adapter/json/json_serialization.py
@@ -1,4 +1,4 @@
-# Copyright (c) 2025 the Eclipse BaSyx Authors
+# Copyright (c) 2026 the Eclipse BaSyx Authors
#
# This program and the accompanying materials are made available under the terms of the MIT License, available in
# the LICENSE file of this project.
@@ -17,7 +17,7 @@
simple python types for an automatic JSON serialization.
To simplify the usage of this module, the :meth:`write_aas_json_file` and :meth:`object_store_to_json` are provided.
The former is used to serialize a given :class:`~basyx.aas.model.provider.AbstractObjectStore` to a file, while the
-latter serializes the object store to a string and returns it.
+latter serializes the ObjectStore to a string and returns it.
The serialization is performed in an iterative approach: The :meth:`~.AASToJsonEncoder.default` function gets called for
every object and checks if an object is an BaSyx Python SDK object. In this case, it calls a special function for the
@@ -476,7 +476,8 @@ def _blob_to_json(cls, obj: model.Blob) -> Dict[str, object]:
:return: dict with the serialized attributes of this object
"""
data = cls._abstract_classes_to_json(obj)
- data['contentType'] = obj.content_type
+ if obj.content_type is not None:
+ data['contentType'] = obj.content_type
if obj.value is not None:
data['value'] = base64.b64encode(obj.value).decode()
return data
@@ -490,7 +491,8 @@ def _file_to_json(cls, obj: model.File) -> Dict[str, object]:
:return: dict with the serialized attributes of this object
"""
data = cls._abstract_classes_to_json(obj)
- data['contentType'] = obj.content_type
+ if obj.content_type is not None:
+ data['contentType'] = obj.content_type
if obj.value is not None:
data['value'] = obj.value
return data
@@ -565,7 +567,10 @@ def _relationship_element_to_json(cls, obj: model.RelationshipElement) -> Dict[s
:return: dict with the serialized attributes of this object
"""
data = cls._abstract_classes_to_json(obj)
- data.update({'first': obj.first, 'second': obj.second})
+ if obj.first is not None:
+ data.update({'first': obj.first})
+ if obj.second is not None:
+ data.update({'second': obj.second})
return data
@classmethod
@@ -576,8 +581,7 @@ def _annotated_relationship_element_to_json(cls, obj: model.AnnotatedRelationshi
:param obj: object of class AnnotatedRelationshipElement
:return: dict with the serialized attributes of this object
"""
- data = cls._abstract_classes_to_json(obj)
- data.update({'first': obj.first, 'second': obj.second})
+ data = cls._relationship_element_to_json(obj)
if not cls.stripped and obj.annotation:
data['annotations'] = list(obj.annotation)
return data
@@ -635,10 +639,11 @@ def _entity_to_json(cls, obj: model.Entity) -> Dict[str, object]:
data = cls._abstract_classes_to_json(obj)
if not cls.stripped and obj.statement:
data['statements'] = list(obj.statement)
- data['entityType'] = _generic.ENTITY_TYPES[obj.entity_type]
- if obj.global_asset_id:
+ if obj.entity_type is not None:
+ data['entityType'] = _generic.ENTITY_TYPES[obj.entity_type]
+ if obj.global_asset_id is not None:
data['globalAssetId'] = obj.global_asset_id
- if obj.specific_asset_id:
+ if obj.specific_asset_id is not None:
data['specificAssetIds'] = list(obj.specific_asset_id)
return data
diff --git a/sdk/basyx/aas/adapter/xml/xml_deserialization.py b/sdk/basyx/aas/adapter/xml/xml_deserialization.py
index b263820d1..6bfb67bce 100644
--- a/sdk/basyx/aas/adapter/xml/xml_deserialization.py
+++ b/sdk/basyx/aas/adapter/xml/xml_deserialization.py
@@ -1,4 +1,4 @@
-# Copyright (c) 2025 the Eclipse BaSyx Authors
+# Copyright (c) 2026 the Eclipse BaSyx Authors
#
# This program and the accompanying materials are made available under the terms of the MIT License, available in
# the LICENSE file of this project.
@@ -15,7 +15,7 @@
- :func:`read_aas_xml_file_into` constructs all elements of an XML document and stores them in a given
:class:`ObjectStore `
- :func:`read_aas_xml_file` constructs all elements of an XML document and returns them in a
- :class:`~basyx.aas.model.provider.DictObjectStore`
+ :class:`~basyx.aas.model.provider.DictIdentifiableStore`
These functions take a decoder class as keyword argument, which allows parsing in failsafe (default) or non-failsafe
mode. Parsing stripped elements - used in the HTTP adapter - is also possible. It is also possible to subclass the
@@ -98,7 +98,7 @@ def _element_pretty_identifier(element: etree._Element) -> str:
If the prefix is known, the namespace in the element tag is replaced by the prefix.
If additionally also the sourceline is known, it is added as a suffix to name.
- For example, instead of "{https://admin-shell.io/aas/3/0}assetAdministrationShell" this function would return
+ For example, instead of "{https://admin-shell.io/aas/3/1}assetAdministrationShell" this function would return
"aas:assetAdministrationShell on line $line", if both, prefix and sourceline, are known.
:param element: The xml element.
@@ -512,8 +512,8 @@ def _construct_relationship_element_internal(cls, element: etree._Element, objec
"""
relationship_element = object_class(
None,
- _child_construct_mandatory(element, NS_AAS + "first", cls.construct_reference),
- _child_construct_mandatory(element, NS_AAS + "second", cls.construct_reference)
+ _failsafe_construct(element.find(NS_AAS + "first"), cls.construct_reference, cls.failsafe),
+ _failsafe_construct(element.find(NS_AAS + "second"), cls.construct_reference, cls.failsafe)
)
cls._amend_abstract_attributes(relationship_element, element)
return relationship_element
@@ -804,7 +804,7 @@ def construct_basic_event_element(cls, element: etree._Element, object_class=mod
def construct_blob(cls, element: etree._Element, object_class=model.Blob, **_kwargs: Any) -> model.Blob:
blob = object_class(
None,
- _child_text_mandatory(element, NS_AAS + "contentType")
+ _get_text_or_none(element.find(NS_AAS + "contentType"))
)
value = _get_text_or_none(element.find(NS_AAS + "value"))
if value is not None:
@@ -827,10 +827,14 @@ def construct_entity(cls, element: etree._Element, object_class=model.Entity, **
for id in _child_construct_multiple(specific_asset_ids, NS_AAS + "specificAssetId",
cls.construct_specific_asset_id, cls.failsafe):
specific_asset_id.add(id)
-
+ entity_type_text = _get_text_or_none(element.find(NS_AAS + "entityType"))
+ if entity_type_text is not None:
+ entity_type = ENTITY_TYPES_INVERSE[entity_type_text]
+ else:
+ entity_type = None
entity = object_class(
id_short=None,
- entity_type=_child_text_mandatory_mapped(element, NS_AAS + "entityType", ENTITY_TYPES_INVERSE),
+ entity_type=entity_type,
global_asset_id=_get_text_or_none(element.find(NS_AAS + "globalAssetId")),
specific_asset_id=specific_asset_id)
@@ -847,7 +851,7 @@ def construct_entity(cls, element: etree._Element, object_class=model.Entity, **
def construct_file(cls, element: etree._Element, object_class=model.File, **_kwargs: Any) -> model.File:
file = object_class(
None,
- _child_text_mandatory(element, NS_AAS + "contentType")
+ _get_text_or_none(element.find(NS_AAS + "contentType"))
)
value = _get_text_or_none(element.find(NS_AAS + "value"))
if value is not None:
@@ -1059,8 +1063,10 @@ def construct_submodel(cls, element: etree._Element, object_class=model.Submodel
@classmethod
def construct_value_reference_pair(cls, element: etree._Element, object_class=model.ValueReferencePair,
**_kwargs: Any) -> model.ValueReferencePair:
+ value_id_element = element.find(NS_AAS + "valueId")
+ value_id = cls.construct_reference(value_id_element, **_kwargs) if value_id_element is not None else None
return object_class(_child_text_mandatory(element, NS_AAS + "value"),
- _child_construct_mandatory(element, NS_AAS + "valueId", cls.construct_reference))
+ value_id)
@classmethod
def construct_value_list(cls, element: etree._Element, **_kwargs: Any) -> model.ValueList:
@@ -1154,7 +1160,7 @@ def construct_data_specification_iec61360(cls, element: etree._Element,
if value_list is not None:
ds_iec.value_list = value_list
value = _get_text_or_none(element.find(NS_AAS + "value"))
- if value is not None and value_format is not None:
+ if value is not None:
ds_iec.value = value
level_type = element.find(NS_AAS + "levelType")
if level_type is not None:
@@ -1421,10 +1427,16 @@ def read_aas_xml_element(file: PathOrIO, construct: XMLConstructables, failsafe:
return _failsafe_construct(element, constructor, decoder_.failsafe, **constructor_kwargs)
-def read_aas_xml_file_into(object_store: model.AbstractObjectStore[model.Identifiable], file: PathOrIO,
- replace_existing: bool = False, ignore_existing: bool = False, failsafe: bool = True,
- stripped: bool = False, decoder: Optional[Type[AASFromXmlDecoder]] = None,
- **parser_kwargs: Any) -> Set[model.Identifier]:
+def read_aas_xml_file_into(
+ object_store: model.AbstractObjectStore[model.Identifier, model.Identifiable],
+ file: PathOrIO,
+ replace_existing: bool = False,
+ ignore_existing: bool = False,
+ failsafe: bool = True,
+ stripped: bool = False,
+ decoder: Optional[Type[AASFromXmlDecoder]] = None,
+ **parser_kwargs: Any
+) -> Set[model.Identifier]:
"""
Read an Asset Administration Shell XML file according to 'Details of the Asset Administration Shell', chapter 5.4
into a given :class:`ObjectStore `.
@@ -1503,10 +1515,10 @@ def read_aas_xml_file_into(object_store: model.AbstractObjectStore[model.Identif
def read_aas_xml_file(file: PathOrIO, failsafe: bool = True, **kwargs: Any)\
- -> model.DictObjectStore[model.Identifiable]:
+ -> model.DictIdentifiableStore:
"""
A wrapper of :meth:`~basyx.aas.adapter.xml.xml_deserialization.read_aas_xml_file_into`, that reads all objects in an
- empty :class:`~basyx.aas.model.provider.DictObjectStore`. This function supports
+ empty :class:`~basyx.aas.model.provider.DictIdentifiableStore`. This function supports
the same keyword arguments as :meth:`~basyx.aas.adapter.xml.xml_deserialization.read_aas_xml_file_into`.
:param file: A filename or file-like object to read the XML-serialized data from
@@ -1519,8 +1531,8 @@ def read_aas_xml_file(file: PathOrIO, failsafe: bool = True, **kwargs: Any)\
:raises (~basyx.aas.model.base.AASConstraintViolation, KeyError, ValueError): **Non-failsafe**: Errors during
construction of the objects
:raises TypeError: **Non-failsafe**: Encountered an undefined top-level list (e.g. ````)
- :return: A :class:`~basyx.aas.model.provider.DictObjectStore` containing all AAS objects from the XML file
+ :return: A :class:`~basyx.aas.model.provider.DictIdentifiableStore` containing all AAS objects from the XML file
"""
- object_store: model.DictObjectStore[model.Identifiable] = model.DictObjectStore()
- read_aas_xml_file_into(object_store, file, failsafe=failsafe, **kwargs)
- return object_store
+ identifiable_store: model.DictIdentifiableStore[model.Identifiable] = model.DictIdentifiableStore()
+ read_aas_xml_file_into(identifiable_store, file, failsafe=failsafe, **kwargs)
+ return identifiable_store
diff --git a/sdk/basyx/aas/adapter/xml/xml_serialization.py b/sdk/basyx/aas/adapter/xml/xml_serialization.py
index 2dc578ca0..4b80e5954 100644
--- a/sdk/basyx/aas/adapter/xml/xml_serialization.py
+++ b/sdk/basyx/aas/adapter/xml/xml_serialization.py
@@ -1,4 +1,4 @@
-# Copyright (c) 2025 the Eclipse BaSyx Authors
+# Copyright (c) 2026 the Eclipse BaSyx Authors
#
# This program and the accompanying materials are made available under the terms of the MIT License, available in
# the LICENSE file of this project.
@@ -322,7 +322,8 @@ def value_reference_pair_to_xml(obj: model.ValueReferencePair,
et_vrp = _generate_element(tag)
# TODO: value_type isn't used at all by _value_to_xml(), thus we can ignore the type here for now
et_vrp.append(_generate_element(NS_AAS+"value", text=obj.value)) # type: ignore
- et_vrp.append(reference_to_xml(obj.value_id, NS_AAS+"valueId"))
+ if obj.value_id is not None:
+ et_vrp.append(reference_to_xml(obj.value_id, NS_AAS+"valueId"))
return et_vrp
@@ -627,7 +628,8 @@ def blob_to_xml(obj: model.Blob,
if obj.value is not None:
et_value.text = base64.b64encode(obj.value).decode()
et_blob.append(et_value)
- et_blob.append(_generate_element(NS_AAS + "contentType", text=obj.content_type))
+ if obj.content_type is not None:
+ et_blob.append(_generate_element(NS_AAS + "contentType", text=obj.content_type))
return et_blob
@@ -643,7 +645,8 @@ def file_to_xml(obj: model.File,
et_file = abstract_classes_to_xml(tag, obj)
if obj.value:
et_file.append(_generate_element(NS_AAS + "value", text=obj.value))
- et_file.append(_generate_element(NS_AAS + "contentType", text=obj.content_type))
+ if obj.content_type is not None:
+ et_file.append(_generate_element(NS_AAS + "contentType", text=obj.content_type))
return et_file
@@ -733,8 +736,10 @@ def relationship_element_to_xml(obj: model.RelationshipElement,
:return: Serialized :class:`~lxml.etree._Element` object
"""
et_relationship_element = abstract_classes_to_xml(tag, obj)
- et_relationship_element.append(reference_to_xml(obj.first, NS_AAS+"first"))
- et_relationship_element.append(reference_to_xml(obj.second, NS_AAS+"second"))
+ if obj.first is not None:
+ et_relationship_element.append(reference_to_xml(obj.first, NS_AAS+"first"))
+ if obj.second is not None:
+ et_relationship_element.append(reference_to_xml(obj.second, NS_AAS+"second"))
return et_relationship_element
@@ -822,7 +827,8 @@ def entity_to_xml(obj: model.Entity,
for statement in obj.statement:
et_statements.append(submodel_element_to_xml(statement))
et_entity.append(et_statements)
- et_entity.append(_generate_element(NS_AAS + "entityType", text=_generic.ENTITY_TYPES[obj.entity_type]))
+ if obj.entity_type:
+ et_entity.append(_generate_element(NS_AAS + "entityType", text=_generic.ENTITY_TYPES[obj.entity_type]))
if obj.global_asset_id:
et_entity.append(_generate_element(NS_AAS + "globalAssetId", text=obj.global_asset_id))
if obj.specific_asset_id:
@@ -899,10 +905,6 @@ def object_to_xml_element(obj: object) -> etree._Element:
return value_reference_pair_to_xml(obj)
elif isinstance(obj, model.ConceptDescription):
return concept_description_to_xml(obj)
- elif isinstance(obj, model.LangStringSet):
- # FIXME: `lang_string_set_to_xml` expects `tag` parameter, `tag` doesn't have default value
- # Issue: https://github.com/eclipse-basyx/basyx-python-sdk/issues/397
- return lang_string_set_to_xml(obj) # type: ignore[call-arg]
elif isinstance(obj, model.EmbeddedDataSpecification):
return embedded_data_specification_to_xml(obj)
elif isinstance(obj, model.DataSpecificationIEC61360):
diff --git a/sdk/basyx/aas/backend/couchdb.py b/sdk/basyx/aas/backend/couchdb.py
index 6f2b3a0fc..fe02345eb 100644
--- a/sdk/basyx/aas/backend/couchdb.py
+++ b/sdk/basyx/aas/backend/couchdb.py
@@ -1,4 +1,4 @@
-# Copyright (c) 2025 the Eclipse BaSyx Authors
+# Copyright (c) 2026 the Eclipse BaSyx Authors
#
# This program and the accompanying materials are made available under the terms of the MIT License, available in
# the LICENSE file of this project.
@@ -8,11 +8,13 @@
This module adds the functionality of storing and retrieving :class:`~basyx.aas.model.base.Identifiable` objects
in a CouchDB.
-The :class:`~CouchDBObjectStore` handles adding, deleting and otherwise managing the AAS objects in a specific CouchDB.
+The :class:`~CouchDBIdentifiableStore` handles adding, deleting and otherwise managing the AAS objects in a specific
+CouchDB.
"""
import threading
+import warnings
import weakref
-from typing import List, Dict, Any, Optional, Iterator, Iterable, Union, Tuple, MutableMapping
+from typing import Dict, Any, Optional, Iterator, Iterable, Tuple, MutableMapping
import urllib.parse
import urllib.request
import urllib.error
@@ -40,7 +42,7 @@ def register_credentials(url: str, username: str, password: str):
.. Warning::
Do not use this function, while other threads may be accessing the credentials via the
- :class:`~.CouchDBObjectStore`!
+ :class:`~.CouchDBIdentifiableStore`!
:param url: Toplevel URL
:param username: Username to that CouchDB instance
@@ -87,18 +89,19 @@ def delete_couchdb_revision(url: str):
del _revision_store[url]
-class CouchDBObjectStore(model.AbstractObjectStore):
+class CouchDBIdentifiableStore(model.AbstractObjectStore[model.Identifier, model.Identifiable]):
"""
An ObjectStore implementation for :class:`~basyx.aas.model.base.Identifiable` BaSyx Python SDK objects backed
by a CouchDB database server.
- All methods of the ``CouchDBObjectStore`` are blocking, i.e. they stop the current thread's execution until they
- receive a response from the CouchDB server (or encounter a timeout). However, the ``CouchDBObjectStore`` objects are
- thread-safe, as long as no CouchDB credentials are added (via ``register_credentials()``) during transactions.
+ All methods of the ``CouchDBIdentifiableStore`` are blocking, i.e. they stop the current thread's execution until
+ they receive a response from the CouchDB server (or encounter a timeout). However, the ``CouchDBIdentifiableStore``
+ objects are thread-safe, as long as no CouchDB credentials are added (via ``register_credentials()``) during
+ transactions.
"""
def __init__(self, url: str, database: str):
"""
- Initializer of class CouchDBObjectStore
+ Initializer of class CouchDBIdentifiableStore
:param url: URL to the CouchDB
:param database: Name of the Database inside the CouchDB
@@ -157,7 +160,6 @@ def get_identifiable_by_couchdb_id(self, couchdb_id: str) -> model.Identifiable:
raise KeyError("No Identifiable with couchdb-id {} found in CouchDB database".format(couchdb_id)) from e
raise
- # Add CouchDB metadata (for later commits) to object
obj = data['data']
if not isinstance(obj, model.Identifiable):
raise CouchDBResponseError("The CouchDB document with id {} does not contain an identifiable AAS object."
@@ -165,17 +167,13 @@ def get_identifiable_by_couchdb_id(self, couchdb_id: str) -> model.Identifiable:
set_couchdb_revision("{}/{}/{}".format(self.url, self.database_name, urllib.parse.quote(couchdb_id, safe='')),
data["_rev"])
- # If we still have a local replication of that object (since it is referenced from anywhere else), update that
- # replication and return it.
with self._object_cache_lock:
if obj.id in self._object_cache:
- old_obj = self._object_cache[obj.id]
- old_obj.update_from(obj)
- return old_obj
- self._object_cache[obj.id] = obj
+ return self._object_cache[obj.id]
+ self._object_cache[obj.id] = obj
return obj
- def get_identifiable(self, identifier: model.Identifier) -> model.Identifiable:
+ def get_item(self, identifier: model.Identifier) -> model.Identifiable:
"""
Retrieve an AAS object from the CouchDB by its :class:`~basyx.aas.model.base.Identifier`
@@ -183,6 +181,9 @@ def get_identifiable(self, identifier: model.Identifier) -> model.Identifiable:
:raises CouchDBError: If error occur during the request to the CouchDB server
(see ``_do_request()`` for details)
"""
+ with self._object_cache_lock:
+ if identifier in self._object_cache:
+ return self._object_cache[identifier]
try:
return self.get_identifiable_by_couchdb_id(self._transform_id(identifier, False))
except KeyError as e:
@@ -217,6 +218,37 @@ def add(self, x: model.Identifiable) -> None:
with self._object_cache_lock:
self._object_cache[x.id] = x
+ def commit(self, x: model.Identifiable) -> None:
+ """
+ Write the current in-memory state of a stored object back to the CouchDB.
+
+ :param x: The object to persist
+ :raises KeyError: If the object is not present in the store or no revision is known
+ :raises CouchDBConflictError: If the object was modified in the database since it was last fetched
+ :raises CouchDBError: If error occur during the request to the CouchDB server
+ (see ``_do_request()`` for details)
+ """
+ doc_url = "{}/{}/{}".format(self.url, self.database_name, self._transform_id(x.id))
+ rev = get_couchdb_revision(doc_url)
+ if rev is None:
+ raise KeyError("No revision found for object with id {} — not fetched from this store".format(x.id))
+ data = json.dumps({'data': x}, cls=json_serialization.AASToJsonEncoder)
+ try:
+ response = self._do_request(
+ "{}?rev={}".format(doc_url, rev),
+ 'PUT',
+ {'Content-type': 'application/json'},
+ data.encode('utf-8'))
+ set_couchdb_revision(doc_url, response["rev"])
+ except CouchDBServerError as e:
+ if e.code == 404:
+ raise KeyError("No AAS object with id {} exists in CouchDB database".format(x.id)) from e
+ elif e.code == 409:
+ raise CouchDBConflictError(
+ "Object with id {} has been modified in the database since it was last fetched."
+ .format(x.id)) from e
+ raise
+
def discard(self, x: model.Identifiable, safe_delete=False) -> None:
"""
Delete an :class:`~basyx.aas.model.base.Identifiable` AAS object from the CouchDB database
@@ -382,7 +414,7 @@ def __iter__(self) -> Iterator[model.Identifiable]:
"""
# Iterator class storing the list of ids and fetching Identifiable objects on the fly
class CouchDBIdentifiableIterator(Iterator[model.Identifiable]):
- def __init__(self, store: CouchDBObjectStore, ids: Iterable[str]):
+ def __init__(self, store: CouchDBIdentifiableStore, ids: Iterable[str]):
self._iter = iter(ids)
self._store = store
@@ -407,6 +439,29 @@ def _transform_id(identifier: model.Identifier, url_quote=True) -> str:
return identifier
+class CouchDBObjectStore(CouchDBIdentifiableStore):
+ """
+ `CouchDBObjectStore` has been renamed to :class:`~.CouchDBIdentifiableStore` and will be removed in a
+ future release. Please migrate to :class:`~.CouchDBIdentifiableStore`.
+ """
+ def __init__(self, url: str, database: str):
+ warnings.warn(
+ "`CouchDBObjectStore` is deprecated and will be removed in a future release. Use "
+ "`CouchDBIdentifiableStore` instead.",
+ DeprecationWarning,
+ stacklevel=2,
+ )
+ super().__init__(url, database)
+
+ def get_identifiable(self, identifier: model.Identifier) -> model.Identifiable:
+ warnings.warn(
+ "`get_identifiable()` is deprecated. Use `get_item()` from `CouchDBIdentifiableStore` instead.",
+ DeprecationWarning,
+ stacklevel=2,
+ )
+ return super().get_item(identifier)
+
+
# #################################################################################################
# Custom Exception classes for reporting errors during interaction with the CouchDB server
diff --git a/sdk/basyx/aas/backend/local_file.py b/sdk/basyx/aas/backend/local_file.py
index 39ff91415..df21ddfee 100644
--- a/sdk/basyx/aas/backend/local_file.py
+++ b/sdk/basyx/aas/backend/local_file.py
@@ -1,4 +1,4 @@
-# Copyright (c) 2025 the Eclipse BaSyx Authors
+# Copyright (c) 2026 the Eclipse BaSyx Authors
#
# This program and the accompanying materials are made available under the terms of the MIT License, available in
# the LICENSE file of this project.
@@ -8,15 +8,17 @@
This module adds the functionality of storing and retrieving :class:`~basyx.aas.model.base.Identifiable` objects
in local files.
-The :class:`~LocalFileObjectStore` handles adding, deleting and otherwise managing
+The :class:`~LocalFileIdentifiableStore` handles adding, deleting and otherwise managing
the AAS objects in a specific Directory.
"""
-from typing import List, Iterator, Iterable, Union
+from typing import Iterator
import logging
import json
import os
import hashlib
+import tempfile
import threading
+import warnings
import weakref
from ..adapter.json import json_serialization, json_deserialization
@@ -26,14 +28,21 @@
logger = logging.getLogger(__name__)
-class LocalFileObjectStore(model.AbstractObjectStore):
+class LocalFileIdentifiableStore(model.AbstractObjectStore[model.Identifier, model.Identifiable]):
"""
An ObjectStore implementation for :class:`~basyx.aas.model.base.Identifiable` BaSyx Python SDK objects backed
by a local file based local backend
+
+ .. warning::
+ This backend is intended for development and testing only. It provides no
+ concurrency control across processes: concurrent writes to the same object
+ (e.g. under a multi-worker WSGI server) will silently overwrite each other,
+ with the last writer winning and no error raised. Use a dedicated database
+ backend for any production deployment.
"""
def __init__(self, directory_path: str):
"""
- Initializer of class LocalFileObjectStore
+ Initializer of class LocalFileIdentifiableStore
:param directory_path: Path to the local file backend (the path where you want to store your AAS JSON files)
"""
@@ -67,34 +76,51 @@ def get_identifiable_by_hash(self, hash_: str) -> model.Identifiable:
:raises KeyError: If the respective file could not be found
"""
- # Try to get the correct file
try:
with open("{}/{}.json".format(self.directory_path, hash_), "r") as file:
data = json.load(file, cls=json_deserialization.AASFromJsonDecoder)
obj = data["data"]
except FileNotFoundError as e:
raise KeyError("No Identifiable with hash {} found in local file database".format(hash_)) from e
- # If we still have a local replication of that object (since it is referenced from anywhere else), update that
- # replication and return it.
with self._object_cache_lock:
if obj.id in self._object_cache:
- old_obj = self._object_cache[obj.id]
- old_obj.update_from(obj)
- return old_obj
- self._object_cache[obj.id] = obj
+ return self._object_cache[obj.id]
+ self._object_cache[obj.id] = obj
return obj
- def get_identifiable(self, identifier: model.Identifier) -> model.Identifiable:
+ def get_item(self, identifier: model.Identifier) -> model.Identifiable:
"""
Retrieve an AAS object from the local file by its :class:`~basyx.aas.model.base.Identifier`
:raises KeyError: If the respective file could not be found
"""
+ with self._object_cache_lock:
+ if identifier in self._object_cache:
+ return self._object_cache[identifier]
try:
return self.get_identifiable_by_hash(self._transform_id(identifier))
except KeyError as e:
raise KeyError("No Identifiable with id {} found in local file database".format(identifier)) from e
+ def _write_atomic(self, x: model.Identifiable) -> None:
+ """
+ Serialize x to a temp file in the store directory, then atomically replace the final file.
+
+ Using os.replace() (rename(2) on POSIX) ensures readers always see a complete file — never
+ a partially-written one from a crash or concurrent access mid-write.
+ """
+ final_path = "{}/{}.json".format(self.directory_path, self._transform_id(x.id))
+ tmp_fd, tmp_path = tempfile.mkstemp(dir=self.directory_path, suffix=".tmp")
+ try:
+ with os.fdopen(tmp_fd, "w") as tmp_file:
+ json.dump({"data": x}, tmp_file, cls=json_serialization.AASToJsonEncoder, indent=4)
+ os.replace(tmp_path, final_path)
+ # Catch all `Exception`s, as well as `KeyboardInterrupt` and `SystemExit` too, so the temp
+ # file is never left behind even if the process is being torn down:
+ except BaseException:
+ os.unlink(tmp_path)
+ raise
+
def add(self, x: model.Identifiable) -> None:
"""
Add an object to the store
@@ -104,10 +130,20 @@ def add(self, x: model.Identifiable) -> None:
logger.debug("Adding object %s to Local File Store ...", repr(x))
if os.path.exists("{}/{}.json".format(self.directory_path, self._transform_id(x.id))):
raise KeyError("Identifiable with id {} already exists in local file database".format(x.id))
- with open("{}/{}.json".format(self.directory_path, self._transform_id(x.id)), "w") as file:
- json.dump({"data": x}, file, cls=json_serialization.AASToJsonEncoder, indent=4)
- with self._object_cache_lock:
- self._object_cache[x.id] = x
+ self._write_atomic(x)
+ with self._object_cache_lock:
+ self._object_cache[x.id] = x
+
+ def commit(self, x: model.Identifiable) -> None:
+ """
+ Write the current in-memory state of a stored object back to its file.
+
+ :param x: The object to persist
+ :raises KeyError: If the object is not present in the store
+ """
+ if not os.path.exists("{}/{}.json".format(self.directory_path, self._transform_id(x.id))):
+ raise KeyError("No AAS object with id {} exists in local file database".format(x.id))
+ self._write_atomic(x)
def discard(self, x: model.Identifiable) -> None:
"""
@@ -122,7 +158,7 @@ def discard(self, x: model.Identifiable) -> None:
except FileNotFoundError as e:
raise KeyError("No AAS object with id {} exists in local file database".format(x.id)) from e
with self._object_cache_lock:
- del self._object_cache[x.id]
+ self._object_cache.pop(x.id, None)
def __contains__(self, x: object) -> bool:
"""
@@ -149,7 +185,7 @@ def __len__(self) -> int:
:return: The number of objects (determined from the number of documents)
"""
logger.debug("Fetching number of documents from database ...")
- return len(os.listdir(self.directory_path))
+ return sum(1 for f in os.listdir(self.directory_path) if f.lower().endswith(".json"))
def __iter__(self) -> Iterator[model.Identifiable]:
"""
@@ -160,7 +196,8 @@ def __iter__(self) -> Iterator[model.Identifiable]:
"""
logger.debug("Iterating over objects in database ...")
for name in os.listdir(self.directory_path):
- yield self.get_identifiable_by_hash(name.rstrip(".json"))
+ if name.lower().endswith(".json"):
+ yield self.get_identifiable_by_hash(name[:-5])
@staticmethod
def _transform_id(identifier: model.Identifier) -> str:
@@ -168,3 +205,26 @@ def _transform_id(identifier: model.Identifier) -> str:
Helper method to represent an ASS Identifier as a string to be used as Local file document id
"""
return hashlib.sha256(identifier.encode("utf-8")).hexdigest()
+
+
+class LocalFileObjectStore(LocalFileIdentifiableStore):
+ """
+ `LocalFileObjectStore` has been renamed to :class:`~.LocalFileIdentifiableStore` and will be removed in a
+ future release. Please migrate to :class:`~.LocalFileIdentifiableStore`.
+ """
+ def __init__(self, directory_path: str):
+ warnings.warn(
+ "`LocalFileObjectStore` is deprecated and will be removed in a future release. Use "
+ "`LocalFileIdentifiableStore` instead.",
+ DeprecationWarning,
+ stacklevel=2,
+ )
+ super().__init__(directory_path)
+
+ def get_identifiable(self, identifier: model.Identifier) -> model.Identifiable:
+ warnings.warn(
+ "`get_identifiable()` is deprecated. Use `get_item()` from `LocalFileIdentifiableStore` instead.",
+ DeprecationWarning,
+ stacklevel=2,
+ )
+ return super().get_item(identifier)
diff --git a/sdk/basyx/aas/examples/data/__init__.py b/sdk/basyx/aas/examples/data/__init__.py
index b82226ad4..3dce392cb 100644
--- a/sdk/basyx/aas/examples/data/__init__.py
+++ b/sdk/basyx/aas/examples/data/__init__.py
@@ -26,22 +26,22 @@
TEST_PDF_FILE = os.path.join(os.path.dirname(__file__), 'TestFile.pdf')
-def create_example() -> model.DictObjectStore:
+def create_example() -> model.DictIdentifiableStore:
"""
creates an object store which is filled with example assets, submodels, concept descriptions and asset
administration shells using the functionality of this package
:return: object store
"""
- obj_store: model.DictObjectStore[model.Identifiable] = model.DictObjectStore()
- obj_store.update(example_aas.create_full_example())
- obj_store.update(example_aas_mandatory_attributes.create_full_example())
- obj_store.update(example_aas_missing_attributes.create_full_example())
- obj_store.add(example_submodel_template.create_example_submodel_template())
- return obj_store
+ identifiable_store: model.DictIdentifiableStore[model.Identifiable] = model.DictIdentifiableStore()
+ identifiable_store.update(example_aas.create_full_example())
+ identifiable_store.update(example_aas_mandatory_attributes.create_full_example())
+ identifiable_store.update(example_aas_missing_attributes.create_full_example())
+ identifiable_store.add(example_submodel_template.create_example_submodel_template())
+ return identifiable_store
-def create_example_aas_binding() -> model.DictObjectStore:
+def create_example_aas_binding() -> model.DictIdentifiableStore:
"""
creates an object store which is filled with example assets, submodels, concept descriptions and asset
administration shells using the functionality of this package where each submodel and concept description is
@@ -49,18 +49,18 @@ def create_example_aas_binding() -> model.DictObjectStore:
:return: object store
"""
- obj_store: model.DictObjectStore[model.Identifiable] = model.DictObjectStore()
- obj_store.update(example_aas.create_full_example())
- obj_store.update(example_aas_mandatory_attributes.create_full_example())
- obj_store.update(example_aas_missing_attributes.create_full_example())
- obj_store.add(example_submodel_template.create_example_submodel_template())
+ identifiable_store: model.DictIdentifiableStore[model.Identifiable] = model.DictIdentifiableStore()
+ identifiable_store.update(example_aas.create_full_example())
+ identifiable_store.update(example_aas_mandatory_attributes.create_full_example())
+ identifiable_store.update(example_aas_missing_attributes.create_full_example())
+ identifiable_store.add(example_submodel_template.create_example_submodel_template())
- aas = obj_store.get_identifiable('https://acplt.org/Test_AssetAdministrationShell')
- sm = obj_store.get_identifiable('https://acplt.org/Test_Submodel_Template')
+ aas = identifiable_store.get_item('https://example.org/Test_AssetAdministrationShell')
+ sm = identifiable_store.get_item('https://example.org/Test_Submodel_Template')
assert (isinstance(aas, model.aas.AssetAdministrationShell)) # make mypy happy
assert (isinstance(sm, model.submodel.Submodel)) # make mypy happy
aas.submodel.add(model.ModelReference.from_referable(sm))
- cd = obj_store.get_identifiable('https://acplt.org/Test_ConceptDescription_Mandatory')
+ cd = identifiable_store.get_item('https://example.org/Test_ConceptDescription_Mandatory')
assert (isinstance(cd, model.concept.ConceptDescription)) # make mypy happy
- return obj_store
+ return identifiable_store
diff --git a/sdk/basyx/aas/examples/data/_helper.py b/sdk/basyx/aas/examples/data/_helper.py
index a4c3edfce..4093de32f 100644
--- a/sdk/basyx/aas/examples/data/_helper.py
+++ b/sdk/basyx/aas/examples/data/_helper.py
@@ -1,4 +1,4 @@
-# Copyright (c) 2025 the Eclipse BaSyx Authors
+# Copyright (c) 2026 the Eclipse BaSyx Authors
#
# This program and the accompanying materials are made available under the terms of the MIT License, available in
# the LICENSE file of this project.
@@ -917,44 +917,48 @@ def _check_value_list_equal(self, object_: model.ValueList, expected_value: mode
self.check(found_elements == set(), 'ValueList must not have extra ValueReferencePairs',
value=found_elements)
- def check_object_store(self, obj_store_1: model.DictObjectStore, obj_store_2: model.DictObjectStore):
+ def check_identifiable_store(
+ self,
+ identifiable_store_1: model.DictIdentifiableStore,
+ identifiable_store_2: model.DictIdentifiableStore
+ ):
"""
Checks if the given object stores are equal
- :param obj_store_1: Given object store to check
- :param obj_store_2: expected object store
+ :param identifiable_store_1: Given object store to check
+ :param identifiable_store_2: expected object store
:return:
"""
# separate different kind of objects
submodel_list_1 = []
concept_description_list_1 = []
shell_list_1 = []
- for obj in obj_store_1:
- if isinstance(obj, model.AssetAdministrationShell):
- shell_list_1.append(obj)
- elif isinstance(obj, model.Submodel):
- submodel_list_1.append(obj)
- elif isinstance(obj, model.ConceptDescription):
- concept_description_list_1.append(obj)
+ for identifiable in identifiable_store_1:
+ if isinstance(identifiable, model.AssetAdministrationShell):
+ shell_list_1.append(identifiable)
+ elif isinstance(identifiable, model.Submodel):
+ submodel_list_1.append(identifiable)
+ elif isinstance(identifiable, model.ConceptDescription):
+ concept_description_list_1.append(identifiable)
else:
- raise KeyError('Check for {} not implemented'.format(obj))
+ raise KeyError('Check for {} not implemented'.format(identifiable))
# separate different kind of objects
submodel_list_2 = []
concept_description_list_2 = []
shell_list_2 = []
- for obj in obj_store_2:
- if isinstance(obj, model.AssetAdministrationShell):
- shell_list_2.append(obj)
- elif isinstance(obj, model.Submodel):
- submodel_list_2.append(obj)
- elif isinstance(obj, model.ConceptDescription):
- concept_description_list_2.append(obj)
+ for identifiable in identifiable_store_2:
+ if isinstance(identifiable, model.AssetAdministrationShell):
+ shell_list_2.append(identifiable)
+ elif isinstance(identifiable, model.Submodel):
+ submodel_list_2.append(identifiable)
+ elif isinstance(identifiable, model.ConceptDescription):
+ concept_description_list_2.append(identifiable)
else:
- raise KeyError('Check for {} not implemented'.format(obj))
+ raise KeyError('Check for {} not implemented'.format(identifiable))
for shell_2 in shell_list_2:
- shell_1 = obj_store_1.get(shell_2.id)
+ shell_1 = identifiable_store_1.get(shell_2.id)
if self.check(shell_1 is not None, 'Asset administration shell {} must exist in given asset administration'
'shell list'.format(shell_2)):
self.check_asset_administration_shell_equal(shell_1, shell_2) # type: ignore
@@ -964,7 +968,7 @@ def check_object_store(self, obj_store_1: model.DictObjectStore, obj_store_2: mo
'administration shells', value=found_elements)
for submodel_2 in submodel_list_2:
- submodel_1 = obj_store_1.get(submodel_2.id)
+ submodel_1 = identifiable_store_1.get(submodel_2.id)
if self.check(submodel_1 is not None, 'Submodel {} must exist in given submodel list'.format(submodel_2)):
self.check_submodel_equal(submodel_1, submodel_2) # type: ignore
@@ -973,7 +977,7 @@ def check_object_store(self, obj_store_1: model.DictObjectStore, obj_store_2: mo
value=found_elements)
for cd_2 in concept_description_list_2:
- cd_1 = obj_store_1.get(cd_2.id)
+ cd_1 = identifiable_store_1.get(cd_2.id)
if self.check(cd_1 is not None, 'Concept description {} must exist in given concept description '
'list'.format(cd_2)):
self.check_concept_description_equal(cd_1, cd_2) # type: ignore
diff --git a/sdk/basyx/aas/examples/data/example_aas.py b/sdk/basyx/aas/examples/data/example_aas.py
index e093c603a..bb080756a 100644
--- a/sdk/basyx/aas/examples/data/example_aas.py
+++ b/sdk/basyx/aas/examples/data/example_aas.py
@@ -1,12 +1,12 @@
-# Copyright (c) 2025 the Eclipse BaSyx Authors
+# Copyright (c) 2026 the Eclipse BaSyx Authors
#
# This program and the accompanying materials are made available under the terms of the MIT License, available in
# the LICENSE file of this project.
#
# SPDX-License-Identifier: MIT
"""
-Module for the creation of an :class:`ObjectStore ` with an example asset
-administration shell and example submodels and an example concept description
+Module for the creation of an :class:`IdentifiableStore ` containing an
+example Asset Administration Shell, example Submodels and an example Concept Description.
To get this object store use the function :meth:`~basyx.aas.examples.data.example_aas.create_full_example`.
If you want to get single example objects or want to get more information use the other functions.
@@ -23,7 +23,7 @@
_embedded_data_specification_iec61360 = model.EmbeddedDataSpecification(
data_specification=model.ExternalReference((model.Key(type_=model.KeyTypes.GLOBAL_REFERENCE,
value='https://admin-shell.io/DataSpecificationTemplates/'
- 'DataSpecificationIEC61360/3/0'),)),
+ 'DataSpecificationIEC61360/3/1'),)),
data_specification_content=model.DataSpecificationIEC61360(preferred_name=model.PreferredNameTypeIEC61360({
'de': 'Test Specification',
'en-US': 'TestSpecification'
@@ -32,36 +32,36 @@
'en-US': 'This is a DataSpecification for testing purposes'}),
short_name=model.ShortNameTypeIEC61360({'de': 'Test Spec', 'en-US': 'TestSpec'}), unit='SpaceUnit',
unit_id=model.ExternalReference((model.Key(type_=model.KeyTypes.GLOBAL_REFERENCE,
- value='http://acplt.org/Units/SpaceUnit'),)),
- source_of_definition='http://acplt.org/DataSpec/ExampleDef', symbol='SU', value_format="M",
+ value='http://example.org/Units/SpaceUnit'),)),
+ source_of_definition='http://example.org/DataSpec/ExampleDef', symbol='SU', value_format="M",
value_list={
model.ValueReferencePair(
value='exampleValue',
value_id=model.ExternalReference((model.Key(type_=model.KeyTypes.GLOBAL_REFERENCE,
- value='http://acplt.org/ValueId/ExampleValueId'),)), ),
+ value='http://example.org/ValueId/ExampleValueId'),)), ),
model.ValueReferencePair(
value='exampleValue2',
value_id=model.ExternalReference((model.Key(type_=model.KeyTypes.GLOBAL_REFERENCE,
- value='http://acplt.org/ValueId/ExampleValueId2'),)), )},
+ value='http://example.org/ValueId/ExampleValueId2'),)), )},
value="TEST", level_types={model.IEC61360LevelType.MIN, model.IEC61360LevelType.MAX})
)
-def create_full_example() -> model.DictObjectStore:
+def create_full_example() -> model.DictIdentifiableStore:
"""
Creates an object store which is filled with an example :class:`~basyx.aas.model.submodel.Submodel`,
:class:`~basyx.aas.model.concept.ConceptDescription` and :class:`~basyx.aas.model.aas.AssetAdministrationShell`
using the functions of this module
- :return: :class:`~basyx.aas.model.provider.DictObjectStore`
+ :return: :class:`~basyx.aas.model.provider.DictIdentifiableStore`
"""
- obj_store: model.DictObjectStore[model.Identifiable] = model.DictObjectStore()
- obj_store.add(create_example_asset_identification_submodel())
- obj_store.add(create_example_bill_of_material_submodel())
- obj_store.add(create_example_submodel())
- obj_store.add(create_example_concept_description())
- obj_store.add(create_example_asset_administration_shell())
- return obj_store
+ identifiable_store: model.DictIdentifiableStore[model.Identifiable] = model.DictIdentifiableStore()
+ identifiable_store.add(create_example_asset_identification_submodel())
+ identifiable_store.add(create_example_bill_of_material_submodel())
+ identifiable_store.add(create_example_submodel())
+ identifiable_store.add(create_example_concept_description())
+ identifiable_store.add(create_example_asset_administration_shell())
+ return identifiable_store
def create_example_asset_identification_submodel() -> model.Submodel:
@@ -74,27 +74,27 @@ def create_example_asset_identification_submodel() -> model.Submodel:
"""
qualifier = model.Qualifier(
- type_='http://acplt.org/Qualifier/ExampleQualifier',
+ type_='http://example.org/Qualifier/ExampleQualifier',
value_type=model.datatypes.Int,
value=100,
value_id=model.ExternalReference((model.Key(type_=model.KeyTypes.GLOBAL_REFERENCE,
- value='http://acplt.org/ValueId/ExampleValueId'),)),
+ value='http://example.org/ValueId/ExampleValueId'),)),
kind=model.QualifierKind.CONCEPT_QUALIFIER)
qualifier2 = model.Qualifier(
- type_='http://acplt.org/Qualifier/ExampleQualifier2',
+ type_='http://example.org/Qualifier/ExampleQualifier2',
value_type=model.datatypes.Int,
value=50,
value_id=model.ExternalReference((model.Key(type_=model.KeyTypes.GLOBAL_REFERENCE,
- value='http://acplt.org/ValueId/ExampleValueId'),)),
+ value='http://example.org/ValueId/ExampleValueId'),)),
kind=model.QualifierKind.TEMPLATE_QUALIFIER)
qualifier3 = model.Qualifier(
- type_='http://acplt.org/Qualifier/ExampleQualifier3',
+ type_='http://example.org/Qualifier/ExampleQualifier3',
value_type=model.datatypes.DateTime,
value=model.datatypes.DateTime(2023, 4, 7, 16, 59, 54, 870123),
value_id=model.ExternalReference((model.Key(type_=model.KeyTypes.GLOBAL_REFERENCE,
- value='http://acplt.org/ValueId/ExampleValueId'),)),
+ value='http://example.org/ValueId/ExampleValueId'),)),
kind=model.QualifierKind.VALUE_QUALIFIER)
extension = model.Extension(
@@ -102,7 +102,7 @@ def create_example_asset_identification_submodel() -> model.Submodel:
value_type=model.datatypes.String,
value="ExampleExtensionValue",
refers_to=[model.ModelReference((model.Key(type_=model.KeyTypes.ASSET_ADMINISTRATION_SHELL,
- value='http://acplt.org/RefersTo/ExampleRefersTo'),),
+ value='http://example.org/RefersTo/ExampleRefersTo'),),
model.AssetAdministrationShell)],)
# Property-Element conform to 'Verwaltungsschale in der Praxis' page 41 ManufacturerName:
@@ -112,7 +112,7 @@ def create_example_asset_identification_submodel() -> model.Submodel:
value_type=model.datatypes.String,
value='ACPLT',
value_id=model.ExternalReference((model.Key(type_=model.KeyTypes.GLOBAL_REFERENCE,
- value='http://acplt.org/ValueId/ExampleValueId'),)),
+ value='http://example.org/ValueId/ExampleValueId'),)),
category="PARAMETER",
description=model.MultiLanguageTextType({
'en-US': 'Legally valid designation of the natural or judicial person which '
@@ -140,7 +140,7 @@ def create_example_asset_identification_submodel() -> model.Submodel:
value_type=model.datatypes.String,
value='978-8234-234-342',
value_id=model.ExternalReference((model.Key(type_=model.KeyTypes.GLOBAL_REFERENCE,
- value='http://acplt.org/ValueId/ExampleValueId'),)),
+ value='http://example.org/ValueId/ExampleValueId'),)),
category="PARAMETER",
description=model.MultiLanguageTextType({
'en-US': 'Legally valid designation of the natural or judicial person which '
@@ -165,7 +165,7 @@ def create_example_asset_identification_submodel() -> model.Submodel:
# asset identification submodel which will be included in the asset object
identification_submodel = model.Submodel(
- id_='http://acplt.org/Submodels/Assets/TestAsset/Identification',
+ id_='http://example.org/Submodels/Assets/TestAsset/Identification',
submodel_element=(identification_submodel_element_manufacturer_name,
identification_submodel_element_instance_id),
id_short='Identification',
@@ -179,13 +179,14 @@ def create_example_asset_identification_submodel() -> model.Submodel:
revision='0',
creator=model.ExternalReference((
model.Key(model.KeyTypes.GLOBAL_REFERENCE,
- 'http://acplt.org/AdministrativeInformation/'
+ 'http://example.org/AdministrativeInformation/'
'TestAsset/Identification'),
)),
- template_id='http://acplt.org/AdministrativeInformation'
+ template_id='http://example.org/AdministrativeInformation'
'Templates/TestAsset/Identification'),
semantic_id=model.ModelReference((model.Key(type_=model.KeyTypes.SUBMODEL,
- value='http://acplt.org/SubmodelTemplates/AssetIdentification'),),
+ value='http://example.org/SubmodelTemplates/'
+ 'AssetIdentification'),),
model.Submodel),
qualifier=(),
kind=model.ModellingKind.INSTANCE,
@@ -208,13 +209,13 @@ def create_example_bill_of_material_submodel() -> model.Submodel:
value_type=model.datatypes.String,
value='exampleValue',
value_id=model.ExternalReference((model.Key(type_=model.KeyTypes.GLOBAL_REFERENCE,
- value='http://acplt.org/ValueId/ExampleValueId'),)),
+ value='http://example.org/ValueId/ExampleValueId'),)),
category='CONSTANT',
description=model.MultiLanguageTextType({'en-US': 'Example Property object',
'de': 'Beispiel Property Element'}),
parent=None,
semantic_id=model.ExternalReference((model.Key(type_=model.KeyTypes.GLOBAL_REFERENCE,
- value='http://acplt.org/Properties/ExampleProperty'),)),
+ value='http://example.org/Properties/ExampleProperty'),)),
qualifier=(),
extension=(),
supplemental_semantic_id=(),
@@ -226,13 +227,13 @@ def create_example_bill_of_material_submodel() -> model.Submodel:
value_type=model.datatypes.String,
value='exampleValue2',
value_id=model.ExternalReference((model.Key(type_=model.KeyTypes.GLOBAL_REFERENCE,
- value='http://acplt.org/ValueId/ExampleValueId'),)),
+ value='http://example.org/ValueId/ExampleValueId'),)),
category='CONSTANT',
description=model.MultiLanguageTextType({'en-US': 'Example Property object',
'de': 'Beispiel Property Element'}),
parent=None,
semantic_id=model.ExternalReference((model.Key(type_=model.KeyTypes.GLOBAL_REFERENCE,
- value='http://acplt.org/Properties/ExampleProperty'),)),
+ value='http://example.org/Properties/ExampleProperty'),)),
qualifier=(),
extension=(),
supplemental_semantic_id=(),
@@ -243,12 +244,12 @@ def create_example_bill_of_material_submodel() -> model.Submodel:
id_short='ExampleEntity',
entity_type=model.EntityType.SELF_MANAGED_ENTITY,
statement={submodel_element_property, submodel_element_property2},
- global_asset_id='http://acplt.org/TestAsset/',
+ global_asset_id='http://example.org/TestAsset/',
specific_asset_id={
model.SpecificAssetId(name="TestKey", value="TestValue",
external_subject_id=model.ExternalReference(
(model.Key(type_=model.KeyTypes.GLOBAL_REFERENCE,
- value='http://acplt.org/SpecificAssetId/'),))
+ value='http://example.org/SpecificAssetId/'),))
)},
category="PARAMETER",
description=model.MultiLanguageTextType({
@@ -302,7 +303,7 @@ def create_example_bill_of_material_submodel() -> model.Submodel:
# bill of material submodel which will be included in the asset object
bill_of_material = model.Submodel(
- id_='http://acplt.org/Submodels/Assets/TestAsset/BillOfMaterial',
+ id_='http://example.org/Submodels/Assets/TestAsset/BillOfMaterial',
submodel_element=(entity,
entity_2),
id_short='BillOfMaterial',
@@ -313,10 +314,10 @@ def create_example_bill_of_material_submodel() -> model.Submodel:
}),
parent=None,
administration=model.AdministrativeInformation(version='9',
- template_id='http://acplt.org/AdministrativeInformation'
+ template_id='http://example.org/AdministrativeInformation'
'Templates/TestAsset/BillOfMaterial'),
semantic_id=model.ModelReference((model.Key(type_=model.KeyTypes.SUBMODEL,
- value='http://acplt.org/SubmodelTemplates/BillOfMaterial'),),
+ value='http://example.org/SubmodelTemplates/BillOfMaterial'),),
model.Submodel),
qualifier=(),
kind=model.ModellingKind.INSTANCE,
@@ -340,7 +341,7 @@ def create_example_submodel() -> model.Submodel:
value_type=model.datatypes.String,
value='exampleValue',
value_id=model.ExternalReference((model.Key(type_=model.KeyTypes.GLOBAL_REFERENCE,
- value='http://acplt.org/ValueId/ExampleValueId'),)),
+ value='http://example.org/ValueId/ExampleValueId'),)),
display_name=model.MultiLanguageNameType({'en-US': 'ExampleProperty',
'de': 'BeispielProperty'}),
category='CONSTANT',
@@ -348,14 +349,14 @@ def create_example_submodel() -> model.Submodel:
'de': 'Beispiel Property Element'}),
parent=None,
semantic_id=model.ExternalReference((model.Key(type_=model.KeyTypes.GLOBAL_REFERENCE,
- value='http://acplt.org/Properties/ExampleProperty'),), ),
+ value='http://example.org/Properties/ExampleProperty'),), ),
qualifier=(),
extension=(),
supplemental_semantic_id=(model.ExternalReference((model.Key(type_=model.KeyTypes.GLOBAL_REFERENCE,
- value='http://acplt.org/Properties/'
+ value='http://example.org/Properties/'
'ExampleProperty/SupplementalId1'),)),
model.ExternalReference((model.Key(type_=model.KeyTypes.GLOBAL_REFERENCE,
- value='http://acplt.org/Properties/'
+ value='http://example.org/Properties/'
'ExampleProperty/SupplementalId2'),))),
embedded_data_specifications=(_embedded_data_specification_iec61360,))
@@ -364,7 +365,7 @@ def create_example_submodel() -> model.Submodel:
value_type=model.datatypes.String,
value='exampleValue',
value_id=model.ExternalReference((model.Key(type_=model.KeyTypes.GLOBAL_REFERENCE,
- value='http://acplt.org/ValueId/ExampleValueId'),)),
+ value='http://example.org/ValueId/ExampleValueId'),)),
display_name=model.MultiLanguageNameType({'en-US': 'ExampleProperty',
'de': 'BeispielProperty'}),
category='CONSTANT',
@@ -372,11 +373,11 @@ def create_example_submodel() -> model.Submodel:
'de': 'Beispiel Property Element'}),
parent=None,
semantic_id=model.ExternalReference((model.Key(type_=model.KeyTypes.GLOBAL_REFERENCE,
- value='http://acplt.org/Properties/ExampleProperty'),)),
+ value='http://example.org/Properties/ExampleProperty'),)),
qualifier=(),
extension=(),
supplemental_semantic_id=(model.ExternalReference((model.Key(type_=model.KeyTypes.GLOBAL_REFERENCE,
- value='http://acplt.org/Properties/'
+ value='http://example.org/Properties/'
'ExampleProperty2/SupplementalId'),)),),
embedded_data_specifications=()
)
@@ -386,17 +387,17 @@ def create_example_submodel() -> model.Submodel:
value=model.MultiLanguageTextType({'en-US': 'Example value of a MultiLanguageProperty element',
'de': 'Beispielwert für ein MultiLanguageProperty-Element'}),
value_id=model.ExternalReference((model.Key(type_=model.KeyTypes.GLOBAL_REFERENCE,
- value='http://acplt.org/ValueId/ExampleMultiLanguageValueId'),)),
+ value='http://example.org/ValueId/ExampleMultiLanguageValueId'),)),
category='CONSTANT',
description=model.MultiLanguageTextType({'en-US': 'Example MultiLanguageProperty object',
'de': 'Beispiel MultiLanguageProperty Element'}),
parent=None,
semantic_id=model.ExternalReference((model.Key(type_=model.KeyTypes.GLOBAL_REFERENCE,
- value='http://acplt.org/MultiLanguageProperties/'
+ value='http://example.org/MultiLanguageProperties/'
'ExampleMultiLanguageProperty'),),
referred_semantic_id=model.ExternalReference((model.Key(
type_=model.KeyTypes.GLOBAL_REFERENCE,
- value='http://acplt.org/Properties/ExampleProperty/Referred'),))),
+ value='http://example.org/Properties/ExampleProperty/Referred'),))),
qualifier=(),
extension=(),
supplemental_semantic_id=(),
@@ -413,7 +414,7 @@ def create_example_submodel() -> model.Submodel:
'de': 'Beispiel Range Element'}),
parent=None,
semantic_id=model.ExternalReference((model.Key(type_=model.KeyTypes.GLOBAL_REFERENCE,
- value='http://acplt.org/Ranges/ExampleRange'),)),
+ value='http://example.org/Ranges/ExampleRange'),)),
qualifier=(),
extension=(),
supplemental_semantic_id=(),
@@ -429,7 +430,7 @@ def create_example_submodel() -> model.Submodel:
'de': 'Beispiel Blob Element'}),
parent=None,
semantic_id=model.ExternalReference((model.Key(type_=model.KeyTypes.GLOBAL_REFERENCE,
- value='http://acplt.org/Blobs/ExampleBlob'),)),
+ value='http://example.org/Blobs/ExampleBlob'),)),
qualifier=(),
extension=(),
supplemental_semantic_id=(),
@@ -445,7 +446,7 @@ def create_example_submodel() -> model.Submodel:
'de': 'Beispiel File Element'}),
parent=None,
semantic_id=model.ExternalReference((model.Key(type_=model.KeyTypes.GLOBAL_REFERENCE,
- value='http://acplt.org/Files/ExampleFile'),)),
+ value='http://example.org/Files/ExampleFile'),)),
qualifier=(),
extension=(),
supplemental_semantic_id=(),
@@ -464,7 +465,7 @@ def create_example_submodel() -> model.Submodel:
}),
parent=None,
semantic_id=model.ExternalReference((model.Key(type_=model.KeyTypes.GLOBAL_REFERENCE,
- value='http://acplt.org/Files/ExampleFile'),)),
+ value='http://example.org/Files/ExampleFile'),)),
qualifier=(),
extension=(),
supplemental_semantic_id=(),
@@ -474,7 +475,7 @@ def create_example_submodel() -> model.Submodel:
submodel_element_reference_element = model.ReferenceElement(
id_short='ExampleReferenceElement',
value=model.ModelReference((model.Key(type_=model.KeyTypes.SUBMODEL,
- value='http://acplt.org/Test_Submodel'),
+ value='http://example.org/Test_Submodel'),
model.Key(type_=model.KeyTypes.PROPERTY,
value='ExampleProperty'),),
model.Property),
@@ -484,7 +485,7 @@ def create_example_submodel() -> model.Submodel:
parent=None,
semantic_id=model.ExternalReference((model.Key(
type_=model.KeyTypes.GLOBAL_REFERENCE,
- value='http://acplt.org/ReferenceElements/ExampleReferenceElement'
+ value='http://example.org/ReferenceElements/ExampleReferenceElement'
),)),
qualifier=(),
extension=(),
@@ -497,7 +498,7 @@ def create_example_submodel() -> model.Submodel:
first=model.ModelReference((
model.Key(
type_=model.KeyTypes.SUBMODEL,
- value='http://acplt.org/Test_Submodel'),
+ value='http://example.org/Test_Submodel'),
model.Key(
type_=model.KeyTypes.PROPERTY,
value='ExampleProperty'),
@@ -505,7 +506,7 @@ def create_example_submodel() -> model.Submodel:
second=model.ModelReference((
model.Key(
type_=model.KeyTypes.SUBMODEL,
- value='http://acplt.org/Test_Submodel'),
+ value='http://example.org/Test_Submodel'),
model.Key(
type_=model.KeyTypes.PROPERTY,
value='ExampleProperty2'),
@@ -515,7 +516,7 @@ def create_example_submodel() -> model.Submodel:
'de': 'Beispiel RelationshipElement Element'}),
parent=None,
semantic_id=model.ModelReference((model.Key(type_=model.KeyTypes.CONCEPT_DESCRIPTION,
- value='https://acplt.org/Test_ConceptDescription'),),
+ value='https://example.org/Test_ConceptDescription'),),
model.ConceptDescription),
qualifier=(),
extension=(),
@@ -525,11 +526,11 @@ def create_example_submodel() -> model.Submodel:
submodel_element_annotated_relationship_element = model.AnnotatedRelationshipElement(
id_short='ExampleAnnotatedRelationshipElement',
- first=model.ModelReference((model.Key(type_=model.KeyTypes.SUBMODEL, value='http://acplt.org/Test_Submodel'),
+ first=model.ModelReference((model.Key(type_=model.KeyTypes.SUBMODEL, value='http://example.org/Test_Submodel'),
model.Key(type_=model.KeyTypes.PROPERTY,
value='ExampleProperty'),),
model.Property),
- second=model.ModelReference((model.Key(type_=model.KeyTypes.SUBMODEL, value='http://acplt.org/Test_Submodel'),
+ second=model.ModelReference((model.Key(type_=model.KeyTypes.SUBMODEL, value='http://example.org/Test_Submodel'),
model.Key(type_=model.KeyTypes.PROPERTY,
value='ExampleProperty2'),),
model.Property),
@@ -550,7 +551,7 @@ def create_example_submodel() -> model.Submodel:
'de': 'Beispiel AnnotatedRelationshipElement Element'}),
parent=None,
semantic_id=model.ExternalReference((model.Key(type_=model.KeyTypes.GLOBAL_REFERENCE,
- value='http://acplt.org/RelationshipElements/'
+ value='http://example.org/RelationshipElements/'
'ExampleAnnotatedRelationshipElement'),)),
qualifier=(),
extension=(),
@@ -563,7 +564,7 @@ def create_example_submodel() -> model.Submodel:
value_type=model.datatypes.String,
value='exampleValue',
value_id=model.ExternalReference((model.Key(type_=model.KeyTypes.GLOBAL_REFERENCE,
- value='http://acplt.org/ValueId/ExampleValueId'),)),
+ value='http://example.org/ValueId/ExampleValueId'),)),
display_name=model.MultiLanguageNameType({'en-US': 'ExampleProperty',
'de': 'BeispielProperty'}),
category='CONSTANT',
@@ -571,7 +572,7 @@ def create_example_submodel() -> model.Submodel:
'de': 'Beispiel Property Element'}),
parent=None,
semantic_id=model.ExternalReference((model.Key(type_=model.KeyTypes.GLOBAL_REFERENCE,
- value='http://acplt.org/Properties/ExamplePropertyInput'),)),
+ value='http://example.org/Properties/ExamplePropertyInput'),)),
qualifier=(),
extension=(),
supplemental_semantic_id=(),
@@ -583,7 +584,7 @@ def create_example_submodel() -> model.Submodel:
value_type=model.datatypes.String,
value='exampleValue',
value_id=model.ExternalReference((model.Key(type_=model.KeyTypes.GLOBAL_REFERENCE,
- value='http://acplt.org/ValueId/ExampleValueId'),)),
+ value='http://example.org/ValueId/ExampleValueId'),)),
display_name=model.MultiLanguageNameType({'en-US': 'ExampleProperty',
'de': 'BeispielProperty'}),
category='CONSTANT',
@@ -591,7 +592,7 @@ def create_example_submodel() -> model.Submodel:
'de': 'Beispiel Property Element'}),
parent=None,
semantic_id=model.ExternalReference((model.Key(type_=model.KeyTypes.GLOBAL_REFERENCE,
- value='http://acplt.org/Properties/ExamplePropertyOutput'),)),
+ value='http://example.org/Properties/ExamplePropertyOutput'),)),
qualifier=(),
extension=(),
supplemental_semantic_id=(),
@@ -603,7 +604,7 @@ def create_example_submodel() -> model.Submodel:
value_type=model.datatypes.String,
value='exampleValue',
value_id=model.ExternalReference((model.Key(type_=model.KeyTypes.GLOBAL_REFERENCE,
- value='http://acplt.org/ValueId/ExampleValueId'),)),
+ value='http://example.org/ValueId/ExampleValueId'),)),
display_name=model.MultiLanguageNameType({'en-US': 'ExampleProperty',
'de': 'BeispielProperty'}),
category='CONSTANT',
@@ -611,7 +612,8 @@ def create_example_submodel() -> model.Submodel:
'de': 'Beispiel Property Element'}),
parent=None,
semantic_id=model.ExternalReference((model.Key(type_=model.KeyTypes.GLOBAL_REFERENCE,
- value='http://acplt.org/Properties/ExamplePropertyInOutput'),)),
+ value='http://example.org/Properties/'
+ 'ExamplePropertyInOutput'),)),
qualifier=(),
extension=(),
supplemental_semantic_id=(),
@@ -628,7 +630,7 @@ def create_example_submodel() -> model.Submodel:
'de': 'Beispiel Operation Element'}),
parent=None,
semantic_id=model.ExternalReference((model.Key(type_=model.KeyTypes.GLOBAL_REFERENCE,
- value='http://acplt.org/Operations/'
+ value='http://example.org/Operations/'
'ExampleOperation'),)),
qualifier=(),
extension=(),
@@ -643,7 +645,7 @@ def create_example_submodel() -> model.Submodel:
'de': 'Beispiel Capability Element'}),
parent=None,
semantic_id=model.ExternalReference((model.Key(type_=model.KeyTypes.GLOBAL_REFERENCE,
- value='http://acplt.org/Capabilities/'
+ value='http://example.org/Capabilities/'
'ExampleCapability'),)),
qualifier=(),
extension=(),
@@ -653,7 +655,8 @@ def create_example_submodel() -> model.Submodel:
submodel_element_basic_event_element = model.BasicEventElement(
id_short='ExampleBasicEventElement',
- observed=model.ModelReference((model.Key(type_=model.KeyTypes.SUBMODEL, value='http://acplt.org/Test_Submodel'),
+ observed=model.ModelReference((model.Key(type_=model.KeyTypes.SUBMODEL,
+ value='http://example.org/Test_Submodel'),
model.Key(type_=model.KeyTypes.PROPERTY,
value='ExampleProperty'),),
model.Property),
@@ -661,7 +664,7 @@ def create_example_submodel() -> model.Submodel:
state=model.StateOfEvent.ON,
message_topic='ExampleTopic',
message_broker=model.ModelReference((model.Key(model.KeyTypes.SUBMODEL,
- "http://acplt.org/ExampleMessageBroker"),),
+ "http://example.org/ExampleMessageBroker"),),
model.Submodel),
last_update=model.datatypes.DateTime(2022, 11, 12, 23, 50, 23, 123456, datetime.timezone.utc),
min_interval=model.datatypes.Duration(microseconds=1),
@@ -672,7 +675,7 @@ def create_example_submodel() -> model.Submodel:
'de': 'Beispiel BasicEventElement Element'}),
parent=None,
semantic_id=model.ExternalReference((model.Key(type_=model.KeyTypes.GLOBAL_REFERENCE,
- value='http://acplt.org/Events/ExampleBasicEventElement'),)),
+ value='http://example.org/Events/ExampleBasicEventElement'),)),
qualifier=(),
extension=(),
supplemental_semantic_id=(),
@@ -685,7 +688,7 @@ def create_example_submodel() -> model.Submodel:
value=(submodel_element_property, submodel_element_property_2),
semantic_id_list_element=model.ExternalReference((model.Key(
type_=model.KeyTypes.GLOBAL_REFERENCE,
- value='http://acplt.org/Properties/ExampleProperty'
+ value='http://example.org/Properties/ExampleProperty'
),)),
value_type_list_element=model.datatypes.String,
order_relevant=True,
@@ -694,7 +697,7 @@ def create_example_submodel() -> model.Submodel:
'de': 'Beispiel SubmodelElementList Element'}),
parent=None,
semantic_id=model.ExternalReference((model.Key(type_=model.KeyTypes.GLOBAL_REFERENCE,
- value='http://acplt.org/SubmodelElementLists/'
+ value='http://example.org/SubmodelElementLists/'
'ExampleSubmodelElementList'),)),
qualifier=(),
extension=(),
@@ -716,7 +719,7 @@ def create_example_submodel() -> model.Submodel:
'de': 'Beispiel SubmodelElementCollection Element'}),
parent=None,
semantic_id=model.ExternalReference((model.Key(type_=model.KeyTypes.GLOBAL_REFERENCE,
- value='http://acplt.org/SubmodelElementCollections/'
+ value='http://example.org/SubmodelElementCollections/'
'ExampleSubmodelElementCollection'),)),
qualifier=(),
extension=(),
@@ -725,7 +728,7 @@ def create_example_submodel() -> model.Submodel:
)
submodel = model.Submodel(
- id_='https://acplt.org/Test_Submodel',
+ id_='https://example.org/Test_Submodel',
submodel_element=(submodel_element_relationship_element,
submodel_element_annotated_relationship_element,
submodel_element_operation,
@@ -741,11 +744,11 @@ def create_example_submodel() -> model.Submodel:
revision='0',
creator=model.ExternalReference((
model.Key(model.KeyTypes.GLOBAL_REFERENCE,
- 'http://acplt.org/AdministrativeInformation/'
+ 'http://example.org/AdministrativeInformation/'
'Test_Submodel'),
)),),
semantic_id=model.ExternalReference((model.Key(type_=model.KeyTypes.GLOBAL_REFERENCE,
- value='http://acplt.org/SubmodelTemplates/'
+ value='http://example.org/SubmodelTemplates/'
'ExampleSubmodel'),)),
qualifier=(),
kind=model.ModellingKind.INSTANCE,
@@ -763,9 +766,9 @@ def create_example_concept_description() -> model.ConceptDescription:
:return: example concept description
"""
concept_description = model.ConceptDescription(
- id_='https://acplt.org/Test_ConceptDescription',
+ id_='https://example.org/Test_ConceptDescription',
is_case_of={model.ExternalReference((model.Key(type_=model.KeyTypes.GLOBAL_REFERENCE,
- value='http://acplt.org/DataSpecifications/'
+ value='http://example.org/DataSpecifications/'
'ConceptDescriptions/TestConceptDescription'),))},
id_short='TestConceptDescription',
category=None,
@@ -776,10 +779,10 @@ def create_example_concept_description() -> model.ConceptDescription:
revision='0',
creator=model.ExternalReference((
model.Key(model.KeyTypes.GLOBAL_REFERENCE,
- 'http://acplt.org/AdministrativeInformation/'
+ 'http://example.org/AdministrativeInformation/'
'Test_ConceptDescription'),
)),
- template_id='http://acplt.org/AdministrativeInformation'
+ template_id='http://example.org/AdministrativeInformation'
'Templates/Test_ConceptDescription',
embedded_data_specifications=(
_embedded_data_specification_iec61360,
@@ -799,17 +802,17 @@ def create_example_asset_administration_shell() -> \
asset_information = model.AssetInformation(
asset_kind=model.AssetKind.INSTANCE,
- global_asset_id='http://acplt.org/TestAsset/',
+ global_asset_id='http://example.org/TestAsset/',
specific_asset_id={model.SpecificAssetId(name="TestKey",
value="TestValue",
external_subject_id=model.ExternalReference(
(model.Key(type_=model.KeyTypes.GLOBAL_REFERENCE,
- value='http://acplt.org/SpecificAssetId/'),)),
+ value='http://example.org/SpecificAssetId/'),)),
semantic_id=model.ExternalReference((model.Key(
model.KeyTypes.GLOBAL_REFERENCE,
- "http://acplt.org/SpecificAssetId/"
+ "http://example.org/SpecificAssetId/"
),)))},
- asset_type='http://acplt.org/TestAssetType/',
+ asset_type='http://example.org/TestAssetType/',
default_thumbnail=model.Resource(
"file:///path/to/thumbnail.png",
"image/png"
@@ -818,7 +821,7 @@ def create_example_asset_administration_shell() -> \
asset_administration_shell = model.AssetAdministrationShell(
asset_information=asset_information,
- id_='https://acplt.org/Test_AssetAdministrationShell',
+ id_='https://example.org/Test_AssetAdministrationShell',
id_short='TestAssetAdministrationShell',
category=None,
description=model.MultiLanguageTextType({
@@ -830,32 +833,35 @@ def create_example_asset_administration_shell() -> \
revision='0',
creator=model.ExternalReference((
model.Key(model.KeyTypes.GLOBAL_REFERENCE,
- 'http://acplt.org/AdministrativeInformation/'
+ 'http://example.org/AdministrativeInformation/'
'Test_AssetAdministrationShell'),
)),
- template_id='http://acplt.org/AdministrativeInformation'
+ template_id='http://example.org/AdministrativeInformation'
'Templates/Test_AssetAdministrationShell'),
submodel={model.ModelReference((model.Key(type_=model.KeyTypes.SUBMODEL,
- value='https://acplt.org/Test_Submodel'),),
+ value='https://example.org/Test_Submodel'),),
model.Submodel,
model.ExternalReference((
model.Key(type_=model.KeyTypes.GLOBAL_REFERENCE,
- value='http://acplt.org/SubmodelTemplates/ExampleSubmodel'),
+ value='http://example.org/SubmodelTemplates/ExampleSubmodel'),
))),
model.ModelReference((model.Key(type_=model.KeyTypes.SUBMODEL,
- value='http://acplt.org/Submodels/Assets/TestAsset/Identification'),),
+ value='http://example.org/Submodels/Assets/'
+ 'TestAsset/Identification'),),
model.Submodel,
model.ModelReference((
model.Key(type_=model.KeyTypes.SUBMODEL,
- value='http://acplt.org/SubmodelTemplates/AssetIdentification'),),
+ value='http://example.org/SubmodelTemplates/'
+ 'AssetIdentification'),),
model.Submodel
)),
model.ModelReference((model.Key(type_=model.KeyTypes.SUBMODEL,
- value='http://acplt.org/Submodels/Assets/TestAsset/BillOfMaterial'),),
+ value='http://example.org/Submodels/Assets/'
+ 'TestAsset/BillOfMaterial'),),
model.Submodel),
},
derived_from=model.ModelReference((model.Key(type_=model.KeyTypes.ASSET_ADMINISTRATION_SHELL,
- value='https://acplt.org/TestAssetAdministrationShell2'),),
+ value='https://example.org/TestAssetAdministrationShell2'),),
model.AssetAdministrationShell),
extension=(),
embedded_data_specifications=(_embedded_data_specification_iec61360,)
@@ -891,6 +897,6 @@ def check_example_submodel(checker: AASDataChecker, submodel: model.Submodel) ->
checker.check_submodel_equal(submodel, expected_submodel)
-def check_full_example(checker: AASDataChecker, obj_store: model.DictObjectStore) -> None:
+def check_full_example(checker: AASDataChecker, identifiable_store: model.DictIdentifiableStore) -> None:
expected_data = create_full_example()
- checker.check_object_store(obj_store, expected_data)
+ checker.check_identifiable_store(identifiable_store, expected_data)
diff --git a/sdk/basyx/aas/examples/data/example_aas_mandatory_attributes.py b/sdk/basyx/aas/examples/data/example_aas_mandatory_attributes.py
index a18f3cbc6..c3911bd99 100644
--- a/sdk/basyx/aas/examples/data/example_aas_mandatory_attributes.py
+++ b/sdk/basyx/aas/examples/data/example_aas_mandatory_attributes.py
@@ -1,13 +1,14 @@
-# Copyright (c) 2025 the Eclipse BaSyx Authors
+# Copyright (c) 2026 the Eclipse BaSyx Authors
#
# This program and the accompanying materials are made available under the terms of the MIT License, available in
# the LICENSE file of this project.
#
# SPDX-License-Identifier: MIT
"""
-Module for the creation of an :class:`ObjectStore ` with an example
-:class:`~basyx.aas.model.aas.AssetAdministrationShell`, example :class:`Submodels `
-and an example :class:`~basyx.aas.model.concept.ConceptDescription`. All objects only contain mandatory attributes.
+Module for the creation of an :class:`IdentifiableStore ` with an
+example :class:`~basyx.aas.model.aas.AssetAdministrationShell`, example
+:class:`Submodels ` and an example
+:class:`~basyx.aas.model.concept.ConceptDescription`. All objects only contain mandatory attributes.
To get this object store use the function
:meth:`~basyx.aas.examples.data.example_aas_mandatory_attributes.create_full_example`. If you want to get single example
@@ -21,21 +22,21 @@
logger = logging.getLogger(__name__)
-def create_full_example() -> model.DictObjectStore:
+def create_full_example() -> model.DictIdentifiableStore:
"""
- Creates an :class:`~.basyx.aas.model.provider.DictObjectStore` which is filled with an example
+ Creates an :class:`~.basyx.aas.model.provider.DictIdentifiableStore` which is filled with an example
:class:`~basyx.aas.model.submodel.Submodel`, :class:`~basyx.aas.model.concept.ConceptDescription`
and :class:`~basyx.aas.model.aas.AssetAdministrationShell` using the functions of this module
- :return: :class:`~basyx.aas.model.provider.DictObjectStore`
+ :return: :class:`~basyx.aas.model.provider.DictIdentifiableStore`
"""
- obj_store: model.DictObjectStore[model.Identifiable] = model.DictObjectStore()
- obj_store.add(create_example_submodel())
- obj_store.add(create_example_empty_submodel())
- obj_store.add(create_example_concept_description())
- obj_store.add(create_example_asset_administration_shell())
- obj_store.add(create_example_empty_asset_administration_shell())
- return obj_store
+ identifiable_store: model.DictIdentifiableStore[model.Identifiable] = model.DictIdentifiableStore()
+ identifiable_store.add(create_example_submodel())
+ identifiable_store.add(create_example_empty_submodel())
+ identifiable_store.add(create_example_concept_description())
+ identifiable_store.add(create_example_asset_administration_shell())
+ identifiable_store.add(create_example_empty_asset_administration_shell())
+ return identifiable_store
def create_example_submodel() -> model.Submodel:
@@ -74,12 +75,12 @@ def create_example_submodel() -> model.Submodel:
submodel_element_relationship_element = model.RelationshipElement(
id_short='ExampleRelationshipElement',
first=model.ModelReference((model.Key(type_=model.KeyTypes.SUBMODEL,
- value='http://acplt.org/Test_Submodel'),
+ value='http://example.org/Test_Submodel'),
model.Key(type_=model.KeyTypes.PROPERTY,
value='ExampleProperty'),),
model.Property),
second=model.ModelReference((model.Key(type_=model.KeyTypes.SUBMODEL,
- value='http://acplt.org/Test_Submodel'),
+ value='http://example.org/Test_Submodel'),
model.Key(type_=model.KeyTypes.PROPERTY,
value='ExampleProperty'),),
model.Property))
@@ -87,12 +88,12 @@ def create_example_submodel() -> model.Submodel:
submodel_element_annotated_relationship_element = model.AnnotatedRelationshipElement(
id_short='ExampleAnnotatedRelationshipElement',
first=model.ModelReference((model.Key(type_=model.KeyTypes.SUBMODEL,
- value='http://acplt.org/Test_Submodel'),
+ value='http://example.org/Test_Submodel'),
model.Key(type_=model.KeyTypes.PROPERTY,
value='ExampleProperty'),),
model.Property),
second=model.ModelReference((model.Key(type_=model.KeyTypes.SUBMODEL,
- value='http://acplt.org/Test_Submodel'),
+ value='http://example.org/Test_Submodel'),
model.Key(type_=model.KeyTypes.PROPERTY,
value='ExampleProperty'),),
model.Property))
@@ -105,7 +106,8 @@ def create_example_submodel() -> model.Submodel:
submodel_element_basic_event_element = model.BasicEventElement(
id_short='ExampleBasicEventElement',
- observed=model.ModelReference((model.Key(type_=model.KeyTypes.SUBMODEL, value='http://acplt.org/Test_Submodel'),
+ observed=model.ModelReference((model.Key(type_=model.KeyTypes.SUBMODEL,
+ value='http://example.org/Test_Submodel'),
model.Key(type_=model.KeyTypes.PROPERTY, value='ExampleProperty'),),
model.Property),
direction=model.Direction.INPUT,
@@ -135,7 +137,7 @@ def create_example_submodel() -> model.Submodel:
value=())
submodel = model.Submodel(
- id_='https://acplt.org/Test_Submodel_Mandatory',
+ id_='https://example.org/Test_Submodel_Mandatory',
submodel_element=(submodel_element_relationship_element,
submodel_element_annotated_relationship_element,
submodel_element_operation,
@@ -153,7 +155,7 @@ def create_example_empty_submodel() -> model.Submodel:
:return: example submodel
"""
return model.Submodel(
- id_='https://acplt.org/Test_Submodel2_Mandatory')
+ id_='https://example.org/Test_Submodel2_Mandatory')
def create_example_concept_description() -> model.ConceptDescription:
@@ -163,7 +165,7 @@ def create_example_concept_description() -> model.ConceptDescription:
:return: example concept description
"""
concept_description = model.ConceptDescription(
- id_='https://acplt.org/Test_ConceptDescription_Mandatory')
+ id_='https://example.org/Test_ConceptDescription_Mandatory')
return concept_description
@@ -177,16 +179,16 @@ def create_example_asset_administration_shell() -> \
"""
asset_information = model.AssetInformation(
asset_kind=model.AssetKind.INSTANCE,
- global_asset_id='http://acplt.org/Test_Asset_Mandatory/')
+ global_asset_id='http://example.org/Test_Asset_Mandatory/')
asset_administration_shell = model.AssetAdministrationShell(
asset_information=asset_information,
- id_='https://acplt.org/Test_AssetAdministrationShell_Mandatory',
+ id_='https://example.org/Test_AssetAdministrationShell_Mandatory',
submodel={model.ModelReference((model.Key(type_=model.KeyTypes.SUBMODEL,
- value='https://acplt.org/Test_Submodel_Mandatory'),),
+ value='https://example.org/Test_Submodel_Mandatory'),),
model.Submodel),
model.ModelReference((model.Key(type_=model.KeyTypes.SUBMODEL,
- value='https://acplt.org/Test_Submodel2_Mandatory'),),
+ value='https://example.org/Test_Submodel2_Mandatory'),),
model.Submodel)},)
return asset_administration_shell
@@ -200,8 +202,8 @@ def create_example_empty_asset_administration_shell() -> model.AssetAdministrati
"""
asset_administration_shell = model.AssetAdministrationShell(
asset_information=model.AssetInformation(
- global_asset_id='http://acplt.org/TestAsset2_Mandatory/'),
- id_='https://acplt.org/Test_AssetAdministrationShell2_Mandatory')
+ global_asset_id='http://example.org/TestAsset2_Mandatory/'),
+ id_='https://example.org/Test_AssetAdministrationShell2_Mandatory')
return asset_administration_shell
@@ -234,6 +236,6 @@ def check_example_empty_submodel(checker: AASDataChecker, submodel: model.Submod
checker.check_submodel_equal(submodel, expected_submodel)
-def check_full_example(checker: AASDataChecker, obj_store: model.DictObjectStore) -> None:
+def check_full_example(checker: AASDataChecker, identifiable_store: model.DictIdentifiableStore) -> None:
expected_data = create_full_example()
- checker.check_object_store(obj_store, expected_data)
+ checker.check_identifiable_store(identifiable_store, expected_data)
diff --git a/sdk/basyx/aas/examples/data/example_aas_missing_attributes.py b/sdk/basyx/aas/examples/data/example_aas_missing_attributes.py
index 83232914a..25f0a1714 100644
--- a/sdk/basyx/aas/examples/data/example_aas_missing_attributes.py
+++ b/sdk/basyx/aas/examples/data/example_aas_missing_attributes.py
@@ -1,12 +1,12 @@
-# Copyright (c) 2025 the Eclipse BaSyx Authors
+# Copyright (c) 2026 the Eclipse BaSyx Authors
#
# This program and the accompanying materials are made available under the terms of the MIT License, available in
# the LICENSE file of this project.
#
# SPDX-License-Identifier: MIT
"""
-Module for the creation of an :class:`ObjectStore ` with missing object
-attribute combination for testing the serialization
+Module for the creation of an :class:`IdentifiableStore ` with missing
+object attribute combinations for testing the serialization.
"""
import datetime
import logging
@@ -17,19 +17,19 @@
logger = logging.getLogger(__name__)
-def create_full_example() -> model.DictObjectStore:
+def create_full_example() -> model.DictIdentifiableStore:
"""
- Creates an :class:`~basyx.aas.model.provider.DictObjectStore` containing an example
+ Creates an :class:`~basyx.aas.model.provider.DictIdentifiableStore` containing an example
:class:`~basyx.aas.model.submodel.Submodel`, an example :class:`~basyx.aas.model.concept.ConceptDescription` and an
example :class:`~basyx.aas.model.aas.AssetAdministrationShell`
- :return: :class:`basyx.aas.model.provider.DictObjectStore`
+ :return: :class:`basyx.aas.model.provider.DictIdentifiableStore`
"""
- obj_store: model.DictObjectStore[model.Identifiable] = model.DictObjectStore()
- obj_store.add(create_example_submodel())
- obj_store.add(create_example_concept_description())
- obj_store.add(create_example_asset_administration_shell())
- return obj_store
+ identifiable_store: model.DictIdentifiableStore[model.Identifiable] = model.DictIdentifiableStore()
+ identifiable_store.add(create_example_submodel())
+ identifiable_store.add(create_example_concept_description())
+ identifiable_store.add(create_example_asset_administration_shell())
+ return identifiable_store
def create_example_submodel() -> model.Submodel:
@@ -40,7 +40,7 @@ def create_example_submodel() -> model.Submodel:
:return: example submodel
"""
qualifier = model.Qualifier(
- type_='http://acplt.org/Qualifier/ExampleQualifier',
+ type_='http://example.org/Qualifier/ExampleQualifier',
value_type=model.datatypes.String)
submodel_element_property = model.Property(
@@ -53,7 +53,7 @@ def create_example_submodel() -> model.Submodel:
'de': 'Beispiel Property Element'}),
parent=None,
semantic_id=model.ExternalReference((model.Key(type_=model.KeyTypes.GLOBAL_REFERENCE,
- value='http://acplt.org/Properties/ExampleProperty'),)),
+ value='http://example.org/Properties/ExampleProperty'),)),
qualifier={qualifier})
submodel_element_multi_language_property = model.MultiLanguageProperty(
@@ -66,7 +66,7 @@ def create_example_submodel() -> model.Submodel:
'de': 'Beispiel MultiLanguageProperty Element'}),
parent=None,
semantic_id=model.ExternalReference((model.Key(type_=model.KeyTypes.GLOBAL_REFERENCE,
- value='http://acplt.org/MultiLanguageProperties/'
+ value='http://example.org/MultiLanguageProperties/'
'ExampleMultiLanguageProperty'),)),
qualifier=())
@@ -80,7 +80,7 @@ def create_example_submodel() -> model.Submodel:
'de': 'Beispiel Range Element'}),
parent=None,
semantic_id=model.ExternalReference((model.Key(type_=model.KeyTypes.GLOBAL_REFERENCE,
- value='http://acplt.org/Ranges/ExampleRange'),)),
+ value='http://example.org/Ranges/ExampleRange'),)),
qualifier=())
submodel_element_blob = model.Blob(
@@ -92,7 +92,7 @@ def create_example_submodel() -> model.Submodel:
'de': 'Beispiel Blob Element'}),
parent=None,
semantic_id=model.ExternalReference((model.Key(type_=model.KeyTypes.GLOBAL_REFERENCE,
- value='http://acplt.org/Blobs/ExampleBlob'),)),
+ value='http://example.org/Blobs/ExampleBlob'),)),
qualifier=())
submodel_element_file = model.File(
@@ -104,13 +104,13 @@ def create_example_submodel() -> model.Submodel:
'de': 'Beispiel File Element'}),
parent=None,
semantic_id=model.ExternalReference((model.Key(type_=model.KeyTypes.GLOBAL_REFERENCE,
- value='http://acplt.org/Files/ExampleFile'),)),
+ value='http://example.org/Files/ExampleFile'),)),
qualifier=())
submodel_element_reference_element = model.ReferenceElement(
id_short='ExampleReferenceElement',
value=model.ModelReference((model.Key(type_=model.KeyTypes.SUBMODEL,
- value='http://acplt.org/Test_Submodel'),
+ value='http://example.org/Test_Submodel'),
model.Key(type_=model.KeyTypes.PROPERTY,
value='ExampleProperty'),), model.Submodel),
category='PARAMETER',
@@ -119,19 +119,19 @@ def create_example_submodel() -> model.Submodel:
parent=None,
semantic_id=model.ExternalReference((model.Key(
type_=model.KeyTypes.GLOBAL_REFERENCE,
- value='http://acplt.org/ReferenceElements/ExampleReferenceElement'
+ value='http://example.org/ReferenceElements/ExampleReferenceElement'
),)),
qualifier=())
submodel_element_relationship_element = model.RelationshipElement(
id_short='ExampleRelationshipElement',
first=model.ModelReference((model.Key(type_=model.KeyTypes.SUBMODEL,
- value='http://acplt.org/Test_Submodel'),
+ value='http://example.org/Test_Submodel'),
model.Key(type_=model.KeyTypes.PROPERTY,
value='ExampleProperty'),),
model.Property),
second=model.ModelReference((model.Key(type_=model.KeyTypes.SUBMODEL,
- value='http://acplt.org/Test_Submodel'),
+ value='http://example.org/Test_Submodel'),
model.Key(type_=model.KeyTypes.PROPERTY,
value='ExampleProperty'),),
model.Property),
@@ -140,19 +140,19 @@ def create_example_submodel() -> model.Submodel:
'de': 'Beispiel RelationshipElement Element'}),
parent=None,
semantic_id=model.ExternalReference((model.Key(type_=model.KeyTypes.GLOBAL_REFERENCE,
- value='http://acplt.org/RelationshipElements/'
+ value='http://example.org/RelationshipElements/'
'ExampleRelationshipElement'),)),
qualifier=())
submodel_element_annotated_relationship_element = model.AnnotatedRelationshipElement(
id_short='ExampleAnnotatedRelationshipElement',
first=model.ModelReference((model.Key(type_=model.KeyTypes.SUBMODEL,
- value='http://acplt.org/Test_Submodel'),
+ value='http://example.org/Test_Submodel'),
model.Key(type_=model.KeyTypes.PROPERTY,
value='ExampleProperty'),),
model.Property),
second=model.ModelReference((model.Key(type_=model.KeyTypes.SUBMODEL,
- value='http://acplt.org/Test_Submodel'),
+ value='http://example.org/Test_Submodel'),
model.Key(type_=model.KeyTypes.PROPERTY,
value='ExampleProperty'),),
model.Property),
@@ -173,7 +173,7 @@ def create_example_submodel() -> model.Submodel:
'de': 'Beispiel AnnotatedRelationshipElement Element'}),
parent=None,
semantic_id=model.ExternalReference((model.Key(type_=model.KeyTypes.GLOBAL_REFERENCE,
- value='http://acplt.org/RelationshipElements/'
+ value='http://example.org/RelationshipElements/'
'ExampleAnnotatedRelationshipElement'),)),
qualifier=())
@@ -182,7 +182,7 @@ def create_example_submodel() -> model.Submodel:
value_type=model.datatypes.String,
value='exampleValue',
value_id=model.ExternalReference((model.Key(type_=model.KeyTypes.GLOBAL_REFERENCE,
- value='http://acplt.org/ValueId/ExampleValueId'),)),
+ value='http://example.org/ValueId/ExampleValueId'),)),
display_name=model.MultiLanguageNameType({'en-US': 'ExampleProperty',
'de': 'BeispielProperty'}),
category='CONSTANT',
@@ -190,7 +190,7 @@ def create_example_submodel() -> model.Submodel:
'de': 'Beispiel Property Element'}),
parent=None,
semantic_id=model.ExternalReference((model.Key(type_=model.KeyTypes.GLOBAL_REFERENCE,
- value='http://acplt.org/Properties/ExampleProperty'),)),
+ value='http://example.org/Properties/ExampleProperty'),)),
qualifier=())
input_variable_property = model.Property(
@@ -198,7 +198,7 @@ def create_example_submodel() -> model.Submodel:
value_type=model.datatypes.String,
value='exampleValue',
value_id=model.ExternalReference((model.Key(type_=model.KeyTypes.GLOBAL_REFERENCE,
- value='http://acplt.org/ValueId/ExampleValueId'),)),
+ value='http://example.org/ValueId/ExampleValueId'),)),
display_name=model.MultiLanguageNameType({'en-US': 'ExampleProperty',
'de': 'BeispielProperty'}),
category='CONSTANT',
@@ -206,7 +206,7 @@ def create_example_submodel() -> model.Submodel:
'de': 'Beispiel Property Element'}),
parent=None,
semantic_id=model.ExternalReference((model.Key(type_=model.KeyTypes.GLOBAL_REFERENCE,
- value='http://acplt.org/Properties/ExamplePropertyInput'),)),
+ value='http://example.org/Properties/ExamplePropertyInput'),)),
qualifier=())
output_variable_property = model.Property(
@@ -214,7 +214,7 @@ def create_example_submodel() -> model.Submodel:
value_type=model.datatypes.String,
value='exampleValue',
value_id=model.ExternalReference((model.Key(type_=model.KeyTypes.GLOBAL_REFERENCE,
- value='http://acplt.org/ValueId/ExampleValueId'),)),
+ value='http://example.org/ValueId/ExampleValueId'),)),
display_name=model.MultiLanguageNameType({'en-US': 'ExampleProperty',
'de': 'BeispielProperty'}),
category='CONSTANT',
@@ -222,7 +222,7 @@ def create_example_submodel() -> model.Submodel:
'de': 'Beispiel Property Element'}),
parent=None,
semantic_id=model.ExternalReference((model.Key(type_=model.KeyTypes.GLOBAL_REFERENCE,
- value='http://acplt.org/Properties/ExamplePropertyOutput'),)),
+ value='http://example.org/Properties/ExamplePropertyOutput'),)),
qualifier=(),
extension=(),
supplemental_semantic_id=(),
@@ -233,7 +233,7 @@ def create_example_submodel() -> model.Submodel:
value_type=model.datatypes.String,
value='exampleValue',
value_id=model.ExternalReference((model.Key(type_=model.KeyTypes.GLOBAL_REFERENCE,
- value='http://acplt.org/ValueId/ExampleValueId'),)),
+ value='http://example.org/ValueId/ExampleValueId'),)),
display_name=model.MultiLanguageNameType({'en-US': 'ExampleProperty',
'de': 'BeispielProperty'}),
category='CONSTANT',
@@ -241,7 +241,8 @@ def create_example_submodel() -> model.Submodel:
'de': 'Beispiel Property Element'}),
parent=None,
semantic_id=model.ExternalReference((model.Key(type_=model.KeyTypes.GLOBAL_REFERENCE,
- value='http://acplt.org/Properties/ExamplePropertyInOutput'),)),
+ value='http://example.org/Properties/'
+ 'ExamplePropertyInOutput'),)),
qualifier=(),
extension=(),
supplemental_semantic_id=(),
@@ -257,7 +258,7 @@ def create_example_submodel() -> model.Submodel:
'de': 'Beispiel Operation Element'}),
parent=None,
semantic_id=model.ExternalReference((model.Key(type_=model.KeyTypes.GLOBAL_REFERENCE,
- value='http://acplt.org/Operations/'
+ value='http://example.org/Operations/'
'ExampleOperation'),)),
qualifier=())
@@ -268,14 +269,14 @@ def create_example_submodel() -> model.Submodel:
'de': 'Beispiel Capability Element'}),
parent=None,
semantic_id=model.ExternalReference((model.Key(type_=model.KeyTypes.GLOBAL_REFERENCE,
- value='http://acplt.org/Capabilities/'
+ value='http://example.org/Capabilities/'
'ExampleCapability'),)),
qualifier=())
submodel_element_basic_event_element = model.BasicEventElement(
id_short='ExampleBasicEventElement',
observed=model.ModelReference((model.Key(type_=model.KeyTypes.SUBMODEL,
- value='http://acplt.org/Test_Submodel'),
+ value='http://example.org/Test_Submodel'),
model.Key(type_=model.KeyTypes.PROPERTY,
value='ExampleProperty'),),
model.Property),
@@ -283,7 +284,7 @@ def create_example_submodel() -> model.Submodel:
state=model.StateOfEvent.ON,
message_topic='ExampleTopic',
message_broker=model.ModelReference((model.Key(model.KeyTypes.SUBMODEL,
- "http://acplt.org/ExampleMessageBroker"),),
+ "http://example.org/ExampleMessageBroker"),),
model.Submodel),
last_update=model.datatypes.DateTime(2022, 11, 12, 23, 50, 23, 123456, datetime.timezone.utc),
min_interval=model.datatypes.Duration(microseconds=1),
@@ -294,7 +295,7 @@ def create_example_submodel() -> model.Submodel:
'de': 'Beispiel BasicEventElement Element'}),
parent=None,
semantic_id=model.ExternalReference((model.Key(type_=model.KeyTypes.GLOBAL_REFERENCE,
- value='http://acplt.org/Events/ExampleBasicEventElement'),)),
+ value='http://example.org/Events/ExampleBasicEventElement'),)),
qualifier=())
submodel_element_submodel_element_collection = model.SubmodelElementCollection(
@@ -310,12 +311,12 @@ def create_example_submodel() -> model.Submodel:
'de': 'Beispiel SubmodelElementCollection Element'}),
parent=None,
semantic_id=model.ExternalReference((model.Key(type_=model.KeyTypes.GLOBAL_REFERENCE,
- value='http://acplt.org/SubmodelElementCollections/'
+ value='http://example.org/SubmodelElementCollections/'
'ExampleSubmodelElementCollection'),)),
qualifier=())
submodel = model.Submodel(
- id_='https://acplt.org/Test_Submodel_Missing',
+ id_='https://example.org/Test_Submodel_Missing',
submodel_element=(submodel_element_relationship_element,
submodel_element_annotated_relationship_element,
submodel_element_operation,
@@ -330,7 +331,7 @@ def create_example_submodel() -> model.Submodel:
administration=model.AdministrativeInformation(version='9',
revision='0'),
semantic_id=model.ExternalReference((model.Key(type_=model.KeyTypes.GLOBAL_REFERENCE,
- value='http://acplt.org/SubmodelTemplates/'
+ value='http://example.org/SubmodelTemplates/'
'ExampleSubmodel'),)),
qualifier=(),
kind=model.ModellingKind.INSTANCE)
@@ -344,7 +345,7 @@ def create_example_concept_description() -> model.ConceptDescription:
:return: example concept description
"""
concept_description = model.ConceptDescription(
- id_='https://acplt.org/Test_ConceptDescription_Missing',
+ id_='https://example.org/Test_ConceptDescription_Missing',
is_case_of=None,
id_short='TestConceptDescription',
category=None,
@@ -370,16 +371,17 @@ def create_example_asset_administration_shell() -> model.AssetAdministrationShel
asset_information = model.AssetInformation(
asset_kind=model.AssetKind.INSTANCE,
- global_asset_id='http://acplt.org/Test_Asset_Missing/',
+ global_asset_id='http://example.org/Test_Asset_Missing/',
specific_asset_id={model.SpecificAssetId(name="TestKey", value="TestValue",
external_subject_id=model.ExternalReference(
(model.Key(type_=model.KeyTypes.GLOBAL_REFERENCE,
- value='http://acplt.org/SpecificAssetId/'),)))},
+ value='http://example.org/'
+ 'SpecificAssetId/'),)))},
default_thumbnail=resource)
asset_administration_shell = model.AssetAdministrationShell(
asset_information=asset_information,
- id_='https://acplt.org/Test_AssetAdministrationShell_Missing',
+ id_='https://example.org/Test_AssetAdministrationShell_Missing',
id_short='TestAssetAdministrationShell',
category=None,
description=model.MultiLanguageTextType({'en-US': 'An Example Asset Administration Shell for the test '
@@ -389,7 +391,7 @@ def create_example_asset_administration_shell() -> model.AssetAdministrationShel
administration=model.AdministrativeInformation(version='9',
revision='0'),
submodel={model.ModelReference((model.Key(type_=model.KeyTypes.SUBMODEL,
- value='https://acplt.org/Test_Submodel_Missing'),),
+ value='https://example.org/Test_Submodel_Missing'),),
model.Submodel)},
derived_from=None)
return asset_administration_shell
@@ -413,6 +415,6 @@ def check_example_submodel(checker: AASDataChecker, submodel: model.Submodel) ->
checker.check_submodel_equal(submodel, expected_submodel)
-def check_full_example(checker: AASDataChecker, obj_store: model.DictObjectStore) -> None:
+def check_full_example(checker: AASDataChecker, identifiable_store: model.DictIdentifiableStore) -> None:
expected_data = create_full_example()
- checker.check_object_store(obj_store, expected_data)
+ checker.check_identifiable_store(identifiable_store, expected_data)
diff --git a/sdk/basyx/aas/examples/data/example_submodel_template.py b/sdk/basyx/aas/examples/data/example_submodel_template.py
index 358e06b05..10d19e0bb 100644
--- a/sdk/basyx/aas/examples/data/example_submodel_template.py
+++ b/sdk/basyx/aas/examples/data/example_submodel_template.py
@@ -1,4 +1,4 @@
-# Copyright (c) 2025 the Eclipse BaSyx Authors
+# Copyright (c) 2026 the Eclipse BaSyx Authors
#
# This program and the accompanying materials are made available under the terms of the MIT License, available in
# the LICENSE file of this project.
@@ -36,7 +36,7 @@ def create_example_submodel_template() -> model.Submodel:
'de': 'Beispiel Property Element'}),
parent=None,
semantic_id=model.ExternalReference((model.Key(type_=model.KeyTypes.GLOBAL_REFERENCE,
- value='http://acplt.org/Properties/ExampleProperty'),)),
+ value='http://example.org/Properties/ExampleProperty'),)),
qualifier=())
submodel_element_multi_language_property = model.MultiLanguageProperty(
@@ -48,7 +48,7 @@ def create_example_submodel_template() -> model.Submodel:
'de': 'Beispiel MultiLanguageProperty Element'}),
parent=None,
semantic_id=model.ExternalReference((model.Key(type_=model.KeyTypes.GLOBAL_REFERENCE,
- value='http://acplt.org/MultiLanguageProperties/'
+ value='http://example.org/MultiLanguageProperties/'
'ExampleMultiLanguageProperty'),)),
qualifier=(),)
@@ -62,7 +62,7 @@ def create_example_submodel_template() -> model.Submodel:
'de': 'Beispiel Range Element'}),
parent=None,
semantic_id=model.ExternalReference((model.Key(type_=model.KeyTypes.GLOBAL_REFERENCE,
- value='http://acplt.org/Ranges/ExampleRange'),)),
+ value='http://example.org/Ranges/ExampleRange'),)),
qualifier=(),)
submodel_element_range_2 = model.Range(
@@ -75,7 +75,7 @@ def create_example_submodel_template() -> model.Submodel:
'de': 'Beispiel Range Element'}),
parent=None,
semantic_id=model.ExternalReference((model.Key(type_=model.KeyTypes.GLOBAL_REFERENCE,
- value='http://acplt.org/Ranges/ExampleRange'),)),
+ value='http://example.org/Ranges/ExampleRange'),)),
qualifier=())
submodel_element_blob = model.Blob(
@@ -87,7 +87,7 @@ def create_example_submodel_template() -> model.Submodel:
'de': 'Beispiel Blob Element'}),
parent=None,
semantic_id=model.ExternalReference((model.Key(type_=model.KeyTypes.GLOBAL_REFERENCE,
- value='http://acplt.org/Blobs/ExampleBlob'),)),
+ value='http://example.org/Blobs/ExampleBlob'),)),
qualifier=())
submodel_element_file = model.File(
@@ -99,7 +99,7 @@ def create_example_submodel_template() -> model.Submodel:
'de': 'Beispiel File Element'}),
parent=None,
semantic_id=model.ExternalReference((model.Key(type_=model.KeyTypes.GLOBAL_REFERENCE,
- value='http://acplt.org/Files/ExampleFile'),)),
+ value='http://example.org/Files/ExampleFile'),)),
qualifier=())
submodel_element_reference_element = model.ReferenceElement(
@@ -111,17 +111,17 @@ def create_example_submodel_template() -> model.Submodel:
parent=None,
semantic_id=model.ExternalReference((model.Key(
type_=model.KeyTypes.GLOBAL_REFERENCE,
- value='http://acplt.org/ReferenceElements/ExampleReferenceElement'
+ value='http://example.org/ReferenceElements/ExampleReferenceElement'
),)),
qualifier=())
submodel_element_relationship_element = model.RelationshipElement(
id_short='ExampleRelationshipElement',
- first=model.ModelReference((model.Key(type_=model.KeyTypes.SUBMODEL, value='http://acplt.org/Test_Submodel'),
+ first=model.ModelReference((model.Key(type_=model.KeyTypes.SUBMODEL, value='http://example.org/Test_Submodel'),
model.Key(type_=model.KeyTypes.PROPERTY,
value='ExampleProperty'),),
model.Property),
- second=model.ModelReference((model.Key(type_=model.KeyTypes.SUBMODEL, value='http://acplt.org/Test_Submodel'),
+ second=model.ModelReference((model.Key(type_=model.KeyTypes.SUBMODEL, value='http://example.org/Test_Submodel'),
model.Key(type_=model.KeyTypes.PROPERTY,
value='ExampleProperty'),),
model.Property),
@@ -130,17 +130,17 @@ def create_example_submodel_template() -> model.Submodel:
'de': 'Beispiel RelationshipElement Element'}),
parent=None,
semantic_id=model.ExternalReference((model.Key(type_=model.KeyTypes.GLOBAL_REFERENCE,
- value='http://acplt.org/RelationshipElements/'
+ value='http://example.org/RelationshipElements/'
'ExampleRelationshipElement'),)),
qualifier=())
submodel_element_annotated_relationship_element = model.AnnotatedRelationshipElement(
id_short='ExampleAnnotatedRelationshipElement',
- first=model.ModelReference((model.Key(type_=model.KeyTypes.SUBMODEL, value='http://acplt.org/Test_Submodel'),
+ first=model.ModelReference((model.Key(type_=model.KeyTypes.SUBMODEL, value='http://example.org/Test_Submodel'),
model.Key(type_=model.KeyTypes.PROPERTY,
value='ExampleProperty'),),
model.Property),
- second=model.ModelReference((model.Key(type_=model.KeyTypes.SUBMODEL, value='http://acplt.org/Test_Submodel'),
+ second=model.ModelReference((model.Key(type_=model.KeyTypes.SUBMODEL, value='http://example.org/Test_Submodel'),
model.Key(type_=model.KeyTypes.PROPERTY,
value='ExampleProperty'),),
model.Property),
@@ -150,7 +150,7 @@ def create_example_submodel_template() -> model.Submodel:
'de': 'Beispiel AnnotatedRelationshipElement Element'}),
parent=None,
semantic_id=model.ExternalReference((model.Key(type_=model.KeyTypes.GLOBAL_REFERENCE,
- value='http://acplt.org/RelationshipElements/'
+ value='http://example.org/RelationshipElements/'
'ExampleAnnotatedRelationshipElement'),)),
qualifier=())
@@ -164,7 +164,7 @@ def create_example_submodel_template() -> model.Submodel:
'de': 'Beispiel Property Element'}),
parent=None,
semantic_id=model.ExternalReference((model.Key(type_=model.KeyTypes.GLOBAL_REFERENCE,
- value='http://acplt.org/Properties/ExamplePropertyInput'),)),
+ value='http://example.org/Properties/ExamplePropertyInput'),)),
qualifier=())
output_variable_property = model.Property(
@@ -177,7 +177,7 @@ def create_example_submodel_template() -> model.Submodel:
'de': 'Beispiel Property Element'}),
parent=None,
semantic_id=model.ExternalReference((model.Key(type_=model.KeyTypes.GLOBAL_REFERENCE,
- value='http://acplt.org/Properties/ExamplePropertyOutput'),)),
+ value='http://example.org/Properties/ExamplePropertyOutput'),)),
qualifier=())
in_output_variable_property = model.Property(
@@ -190,7 +190,8 @@ def create_example_submodel_template() -> model.Submodel:
'de': 'Beispiel Property Element'}),
parent=None,
semantic_id=model.ExternalReference((model.Key(type_=model.KeyTypes.GLOBAL_REFERENCE,
- value='http://acplt.org/Properties/ExamplePropertyInOutput'),)),
+ value='http://example.org/Properties/'
+ 'ExamplePropertyInOutput'),)),
qualifier=())
submodel_element_operation = model.Operation(
@@ -203,7 +204,7 @@ def create_example_submodel_template() -> model.Submodel:
'de': 'Beispiel Operation Element'}),
parent=None,
semantic_id=model.ExternalReference((model.Key(type_=model.KeyTypes.GLOBAL_REFERENCE,
- value='http://acplt.org/Operations/'
+ value='http://example.org/Operations/'
'ExampleOperation'),)),
qualifier=())
@@ -214,13 +215,14 @@ def create_example_submodel_template() -> model.Submodel:
'de': 'Beispiel Capability Element'}),
parent=None,
semantic_id=model.ExternalReference((model.Key(type_=model.KeyTypes.GLOBAL_REFERENCE,
- value='http://acplt.org/Capabilities/'
+ value='http://example.org/Capabilities/'
'ExampleCapability'),)),
qualifier=())
submodel_element_basic_event_element = model.BasicEventElement(
id_short='ExampleBasicEventElement',
- observed=model.ModelReference((model.Key(type_=model.KeyTypes.SUBMODEL, value='http://acplt.org/Test_Submodel'),
+ observed=model.ModelReference((model.Key(type_=model.KeyTypes.SUBMODEL,
+ value='http://example.org/Test_Submodel'),
model.Key(type_=model.KeyTypes.PROPERTY,
value='ExampleProperty'),),
model.Property),
@@ -228,7 +230,7 @@ def create_example_submodel_template() -> model.Submodel:
state=model.StateOfEvent.ON,
message_topic='ExampleTopic',
message_broker=model.ModelReference((model.Key(model.KeyTypes.SUBMODEL,
- "http://acplt.org/ExampleMessageBroker"),),
+ "http://example.org/ExampleMessageBroker"),),
model.Submodel),
last_update=model.datatypes.DateTime(2022, 11, 12, 23, 50, 23, 123456, datetime.timezone.utc),
min_interval=model.datatypes.Duration(microseconds=1),
@@ -239,7 +241,7 @@ def create_example_submodel_template() -> model.Submodel:
'de': 'Beispiel BasicEventElement Element'}),
parent=None,
semantic_id=model.ExternalReference((model.Key(type_=model.KeyTypes.GLOBAL_REFERENCE,
- value='http://acplt.org/Events/ExampleBasicEventElement'),)),
+ value='http://example.org/Events/ExampleBasicEventElement'),)),
qualifier=())
submodel_element_submodel_element_collection = model.SubmodelElementCollection(
@@ -257,7 +259,7 @@ def create_example_submodel_template() -> model.Submodel:
'de': 'Beispiel SubmodelElementCollection Element'}),
parent=None,
semantic_id=model.ExternalReference((model.Key(type_=model.KeyTypes.GLOBAL_REFERENCE,
- value='http://acplt.org/SubmodelElementCollections/'
+ value='http://example.org/SubmodelElementCollections/'
'ExampleSubmodelElementCollection'),)),
qualifier=())
@@ -269,7 +271,7 @@ def create_example_submodel_template() -> model.Submodel:
'de': 'Beispiel SubmodelElementCollection Element'}),
parent=None,
semantic_id=model.ExternalReference((model.Key(type_=model.KeyTypes.GLOBAL_REFERENCE,
- value='http://acplt.org/SubmodelElementCollections/'
+ value='http://example.org/SubmodelElementCollections/'
'ExampleSubmodelElementCollection'),)),
qualifier=())
@@ -278,15 +280,16 @@ def create_example_submodel_template() -> model.Submodel:
type_value_list_element=model.SubmodelElementCollection,
value=(submodel_element_submodel_element_collection, submodel_element_submodel_element_collection_2),
semantic_id_list_element=model.ExternalReference((model.Key(type_=model.KeyTypes.GLOBAL_REFERENCE,
- value='http://acplt.org/SubmodelElementCollections/'
- 'ExampleSubmodelElementCollection'),)),
+ value='http://example.org/'
+ 'SubmodelElementCollections/'
+ 'ExampleSubmodelElementCollection'),)),
order_relevant=True,
category='PARAMETER',
description=model.MultiLanguageTextType({'en-US': 'Example SubmodelElementList object',
'de': 'Beispiel SubmodelElementList Element'}),
parent=None,
semantic_id=model.ExternalReference((model.Key(type_=model.KeyTypes.GLOBAL_REFERENCE,
- value='http://acplt.org/SubmodelElementLists/'
+ value='http://example.org/SubmodelElementLists/'
'ExampleSubmodelElementList'),)),
qualifier=())
@@ -295,7 +298,8 @@ def create_example_submodel_template() -> model.Submodel:
type_value_list_element=model.Capability,
value=(),
semantic_id_list_element=model.ExternalReference((model.Key(type_=model.KeyTypes.GLOBAL_REFERENCE,
- value='http://acplt.org/SubmodelElementCollections/'
+ value='http://example.org/'
+ 'SubmodelElementCollections/'
'ExampleSubmodelElementCollection'),)),
order_relevant=True,
category='PARAMETER',
@@ -303,12 +307,12 @@ def create_example_submodel_template() -> model.Submodel:
'de': 'Beispiel SubmodelElementList Element'}),
parent=None,
semantic_id=model.ExternalReference((model.Key(type_=model.KeyTypes.GLOBAL_REFERENCE,
- value='http://acplt.org/SubmodelElementLists/'
+ value='http://example.org/SubmodelElementLists/'
'ExampleSubmodelElementList'),)),
qualifier=())
submodel = model.Submodel(
- id_='https://acplt.org/Test_Submodel_Template',
+ id_='https://example.org/Test_Submodel_Template',
submodel_element=(submodel_element_relationship_element,
submodel_element_annotated_relationship_element,
submodel_element_operation,
@@ -324,7 +328,7 @@ def create_example_submodel_template() -> model.Submodel:
administration=model.AdministrativeInformation(version='9',
revision='0'),
semantic_id=model.ExternalReference((model.Key(type_=model.KeyTypes.GLOBAL_REFERENCE,
- value='http://acplt.org/SubmodelTemplates/'
+ value='http://example.org/SubmodelTemplates/'
'ExampleSubmodel'),)),
qualifier=(),
kind=model.ModellingKind.TEMPLATE)
@@ -339,7 +343,7 @@ def check_example_submodel(checker: AASDataChecker, submodel: model.Submodel) ->
checker.check_submodel_equal(submodel, expected_submodel)
-def check_full_example(checker: AASDataChecker, obj_store: model.DictObjectStore) -> None:
- expected_data: model.DictObjectStore[model.Identifiable] = model.DictObjectStore()
+def check_full_example(checker: AASDataChecker, identifiable_store: model.DictIdentifiableStore) -> None:
+ expected_data: model.DictIdentifiableStore[model.Identifiable] = model.DictIdentifiableStore()
expected_data.add(create_example_submodel_template())
- checker.check_object_store(obj_store, expected_data)
+ checker.check_identifiable_store(identifiable_store, expected_data)
diff --git a/sdk/basyx/aas/examples/tutorial_aasx.py b/sdk/basyx/aas/examples/tutorial_aasx.py
index 9f5ffa261..8878ed0c8 100755
--- a/sdk/basyx/aas/examples/tutorial_aasx.py
+++ b/sdk/basyx/aas/examples/tutorial_aasx.py
@@ -30,26 +30,26 @@
# See `tutorial_create_simple_aas.py` for more details.
submodel = model.Submodel(
- id_='https://acplt.org/Simple_Submodel'
+ id_='https://example.org/Simple_Submodel'
)
aas = model.AssetAdministrationShell(
- id_='https://acplt.org/Simple_AAS',
+ id_='https://example.org/Simple_AAS',
asset_information=model.AssetInformation(
asset_kind=model.AssetKind.INSTANCE,
- global_asset_id='http://acplt.org/Simple_Asset'
+ global_asset_id='http://example.org/Simple_Asset'
),
submodel={model.ModelReference.from_referable(submodel)}
)
# Another submodel, which is not related to the AAS:
unrelated_submodel = model.Submodel(
- id_='https://acplt.org/Unrelated_Submodel'
+ id_='https://example.org/Unrelated_Submodel'
)
-# We add these objects to an ObjectStore for easy retrieval by id.
-# See `tutorial_storage.py` for more details. We could also use a database-backed ObjectStore here
+# We add these objects to an IdentifiableStore for easy retrieval by id.
+# See `tutorial_storage.py` for more details. We could also use a database-backed IdentifiableStore here
# (see `tutorial_backend_couchdb.py`).
-object_store = model.DictObjectStore([submodel, aas, unrelated_submodel])
+identifiable_store = model.DictIdentifiableStore([submodel, aas, unrelated_submodel])
# For holding auxiliary files, which will eventually be added to an AASX package, we need a SupplementaryFileContainer.
@@ -94,16 +94,16 @@
with aasx.AASXWriter("MyAASXPackage.aasx") as writer:
# Write the AAS and everything belonging to it to the AASX package
# The `write_aas()` method will automatically fetch the AAS object with the given id
- # and all referenced Submodel objects from the ObjectStore. It will also scan every object for
- # semanticIds referencing ConceptDescription, fetch them from the ObjectStore, and scan all submodels for `File`
- # objects and fetch the referenced auxiliary files from the SupplementaryFileContainer.
+ # and all referenced Submodel objects from the IdentifiableStore. It will also scan every object for
+ # semanticIds referencing ConceptDescription, fetch them from the IdentifiableStore, and scan all submodels for
+ # `File` objects and fetch the referenced auxiliary files from the SupplementaryFileContainer.
# In order to add more than one AAS to the package, we can simply add more Identifiers to the `aas_ids` list.
#
# ATTENTION: As of Version 3.0 RC01 of Details of the Asset Administration Shell, it is no longer valid to add more
# than one "aas-spec" part (JSON/XML part with AAS objects) to an AASX package. Thus, `write_aas` MUST
# only be called once per AASX package!
- writer.write_aas(aas_ids=['https://acplt.org/Simple_AAS'],
- object_store=object_store,
+ writer.write_aas(aas_ids=['https://example.org/Simple_AAS'],
+ object_store=identifiable_store,
file_store=file_store)
# Alternatively, we can use a more low-level interface to add a JSON/XML part with any Identifiable objects (not
@@ -113,9 +113,11 @@
# ATTENTION: As of Version 3.0 RC01 of Details of the Asset Administration Shell, it is no longer valid to add more
# than one "aas-spec" part (JSON/XML part with AAS objects) to an AASX package. Thus, `write_all_aas_objects` SHALL
# only be used as an alternative to `write_aas` and SHALL only be called once!
- objects_to_be_written: model.DictObjectStore[model.Identifiable] = model.DictObjectStore([unrelated_submodel])
+ identifiables_to_be_written: model.DictIdentifiableStore[model.Identifiable] = model.DictIdentifiableStore(
+ [unrelated_submodel]
+ )
writer.write_all_aas_objects(part_name="/aasx/my_aas_part.xml",
- objects=objects_to_be_written,
+ objects=identifiables_to_be_written,
file_store=file_store)
# We can also add a thumbnail image to the package (using `writer.write_thumbnail()`) or add metadata:
@@ -134,15 +136,15 @@
########################################################################
# Let's read the AASX package file, we have just written.
-# We'll use a fresh ObjectStore and SupplementaryFileContainer to read AAS objects and auxiliary files into.
-new_object_store: model.DictObjectStore[model.Identifiable] = model.DictObjectStore()
+# We'll use a fresh IdentifiableStore and SupplementaryFileContainer to read AAS objects and auxiliary files into.
+new_identifiable_store: model.DictIdentifiableStore[model.Identifiable] = model.DictIdentifiableStore()
new_file_store = aasx.DictSupplementaryFileContainer()
# Again, we need to use the AASXReader as a context manager (or call `.close()` in the end) to make sure the AASX
# package file is properly closed when we are finished.
with aasx.AASXReader("MyAASXPackage.aasx") as reader:
# Read all contained AAS objects and all referenced auxiliary files
- reader.read_into(object_store=new_object_store,
+ reader.read_into(object_store=new_identifiable_store,
file_store=new_file_store)
# We can also read the metadata
@@ -152,6 +154,6 @@
# Some quick checks to make sure, reading worked as expected
-assert 'https://acplt.org/Simple_Submodel' in new_object_store
+assert 'https://example.org/Simple_Submodel' in new_identifiable_store
assert actual_file_name in new_file_store
assert new_meta_data.creator == "Chair of Process Control Engineering"
diff --git a/sdk/basyx/aas/examples/tutorial_backend_couchdb.py b/sdk/basyx/aas/examples/tutorial_backend_couchdb.py
index dc86bac85..ed09cf432 100755
--- a/sdk/basyx/aas/examples/tutorial_backend_couchdb.py
+++ b/sdk/basyx/aas/examples/tutorial_backend_couchdb.py
@@ -3,7 +3,7 @@
# See http://creativecommons.org/publicdomain/zero/1.0/ for more information.
"""
Tutorial for storing Asset Administration Shells, Submodels and Assets in a CouchDB database server, using the
-CouchDBObjectStore and CouchDB Backend.
+CouchDBIdentifiableStore and CouchDB Backend.
"""
from configparser import ConfigParser
@@ -29,7 +29,7 @@
# Step-by-Step Guide:
# step 1: connecting to a CouchDB server
-# step 2: storing objects in CouchDBObjectStore
+# step 2: storing objects in a CouchDBIdentifiableStore
##########################################
@@ -54,25 +54,25 @@
# Provide the login credentials to the CouchDB backend.
-# These credentials are used whenever communication with this CouchDB server is required via the CouchDBObjectStore.
+# These credentials are used when communication with this CouchDB server is required via the CouchDBIdentifiableStore.
basyx.aas.backend.couchdb.register_credentials(couchdb_url, couchdb_user, couchdb_password)
-# Now, we create a CouchDBObjectStore as an interface for managing the objects in the CouchDB server.
-object_store = basyx.aas.backend.couchdb.CouchDBObjectStore(couchdb_url, couchdb_database)
+# Now, we create a CouchDBIdentifiableStore as an interface for managing the objects in the CouchDB server.
+identifiable_store = basyx.aas.backend.couchdb.CouchDBIdentifiableStore(couchdb_url, couchdb_database)
-#####################################################
-# Step 2: Storing objects in the CouchDBObjectStore #
-#####################################################
+###########################################################
+# Step 2: Storing objects in the CouchDBIdentifiableStore #
+###########################################################
# Create some example objects
example_submodel1 = basyx.aas.examples.data.example_aas.create_example_asset_identification_submodel()
example_submodel2 = basyx.aas.examples.data.example_aas.create_example_bill_of_material_submodel()
-# The CouchDBObjectStore behaves just like other ObjectStore implementations (see `tutorial_storage.py`). The objects
-# are transferred to the CouchDB immediately.
-object_store.add(example_submodel1)
-object_store.add(example_submodel2)
+# The CouchDBIdentifiableStore behaves just like other ObjectStore implementations (see `tutorial_storage.py`). The
+# objects are transferred to the CouchDB immediately.
+identifiable_store.add(example_submodel1)
+identifiable_store.add(example_submodel2)
# For more information on how to use `ObjectStore`s in general, please refer to `tutorial_storage.py`.
@@ -81,5 +81,5 @@
############
# Let's delete the Submodels from the CouchDB to leave it in a clean state
-object_store.discard(example_submodel1)
-object_store.discard(example_submodel2)
+identifiable_store.discard(example_submodel1)
+identifiable_store.discard(example_submodel2)
diff --git a/sdk/basyx/aas/examples/tutorial_create_simple_aas.py b/sdk/basyx/aas/examples/tutorial_create_simple_aas.py
index afa8b46c0..3cacd74ab 100755
--- a/sdk/basyx/aas/examples/tutorial_create_simple_aas.py
+++ b/sdk/basyx/aas/examples/tutorial_create_simple_aas.py
@@ -26,11 +26,11 @@
# Step 1.1: create the AssetInformation object
asset_information = model.AssetInformation(
asset_kind=model.AssetKind.INSTANCE,
- global_asset_id='http://acplt.org/Simple_Asset'
+ global_asset_id='http://example.org/Simple_Asset'
)
# step 1.2: create the Asset Administration Shell
-identifier = 'https://acplt.org/Simple_AAS'
+identifier = 'https://example.org/Simple_AAS'
aas = model.AssetAdministrationShell(
id_=identifier, # set identifier
asset_information=asset_information
@@ -42,7 +42,7 @@
#############################################################
# Step 2.1: create the Submodel object
-identifier = 'https://acplt.org/Simple_Submodel'
+identifier = 'https://example.org/Simple_Submodel'
submodel = model.Submodel(
id_=identifier
)
@@ -55,10 +55,10 @@
# ALTERNATIVE: step 1 and 2 can alternatively be done in one step
# In this version, the Submodel reference is passed to the Asset Administration Shell's constructor.
submodel = model.Submodel(
- id_='https://acplt.org/Simple_Submodel'
+ id_='https://example.org/Simple_Submodel'
)
aas = model.AssetAdministrationShell(
- id_='https://acplt.org/Simple_AAS',
+ id_='https://example.org/Simple_AAS',
asset_information=asset_information,
submodel={model.ModelReference.from_referable(submodel)}
)
@@ -73,7 +73,7 @@
semantic_reference = model.ExternalReference(
(model.Key(
type_=model.KeyTypes.GLOBAL_REFERENCE,
- value='http://acplt.org/Properties/SimpleProperty'
+ value='http://example.org/Properties/SimpleProperty'
),)
)
@@ -93,7 +93,7 @@
# ALTERNATIVE: step 2 and 3 can also be combined in a single statement:
# Again, we pass the Property to the Submodel's constructor instead of adding it afterward.
submodel = model.Submodel(
- id_='https://acplt.org/Simple_Submodel',
+ id_='https://example.org/Simple_Submodel',
submodel_element={
model.Property(
id_short='ExampleProperty',
@@ -102,7 +102,7 @@
semantic_id=model.ExternalReference(
(model.Key(
type_=model.KeyTypes.GLOBAL_REFERENCE,
- value='http://acplt.org/Properties/SimpleProperty'
+ value='http://example.org/Properties/SimpleProperty'
),)
)
)
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..2bbb9f89d
--- /dev/null
+++ b/sdk/basyx/aas/examples/tutorial_navigate_aas.py
@@ -0,0 +1,199 @@
+#!/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
+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.
+#
+# Step-by-Step Guide:
+# 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
+
+
+########################################################################
+# 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 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 SubmodelElementCollection"
+ ),
+ model.Property(
+ id_short="MyProperty1",
+ value_type=model.datatypes.String,
+ value="I am the second of two Properties within a SubmodelElementCollection"
+ )
+ }
+)
+submodel.submodel_element.add(my_property_collection)
+
+# 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,
+ 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 SubmodelElementList"
+ ),
+ model.Property(
+ id_short=None,
+ value_type=model.datatypes.String,
+ value="I am Property 1 within a SubmodelElementList"
+ )
+ ]
+)
+submodel.submodel_element.add(my_property_list)
+
+# 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 SubmodelElementCollection 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 SubmodelElementCollection 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 SubmodelElementCollection 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 = 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: 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(
+ 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 SubmodelElementCollection via its IdShortPath
+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: 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(
+ 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 SubmodelElementList via its IdShortPath
+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: 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(
+ 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 SubmodelElementList of SubmodelElementCollections via its IdShortPath
+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}"
+)
diff --git a/sdk/basyx/aas/examples/tutorial_serialization_deserialization.py b/sdk/basyx/aas/examples/tutorial_serialization_deserialization.py
index ec281818b..955cb062a 100755
--- a/sdk/basyx/aas/examples/tutorial_serialization_deserialization.py
+++ b/sdk/basyx/aas/examples/tutorial_serialization_deserialization.py
@@ -32,7 +32,7 @@
# For more details, take a look at `tutorial_create_simple_aas.py`
submodel = model.Submodel(
- id_='https://acplt.org/Simple_Submodel',
+ id_='https://example.org/Simple_Submodel',
submodel_element={
model.Property(
id_short='ExampleProperty',
@@ -40,13 +40,13 @@
value='exampleValue',
semantic_id=model.ExternalReference((model.Key(
type_=model.KeyTypes.GLOBAL_REFERENCE,
- value='http://acplt.org/Properties/SimpleProperty'
+ value='http://example.org/Properties/SimpleProperty'
),)
)
)}
)
aashell = model.AssetAdministrationShell(
- id_='https://acplt.org/Simple_AAS',
+ id_='https://example.org/Simple_AAS',
asset_information=model.AssetInformation(global_asset_id="test"),
submodel={model.ModelReference.from_referable(submodel)}
)
@@ -91,38 +91,38 @@
# Step 4: Writing Multiple Identifiable Objects to a (Standard-compliant) JSON/XML File #
#########################################################################################
-# step 4.1: creating an ObjectStore containing the objects to be serialized
+# step 4.1: creating an IdentifiableStore containing the objects to be serialized
# For more information, take a look into `tutorial_storage.py`
-obj_store: model.DictObjectStore[model.Identifiable] = model.DictObjectStore()
-obj_store.add(submodel)
-obj_store.add(aashell)
+identifiable_store: model.DictIdentifiableStore[model.Identifiable] = model.DictIdentifiableStore()
+identifiable_store.add(submodel)
+identifiable_store.add(aashell)
-# step 4.2: writing the contents of the ObjectStore to a JSON file
-basyx.aas.adapter.json.write_aas_json_file('data.json', obj_store)
+# step 4.2: writing the contents of the IdentifiableStore to a JSON file
+basyx.aas.adapter.json.write_aas_json_file('data.json', identifiable_store)
# We can pass the additional keyword argument `indent=4` to `write_aas_json_file()` to format the JSON file in a more
# human-readable (but much more space-consuming) manner.
-# step 4.3: writing the contents of the ObjectStore to an XML file
-basyx.aas.adapter.xml.write_aas_xml_file('data.xml', obj_store)
+# step 4.3: writing the contents of the IdentifiableStore to an XML file
+basyx.aas.adapter.xml.write_aas_xml_file('data.xml', identifiable_store)
##################################################################
# Step 5: Reading the Serialized AAS Objects From JSON/XML Files #
##################################################################
-# step 5.1: reading contents of the JSON file as an ObjectStore
+# step 5.1: reading contents of the JSON file as an IdentifiableStore
json_file_data = basyx.aas.adapter.json.read_aas_json_file('data.json')
# By passing the `failsafe=False` argument to `read_aas_json_file()`, we can switch to the `StrictAASFromJsonDecoder`
# (see step 3) for a stricter error reporting.
-# step 5.2: reading contents of the XML file as an ObjectStore
+# step 5.2: reading contents of the XML file as an IdentifiableStore
xml_file_data = basyx.aas.adapter.xml.read_aas_xml_file('data.xml')
# Again, we can use `failsafe=False` for switching on stricter error reporting in the parser.
-# step 5.3: Retrieving the objects from the ObjectStore
+# step 5.3: Retrieving the objects from the IdentifiableStore
# For more information on the available techniques, see `tutorial_storage.py`.
-submodel_from_xml = xml_file_data.get_identifiable('https://acplt.org/Simple_Submodel')
+submodel_from_xml = xml_file_data.get_item('https://example.org/Simple_Submodel')
assert isinstance(submodel_from_xml, model.Submodel)
diff --git a/sdk/basyx/aas/examples/tutorial_storage.py b/sdk/basyx/aas/examples/tutorial_storage.py
index fe978b11b..c422a7710 100755
--- a/sdk/basyx/aas/examples/tutorial_storage.py
+++ b/sdk/basyx/aas/examples/tutorial_storage.py
@@ -2,21 +2,21 @@
# 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 storing Asset Administration Shells, Submodels and Assets in an ObjectStore and using it for fetching these
-objects by id and resolving references.
+Tutorial for storing Asset Administration Shells, Submodels and Assets in an IdentifiableStore and using it for fetching
+these identifiables by id and resolving references.
"""
-# For managing a larger number of Identifiable AAS objects (AssetAdministrationShells, Assets, Submodels,
-# ConceptDescriptions), the BaSyx Python SDK provides the `ObjectStore` functionality. This tutorial shows the basic
-# features of an ObjectStore and how to use them. This includes usage of the built-in `resolve()` method of Reference
-# objects, which can be used to easily get the Submodel objects, which are referenced by the
+# For managing a larger number of identifiable AAS objects (AssetAdministrationShells, Assets, Submodels,
+# ConceptDescriptions), the BaSyx Python SDK provides the `IdentifiableStore` functionality. This tutorial shows the
+# basic features of an IdentifiableStore and how to use them. This includes usage of the built-in `resolve()` method of
+# reference objects, which can be used to easily get the Submodel objects, which are referenced by the
# `AssetAdministrationShell.submodel` set, etc.
#
# Step-by-Step Guide:
# Step 1: creating AssetInformation, Submodel and Asset Administration Shell objects
-# Step 2: storing the data in an ObjectStore for easier handling
+# Step 2: storing the data in an IdentifiableStore for easier handling
# Step 3: retrieving objects from the store by their identifier
-# Step 4: using the ObjectStore to resolve a reference
+# Step 4: using the IdentifiableStore to resolve a reference
from basyx.aas import model
@@ -31,7 +31,7 @@
asset_information = AssetInformation(
asset_kind=model.AssetKind.INSTANCE,
- global_asset_id='http://acplt.org/Simple_Asset'
+ global_asset_id='http://example.org/Simple_Asset'
)
prop = model.Property(
@@ -41,58 +41,58 @@
semantic_id=model.ExternalReference(
(model.Key(
type_=model.KeyTypes.GLOBAL_REFERENCE,
- value='http://acplt.org/Properties/SimpleProperty'
+ value='http://example.org/Properties/SimpleProperty'
),)
)
)
submodel = Submodel(
- id_='https://acplt.org/Simple_Submodel',
+ id_='https://example.org/Simple_Submodel',
submodel_element={prop}
)
aas = AssetAdministrationShell(
- id_='https://acplt.org/Simple_AAS',
+ id_='https://example.org/Simple_AAS',
asset_information=asset_information,
submodel={model.ModelReference.from_referable(submodel)}
)
-##################################################################
-# Step 2: Storing the Data in an ObjectStore for Easier Handling #
-##################################################################
+########################################################################
+# Step 2: Storing the Data in an IdentifiableStore for Easier Handling #
+########################################################################
-# Step 2.1: create an ObjectStore for identifiable objects
+# Step 2.1: create an IdentifiableStore for identifiable objects
#
-# In this tutorial, we use a `DictObjectStore`, which is a simple in-memory store: It just keeps track of the Python
-# objects using a dict.
+# In this tutorial, we use a `DictIdentifiableStore`, which is a simple in-memory store: It just keeps track of the
+# Python objects using a dict.
# This may not be a suitable solution, if you need to manage large numbers of objects or objects must be kept in a
-# persistent memory (i.e. on hard disk). In this case, you may choose the `CouchDBObjectStore` from
+# persistent memory (i.e. on hard disk). In this case, you may choose the `CouchDBIdentifiableStore` from
# `aas.backends.couchdb` to use a CouchDB database server as persistent storage. Both ObjectStore implementations
-# provide the same interface. In addition, the CouchDBObjectStores allows synchronizing the local object with the
+# provide the same interface. In addition, the CouchDBIdentifiableStore allows synchronizing the local object with the
# database via a Backend. See the `tutorial_backend_couchdb.py` for more information.
-obj_store: model.DictObjectStore[model.Identifiable] = model.DictObjectStore()
+identifiable_store: model.DictIdentifiableStore[model.Identifiable] = model.DictIdentifiableStore()
# step 2.2: add submodel and asset administration shell to store
-obj_store.add(submodel)
-obj_store.add(aas)
+identifiable_store.add(submodel)
+identifiable_store.add(aas)
#################################################################
# Step 3: Retrieving Objects From the Store by Their Identifier #
#################################################################
-tmp_submodel = obj_store.get_identifiable(
- 'https://acplt.org/Simple_Submodel')
+tmp_submodel = identifiable_store.get_item(
+ 'https://example.org/Simple_Submodel')
assert submodel is tmp_submodel
-########################################################
-# Step 4: Using the ObjectStore to Resolve a Reference #
-########################################################
+##############################################################
+# Step 4: Using the IdentifiableStore to Resolve a Reference #
+##############################################################
# The `aas` object already contains a reference to the submodel.
# Let's create a list of all submodels, to which the AAS has references, by resolving each of the submodel references:
-submodels = [reference.resolve(obj_store)
+submodels = [reference.resolve(identifiable_store)
for reference in aas.submodel]
# The first (and only) element of this list should be our example submodel:
@@ -104,7 +104,7 @@
property_reference = model.ModelReference(
(model.Key(
type_=model.KeyTypes.SUBMODEL,
- value='https://acplt.org/Simple_Submodel'),
+ value='https://example.org/Simple_Submodel'),
model.Key(
type_=model.KeyTypes.PROPERTY,
value='ExampleProperty'),
@@ -113,7 +113,7 @@
)
# Now, we can resolve this new reference.
-# The `resolve()` method will fetch the Submodel object from the ObjectStore, traverse down to the included Property
-# object and return this object.
-tmp_property = property_reference.resolve(obj_store)
+# The `resolve()` method will fetch the Submodel object from the IdentifiableStore, traverse down to the included
+# Property object and return this object.
+tmp_property = property_reference.resolve(identifiable_store)
assert prop is tmp_property
diff --git a/sdk/basyx/aas/model/_string_constraints.py b/sdk/basyx/aas/model/_string_constraints.py
index 376f76b0e..da4c8b178 100644
--- a/sdk/basyx/aas/model/_string_constraints.py
+++ b/sdk/basyx/aas/model/_string_constraints.py
@@ -1,4 +1,4 @@
-# Copyright (c) 2025 the Eclipse BaSyx Authors
+# Copyright (c) 2026 the Eclipse BaSyx Authors
#
# This program and the accompanying materials are made available under the terms of the MIT License, available in
# the LICENSE file of this project.
@@ -21,7 +21,6 @@
- :class:`~basyx.aas.model.base.NameType`
- :class:`~basyx.aas.model.base.PathType`
- :class:`~basyx.aas.model.base.RevisionType`
-- :class:`~basyx.aas.model.base.ShortNameType`
- :class:`~basyx.aas.model.base.QualifierType`
- :class:`~basyx.aas.model.base.VersionType`
- :class:`~basyx.aas.model.base.ValueTypeIEC61360`
@@ -64,11 +63,11 @@ def check(value: str, type_name: str, min_length: int = 0, max_length: Optional[
def check_content_type(value: str, type_name: str = "ContentType") -> None:
- return check(value, type_name, 1, 100)
+ return check(value, type_name, 1, 128)
def check_identifier(value: str, type_name: str = "Identifier") -> None:
- return check(value, type_name, 1, 2000)
+ return check(value, type_name, 1, 2048)
def check_label_type(value: str, type_name: str = "LabelType") -> None:
@@ -84,7 +83,7 @@ def check_name_type(value: str, type_name: str = "NameType") -> None:
def check_path_type(value: str, type_name: str = "PathType") -> None:
- return check(value, type_name, 1, 2000)
+ return check(value, type_name, 1, 2048)
def check_qualifier_type(value: str, type_name: str = "QualifierType") -> None:
@@ -95,12 +94,8 @@ def check_revision_type(value: str, type_name: str = "RevisionType") -> None:
return check(value, type_name, 1, 4, re.compile(r"([0-9]|[1-9][0-9]*)"))
-def check_short_name_type(value: str, type_name: str = "ShortNameType") -> None:
- return check(value, type_name, 1, 64)
-
-
def check_value_type_iec61360(value: str, type_name: str = "ValueTypeIEC61360") -> None:
- return check(value, type_name, 1, 2000)
+ return check(value, type_name, 1, 2048)
def check_version_type(value: str, type_name: str = "VersionType") -> None:
@@ -110,14 +105,12 @@ def check_version_type(value: str, type_name: str = "VersionType") -> None:
def create_check_function(min_length: int = 0, max_length: Optional[int] = None, pattern: Optional[re.Pattern] = None) \
-> Callable[[str, str], None]:
"""
- Returns a new ``check_type`` function with mandatory ``type_name`` for the given min_length, max_length and pattern
- constraints.
-
- This is the type-independent alternative to :func:`~.check_content_type`, :func:`~.check_identifier`, etc. It is
- used for the definition of the :class:`ConstrainedLangStringSets `,
- as a "Basic" constrained string type only exists for :class:`~basyx.aas.model.base.MultiLanguageNameType`, where all
- values are :class:`ShortNames `. All other
- :class:`:class:`ConstrainedLangStringSets ` use custom constraints.
+ Returns a ``check_type`` function for the given constraints.
+
+ Use this instead of the named :func:`~.check_content_type`, :func:`~.check_identifier`, etc. when the
+ constraints do not correspond to a predefined constrained string type — for example, in
+ :class:`ConstrainedLangStringSets ` that define their own
+ length and pattern rules.
"""
def check_fn(value: str, type_name: str) -> None:
return check(value, type_name, min_length, max_length, pattern)
@@ -177,10 +170,6 @@ def constrain_revision_type(pub_attr_name: str) -> Callable[[Type[_T]], Type[_T]
return constrain_attr(pub_attr_name, check_revision_type)
-def constrain_short_name_type(pub_attr_name: str) -> Callable[[Type[_T]], Type[_T]]:
- return constrain_attr(pub_attr_name, check_short_name_type)
-
-
def constrain_version_type(pub_attr_name: str) -> Callable[[Type[_T]], Type[_T]]:
return constrain_attr(pub_attr_name, check_version_type)
diff --git a/sdk/basyx/aas/model/base.py b/sdk/basyx/aas/model/base.py
index 35ccad5a1..bd64a1e6d 100644
--- a/sdk/basyx/aas/model/base.py
+++ b/sdk/basyx/aas/model/base.py
@@ -1,4 +1,4 @@
-# Copyright (c) 2025 the Eclipse BaSyx Authors
+# Copyright (c) 2026 the Eclipse BaSyx Authors
#
# This program and the accompanying materials are made available under the terms of the MIT License, available in
# the LICENSE file of this project.
@@ -28,7 +28,7 @@
BlobType = bytes
# The following string aliases are constrained by the decorator functions defined in the string_constraints module,
-# wherever they are used for an instance attributes.
+# wherever they are used for an instance's attributes.
ContentType = str # any mimetype as in RFC2046
Identifier = str
LabelType = str
@@ -223,7 +223,7 @@ class ModellingKind(Enum):
@unique
class AssetKind(Enum):
"""
- Enumeration for denoting whether an asset is a type asset or an instance asset or whether this kind of
+ Enumeration for denoting whether an asset is a type asset or an instance asset or role asset or whether this kind of
classification is not applicable.
.. note::
@@ -235,12 +235,14 @@ class AssetKind(Enum):
:cvar TYPE: Type asset
:cvar INSTANCE: Instance asset
- :cvar NOT_APPLICABLE: Neither a type asset nor an instance asset
+ :cvar ROLE: Role asset
+ :cvar NOT_APPLICABLE: Neither a type asset nor an instance asset nor a role asset
"""
TYPE = 0
INSTANCE = 1
NOT_APPLICABLE = 2
+ ROLE = 3
class QualifierKind(Enum):
@@ -291,7 +293,8 @@ class LangStringSet(MutableMapping[str, str]):
"""
def __init__(self, dict_: Dict[str, str]):
self._dict: Dict[str, str] = {}
-
+ if not isinstance(dict_, dict):
+ raise TypeError(f"A {self.__class__.__name__} must be initialized with a dict!, got {type(dict_)}")
if len(dict_) < 1:
raise ValueError(f"A {self.__class__.__name__} must not be empty!")
for ltag in dict_:
@@ -300,11 +303,36 @@ def __init__(self, dict_: Dict[str, str]):
@classmethod
def _check_language_tag_constraints(cls, ltag: str):
- split = ltag.split("-", 1)
- lang_code = split[0]
- if len(lang_code) != 2 or not lang_code.isalpha() or not lang_code.islower():
- raise ValueError(f"The language code of the language tag must consist of exactly two lower-case letters! "
- f"Given language tag and language code: '{ltag}', '{lang_code}'")
+ alphanum = "[a-zA-Z0-9]"
+ singleton = "[0-9A-WY-Za-wy-z]"
+ extension = f"{singleton}(-({alphanum}){{2,8}})+"
+ extlang = "[a-zA-Z]{3}(-[a-zA-Z]{3}){0,2}"
+ irregular = (
+ "(en-GB-oed|i-ami|i-bnn|i-default|i-enochian|i-hak|"
+ "i-klingon|i-lux|i-mingo|i-navajo|i-pwn|i-tao|i-tay|"
+ "i-tsu|sgn-BE-FR|sgn-BE-NL|sgn-CH-DE)"
+ )
+ regular = (
+ "(art-lojban|cel-gaulish|no-bok|no-nyn|zh-guoyu|zh-hakka|"
+ "zh-min|zh-min-nan|zh-xiang)"
+ )
+ grandfathered = f"({irregular}|{regular})"
+ language = f"([a-zA-Z]{{2,3}}(-{extlang})?|[a-zA-Z]{{4}}|[a-zA-Z]{{5,8}})"
+ script = "[a-zA-Z]{4}"
+ region = "([a-zA-Z]{2}|[0-9]{3})"
+ variant = f"(({alphanum}){{5,8}}|[0-9]({alphanum}){{3}})"
+ privateuse = f"[xX](-({alphanum}){{1,8}})+"
+ langtag = (
+ f"{language}(-{script})?(-{region})?(-{variant})*(-{extension})*(-"
+ f"{privateuse})?"
+ )
+ language_tag = f"({langtag}|{privateuse}|{grandfathered})"
+
+ pattern = f"^{language_tag}$"
+
+ if re.match(pattern, ltag) is None:
+ raise ValueError(f"The language tag must follow the format defined in BCP 47. "
+ f"Given language tag: {ltag}")
def __getitem__(self, item: str) -> str:
return self._dict[item]
@@ -355,11 +383,11 @@ def __setitem__(self, key: str, value: str) -> None:
class MultiLanguageNameType(ConstrainedLangStringSet):
"""
- A :class:`~.ConstrainedLangStringSet` where each value is a :class:`ShortNameType`.
- See also: :func:`basyx.aas.model._string_constraints.check_short_name_type`
+ A :class:`~.ConstrainedLangStringSet` where each value is a :class:`NameType`.
+ See also: :func:`basyx.aas.model._string_constraints.check_name_type`
"""
def __init__(self, dict_: Dict[str, str]):
- super().__init__(dict_, _string_constraints.check_short_name_type)
+ super().__init__(dict_, _string_constraints.check_name_type)
class MultiLanguageTextType(ConstrainedLangStringSet):
@@ -548,7 +576,7 @@ class HasExtension(Namespace, metaclass=abc.ABCMeta):
<>
- **Constraint AASd-077:** The name of an Extension within HasExtensions needs to be unique.
+ **Constraint AASd-077:** The name of an Extension within HasExtensions shall be unique.
:ivar namespace_element_sets: List of :class:`NamespaceSets `
:ivar extension: A :class:`~.NamespaceSet` of :class:`Extensions <.Extension>` of the element.
@@ -596,8 +624,9 @@ class Referable(HasExtension, metaclass=abc.ABCMeta):
**Constraint AASd-001:** In case of a referable element not being an identifiable element the
idShort is mandatory and used for referring to the element in its name space.
- **Constraint AASd-002:** idShort shall only feature letters, digits, underscore (``_``); starting
- mandatory with a letter.
+ **Constraint AASd-002:** idShort shall only feature letters, digits, underscore (``_``), hyphen (``-``);
+ starting mandatory with a letter and not ending with a hyphen.
+ I.e. ``^[a-zA-Z]|[a-zA-Z][a-zA-Z0-9_-]*[a-zA-Z0-9_]$``
**Constraint AASd-004:** Add parent in case of non-identifiable elements.
@@ -614,9 +643,9 @@ class Referable(HasExtension, metaclass=abc.ABCMeta):
def __init__(self):
super().__init__()
self._id_short: Optional[NameType] = None
- self.display_name: Optional[MultiLanguageNameType] = dict()
+ self._display_name: Optional[MultiLanguageNameType] = None
self._category: Optional[NameType] = None
- self.description: Optional[MultiLanguageTextType] = dict()
+ self._description: Optional[MultiLanguageTextType] = None
# We use a Python reference to the parent Namespace instead of a Reference Object, as specified. This allows
# simpler and faster navigation/checks and it has no effect in the serialized data formats anyway.
self.parent: Optional[UniqueIdShortNamespace] = None
@@ -758,8 +787,9 @@ def validate_id_short(cls, id_short: NameType) -> None:
"""
Validates an id_short against Constraint AASd-002 and :class:`NameType` restrictions.
- **Constraint AASd-002:** idShort of Referables shall only feature letters, digits, underscore (``_``); starting
- mandatory with a letter. I.e. ``[a-zA-Z][a-zA-Z0-9_]+``
+ **Constraint AASd-002:** idShort shall only feature letters, digits, underscore (``_``), hyphen (``-``);
+ starting mandatory with a letter and not ending with a hyphen.
+ I.e. ``^[a-zA-Z]|[a-zA-Z][a-zA-Z0-9_-]*[a-zA-Z0-9_]$``
:param id_short: The id_short to validate
:raises ValueError: If the id_short doesn't comply to the constraints imposed by :class:`NameType`
@@ -768,16 +798,21 @@ def validate_id_short(cls, id_short: NameType) -> None:
"""
_string_constraints.check_name_type(id_short)
test_id_short: NameType = str(id_short)
- if not re.fullmatch("[a-zA-Z0-9_]*", test_id_short):
+ if not re.fullmatch("[A-Za-z0-9_-]*", test_id_short):
raise AASConstraintViolation(
2,
- "The id_short must contain only letters, digits and underscore"
+ "The id_short must contain only letters, digits underscore and hyphen"
)
if not test_id_short[0].isalpha():
raise AASConstraintViolation(
2,
"The id_short must start with a letter"
)
+ if test_id_short.endswith("-"):
+ raise AASConstraintViolation(
+ 2,
+ "The id_short must not end with a hyphen"
+ )
category = property(_get_category, _set_category)
@@ -785,8 +820,9 @@ def _set_id_short(self, id_short: Optional[NameType]):
"""
Check the input string
- **Constraint AASd-002:** idShort of Referables shall only feature letters, digits, underscore (``_``); starting
- mandatory with a letter. I.e. ``[a-zA-Z][a-zA-Z0-9_]+``
+ **Constraint AASd-002:** idShort shall only feature letters, digits, underscore (``_``), hyphen (``-``);
+ starting mandatory with a letter and not ending with a hyphen.
+ I.e. ``^[a-zA-Z]|[a-zA-Z][a-zA-Z0-9_-]*[a-zA-Z0-9_]$``
**Constraint AASd-022:** idShort of non-identifiable referables shall be unique in its namespace
(case-sensitive)
@@ -808,9 +844,6 @@ def _set_id_short(self, id_short: Optional[NameType]):
raise AASConstraintViolation(117, f"id_short of {self!r} cannot be unset, since it is already "
f"contained in {self.parent!r}")
from .submodel import SubmodelElementList
- if isinstance(self.parent, SubmodelElementList):
- raise AASConstraintViolation(120, f"id_short of {self!r} cannot be set, because it is "
- f"contained in a {self.parent!r}")
for set_ in self.parent.namespace_element_sets:
if set_.contains_id("id_short", id_short):
raise AASConstraintViolation(22, "Object with id_short '{}' is already present in the parent "
@@ -827,6 +860,28 @@ def _set_id_short(self, id_short: Optional[NameType]):
# Redundant to the line above. However, this way, we make sure that we really update the _id_short
self._id_short = id_short
+ @property
+ def display_name(self) -> Optional[MultiLanguageNameType]:
+ """Display name of the element (MultiLanguageNameType)."""
+ return self._display_name
+
+ @display_name.setter
+ def display_name(self, value: Union[MultiLanguageNameType, dict, None]) -> None:
+ if value is not None and not isinstance(value, MultiLanguageNameType):
+ value = MultiLanguageNameType(value)
+ self._display_name = value
+
+ @property
+ def description(self) -> Optional[MultiLanguageTextType]:
+ """Description of the element (MultiLanguageTextType)."""
+ return self._description
+
+ @description.setter
+ def description(self, value: Union[MultiLanguageTextType, dict, None]) -> None:
+ if value is not None and not isinstance(value, MultiLanguageTextType):
+ value = MultiLanguageTextType(value)
+ self._description = value
+
def update_from(self, other: "Referable"):
"""
Internal function to update the object's attributes from a different version of the exact same object.
@@ -1047,7 +1102,7 @@ def resolve(self, provider_: "provider.AbstractObjectProvider") -> _RT:
raise AssertionError(f"Retrieving the identifier of the first {self.key[0]!r} failed.")
try:
- item: Referable = provider_.get_identifiable(identifier)
+ item: Referable = provider_.get_item(identifier)
except KeyError as e:
raise KeyError("Could not resolve identifier {}".format(identifier)) from e
@@ -1149,7 +1204,7 @@ class DataSpecificationContent:
**Constraint AASc-3a-050:** If the ``Data_specification_IEC_61360`` is used
for an element, the value of ``HasDataSpecification.embedded_data_specifications``
shall contain the external reference to the IRI of the corresponding data specification
- template ``https://admin-shell.io/DataSpecificationTemplates/DataSpecificationIEC61360/3/0``
+ template ``https://admin-shell.io/DataSpecificationTemplates/DataSpecificationIEC61360/3/1``
"""
@abc.abstractmethod
def __init__(self):
@@ -1276,7 +1331,8 @@ def __repr__(self) -> str:
@_string_constraints.constrain_identifier("id")
class Identifiable(Referable, metaclass=abc.ABCMeta):
"""
- An element that has a globally unique :class:`Identifier`.
+ Identifiable element with a globally unique :class:`Identifier` and, optionally, additional
+ :class:`~.AdministrativeInformation`.
<>
@@ -1607,8 +1663,8 @@ class Qualifier(HasSemantics):
"""
A qualifier is a type-value pair that makes additional statements w.r.t. the value of the element.
- **Constraint AASd-006:** If both, the value and the valueId of a Qualifier are present, the value needs
- to be identical to the value of the referenced coded value in Qualifier/valueId.
+ **Constraint AASd-006:** If both, the value and the valueId of a Qualifier are present, the value shall
+ be identical to the value of the referenced coded value in Qualifier/valueId.
**Constraint AASd-020:** The value of Qualifier/value shall be consistent with the
data type as defined in Qualifier/valueType.
@@ -1696,14 +1752,14 @@ class ValueReferencePair:
def __init__(self,
value: ValueTypeIEC61360,
- value_id: Reference):
+ value_id: Optional[Reference] = None):
"""
TODO: Add instruction what to do after construction
"""
- self.value_id: Reference = value_id
self.value: ValueTypeIEC61360 = value
+ self.value_id: Optional[Reference] = value_id
def __repr__(self) -> str:
return "ValueReferencePair(value={}, value_id={})".format(self.value, self.value_id)
@@ -2021,8 +2077,10 @@ def discard(self, x: _NSO) -> None:
def pop(self) -> _NSO:
_, value = next(iter(self._backend.values()))[0].popitem()
+ for key_attr_name, (backend_dict, case_sensitive) in self._backend.items():
+ key_attr_value = self._get_attribute(value, key_attr_name, case_sensitive)
+ backend_dict.pop(key_attr_value, None)
self._execute_item_del_hook(value)
- value.parent = None
return value
def clear(self) -> None:
@@ -2183,9 +2241,15 @@ def __setitem__(self, s: slice, o: Iterable[_NSO]) -> None: ...
def __setitem__(self, s, o) -> None:
if isinstance(s, int):
- deleted_items = [self._order[s]]
- super().add(o)
+ old_item = self._order[s]
+ super().remove(old_item)
+ try:
+ super().add(o)
+ except Exception:
+ super().add(old_item)
+ raise
self._order[s] = o
+ return
else:
deleted_items = self._order[s]
new_items = itertools.islice(o, len(deleted_items))
@@ -2199,7 +2263,7 @@ def __setitem__(self, s, o) -> None:
for i in successful_new_items:
super().remove(i)
raise
- self._order[s] = new_items
+ self._order[s] = successful_new_items
for i in deleted_items:
super().remove(i)
diff --git a/sdk/basyx/aas/model/datatypes.py b/sdk/basyx/aas/model/datatypes.py
index d5acc6d45..07de9700c 100644
--- a/sdk/basyx/aas/model/datatypes.py
+++ b/sdk/basyx/aas/model/datatypes.py
@@ -1,4 +1,4 @@
-# Copyright (c) 2025 the Eclipse BaSyx Authors
+# Copyright (c) 2026 the Eclipse BaSyx Authors
#
# This program and the accompanying materials are made available under the terms of the MIT License, available in
# the LICENSE file of this project.
@@ -106,7 +106,12 @@ def __init__(self, year: int, month: int, tzinfo: Optional[datetime.tzinfo] = No
self.tzinfo: Optional[datetime.tzinfo] = tzinfo
def into_date(self, day: int = 1) -> Date:
- return Date(self.year, self.month, day, self.tzinfo)
+ try:
+ return Date(self.year, self.month, day, self.tzinfo)
+ except ValueError as e:
+ if self.year < 0:
+ raise ValueError("Negative years are not supported by Python's `datetime` library.") from e
+ raise e
@classmethod
def from_date(cls, date: datetime.date) -> "GYearMonth":
@@ -131,7 +136,12 @@ def __init__(self, year: int, tzinfo: Optional[datetime.tzinfo] = None):
self.tzinfo: Optional[datetime.tzinfo] = tzinfo
def into_date(self, month: int = 1, day: int = 1) -> Date:
- return Date(self.year, month, day, self.tzinfo)
+ try:
+ return Date(self.year, month, day, self.tzinfo)
+ except ValueError as e:
+ if self.year < 0:
+ raise ValueError("Negative years are not supported by Python's `datetime` library.") from e
+ raise e
@classmethod
def from_date(cls, date: datetime.date) -> "GYear":
@@ -166,7 +176,7 @@ def into_date(self, year: int = 1970) -> Date:
@classmethod
def from_date(cls, date: datetime.date) -> "GMonthDay":
tzinfo = date.tzinfo if hasattr(date, 'tzinfo') else None # type: ignore
- return cls(date.month, date.year, tzinfo)
+ return cls(date.month, date.day, tzinfo)
def __eq__(self, other: object) -> bool:
if not isinstance(other, GMonthDay):
@@ -398,7 +408,8 @@ def from_string(cls, value: str) -> "NormalizedString":
PositiveInteger: "positiveInteger",
UnsignedLong: "unsignedLong",
UnsignedShort: "unsignedShort",
- UnsignedInt: "unsignedByte",
+ UnsignedInt: "unsignedInt",
+ UnsignedByte: "unsignedByte",
AnyURI: "anyURI",
String: "string",
NormalizedString: "normalizedString",
@@ -604,27 +615,69 @@ def _parse_xsd_date(value: str) -> Date:
if not match:
raise ValueError("Value is not a valid XSD date string")
if match[1]:
- raise ValueError("Negative Dates are not supported by Python")
- return Date(int(match[2]), int(match[3]), int(match[4]), _parse_xsd_date_tzinfo(match[5]))
+ raise NotImplementedError("Negative dates are not supported: Python stdlib datetime requires year >= 1. "
+ "Report at https://github.com/eclipse-basyx/basyx-python-sdk/issues")
+ return Date(
+ year=int(match[2]),
+ month=int(match[3]),
+ day=int(match[4]),
+ tzinfo=_parse_xsd_date_tzinfo(match[5]),
+ )
def _parse_xsd_datetime(value: str) -> DateTime:
match = DATETIME_RE.match(value)
if not match:
- raise ValueError("Value is not a valid XSD datetime string")
+ raise ValueError(f"{value} is not a valid XSD datetime string")
if match[1]:
- raise ValueError("Negative Dates are not supported by Python")
+ raise NotImplementedError("Negative dates are not supported: Python stdlib datetime requires year >= 1. "
+ "Report at https://github.com/eclipse-basyx/basyx-python-sdk/issues")
microseconds = int(float(match[8]) * 1e6) if match[8] else 0
- return DateTime(int(match[2]), int(match[3]), int(match[4]), int(match[5]), int(match[6]), int(match[7]),
- microseconds, _parse_xsd_date_tzinfo(match[9]))
+ hour = int(match[5])
+ # xsd_datetime allows for hour=24 to represent midnight,
+ # Python's datetime.DateTime doesn't.
+ # If we get an hour=24, we accept and parse it as hour=0 of the next day.
+ # See: https://github.com/eclipse-basys/basys-python-sdk/issues/564
+ is_midnight_24 = False
+ if hour == 24:
+ if int(match[6]) != 0 or int(match[7]) != 0 or microseconds != 0:
+ raise ValueError(f"{value} is not a valid xsd:datetime.")
+ hour = 0
+ is_midnight_24 = True
+ res = DateTime(
+ year=int(match[2]),
+ month=int(match[3]),
+ day=int(match[4]),
+ hour=hour,
+ minute=int(match[6]),
+ second=int(match[7]),
+ microsecond=microseconds,
+ tzinfo=_parse_xsd_date_tzinfo(match[9]),
+ )
+ return res + datetime.timedelta(days=1) if is_midnight_24 else res
def _parse_xsd_time(value: str) -> Time:
match = TIME_RE.match(value)
if not match:
- raise ValueError("Value is not a valid XSD datetime string")
+ raise ValueError(f"{value} is not a valid XSD time string")
microseconds = int(float(match[4]) * 1e6) if match[4] else 0
- return Time(int(match[1]), int(match[2]), int(match[3]), microseconds, _parse_xsd_date_tzinfo(match[5]))
+ hour = int(match[1])
+ # xsd_time allows for hour=24 to represent midnight,
+ # Python's datetime.Time doesn't.
+ # If we get an hour=24, we accept and parse it as hour=0.
+ # See: https://github.com/eclipse-basys/basys-python-sdk/issues/564
+ if hour == 24:
+ if int(match[2]) != 0 or int(match[3]) != 0 or microseconds != 0:
+ raise ValueError(f"{value} is not a valid xsd:time.")
+ hour = 0
+ return Time(
+ hour=hour,
+ minute=int(match[2]),
+ second=int(match[3]),
+ microsecond=microseconds,
+ tzinfo=_parse_xsd_date_tzinfo(match[5]),
+ )
def _parse_xsd_bool(value: str) -> Boolean:
@@ -636,10 +689,10 @@ def _parse_xsd_bool(value: str) -> Boolean:
raise ValueError("Invalid literal for XSD bool type")
-GYEAR_RE = re.compile(r'^(\d\d\d\d)([+\-]\d\d:\d\d|Z)?$')
+GYEAR_RE = re.compile(r'^(-?)(\d{4,})([+\-]\d\d:\d\d|Z)?$')
GMONTH_RE = re.compile(r'^--(\d\d)([+\-]\d\d:\d\d|Z)?$')
GDAY_RE = re.compile(r'^---(\d\d)([+\-]\d\d:\d\d|Z)?$')
-GYEARMONTH_RE = re.compile(r'^(\d\d\d\d)-(\d\d)([+\-]\d\d:\d\d|Z)?$')
+GYEARMONTH_RE = re.compile(r'^(-?)(\d{4,})-(\d\d)([+\-]\d\d:\d\d|Z)?$')
GMONTHDAY_RE = re.compile(r'^--(\d\d)-(\d\d)([+\-]\d\d:\d\d|Z)?$')
@@ -647,7 +700,10 @@ def _parse_xsd_gyear(value: str) -> GYear:
match = GYEAR_RE.match(value)
if not match:
raise ValueError("Value is not a valid XSD GYear string")
- return GYear(int(match[1]), _parse_xsd_date_tzinfo(match[2]))
+ year = int(match[2])
+ if match[1]:
+ year = -year
+ return GYear(year, _parse_xsd_date_tzinfo(match[3]))
def _parse_xsd_gmonth(value: str) -> GMonth:
@@ -668,7 +724,10 @@ def _parse_xsd_gyearmonth(value: str) -> GYearMonth:
match = GYEARMONTH_RE.match(value)
if not match:
raise ValueError("Value is not a valid XSD GYearMonth string")
- return GYearMonth(int(match[1]), int(match[2]), _parse_xsd_date_tzinfo(match[3]))
+ year = int(match[2])
+ if match[1]:
+ year = -year
+ return GYearMonth(year, int(match[3]), _parse_xsd_date_tzinfo(match[4]))
def _parse_xsd_gmonthday(value: str) -> GMonthDay:
diff --git a/sdk/basyx/aas/model/provider.py b/sdk/basyx/aas/model/provider.py
index d13758308..9a91a346d 100644
--- a/sdk/basyx/aas/model/provider.py
+++ b/sdk/basyx/aas/model/provider.py
@@ -1,4 +1,4 @@
-# Copyright (c) 2025 the Eclipse BaSyx Authors
+# Copyright (c) 2026 the Eclipse BaSyx Authors
#
# This program and the accompanying materials are made available under the terms of the MIT License, available in
# the LICENSE file of this project.
@@ -11,135 +11,157 @@
"""
import abc
-from typing import MutableSet, Iterator, Generic, TypeVar, Dict, List, Optional, Iterable, Set, Tuple, cast
+import warnings
+from typing import MutableSet, Iterator, Generic, TypeVar, Dict, List, Optional, Iterable, Set, Tuple
from .base import Identifier, Identifiable
-class AbstractObjectProvider(metaclass=abc.ABCMeta):
+_KEY = TypeVar('_KEY') # Generic key type
+_VALUE = TypeVar('_VALUE') # Generic value type
+
+
+class AbstractObjectProvider(Generic[_KEY, _VALUE], metaclass=abc.ABCMeta):
"""
- Abstract baseclass for all objects, that allow to retrieve :class:`~basyx.aas.model.base.Identifiable` objects
- (resp. proxy objects for remote :class:`~basyx.aas.model.base.Identifiable` objects) by their
- :class:`~basyx.aas.model.base.Identifier`.
+ Abstract base class for all objects that allow retrieving values by a key.
This includes local object stores, database clients and AAS API clients.
"""
- @abc.abstractmethod
- def get_identifiable(self, identifier: Identifier) -> Identifiable:
- """
- Find an :class:`~basyx.aas.model.base.Identifiable` by its :class:`~basyx.aas.model.base.Identifier`
- This may include looking up the object's endpoint in a registry and fetching it from an HTTP server or a
- database.
-
- :param identifier: :class:`~basyx.aas.model.base.Identifier` of the object to return
- :return: The :class:`~basyx.aas.model.base.Identifiable` object (or a proxy object for a remote
- :class:`~basyx.aas.model.base.Identifiable` object)
- :raises KeyError: If no such :class:`~.basyx.aas.model.base.Identifiable` can be found
- """
+ @abc.abstractmethod
+ def get_item(self, key: _KEY) -> _VALUE:
+ """Retrieve the item or raise a KeyError."""
pass
- def get(self, identifier: Identifier, default: Optional[Identifiable] = None) -> Optional[Identifiable]:
- """
- Find an object in this set by its :class:`id `, with fallback parameter
-
- :param identifier: :class:`~basyx.aas.model.base.Identifier` of the object to return
- :param default: An object to be returned, if no object with the given
- :class:`id ` is found
- :return: The :class:`~basyx.aas.model.base.Identifiable` object with the given
- :class:`id ` in the provider. Otherwise, the ``default`` object
- or None, if none is given.
- """
+ def get(self, key: _KEY, default: Optional[_VALUE] = None) -> Optional[_VALUE]:
+ """Retrieve the item or return a default value."""
try:
- return self.get_identifiable(identifier)
+ return self.get_item(key)
except KeyError:
return default
-_IT = TypeVar('_IT', bound=Identifiable)
-
-
-class AbstractObjectStore(AbstractObjectProvider, MutableSet[_IT], Generic[_IT], metaclass=abc.ABCMeta):
+class AbstractObjectStore(AbstractObjectProvider[_KEY, _VALUE], MutableSet[_VALUE]):
"""
- Abstract baseclass of for container-like objects for storage of :class:`~basyx.aas.model.base.Identifiable` objects.
+ Abstract base class for container-like objects for storage of values.
- ObjectStores are special ObjectProvides that – in addition to retrieving objects by
- :class:`~basyx.aas.model.base.Identifier` – allow to add and delete objects (i.e. behave like a Python set).
- This includes local object stores (like :class:`~.DictObjectStore`) and specific object stores
- (like :class:`~basyx.aas.backend.couchdb.CouchDBObjectStore` and
- :class:`~basyx.aas.backend.local_file.LocalFileObjectStore`).
+ ObjectStores are special ObjectProviders that, in addition to retrieving values by a key, allow adding and deleting
+ values (i.e. behave like a Python set). This includes local IdentifiableStores (like
+ :class:`~.DictIdentifiableStore`) and specific IdentifiableStores (like
+ :class:`~basyx.aas.backend.couchdb.CouchDBIdentifiableStore` and
+ :class:`~basyx.aas.backend.local_file.LocalFileIdentifiableStore`).
The AbstractObjectStore inherits from the :class:`~collections.abc.MutableSet` abstract collections class and
therefore implements all the functions of this class.
"""
+
@abc.abstractmethod
def __init__(self):
pass
- def update(self, other: Iterable[_IT]) -> None:
+ def commit(self, x: _VALUE) -> None:
+ """
+ Persist an in-memory mutation of a stored object back to the underlying storage.
+
+ Persistent backends (e.g. file-based or database-backed stores) must override this to
+ write the updated object back to storage. In-memory stores should override this with an
+ explicit no-op to make the intent clear.
+
+ :param x: The object whose current in-memory state should be persisted
+ """
+ raise NotImplementedError()
+
+ def update(self, other: Iterable[_VALUE]) -> None:
for x in other:
self.add(x)
- def sync(self, other: Iterable[_IT], overwrite: bool) -> Tuple[int, int, int]:
+ def sync(self, other: Iterable[_VALUE], overwrite: bool) -> Tuple[int, int, int]:
"""
- Merge :class:`Identifiables ` from an
- :class:`~collections.abc.Iterable` into this :class:`~basyx.aas.model.provider.AbstractObjectStore`.
+ Merge values from an :class:`~collections.abc.Iterable` into this
+ :class:`~basyx.aas.model.provider.AbstractObjectStore`.
:param other: :class:`~collections.abc.Iterable` to sync with
- :param overwrite: Flag to overwrite existing :class:`Identifiables ` in this
+ :param overwrite: Flag to overwrite existing values in this
:class:`~basyx.aas.model.provider.AbstractObjectStore` with updated versions from ``other``,
- :class:`Identifiables ` unique to this
- :class:`~basyx.aas.model.provider.AbstractObjectStore` are always preserved
- :return: Counts of processed :class:`Identifiables ` as
- ``(added, overwritten, skipped)``
+ values unique to this :class:`~basyx.aas.model.provider.AbstractObjectStore` are always preserved
+ :return: Counts of processed values as``(added, overwritten, skipped)``
"""
-
added, overwritten, skipped = 0, 0, 0
- for identifiable in other:
- identifiable_id = identifiable.id
- if identifiable_id in self:
+ for value in other:
+ if value in self:
if overwrite:
- existing = self.get_identifiable(identifiable_id)
- self.discard(cast(_IT, existing))
- self.add(identifiable)
+ self.discard(value)
+ self.add(value)
overwritten += 1
else:
skipped += 1
else:
- self.add(identifiable)
+ self.add(value)
added += 1
return added, overwritten, skipped
-class DictObjectStore(AbstractObjectStore[_IT], Generic[_IT]):
+class ObjectProviderMultiplexer(AbstractObjectProvider[_KEY, _VALUE]):
+ """
+ A multiplexer for :class:`AbstractObjectProviders <.AbstractObjectProvider>`.
+
+ This class combines multiple :class:`AbstractObjectProviders <.AbstractObjectProvider>` into a single one to allow
+ retrieving values from different sources. It implements the :class:`~.AbstractObjectProvider` interface to be used
+ as registry itself.
+
+ :param registries: A list of :class:`AbstractObjectProviders <.AbstractObjectProvider>` to query when looking up a
+ key
+ """
+
+ def __init__(self, registries: Optional[List[AbstractObjectProvider[_KEY, _VALUE]]] = None) -> None:
+ self.providers: List[AbstractObjectProvider[_KEY, _VALUE]] = registries if registries is not None else []
+
+ def get_item(self, key: _KEY) -> _VALUE:
+ for provider in self.providers:
+ try:
+ return provider.get_item(key)
+ except KeyError:
+ pass
+ raise KeyError("Key could not be found in any of the {} consulted registries."
+ .format(len(self.providers)))
+
+
+_IDENTIFIABLE = TypeVar('_IDENTIFIABLE', bound=Identifiable)
+
+
+class DictIdentifiableStore(AbstractObjectStore[Identifier, _IDENTIFIABLE]):
"""
A local in-memory object store for :class:`~basyx.aas.model.base.Identifiable` objects, backed by a dict, mapping
:class:`~basyx.aas.model.base.Identifier` → :class:`~basyx.aas.model.base.Identifiable`
.. note::
- The `DictObjectStore` provides efficient retrieval of objects by their :class:`~basyx.aas.model.base.Identifier`
- However, since object stores are not referenced via the parent attribute, the mapping is not updated
- if the :class:`~basyx.aas.model.base.Identifier` of an :class:`~basyx.aas.model.base.Identifiable` changes.
- For more details, see [issue #216](https://github.com/eclipse-basyx/basyx-python-sdk/issues/216).
- As a result, the `DictObjectStore` is unsuitable for storing objects whose
- :class:`~basyx.aas.model.base.Identifier` may change.
- In such cases, consider using a :class:`~.SetObjectStore` instead.
+ The `DictIdentifiableStore` provides efficient retrieval of objects by their
+ :class:`~basyx.aas.model.base.Identifier`. However, since object stores are not referenced via the parent
+ attribute, the mapping is not updated if the :class:`~basyx.aas.model.base.Identifier` of an
+ :class:`~basyx.aas.model.base.Identifiable` changes. For more details, see
+ [issue #216](https://github.com/eclipse-basyx/basyx-python-sdk/issues/216). As a result, the
+ `DictIdentifiableStore` is unsuitable for storing objects whose :class:`~basyx.aas.model.base.Identifier` may
+ change. In such cases, consider using a :class:`~.SetIdentifiableStore` instead.
"""
- def __init__(self, objects: Iterable[_IT] = ()) -> None:
- self._backend: Dict[Identifier, _IT] = {}
- for x in objects:
+
+ def __init__(self, identifiables: Iterable[_IDENTIFIABLE] = ()) -> None:
+ self._backend: Dict[Identifier, _IDENTIFIABLE] = {}
+ for x in identifiables:
self.add(x)
- def get_identifiable(self, identifier: Identifier) -> _IT:
+ def get_item(self, identifier: Identifier) -> _IDENTIFIABLE:
return self._backend[identifier]
- def add(self, x: _IT) -> None:
+ def add(self, x: _IDENTIFIABLE) -> None:
if x.id in self._backend and self._backend.get(x.id) is not x:
raise KeyError("Identifiable object with same id {} is already stored in this store"
.format(x.id))
self._backend[x.id] = x
- def discard(self, x: _IT) -> None:
+ def commit(self, x: _IDENTIFIABLE) -> None:
+ pass
+
+ def discard(self, x: _IDENTIFIABLE) -> None:
if self._backend.get(x.id) is x:
del self._backend[x.id]
@@ -153,54 +175,82 @@ def __contains__(self, x: object) -> bool:
def __len__(self) -> int:
return len(self._backend)
- def __iter__(self) -> Iterator[_IT]:
+ def __iter__(self) -> Iterator[_IDENTIFIABLE]:
return iter(self._backend.values())
-class SetObjectStore(AbstractObjectStore[_IT], Generic[_IT]):
+class DictObjectStore(DictIdentifiableStore[_IDENTIFIABLE]):
+ """
+ `DictObjectStore` has been renamed to :class:`~.DictIdentifiableStore` and will be removed in a future release.
+ Please migrate to :class:`~.DictIdentifiableStore`.
+ """
+
+ def __init__(self, identifiables: Iterable[_IDENTIFIABLE] = ()) -> None:
+ warnings.warn(
+ "`DictObjectStore` is deprecated and will be removed in a future release. Use "
+ "`DictIdentifiableStore` instead.",
+ DeprecationWarning,
+ stacklevel=2,
+ )
+ super().__init__(identifiables)
+
+ def get_identifiable(self, identifier: Identifier) -> _IDENTIFIABLE:
+ warnings.warn(
+ "`get_identifiable()` is deprecated. Use `get_item()` from `DictIdentifiableStore` instead.",
+ DeprecationWarning,
+ stacklevel=2,
+ )
+ return super().get_item(identifier)
+
+
+class SetIdentifiableStore(AbstractObjectStore[Identifier, _IDENTIFIABLE]):
"""
A local in-memory object store for :class:`~basyx.aas.model.base.Identifiable` objects, backed by a set
.. note::
- The `SetObjectStore` is slower than the `DictObjectStore` for retrieval of objects, because it has to iterate
- over all objects to find the one with the correct :class:`~basyx.aas.model.base.Identifier`.
- On the other hand, the `SetObjectStore` is more secure, because it is less affected by changes in the
+ The `SetIdentifiableStore` is slower than the `DictIdentifiableStore` for retrieval of objects, because it has
+ to iterate over all objects to find the one with the correct :class:`~basyx.aas.model.base.Identifier`.
+ On the other hand, the `SetIdentifiableStore` is more secure, because it is less affected by changes in the
:class:`~basyx.aas.model.base.Identifier` of an :class:`~basyx.aas.model.base.Identifiable` object.
- Therefore, the `SetObjectStore` is suitable for storing objects whose :class:`~basyx.aas.model.base.Identifier`
- may change.
+ Therefore, the `SetIdentifiableStore` is suitable for storing objects whose
+ :class:`~basyx.aas.model.base.Identifier` may change.
"""
- def __init__(self, objects: Iterable[_IT] = ()) -> None:
- self._backend: Set[_IT] = set()
+
+ def __init__(self, objects: Iterable[_IDENTIFIABLE] = ()) -> None:
+ self._backend: Set[_IDENTIFIABLE] = set()
for x in objects:
self.add(x)
- def get_identifiable(self, identifier: Identifier) -> _IT:
+ def get_item(self, identifier: Identifier) -> _IDENTIFIABLE:
for x in self._backend:
if x.id == identifier:
return x
raise KeyError(identifier)
- def add(self, x: _IT) -> None:
+ def add(self, x: _IDENTIFIABLE) -> None:
if x in self:
# Object is already in store
return
try:
- self.get_identifiable(x.id)
+ self.get_item(x.id)
except KeyError:
self._backend.add(x)
else:
raise KeyError(f"Identifiable object with same id {x.id} is already stored in this store")
- def discard(self, x: _IT) -> None:
+ def commit(self, x: _IDENTIFIABLE) -> None:
+ pass
+
+ def discard(self, x: _IDENTIFIABLE) -> None:
self._backend.discard(x)
- def remove(self, x: _IT) -> None:
+ def remove(self, x: _IDENTIFIABLE) -> None:
self._backend.remove(x)
def __contains__(self, x: object) -> bool:
if isinstance(x, Identifier):
try:
- self.get_identifiable(x)
+ self.get_item(x)
return True
except KeyError:
return False
@@ -211,29 +261,29 @@ def __contains__(self, x: object) -> bool:
def __len__(self) -> int:
return len(self._backend)
- def __iter__(self) -> Iterator[_IT]:
+ def __iter__(self) -> Iterator[_IDENTIFIABLE]:
return iter(self._backend)
-class ObjectProviderMultiplexer(AbstractObjectProvider):
+class SetObjectStore(SetIdentifiableStore[_IDENTIFIABLE]):
"""
- A multiplexer for Providers of :class:`~basyx.aas.model.base.Identifiable` objects.
-
- This class combines multiple registries of :class:`~basyx.aas.model.base.Identifiable` objects into a single one
- to allow retrieving :class:`~basyx.aas.model.base.Identifiable` objects from different sources.
- It implements the :class:`~.AbstractObjectProvider` interface to be used as registry itself.
-
- :param registries: A list of :class:`AbstractObjectProviders <.AbstractObjectProvider>` to query when looking up an
- object
+ `SetObjectStore` has been renamed to :class:`~.SetIdentifiableStore` and will be removed in a future release.
+ Please migrate to :class:`~.SetIdentifiableStore`.
"""
- def __init__(self, registries: Optional[List[AbstractObjectProvider]] = None):
- self.providers: List[AbstractObjectProvider] = registries if registries is not None else []
- def get_identifiable(self, identifier: Identifier) -> Identifiable:
- for provider in self.providers:
- try:
- return provider.get_identifiable(identifier)
- except KeyError:
- pass
- raise KeyError("Identifier could not be found in any of the {} consulted registries."
- .format(len(self.providers)))
+ def __init__(self, objects: Iterable[_IDENTIFIABLE] = ()) -> None:
+ warnings.warn(
+ "`SetObjectStore` is deprecated and will be removed in a future release. Use `SetIdentifiableStore`"
+ "instead.",
+ DeprecationWarning,
+ stacklevel=2,
+ )
+ super().__init__(objects)
+
+ def get_identifiable(self, identifier: Identifier) -> _IDENTIFIABLE:
+ warnings.warn(
+ "`get_identifiable()` is deprecated. Use `get_item()` from `SetIdentifiableStore` instead.",
+ DeprecationWarning,
+ stacklevel=2,
+ )
+ return super().get_item(identifier)
diff --git a/sdk/basyx/aas/model/submodel.py b/sdk/basyx/aas/model/submodel.py
index 9e7321c41..a361ce341 100644
--- a/sdk/basyx/aas/model/submodel.py
+++ b/sdk/basyx/aas/model/submodel.py
@@ -1,4 +1,4 @@
-# Copyright (c) 2025 the Eclipse BaSyx Authors
+# Copyright (c) 2026 the Eclipse BaSyx Authors
#
# This program and the accompanying materials are made available under the terms of the MIT License, available in
# the LICENSE file of this project.
@@ -150,13 +150,6 @@ def __init__(self,
self.embedded_data_specifications: List[base.EmbeddedDataSpecification] = list(embedded_data_specifications)
-ALLOWED_DATA_ELEMENT_CATEGORIES: Set[str] = {
- "CONSTANT",
- "PARAMETER",
- "VARIABLE"
-}
-
-
class DataElement(SubmodelElement, metaclass=abc.ABCMeta):
"""
A data element is a :class:`~.SubmodelElement` that is not further composed out of other
@@ -203,28 +196,13 @@ def __init__(self,
super().__init__(id_short, display_name, category, description, parent, semantic_id, qualifier, extension,
supplemental_semantic_id, embedded_data_specifications)
- def _set_category(self, category: Optional[str]):
- if category == "":
- raise base.AASConstraintViolation(100,
- "category is not allowed to be an empty string")
- if category is None:
- self._category = None
- else:
- if category not in ALLOWED_DATA_ELEMENT_CATEGORIES:
- if not (isinstance(self, File) or isinstance(self, Blob)):
- raise base.AASConstraintViolation(
- 90,
- "DataElement.category must be one of the following: " +
- ", ".join(ALLOWED_DATA_ELEMENT_CATEGORIES))
- self._category = category
-
class Property(DataElement):
"""
A property is a :class:`DataElement` that has a single value.
**Constraint AASd-007:** If both, the value and the valueId of a Qualifier are present,
- the value needs to be identical to the value of the referenced coded value in Qualifier/valueId.
+ the value shall be identical to the value of the referenced coded value in Qualifier/valueId.
:ivar id_short: Identifying string of the element within its name space. (inherited from
:class:`~basyx.aas.model.base.Referable`)
@@ -344,6 +322,16 @@ def __init__(self,
self.value: Optional[base.MultiLanguageTextType] = value
self.value_id: Optional[base.Reference] = value_id
+ @property
+ def value(self) -> Optional[base.MultiLanguageTextType]:
+ return self._value
+
+ @value.setter
+ def value(self, value: Union[base.MultiLanguageTextType, dict, None]) -> None:
+ if value is not None and not isinstance(value, base.MultiLanguageTextType):
+ value = base.MultiLanguageTextType(value)
+ self._value = value
+
class Range(DataElement):
"""
@@ -464,7 +452,7 @@ class Blob(DataElement):
def __init__(self,
id_short: Optional[base.NameType],
- content_type: base.ContentType,
+ content_type: Optional[base.ContentType] = None,
value: Optional[base.BlobType] = None,
display_name: Optional[base.MultiLanguageNameType] = None,
category: Optional[base.NameType] = None,
@@ -482,7 +470,7 @@ def __init__(self,
super().__init__(id_short, display_name, category, description, parent, semantic_id, qualifier, extension,
supplemental_semantic_id, embedded_data_specifications)
self.value: Optional[base.BlobType] = value
- self.content_type: base.ContentType = content_type
+ self.content_type: Optional[base.ContentType] = content_type
@_string_constraints.constrain_content_type("content_type")
@@ -518,7 +506,7 @@ class File(DataElement):
def __init__(self,
id_short: Optional[base.NameType],
- content_type: base.ContentType,
+ content_type: Optional[base.ContentType] = None,
value: Optional[base.PathType] = None,
display_name: Optional[base.MultiLanguageNameType] = None,
category: Optional[base.NameType] = None,
@@ -536,7 +524,7 @@ def __init__(self,
super().__init__(id_short, display_name, category, description, parent, semantic_id, qualifier, extension,
supplemental_semantic_id, embedded_data_specifications)
self.value: Optional[base.PathType] = value
- self.content_type: base.ContentType = content_type
+ self.content_type: Optional[base.ContentType] = content_type
class ReferenceElement(DataElement):
@@ -740,17 +728,16 @@ def __init__(self,
raise
def _generate_id_short(self, new: _SE) -> None:
- if new.id_short is not None:
- raise base.AASConstraintViolation(120, "Objects with an id_short may not be added to a "
- f"SubmodelElementList, got {new!r} with id_short={new.id_short}")
# Generate a unique id_short when a SubmodelElement is added, because children of a SubmodelElementList may not
# have an id_short. The alternative would be making SubmodelElementList a special kind of base.Namespace without
# a unique attribute for child-elements (which contradicts the definition of a Namespace).
- new.id_short = "generated_submodel_list_hack_" + uuid.uuid1(clock_seq=self._uuid_seq).hex
- self._uuid_seq += 1
+ if new.id_short is None:
+ new.id_short = "generated_submodel_list_hack_" + uuid.uuid1(clock_seq=self._uuid_seq).hex
+ self._uuid_seq += 1
def _unset_id_short(self, old: _SE) -> None:
- old.id_short = None
+ if old.id_short is not None and old.id_short.startswith("generated_submodel_list_hack_"):
+ old.id_short = None
def _check_constraints(self, new: _SE, existing: Iterable[_SE]) -> None:
# Since the id_short contains randomness, unset it temporarily for pretty and predictable error messages.
@@ -860,8 +847,8 @@ class RelationshipElement(SubmodelElement):
def __init__(self,
id_short: Optional[base.NameType],
- first: base.Reference,
- second: base.Reference,
+ first: Optional[base.Reference] = None,
+ second: Optional[base.Reference] = None,
display_name: Optional[base.MultiLanguageNameType] = None,
category: Optional[base.NameType] = None,
description: Optional[base.MultiLanguageTextType] = None,
@@ -877,8 +864,8 @@ def __init__(self,
super().__init__(id_short, display_name, category, description, parent, semantic_id, qualifier, extension,
supplemental_semantic_id, embedded_data_specifications)
- self.first: base.Reference = first
- self.second: base.Reference = second
+ self.first: Optional[base.Reference] = first
+ self.second: Optional[base.Reference] = second
class AnnotatedRelationshipElement(RelationshipElement, base.UniqueIdShortNamespace):
@@ -917,8 +904,8 @@ class AnnotatedRelationshipElement(RelationshipElement, base.UniqueIdShortNamesp
def __init__(self,
id_short: Optional[base.NameType],
- first: base.Reference,
- second: base.Reference,
+ first: Optional[base.Reference] = None,
+ second: Optional[base.Reference] = None,
display_name: Optional[base.MultiLanguageNameType] = None,
annotation: Iterable[DataElement] = (),
category: Optional[base.NameType] = None,
@@ -1053,8 +1040,8 @@ class Entity(SubmodelElement, base.UniqueIdShortNamespace):
"""
An entity is a :class:`~.SubmodelElement` that is used to model entities
- **Constraint AASd-014:** global_asset_id or specific_asset_id must be set if ``entity_type`` is set to
- :attr:`~basyx.aas.model.base.EntityType.SELF_MANAGED_ENTITY`. They must be empty otherwise.
+ **Constraint AASd-014:** Either the attribute ``globalAssetId`` or ``specificAssetId`` of an ``Entity``
+ must be set if ``Entity/entityType`` is set to ``SelfManagedEntity``.
:ivar id_short: Identifying string of the element within its name space. (inherited from
:class:`~basyx.aas.model.base.Referable`)
@@ -1088,7 +1075,7 @@ class Entity(SubmodelElement, base.UniqueIdShortNamespace):
def __init__(self,
id_short: Optional[base.NameType],
- entity_type: base.EntityType,
+ entity_type: Optional[base.EntityType],
statement: Iterable[SubmodelElement] = (),
global_asset_id: Optional[base.Identifier] = None,
specific_asset_id: Iterable[base.SpecificAssetId] = (),
@@ -1108,7 +1095,7 @@ def __init__(self,
supplemental_semantic_id, embedded_data_specifications)
self.statement = base.NamespaceSet(self, [("id_short", True)], statement)
# assign private attributes, bypassing setters, as constraints will be checked below
- self._entity_type: base.EntityType = entity_type
+ self._entity_type: Optional[base.EntityType] = entity_type
self._global_asset_id: Optional[base.Identifier] = global_asset_id
self._specific_asset_id: base.ConstrainedList[base.SpecificAssetId] = base.ConstrainedList(
specific_asset_id,
@@ -1120,11 +1107,11 @@ def __init__(self,
self._validate_aasd_014(entity_type, global_asset_id, bool(specific_asset_id))
@property
- def entity_type(self) -> base.EntityType:
+ def entity_type(self) -> Optional[base.EntityType]:
return self._entity_type
@entity_type.setter
- def entity_type(self, entity_type: base.EntityType) -> None:
+ def entity_type(self, entity_type: Optional[base.EntityType]) -> None:
self._validate_aasd_014(entity_type, self.global_asset_id, bool(self.specific_asset_id))
self._entity_type = entity_type
@@ -1167,9 +1154,11 @@ def _validate_global_asset_id(global_asset_id: Optional[base.Identifier]) -> Non
_string_constraints.check_identifier(global_asset_id)
@staticmethod
- def _validate_aasd_014(entity_type: base.EntityType,
+ def _validate_aasd_014(entity_type: Optional[base.EntityType],
global_asset_id: Optional[base.Identifier],
specific_asset_id_nonempty: bool) -> None:
+ if entity_type is None:
+ return
if entity_type == base.EntityType.SELF_MANAGED_ENTITY and global_asset_id is None \
and not specific_asset_id_nonempty:
raise base.AASConstraintViolation(
diff --git a/sdk/basyx/aas/util/identification.py b/sdk/basyx/aas/util/identification.py
index 74cccd99a..8d6a1b77e 100644
--- a/sdk/basyx/aas/util/identification.py
+++ b/sdk/basyx/aas/util/identification.py
@@ -1,4 +1,4 @@
-# Copyright (c) 2025 the Eclipse BaSyx Authors
+# Copyright (c) 2026 the Eclipse BaSyx Authors
#
# This program and the accompanying materials are made available under the terms of the MIT License, available in
# the LICENSE file of this project.
@@ -17,7 +17,7 @@
import abc
import re
import uuid
-from typing import Optional, Dict, Union, Set
+from typing import Optional, Dict, Union
from .. import model
@@ -70,7 +70,7 @@ class NamespaceIRIGenerator(AbstractIdentifierGenerator):
:ivar provider: An :class:`~basyx.aas.model.provider.AbstractObjectProvider` to check existence of
:class:`Identifiers `
"""
- def __init__(self, namespace: str, provider: model.AbstractObjectProvider):
+ def __init__(self, namespace: str, provider: model.AbstractObjectProvider[model.Identifier, model.Identifiable]):
"""
Create a new NamespaceIRIGenerator
:param namespace: The IRI Namespace to generate Identifiers in. It must be a valid IRI (starting with a
@@ -100,7 +100,7 @@ def generate_id(self, proposal: Optional[str] = None) -> model.Identifier:
iri = "{}{}".format(self._namespace, proposal)
# Try to find iri in provider. If it does not exist (KeyError), we found a unique one to return
try:
- self.provider.get_identifiable(iri)
+ self.provider.get_item(iri)
except KeyError:
self._counter_cache[proposal] = counter
return iri
diff --git a/sdk/basyx/aas/util/traversal.py b/sdk/basyx/aas/util/traversal.py
index 0b288ddf7..34f809a72 100644
--- a/sdk/basyx/aas/util/traversal.py
+++ b/sdk/basyx/aas/util/traversal.py
@@ -1,4 +1,4 @@
-# Copyright (c) 2025 the Eclipse BaSyx Authors
+# Copyright (c) 2026 the Eclipse BaSyx Authors
#
# This program and the accompanying materials are made available under the terms of the MIT License, available in
# the LICENSE file of this project.
@@ -8,26 +8,51 @@
A module with helper functions for traversing AAS object structures.
"""
-from typing import Union, Iterator
+from typing import Iterator
from .. import model
-def walk_submodel(collection: Union[model.Submodel, model.SubmodelElementCollection, model.SubmodelElementList]) \
- -> Iterator[model.SubmodelElement]:
+def walk_submodel_element(element: model.SubmodelElement) -> Iterator[model.SubmodelElement]:
+ """
+ Traverse all :class:`SubmodelElements ` contained within the given
+ element recursively, i.e. the children of:
+ :class:`~basyx.aas.model.submodel.SubmodelElementCollection`,
+ :class:`~basyx.aas.model.submodel.SubmodelElementList`,
+ :class:`~basyx.aas.model.submodel.Entity` (via ``statement``) or
+ :class:`~basyx.aas.model.submodel.Operation` (via ``input_variable``, ``output_variable``, ``in_output_variable``).
+
+ The given element itself is not yielded. This is a generator function, yielding all the
+ :class:`SubmodelElements `.
+ No :class:`SubmodelElements ` should be added, removed or
+ moved while iterating, as this could result in undefined behaviour.
+ """
+ if isinstance(element, (model.SubmodelElementCollection, model.SubmodelElementList)):
+ for sub_element in element.value:
+ yield from walk_submodel_element(sub_element)
+ yield sub_element
+ elif isinstance(element, model.Operation):
+ for var_list in (element.input_variable, element.output_variable, element.in_output_variable):
+ for sub_element in var_list:
+ yield from walk_submodel_element(sub_element)
+ yield sub_element
+ elif isinstance(element, model.Entity):
+ for sub_element in element.statement:
+ yield from walk_submodel_element(sub_element)
+ yield sub_element
+
+
+def walk_submodel(submodel: model.Submodel) -> Iterator[model.SubmodelElement]:
"""
Traverse the :class:`SubmodelElements ` in a
- :class:`~basyx.aas.model.submodel.Submodel`, :class:`~basyx.aas.model.submodel.SubmodelElementCollection` or a
- :class:`~basyx.aas.model.submodel.SubmodelElementList` recursively in post-order tree-traversal.
+ :class:`~basyx.aas.model.submodel.Submodel` recursively.
This is a generator function, yielding all the :class:`SubmodelElements `.
No :class:`SubmodelElements ` should be added, removed or
moved while iterating, as this could result in undefined behaviour.
"""
- elements = collection.submodel_element if isinstance(collection, model.Submodel) else collection.value
- for element in elements:
- if isinstance(element, (model.SubmodelElementCollection, model.SubmodelElementList)):
- yield from walk_submodel(element)
+ for element in submodel.submodel_element:
+ yield from walk_submodel_element(element)
yield element
diff --git a/sdk/docs/source/constraints.rst b/sdk/docs/source/constraints.rst
index f51ea0ed5..24157c56f 100644
--- a/sdk/docs/source/constraints.rst
+++ b/sdk/docs/source/constraints.rst
@@ -14,29 +14,27 @@ The status information means the following:
In most cases, if a constraint violation is detected,
an :class:`~basyx.aas.model.base.AASConstraintViolation` will be raised
-.. |aasd002| replace:: ``idShort`` of ``Referable`` s shall only feature letters, digits, underscore (``_``); starting mandatory with a letter, i.e. ``[a-zA-Z][a-zA-Z0-9_]*``.
+.. |aasd002| replace:: ``idShort`` of ``Referable`` s shall only feature letters, digits, underscore (``_``), hyphen (``-``); starting mandatory with a letter and not ending with a hyphen. I.e. ``^[a-zA-Z]|[a-zA-Z][a-zA-Z0-9_-]*[a-zA-Z0-9_]$``.
.. |aasd005| replace:: If ``AdministrativeInformation/version`` is not specified, ``AdministrativeInformation/revision`` shall also be unspecified. This means that a revision requires a version. If there is no version, there is no revision. Revision is optional.
-.. |aasd006| replace:: If both, the ``value`` and the ``valueId`` of a ``Qualifier`` are present, the value needs to be identical to the value of the referenced coded value in ``Qualifier/valueId``.
-.. |aasd007| replace:: If both the ``Property/value`` and the ``Property/valueId`` are present, the value of ``Property/value`` needs to be identical to the value of the referenced coded value in ``Property/valueId``.
+.. |aasd006| replace:: If both, the ``value`` and the ``valueId`` of a ``Qualifier`` are present, the value shall be identical to the value of the referenced coded value in ``Qualifier/valueId``.
+.. |aasd007| replace:: If both the ``Property/value`` and the ``Property/valueId`` are present, the value of ``Property/value`` shall be identical to the value of the referenced coded value in ``Property/valueId``.
.. |aasd012| replace:: if both the ``MultiLanguageProperty/value`` and the ``MultiLanguageProperty/valueId`` are present, the meaning must be the same for each string in a specific language, as specified in ``MultiLanguageProperty/valueId``.
-.. |aasd014| replace:: Either the attribute ``globalAssetId`` or ``specificAssetId`` of an ``Entity`` must be set if ``Entity/entityType`` is set to ``SelfManagedEntity``. Otherwise, they do not exist.
+.. |aasd014| replace:: Either the attribute ``globalAssetId`` or ``specificAssetId`` of an ``Entity`` must be set if ``Entity/entityType`` is set to ``SelfManagedEntity``.
.. |aasd020| replace:: The value of ``Qualifier/value`` shall be consistent with the data type as defined in ``Qualifier/valueType``.
-.. |aasd021| replace:: Every qualifiable can only have one qualifier with the same ``Qualifier/type``.
+.. |aasd021| replace:: Every qualifiable shall only have one qualifier with the same ``Qualifier/type``.
.. |aasd022| replace:: ``idShort`` of non-identifiable referables within the same name space shall be unique (case-sensitive).
-.. |aasd077| replace:: The name of an extension (``Extension/name``) within ``HasExtensions`` needs to be unique.
+.. |aasd077| replace:: The name of an extension (``Extension/name``) within ``HasExtensions`` shall be unique.
.. |aasd080| replace:: In case ``Key/type`` == ``GlobalReference`` ``idType`` shall not be any LocalKeyType (``IdShort, FragmentId``).
.. |aasd081| replace:: In case ``Key/type`` == ``AssetAdministrationShell`` ``Key/idType`` shall not be any LocalKeyType (``IdShort``, ``FragmentId``).
-.. |aasd090| replace:: for data elements, ``category`` (inherited by ``Referable``) shall be one of the following values: CONSTANT, PARAMETER or VARIABLE. Default: VARIABLE
.. |aasd107| replace:: If a first level child element in a ``SubmodelElementList`` has a semanticId, it shall be identical to ``SubmodelElementList/semanticIdListElement``.
.. |aasd108| replace:: All first level child elements in a ``SubmodelElementList`` shall have the same submodel element type as specified in ``SubmodelElementList/typeValueListElement``.
.. |aasd109| replace:: If ``SubmodelElementList/typeValueListElement`` is equal to ``Property`` or ``Range,`` ``SubmodelElementList/valueTypeListElement`` shall be set and all first level child elements in the ``SubmodelElementList`` shall have the value type as specified in ``SubmodelElementList/valueTypeListElement``.
.. |aasd114| replace:: If two first level child elements in a ``SubmodelElementList`` have a ``semanticId``, they shall be identical.
.. |aasd115| replace:: If a first level child element in a ``SubmodelElementList`` does not specify a ``semanticId``, the value is assumed to be identical to ``SubmodelElementList/semanticIdListElement``.
-.. |aasd116| replace:: ``globalAssetId`` (case-insensitive) is a reserved key. If used as value for ``SpecificAssetId/name,`` ``SpecificAssetId/value`` shall be identical to ``AssetInformation/globalAssetId``.
+.. |aasd116| replace:: ``globalAssetId`` (case-insensitive) is a reserved key for ``SpecificAssetId/name`` with the semantics as defined in ``:attr:`basyx.aas.model.aas.AssetInformation.global_asset_id``.
.. |aasd117| replace:: ``idShort`` of non-identifiable ``Referables`` not being a direct child of a ``SubmodelElementList`` shall be specified.
.. |aasd118| replace:: If a supplemental semantic ID (``HasSemantics/supplementalSemanticId``) is defined, there shall also be a main semantic ID (``HasSemantics/semanticId``).
.. |aasd119| replace:: If any ``Qualifier/kind`` value of a ``Qualifiable/qualifier`` is equal to ``TemplateQualifier`` and the qualified element inherits from ``HasKind``, the qualified element shall be of kind ``Template`` (``HasKind/kind = Template``).
-.. |aasd120| replace:: ``idShort`` of submodel elements being a direct child of a ``SubmodelElementList`` shall not be specified.
.. |aasd121| replace:: For ``References``, the value of ``Key/type`` of the first ``key`` of ``Reference/keys`` shall be one of ``GloballyIdentifiables``.
.. |aasd122| replace:: For external references, i.e. ``References`` with ``Reference/type = ExternalReference``, the value of ``Key/type`` of the first key of ``Reference/keys`` shall be one of ``GenericGloballyIdentifiables``.
.. |aasd123| replace:: For model references, i.e. ``References`` with ``Reference/type = ModellReference``, the value of ``Key/type`` of the first ``key`` of ``Reference/keys`` shall be one of ``AasIdentifiables``.
@@ -57,8 +55,8 @@ an :class:`~basyx.aas.model.base.AASConstraintViolation` will be raised
.. |aasc006| replace:: For a ``ConceptDescription`` with ``category`` DOCUMENT using data specification template IEC61360 - ``DataSpecificationIEC61360/dataType`` shall be one of the following values: STRING or URL.
.. |aasc007| replace:: For a ``ConceptDescription`` with ``category`` QUALIFIER_TYPE using data specification template IEC61360 - ``DataSpecificationIEC61360/dataType`` is mandatory and shall be defined.
.. |aasc008| replace:: For a ConceptDescriptions except for a ``ConceptDescription`` of ``category`` VALUE using data specification template IEC61360 - ``DataSpecificationIEC61360/definition`` is mandatory and shall be defined at least in English.
-.. |aasc009| replace:: If ``DataSpecificationIEC61360/dataType`` one of: INTEGER_MEASURE, REAL_MEASURE, RATIONAL_MEASURE, INTEGER_CURRENCY, REAL_CURRENCY, then ``DataSpecificationIEC61360/unit`` or ``DataSpecificationIEC61360/unitId`` shall be defined.
-.. |aasc010| replace:: If ``DataSpecificationIEC61360/value`` is not empty then ``DataSpecificationIEC61360/valueList`` shall be empty and vice versa.
+.. |aasc3a009| replace:: If ``DataSpecificationIEC61360/dataType`` is one of: INTEGER_MEASURE, REAL_MEASURE, RATIONAL_MEASURE, INTEGER_CURRENCY, REAL_CURRENCY, then ``DataSpecificationIEC61360/unit`` or ``DataSpecificationIEC61360/unitId`` shall be defined.
+.. |aasc3a010| replace:: If ``DataSpecificationIEC61360/value`` is not empty then ``DataSpecificationIEC61360/valueList`` shall be empty and vice versa.
.. csv-table::
@@ -76,7 +74,6 @@ an :class:`~basyx.aas.model.base.AASConstraintViolation` will be raised
AASd-077, |aasd077|, ✅,
AASd-080, |aasd080|, ✅,
AASd-081, |aasd081|, ✅,
- AASd-090, |aasd090|, ✅,
AASd-107, |aasd107|, ✅,
AASd-108, |aasd108|, ✅,
AASd-109, |aasd109|, ✅,
@@ -86,7 +83,6 @@ an :class:`~basyx.aas.model.base.AASConstraintViolation` will be raised
AASd-117, |aasd117|, ✅,
AASd-118, |aasd118|, ✅,
AASd-119, |aasd119|, ❌, See `#119 `__
- AASd-120, |aasd120|, ✅,
AASd-121, |aasd121|, ✅,
AASd-122, |aasd122|, ✅,
AASd-123, |aasd123|, ✅,
@@ -106,5 +102,5 @@ an :class:`~basyx.aas.model.base.AASConstraintViolation` will be raised
AASc-006, |aasc006|, tbd
AASc-007, |aasc007|, tbd
AASc-008, |aasc008|, tbd
- AASc-009, |aasc009|, tbd
- AASc-010, |aasc010|, tbd
+ AASc-3a-009, |aasc3a009|, tbd
+ AASc-3a-010, |aasc3a010|, tbd
diff --git a/sdk/docs/source/model/provider.rst b/sdk/docs/source/model/provider.rst
index a9d990154..e245f5523 100644
--- a/sdk/docs/source/model/provider.rst
+++ b/sdk/docs/source/model/provider.rst
@@ -3,4 +3,6 @@ provider - Providers for storing and retrieving AAS-objects
.. automodule:: basyx.aas.model.provider
-.. autoclass:: _IT
+.. autoclass:: _KEY
+.. autoclass:: _VALUE
+.. autoclass:: _IDENTIFIABLE
diff --git a/sdk/docs/source/tutorials/index.rst b/sdk/docs/source/tutorials/index.rst
index ce2fe76ad..24f0282ab 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_backend_couchdb
+ tutorial_serialization_deserialization
tutorial_aasx
+ tutorial_backend_couchdb
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
diff --git a/sdk/test/adapter/aasx/TestFile.pdf b/sdk/test/adapter/aasx/TestFile.pdf
index 2bccbec5f..4c97666ae 100644
Binary files a/sdk/test/adapter/aasx/TestFile.pdf and b/sdk/test/adapter/aasx/TestFile.pdf differ
diff --git a/sdk/test/adapter/aasx/test.png b/sdk/test/adapter/aasx/test.png
new file mode 100644
index 000000000..58634849b
Binary files /dev/null and b/sdk/test/adapter/aasx/test.png differ
diff --git a/sdk/test/adapter/aasx/test_aasx.py b/sdk/test/adapter/aasx/test_aasx.py
index a83c60186..e4ab1a1dd 100644
--- a/sdk/test/adapter/aasx/test_aasx.py
+++ b/sdk/test/adapter/aasx/test_aasx.py
@@ -1,4 +1,4 @@
-# Copyright (c) 2025 the Eclipse BaSyx Authors
+# Copyright (c) 2026 the Eclipse BaSyx Authors
#
# This program and the accompanying materials are made available under the terms of the MIT License, available in
# the LICENSE file of this project.
@@ -11,6 +11,7 @@
import tempfile
import unittest
import warnings
+from pathlib import Path
import pyecma376_2
from basyx.aas import model
@@ -22,30 +23,54 @@ class TestAASXUtils(unittest.TestCase):
def test_supplementary_file_container(self) -> None:
container = aasx.DictSupplementaryFileContainer()
with open(os.path.join(os.path.dirname(__file__), 'TestFile.pdf'), 'rb') as f:
- new_name = container.add_file("/TestFile.pdf", f, "application/pdf")
+ saved_file_name = container.add_file("/TestFile.pdf", f, "application/pdf")
# Name should not be modified, since there is no conflict
- self.assertEqual("/TestFile.pdf", new_name)
+ self.assertEqual("/TestFile.pdf", saved_file_name)
f.seek(0)
- container.add_file("/TestFile.pdf", f, "application/pdf")
+ # Add the same file again with the same name
+ same_file_with_same_name = container.add_file("/TestFile.pdf", f, "application/pdf")
# Name should not be modified, since there is still no conflict
- self.assertEqual("/TestFile.pdf", new_name)
+ self.assertEqual("/TestFile.pdf", same_file_with_same_name)
+ # Add other file with the same name to create a conflict
with open(__file__, 'rb') as f:
- new_name = container.add_file("/TestFile.pdf", f, "application/pdf")
+ saved_file_name_2 = container.add_file("/TestFile.pdf", f, "application/pdf")
# Now, we have a conflict
- self.assertNotEqual("/TestFile.pdf", new_name)
- self.assertIn(new_name, container)
+ self.assertNotEqual(saved_file_name, saved_file_name_2)
+ self.assertIn(saved_file_name_2, container)
+
+ # Rename file to a new unique name
+ renamed = container.rename_file(saved_file_name_2, "/RenamedTestFile.pdf")
+ self.assertIn(renamed, container)
+ # Old name should no longer exist
+ self.assertNotIn(saved_file_name_2, container)
+ self.assertEqual(renamed, "/RenamedTestFile.pdf")
+
+ # Renaming to the same name should be no-op
+ renamed_same = container.rename_file(renamed, renamed)
+ self.assertEqual(renamed, renamed_same)
+
+ # Renaming to an existing name should create a conflict
+ renamed_conflict = container.rename_file(renamed, "/TestFile.pdf")
+ self.assertNotEqual(renamed_conflict, "/TestFile.pdf")
+ self.assertIn(renamed_conflict, container)
+
+ # Renaming a non-existing file should raise KeyError
+ with self.assertRaises(KeyError):
+ container.rename_file("/NonExistingFile.pdf", "/AnotherName.pdf")
+
+ new_name = renamed_conflict
# Check metadata
self.assertEqual("application/pdf", container.get_content_type("/TestFile.pdf"))
- self.assertEqual("b18229b24a4ee92c6c2b6bc6a8018563b17472f1150d35d5a5945afeb447ed44",
+ self.assertEqual("142a0061de1ef5c22137ab05bb6001335596c0fc8693d33fa9b011ceac652342",
container.get_sha256("/TestFile.pdf").hex())
self.assertIn("/TestFile.pdf", container)
# Check contents
file_content = io.BytesIO()
container.write_file("/TestFile.pdf", file_content)
- self.assertEqual(hashlib.sha1(file_content.getvalue()).hexdigest(), "78450a66f59d74c073bf6858db340090ea72a8b1")
+ self.assertEqual(hashlib.sha1(file_content.getvalue()).hexdigest(), "241e62aef8b4cdad0975f6c68a4ed8b3923d8db1")
# Add same file again with different content_type to test reference counting
with open(__file__, 'rb') as f:
@@ -64,26 +89,287 @@ def test_supplementary_file_container(self) -> None:
with self.assertRaises(KeyError):
container.write_file(duplicate_file, file_content)
+ def test_supplementary_file_container_refcount(self) -> None:
+ container = aasx.DictSupplementaryFileContainer()
+ data = b"test content"
+ name1 = container.add_file("/file1.bin", io.BytesIO(data), "application/octet-stream")
+ name2 = container.add_file("/file2.bin", io.BytesIO(data), "application/octet-stream")
+ content_hash = container.get_sha256(name1)
+
+ # Both names point to same content — backing store must be present
+ self.assertIn(content_hash, container._store)
+
+ # Deleting one reference must NOT free the backing store
+ container.delete_file(name1)
+ self.assertIn(content_hash, container._store)
+
+ # Deleting the last reference must free the backing store
+ container.delete_file(name2)
+ self.assertNotIn(content_hash, container._store)
+
class AASXWriterTest(unittest.TestCase):
+ def test_write_missing_aas_objects(self):
+ with tempfile.TemporaryDirectory() as tmpdir:
+ tmpdir_path = Path(tmpdir)
+
+ # ---- Arange ----
+ data = example_aas.create_full_example()
+
+ # ---- Act & Assert -----
+ with self.assertLogs(level="WARNING") as log:
+ with aasx.AASXWriter(tmpdir_path / "tmp.aasx", failsafe=True) as writer:
+ # try to write non-existing object
+ writer.write_aas_objects(
+ "/aasx/selection.xml",
+ ["https://example.org/Test_AssetAdministrationShell",
+ "http://false-identifier.org/",
+ "http://example.org/Submodels/Assets/TestAsset/Identification"],
+ data, aasx.DictSupplementaryFileContainer()
+ )
+
+ self.assertIn("Could not find identifiable http://false-identifier.org/ in IdentifiableStore",
+ log.output[0])
+
+ # assert only the two existing objects have been written to aasx file
+ object_store = model.DictIdentifiableStore()
+ with aasx.AASXReader(tmpdir_path / "tmp.aasx") as reader:
+ reader.read_into(object_store, aasx.DictSupplementaryFileContainer())
+ self.assertEqual(len(object_store), 2)
+
+ def test_writing_with_missing_file(self) -> None:
+ with tempfile.TemporaryDirectory() as tmpdir:
+ tmpdir_path = Path(tmpdir)
+
+ # ---- Arange ----
+ # data contains a submodel with a File submodel_element
+ # the empty_file_store does not contain the referenced file
+ data = example_aas.create_full_example()
+ empty_file_store = aasx.DictSupplementaryFileContainer()
+
+ # ---- Act & Assert ----
+ # assert warning is present in failsafe mode
+ with self.assertLogs(level="WARNING") as log:
+ with aasx.AASXWriter(tmpdir_path / "tmp.aasx", failsafe=True) as writer:
+ writer.write_all_aas_objects("/aasx/data.xml", data, empty_file_store)
+ self.assertIn("Could not find file", log.output[0])
+
+ # assert exception is rose in non-failsafe mode
+ with self.assertRaises(KeyError) as cm:
+ with aasx.AASXWriter(tmpdir_path / "tmp.aasx", failsafe=False) as writer:
+ writer.write_all_aas_objects("/aasx/data.xml", data, empty_file_store)
+ self.assertIn("Could not find file", cm.exception.args[0])
+
+ def test_writing_file_twice(self) -> None:
+ with (tempfile.TemporaryDirectory() as tmpdir):
+ tmpdir_path = Path(tmpdir)
+
+ # ---- Arange ----
+ file_store = aasx.DictSupplementaryFileContainer()
+ with open(Path(__file__).parent / "TestFile.pdf", "rb") as pdf:
+ resulting_file_name = file_store.add_file("/TestFile.pdf", pdf, "application/pdf")
+
+ # create two submodels that reference the same file in file_store
+ first_submodel = model.Submodel(
+ id_="http://example.org/First_Submodel",
+ submodel_element=[model.File(
+ id_short="ExampleFile",
+ content_type="application/pdf",
+ value=resulting_file_name
+ )]
+ )
+ second_submodel = model.Submodel(
+ id_="http://example.org/SecondSubmodel",
+ submodel_element=[model.File(
+ id_short="ExampleFile",
+ content_type="application/pdf",
+ value=resulting_file_name
+ )]
+ )
+ data: model.DictIdentifiableStore[model.Identifiable] \
+ = model.DictIdentifiableStore([first_submodel, second_submodel])
+
+ # ---- Act & Assert ----
+ with self.assertNoLogs(level="WARNING"):
+ with aasx.AASXWriter(tmpdir_path / "tmp.aasx") as writer:
+ writer.write_all_aas_objects("/aasx/data.xml", data, file_store)
+
+ def test_write_non_aas(self) -> None:
+ with tempfile.TemporaryDirectory() as tmpdir:
+ tmpdir_path = Path(tmpdir)
+
+ # ---- Arange ----
+ data = example_aas.create_full_example()
+ file_store = aasx.DictSupplementaryFileContainer()
+ with open(Path(__file__).parent / "TestFile.pdf", "rb") as pdf:
+ file_store.add_file("/TestFile.pdf", pdf, "application/pdf")
+
+ # ---- Act & Assert ----
+ # assert warning is present in failsafe mode
+ with self.assertLogs(level="WARNING") as log:
+ with aasx.AASXWriter(tmpdir_path / "tmp.aasx", failsafe=True) as writer:
+ # try to write a non AAS object
+ writer.write_aas("https://example.org/Test_Submodel", data, file_store)
+ self.assertIn("Skipping AAS https://example.org/Test_Submodel", log.output[0])
+
+ # assert exception is rose in non-failsafe mode
+ with self.assertRaises(TypeError) as cm:
+ with aasx.AASXWriter(tmpdir_path / "tmp.aasx", failsafe=False) as writer:
+ # try to write a non AAS object
+ writer.write_aas("https://example.org/Test_Submodel", data, file_store)
+ self.assertIn("Identifier https://example.org/Test_Submodel does not belong "
+ "to an AssetAdministrationShell", cm.exception.args[0])
+
+ def test_write_aas_missing_submodel(self) -> None:
+ with tempfile.TemporaryDirectory() as tmpdir:
+ tmpdir_path = Path(tmpdir)
+
+ # ---- Arange ----
+ # leave example_submodel out of object store
+ data = model.DictIdentifiableStore([
+ example_aas.create_example_asset_administration_shell(),
+ example_aas.create_example_asset_identification_submodel(),
+ example_aas.create_example_bill_of_material_submodel()
+ ])
+ empty_file_store = aasx.DictSupplementaryFileContainer()
+
+ # ---- Act & Assert ----
+ # assert warning is present in failsafe mode
+ with self.assertLogs(level="WARNING") as log:
+ with aasx.AASXWriter(tmpdir_path / "tmp.aasx", failsafe=True) as writer:
+ writer.write_aas("https://example.org/Test_AssetAdministrationShell", data, empty_file_store)
+ self.assertIn("Could not find Submodel", log.output[0])
+
+ # assert exception is rose in non-failsafe mode
+ with self.assertRaises(KeyError) as cm:
+ with aasx.AASXWriter(tmpdir_path / "tmp.aasx", failsafe=False) as writer:
+ writer.write_aas("https://example.org/Test_AssetAdministrationShell", data, empty_file_store)
+ self.assertIn("Could not find Submodel", cm.exception.args[0])
+
+ def test_write_aas_missing_concept_description(self) -> None:
+ with tempfile.TemporaryDirectory() as tmpdir:
+ tmpdir_path = Path(tmpdir)
+
+ # ---- Arange ----
+ # leave example_concept_description out of object store
+ data = model.DictIdentifiableStore([
+ example_aas.create_example_asset_administration_shell(),
+ example_aas.create_example_submodel(),
+ example_aas.create_example_asset_identification_submodel(),
+ example_aas.create_example_bill_of_material_submodel()
+ ])
+ file_store = aasx.DictSupplementaryFileContainer()
+ with open(Path(__file__).parent / "TestFile.pdf", "rb") as pdf:
+ file_store.add_file("/TestFile.pdf", pdf, "application/pdf")
+
+ # ---- Act & Assert ----
+ # assert warning is present in failsafe mode
+ with self.assertLogs(level="WARNING") as log:
+ with aasx.AASXWriter(tmpdir_path / "tmp.aasx", failsafe=True) as writer:
+ writer.write_aas("https://example.org/Test_AssetAdministrationShell", data, file_store)
+ self.assertIn("https://example.org/Test_ConceptDescription", log.output[0])
+ self.assertRegex(log.output[0], "ConceptDescription .* not found")
+
+ # assert exception is rose in non-failsafe mode
+ with self.assertRaises(KeyError) as cm:
+ with aasx.AASXWriter(tmpdir_path / "tmp.aasx", failsafe=False) as writer:
+ writer.write_aas("https://example.org/Test_AssetAdministrationShell", data, file_store)
+ self.assertIn("https://example.org/Test_ConceptDescription", cm.exception.args[0])
+ self.assertRegex(cm.exception.args[0], "ConceptDescription .* not found")
+
+ def test_write_aas_false_semantic_id(self) -> None:
+ with tempfile.TemporaryDirectory() as tmpdir:
+ tmpdir_path = Path(tmpdir)
+
+ # ---- Arange ----
+ # semanticId of submodel holds reference to an object
+ # that is no ContentDescription
+ second_submodel = model.Submodel(
+ id_="https://example.org/Second_Submodel"
+ )
+ submodel = model.Submodel(
+ id_="https://example.org/Test_Submodel",
+ semantic_id=model.ModelReference(
+ key=(model.Key(type_=model.KeyTypes.SUBMODEL, value="https://example.org/Second_Submodel"),),
+ type_=model.ConceptDescription
+ )
+ )
+ data = model.DictIdentifiableStore([
+ example_aas.create_example_asset_administration_shell(),
+ example_aas.create_example_asset_identification_submodel(),
+ example_aas.create_example_bill_of_material_submodel(),
+ submodel, second_submodel
+ ])
+ empty_file_store = aasx.DictSupplementaryFileContainer()
+
+ # ---- Act & Assert ----
+ # assert warning is present in failsafe mode
+ with self.assertLogs(level="WARNING") as log:
+ with aasx.AASXWriter(tmpdir_path / "tmp.aasx", failsafe=True) as writer:
+ writer.write_aas("https://example.org/Test_AssetAdministrationShell", data, empty_file_store)
+ self.assertIn("which is not a ConceptDescription", log.output[0])
+
+ # assert exception is rose in non-failsafe mode
+ with self.assertRaises(TypeError) as cm:
+ with aasx.AASXWriter(tmpdir_path / "tmp.aasx", failsafe=False) as writer:
+ writer.write_aas("https://example.org/Test_AssetAdministrationShell", data, empty_file_store)
+ self.assertIn("which is not a ConceptDescription", cm.exception.args[0])
+
+ def test_write_core_properties_twice(self) -> None:
+ with tempfile.TemporaryDirectory() as tmpdir:
+ tmpdir_path = Path(tmpdir)
+
+ # ---- Arrange ----
+ cp = pyecma376_2.OPCCoreProperties()
+ cp.created = datetime.datetime.now()
+ cp.creator = "Eclipse BaSyx Python Testing Framework"
+
+ # ---- Act & Assert ----
+ with aasx.AASXWriter(tmpdir_path / "tmp.aasx") as writer:
+ writer.write_core_properties(cp)
+
+ # expect RuntimeError on second write
+ with self.assertRaises(RuntimeError) as cm:
+ writer.write_core_properties(cp)
+
+ self.assertIn("Core Properties have already been written", cm.exception.args[0])
+
+ def test_write_thumbnail_twice(self) -> None:
+ with tempfile.TemporaryDirectory() as tmpdir:
+ tmpdir_path = Path(tmpdir)
+
+ # ---- Arrange ----
+ with open(Path(__file__).parent / "test.png", "rb") as png:
+ thumbnail = png.read()
+
+ # ---- Act & Assert ----
+ with aasx.AASXWriter(tmpdir_path / "tmp.aasx") as writer:
+ writer.write_thumbnail("/aasx/thumbnail.png", bytearray(thumbnail), "image/png")
+
+ # expect RuntimeError on second write
+ with self.assertRaises(RuntimeError) as cm:
+ writer.write_thumbnail("/aasx/thumbnail.png", bytearray(thumbnail), "image/png")
+
+ self.assertIn("package thumbnail has already been written", cm.exception.args[0])
+
def test_writing_reading_example_aas(self) -> None:
# Create example data and file_store
- data = example_aas.create_full_example()
- files = aasx.DictSupplementaryFileContainer()
+ data = example_aas.create_full_example() # creates a complete, valid example AAS
+ files = aasx.DictSupplementaryFileContainer() # in-memory store for attached files
with open(os.path.join(os.path.dirname(__file__), 'TestFile.pdf'), 'rb') as f:
- files.add_file("/TestFile.pdf", f, "application/pdf")
+ files.add_file("/TestFile.pdf", f, "application/pdf") # add a real supplementary pdf file
f.seek(0)
-
# Create OPC/AASX core properties
+ # create AASX metadata (core properties)
cp = pyecma376_2.OPCCoreProperties()
cp.created = datetime.datetime.now()
cp.creator = "Eclipse BaSyx Python Testing Framework"
# Write AASX file
- for write_json in (False, True):
+ for write_json in (False, True): # Loop over both XML and JSON modes
with self.subTest(write_json=write_json):
- fd, filename = tempfile.mkstemp(suffix=".aasx")
- os.close(fd)
+ fd, filename = tempfile.mkstemp(suffix=".aasx") # create temporary file
+ os.close(fd) # close file descriptor
# Write AASX file
# the zipfile library reports errors as UserWarnings via the warnings library. Let's check for
@@ -91,7 +377,7 @@ def test_writing_reading_example_aas(self) -> None:
with warnings.catch_warnings(record=True) as w:
with aasx.AASXWriter(filename) as writer:
# TODO test writing multiple AAS
- writer.write_aas('https://acplt.org/Test_AssetAdministrationShell',
+ writer.write_aas('https://example.org/Test_AssetAdministrationShell',
data, files, write_json=write_json)
writer.write_core_properties(cp)
@@ -100,7 +386,7 @@ def test_writing_reading_example_aas(self) -> None:
f"{[warning.message for warning in w]}")
# Read AASX file
- new_data: model.DictObjectStore[model.Identifiable] = model.DictObjectStore()
+ new_data: model.DictIdentifiableStore[model.Identifiable] = model.DictIdentifiableStore()
new_files = aasx.DictSupplementaryFileContainer()
with aasx.AASXReader(filename) as reader:
reader.read_into(new_data, new_files)
@@ -123,6 +409,233 @@ def test_writing_reading_example_aas(self) -> None:
file_content = io.BytesIO()
new_files.write_file("/TestFile.pdf", file_content)
self.assertEqual(hashlib.sha1(file_content.getvalue()).hexdigest(),
- "78450a66f59d74c073bf6858db340090ea72a8b1")
+ "241e62aef8b4cdad0975f6c68a4ed8b3923d8db1")
+
+ os.unlink(filename)
+
+
+class AASXReaderTest(unittest.TestCase):
+ def _create_test_aasx(self) -> str:
+ data = example_aas.create_full_example()
+ files = aasx.DictSupplementaryFileContainer()
+
+ with open(os.path.join(os.path.dirname(__file__), 'TestFile.pdf'), 'rb') as f:
+ files.add_file("/TestFile.pdf", f, "application/pdf")
+ f.seek(0)
+
+ # Core properties
+ cp = pyecma376_2.OPCCoreProperties()
+ cp.created = datetime.datetime.now()
+ cp.creator = "Eclipse BaSyx Python Testing Framework"
+
+ fd, filename = tempfile.mkstemp(suffix=".aasx")
+ os.close(fd)
+
+ with aasx.AASXWriter(filename) as writer:
+ writer.write_aas(
+ 'https://example.org/Test_AssetAdministrationShell',
+ data, files, write_json=False
+ )
+ writer.write_core_properties(cp)
+
+ return filename
+
+ def test_init_file_handling(self) -> None:
+ # Missing file assertion test
+ with self.assertRaises(FileNotFoundError):
+ aasx.AASXReader("does_not_exist.aasx")
+
+ # Invalid file assertion test
+ fd, invalid_path = tempfile.mkstemp()
+ os.write(fd, b"not a file")
+ os.close(fd)
+
+ try:
+ with self.assertRaises(ValueError):
+ aasx.AASXReader(invalid_path)
+ finally:
+ os.unlink(invalid_path)
+
+ def test_reading_core_properties(self) -> None:
+ filename = self._create_test_aasx()
+
+ try:
+ with aasx.AASXReader(filename) as reader:
+ cp = reader.get_core_properties()
+
+ self.assertIsInstance(cp.created, datetime.datetime)
+ self.assertEqual(cp.creator, "Eclipse BaSyx Python Testing Framework")
+ self.assertIsNone(cp.lastModifiedBy)
+ finally:
+ os.unlink(filename)
+
+ def test_get_thumbnail(self) -> None:
+ with tempfile.TemporaryDirectory() as tmpdir:
+ # ---- Arange ----
+ tmpdir_path = Path(tmpdir)
+
+ data: model.DictIdentifiableStore[model.Identifiable] = model.DictIdentifiableStore([
+ model.AssetAdministrationShell(
+ id_="http://example.org/Test_AAS",
+ asset_information=model.AssetInformation(
+ global_asset_id="http://example.org/Test_Asset"
+ )
+ )
+ ])
+
+ with aasx.AASXWriter(tmpdir_path / "test_thumbnail.aasx") as writer:
+ writer.write_aas(
+ 'http://example.org/Test_AAS',
+ data, aasx.DictSupplementaryFileContainer(), write_json=False
+ )
+ with open(Path(__file__).parent / "test.png", "rb") as png:
+ thumbnail = png.read()
+ writer.write_thumbnail("/aasx/thumbnail.png", bytearray(thumbnail), "image/png")
+
+ # ---- Act ----
+ with aasx.AASXReader(tmpdir_path / "test_thumbnail.aasx") as reader:
+ new_thumbnail = reader.get_thumbnail()
+
+ # ---- Assert ----
+ self.assertEqual(new_thumbnail, thumbnail)
+
+ def test_missing_thumbnail(self) -> None:
+ # ---- Arange ----
+ filename = self._create_test_aasx()
+
+ try:
+ # ---- Act ----
+ with aasx.AASXReader(filename) as reader:
+ thumbnail = reader.get_thumbnail()
+
+ # ---- Assert ----
+ self.assertIsNone(thumbnail)
+ finally:
+ os.unlink(filename)
+
+ def test_read_into(self) -> None:
+ filename = self._create_test_aasx()
+
+ try:
+ objects: model.DictIdentifiableStore[model.Identifiable] = model.DictIdentifiableStore()
+ files = aasx.DictSupplementaryFileContainer()
+
+ with warnings.catch_warnings(record=True) as w:
+ with aasx.AASXReader(filename) as reader:
+ ids = reader.read_into(objects, files)
+
+ assert isinstance(w, list)
+ self.assertEqual(0, len(w)) # Ensure no warnings were raised
+
+ self.assertGreater(len(ids), 0) # Ensure at least one AAS was read
+ self.assertGreater(len(objects), 0) # Ensure objects were populated
+ self.assertGreater(len(files), 0)
+ self.assertEqual(
+ files.get_content_type("/TestFile.pdf"),
+ "application/pdf"
+ )
+ finally:
+ os.unlink(filename)
+
+ def test_supplementary_file_integrity(self) -> None:
+ filename = self._create_test_aasx()
+
+ try:
+ objects: model.DictIdentifiableStore[model.Identifiable] = model.DictIdentifiableStore()
+ files = aasx.DictSupplementaryFileContainer()
+
+ with aasx.AASXReader(filename) as reader:
+ reader.read_into(objects, files)
+
+ buf = io.BytesIO()
+ files.write_file("/TestFile.pdf", buf)
+
+ self.assertEqual(
+ hashlib.sha1(buf.getvalue()).hexdigest(),
+ "241e62aef8b4cdad0975f6c68a4ed8b3923d8db1"
+ )
+ finally:
+ os.unlink(filename)
+
+
+class AASXWriterReferencedSubmodelsTest(unittest.TestCase):
+
+ def test_only_referenced_submodels(self):
+ """
+ Test that verifies that all Submodels (referenced and unreferenced) are written to the AASX package when using
+ the convenience function write_all_aas_objects().
+ When calling the higher-level function write_aas(), however, only
+ referenced Submodels in the IdentifiableStore should be included.
+ """
+ # Create referenced and unreferenced Submodels
+ referenced_submodel = model.Submodel(id_="ref_submodel")
+ unreferenced_submodel = model.Submodel(id_="unref_submodel")
+
+ aas = model.AssetAdministrationShell(
+ id_="Test_AAS",
+ asset_information=model.AssetInformation(
+ asset_kind=model.AssetKind.INSTANCE,
+ global_asset_id="http://example.org/Test_Asset"
+ ),
+ submodel={model.ModelReference.from_referable(referenced_submodel)}
+ )
+
+ # IdentifiableStore containing all objects
+ identifiable_store = model.DictIdentifiableStore([aas, referenced_submodel, unreferenced_submodel])
+
+ # Empty SupplementaryFileContainer (no files needed)
+ file_store = aasx.DictSupplementaryFileContainer()
+
+ # --- Step 1: Check write_aas() behavior ---
+ for write_json in (False, True):
+ with self.subTest(method="write_aas", write_json=write_json):
+ fd, filename = tempfile.mkstemp(suffix=".aasx")
+ os.close(fd)
+
+ with warnings.catch_warnings(record=True) as w:
+ with aasx.AASXWriter(filename) as writer:
+ # write_aas only takes the AAS id and IdentifiableStore
+ writer.write_aas(
+ aas_ids=[aas.id],
+ object_store=identifiable_store,
+ file_store=file_store,
+ write_json=write_json
+ )
+
+ # Read back
+ new_data: model.DictIdentifiableStore[model.Identifiable] = model.DictIdentifiableStore()
+ new_files = aasx.DictSupplementaryFileContainer()
+ with aasx.AASXReader(filename) as reader:
+ reader.read_into(new_data, new_files)
+
+ # Assertions
+ self.assertIn(referenced_submodel.id, new_data) # referenced Submodel is included
+ self.assertNotIn(unreferenced_submodel.id, new_data) # unreferenced Submodel is excluded
+
+ os.unlink(filename)
+
+ # --- Step 2: Check write_all_aas_objects ---
+ for write_json in (False, True):
+ with self.subTest(method="write_all_aas_objects", write_json=write_json):
+ fd, filename = tempfile.mkstemp(suffix=".aasx")
+ os.close(fd)
+
+ with warnings.catch_warnings(record=True) as w:
+ with aasx.AASXWriter(filename) as writer:
+ writer.write_all_aas_objects(
+ part_name="/aasx/my_aas_part.xml",
+ objects=identifiable_store,
+ file_store=file_store,
+ write_json=write_json
+ )
+
+ # Read back
+ new_data: model.DictIdentifiableStore[model.Identifiable] = model.DictIdentifiableStore()
+ new_files = aasx.DictSupplementaryFileContainer()
+ with aasx.AASXReader(filename) as reader:
+ reader.read_into(new_data, new_files)
+ # Assertions
+ self.assertIn(referenced_submodel.id, new_data)
+ self.assertIn(unreferenced_submodel.id, new_data) # all objects are written
os.unlink(filename)
diff --git a/sdk/test/adapter/http-api-oas-aas.yaml b/sdk/test/adapter/http-api-oas-aas.yaml
deleted file mode 100644
index 27d029373..000000000
--- a/sdk/test/adapter/http-api-oas-aas.yaml
+++ /dev/null
@@ -1,1421 +0,0 @@
-openapi: 3.0.0
-info:
- version: "1"
- title: PyI40AAS REST API
- description: "REST API Specification for the [PyI40AAS framework](https://git.rwth-aachen.de/acplt/pyi40aas).
-
-
- **AAS Interface**
-
-
- Any identifier/identification objects are encoded as follows `{identifierType}:URIencode(URIencode({identifier}))`, e.g. `IRI:http:%252F%252Facplt.org%252Fasset`."
- contact:
- name: "Michael Thies, Torben Miny, Leon Möller"
- license:
- name: Use under Eclipse Public License 2.0
- url: "https://www.eclipse.org/legal/epl-2.0/"
-servers:
- - url: http://{authority}/{basePath}/{api-version}
- description: This is the Server to access the Asset Administration Shell
- variables:
- authority:
- default: localhost:8080
- description: The authority is the server url (made of IP-Address or DNS-Name, user information, and/or port information) of the hosting environment for the Asset Administration Shell
- basePath:
- default: api
- description: The basePath variable is additional path information for the hosting environment. It may contain the name of an aggregation point like 'shells' and/or API version information and/or tenant-id information, etc.
- api-version:
- default: v1
- description: The Version of the API-Specification
-paths:
- "/":
- get:
- summary: Retrieves the stripped AssetAdministrationShell, without Submodel-References and Views.
- operationId: ReadAAS
- responses:
- "200":
- description: Success
- content:
- "application/json":
- schema:
- $ref: "#/components/schemas/AssetAdministrationShellResult"
- "404":
- description: AssetAdministrationShell not found
- content:
- "application/json":
- schema:
- $ref: "#/components/schemas/BaseResult"
- tags:
- - Asset Administration Shell Interface
- "/submodels/":
- get:
- summary: Returns all Submodel-References of the AssetAdministrationShell
- operationId: ReadAASSubmodelReferences
- responses:
- "200":
- description: Success
- content:
- "application/json":
- schema:
- $ref: "#/components/schemas/ReferenceListResult"
- "404":
- description: AssetAdministrationShell not found
- content:
- "application/json":
- schema:
- $ref: "#/components/schemas/BaseResult"
- tags:
- - Asset Administration Shell Interface
- post:
- summary: Adds a new Submodel-Reference to the AssetAdministrationShell
- operationId: CreateAASSubmodelReference
- requestBody:
- description: The Submodel-Reference to create
- required: true
- content:
- "application/json":
- schema:
- $ref: "#/components/schemas/Reference"
- responses:
- "201":
- description: Success
- content:
- "application/json":
- schema:
- $ref: "#/components/schemas/ReferenceResult"
- headers:
- Location:
- description: The URL of the created Submodel-Reference
- schema:
- type: string
- "404":
- description: AssetAdministrationShell not found
- content:
- "application/json":
- schema:
- $ref: "#/components/schemas/BaseResult"
- "409":
- description: Submodel-Reference already exists
- content:
- "application/json":
- schema:
- $ref: "#/components/schemas/BaseResult"
- "422":
- description: Request body is not a Reference or not resolvable
- content:
- "application/json":
- schema:
- $ref: "#/components/schemas/BaseResult"
- tags:
- - Asset Administration Shell Interface
- "/submodels/{submodel-identifier}/":
- parameters:
- - name: submodel-identifier
- in: path
- description: The Identifier of the referenced Submodel
- required: true
- schema:
- type: string
- get:
- summary: Returns the Reference specified by submodel-identifier
- operationId: ReadAASSubmodelReference
- responses:
- "200":
- description: Success
- content:
- "application/json":
- schema:
- $ref: "#/components/schemas/ReferenceResult"
- "400":
- description: Invalid submodel-identifier format
- content:
- "application/json":
- schema:
- $ref: "#/components/schemas/BaseResult"
- "404":
- description: AssetAdministrationShell not found or the specified Submodel is not referenced
- content:
- "application/json":
- schema:
- $ref: "#/components/schemas/BaseResult"
- tags:
- - Asset Administration Shell Interface
- delete:
- summary: Deletes the Reference specified by submodel-identifier
- operationId: DeleteAASSubmodelReference
- responses:
- "200":
- description: Success
- content:
- "application/json":
- schema:
- $ref: "#/components/schemas/BaseResult"
- "400":
- description: Invalid submodel-identifier format
- content:
- "application/json":
- schema:
- $ref: "#/components/schemas/BaseResult"
- "404":
- description: AssetAdministrationShell not found or the specified Submodel is not referenced
- content:
- "application/json":
- schema:
- $ref: "#/components/schemas/BaseResult"
- tags:
- - Asset Administration Shell Interface
- "/views/":
- get:
- summary: Returns all Views of the AssetAdministrationShell
- operationId: ReadAASViews
- responses:
- "200":
- description: Success
- content:
- "application/json":
- schema:
- $ref: "#/components/schemas/ViewListResult"
- "404":
- description: AssetAdministrationShell not found
- content:
- "application/json":
- schema:
- $ref: "#/components/schemas/BaseResult"
- tags:
- - Asset Administration Shell Interface
- post:
- summary: Adds a new View to the AssetAdministrationShell
- operationId: CreateAASView
- requestBody:
- description: The View to create
- required: true
- content:
- "application/json":
- schema:
- $ref: "#/components/schemas/View"
- responses:
- "201":
- description: Success
- content:
- "application/json":
- schema:
- $ref: "#/components/schemas/ViewResult"
- headers:
- Location:
- description: The URL of the created View
- schema:
- type: string
- links:
- ReadAASViewByIdShort:
- $ref: "#/components/links/UpdateAASViewByIdShort"
- UpdateAASViewByIdShort:
- $ref: "#/components/links/UpdateAASViewByIdShort"
- DeleteAASViewByIdShort:
- $ref: "#/components/links/DeleteAASViewByIdShort"
- "404":
- description: AssetAdministrationShell not found
- content:
- "application/json":
- schema:
- $ref: "#/components/schemas/BaseResult"
- "409":
- description: View with same idShort already exists
- content:
- "application/json":
- schema:
- $ref: "#/components/schemas/BaseResult"
- "422":
- description: Request body is not a valid View
- content:
- "application/json":
- schema:
- $ref: "#/components/schemas/BaseResult"
- tags:
- - Asset Administration Shell Interface
- "/views/{view-idShort}/":
- parameters:
- - name: view-idShort
- in: path
- description: The idShort of the View
- required: true
- schema:
- type: string
- get:
- summary: Returns a specific View of the AssetAdministrationShell
- operationId: ReadAASView
- responses:
- "200":
- description: Success
- content:
- "application/json":
- schema:
- $ref: "#/components/schemas/ViewResult"
- links:
- ReadAASViewByIdShort:
- $ref: "#/components/links/ReadAASViewByIdShort"
- UpdateAASViewByIdShort:
- $ref: "#/components/links/UpdateAASViewByIdShort"
- DeleteAASViewByIdShort:
- $ref: "#/components/links/DeleteAASViewByIdShort"
- "404":
- description: AssetAdministrationShell or View not found
- content:
- "application/json":
- schema:
- $ref: "#/components/schemas/BaseResult"
- tags:
- - Asset Administration Shell Interface
- put:
- summary: Updates a specific View of the AssetAdministrationShell
- operationId: UpdateAASView
- requestBody:
- description: The View used to overwrite the existing View
- required: true
- content:
- "application/json":
- schema:
- $ref: "#/components/schemas/View"
- responses:
- "200":
- description: Success
- content:
- "application/json":
- schema:
- $ref: "#/components/schemas/ViewResult"
- links:
- ReadAASViewByIdShort:
- $ref: "#/components/links/ReadAASViewByIdShort"
- UpdateAASViewByIdShort:
- $ref: "#/components/links/UpdateAASViewByIdShort"
- DeleteAASViewByIdShort:
- $ref: "#/components/links/DeleteAASViewByIdShort"
- "201":
- description: Success (idShort changed)
- content:
- "application/json":
- schema:
- $ref: "#/components/schemas/ViewResult"
- headers:
- Location:
- description: The new URL of the View
- schema:
- type: string
- links:
- ReadAASViewByIdShort:
- $ref: "#/components/links/ReadAASViewByIdShort"
- DeleteAASViewByIdShort:
- $ref: "#/components/links/DeleteAASViewByIdShort"
- "404":
- description: AssetAdministrationShell or View not found
- content:
- "application/json":
- schema:
- $ref: "#/components/schemas/BaseResult"
- "409":
- description: idShort changed and new idShort already exists
- content:
- "application/json":
- schema:
- $ref: "#/components/schemas/BaseResult"
- "422":
- description: Request body is not a valid View
- content:
- "application/json":
- schema:
- $ref: "#/components/schemas/BaseResult"
- tags:
- - Asset Administration Shell Interface
- delete:
- summary: Deletes a specific View from the Asset Administration Shell
- operationId: DeleteAASView
- responses:
- "200":
- description: Success
- content:
- "application/json":
- schema:
- $ref: "#/components/schemas/BaseResult"
- "404":
- description: AssetAdministrationShell or View not found
- content:
- "application/json":
- schema:
- $ref: "#/components/schemas/BaseResult"
- tags:
- - Asset Administration Shell Interface
-components:
- links:
- ReadAASViewByIdShort:
- description: The `idShort` of the returned View can be used to read the View.
- operationId: ReadAASView
- parameters:
- view-idShort: "$response.body#/data/idShort"
- UpdateAASViewByIdShort:
- description: The `idShort` of the returned View can be used to update the View.
- operationId: UpdateAASView
- parameters:
- view-idShort: "$response.body#/data/idShort"
- DeleteAASViewByIdShort:
- description: The `idShort` of the returned View can be used to delete the View.
- operationId: DeleteAASView
- parameters:
- view-idShort: "$response.body#/data/idShort"
- schemas:
- BaseResult:
- type: object
- properties:
- success:
- type: boolean
- error:
- type: object
- nullable: true
- properties:
- type:
- enum:
- - Unspecified
- - Debug
- - Information
- - Warning
- - Error
- - Fatal
- - Exception
- type: string
- code:
- type: string
- text:
- type: string
- data:
- nullable: true
- AssetAdministrationShellResult:
- allOf:
- - $ref: "#/components/schemas/BaseResult"
- - properties:
- data:
- $ref: "#/components/schemas/StrippedAssetAdministrationShell"
- error:
- nullable: true
- ReferenceResult:
- allOf:
- - $ref: "#/components/schemas/BaseResult"
- - properties:
- data:
- $ref: "#/components/schemas/Reference"
- error:
- nullable: true
- ReferenceListResult:
- allOf:
- - $ref: "#/components/schemas/BaseResult"
- - properties:
- data:
- type: array
- items:
- $ref: "#/components/schemas/Reference"
- error:
- nullable: true
- ViewResult:
- allOf:
- - $ref: "#/components/schemas/BaseResult"
- - properties:
- data:
- $ref: "#/components/schemas/View"
- error:
- nullable: true
- ViewListResult:
- allOf:
- - $ref: "#/components/schemas/BaseResult"
- - properties:
- data:
- type: array
- items:
- $ref: "#/components/schemas/View"
- error:
- nullable: true
- SubmodelResult:
- allOf:
- - $ref: "#/components/schemas/BaseResult"
- - properties:
- data:
- $ref: "#/components/schemas/StrippedSubmodel"
- error:
- nullable: true
- SubmodelElementResult:
- allOf:
- - $ref: "#/components/schemas/BaseResult"
- - properties:
- data:
- $ref: "#/components/schemas/StrippedSubmodelElement"
- error:
- nullable: true
- SubmodelElementListResult:
- allOf:
- - $ref: "#/components/schemas/BaseResult"
- - properties:
- data:
- type: array
- items:
- $ref: "#/components/schemas/StrippedSubmodelElement"
- error:
- nullable: true
- ConstraintResult:
- allOf:
- - $ref: "#/components/schemas/BaseResult"
- - properties:
- data:
- $ref: "#/components/schemas/Constraint"
- error:
- nullable: true
- ConstraintListResult:
- allOf:
- - $ref: "#/components/schemas/BaseResult"
- - properties:
- data:
- type: array
- items:
- $ref: "#/components/schemas/Constraint"
- error:
- nullable: true
- StrippedAssetAdministrationShell:
- allOf:
- - $ref: "#/components/schemas/AssetAdministrationShell"
- - properties:
- views:
- not: {}
- submodels:
- not: {}
- conceptDictionaries:
- not: {}
- StrippedSubmodel:
- allOf:
- - $ref: "#/components/schemas/Submodel"
- - properties:
- submodelElements:
- not: {}
- qualifiers:
- not: {}
- StrippedSubmodelElement:
- allOf:
- - $ref: "#/components/schemas/SubmodelElement"
- - properties:
- qualifiers:
- not: {}
- Referable:
- allOf:
- - $ref: '#/components/schemas/HasExtensions'
- - properties:
- idShort:
- type: string
- category:
- type: string
- displayName:
- type: string
- description:
- type: array
- items:
- $ref: '#/components/schemas/LangString'
- modelType:
- $ref: '#/components/schemas/ModelType'
- required:
- - modelType
- Identifiable:
- allOf:
- - $ref: '#/components/schemas/Referable'
- - properties:
- identification:
- $ref: '#/components/schemas/Identifier'
- administration:
- $ref: '#/components/schemas/AdministrativeInformation'
- required:
- - identification
- Qualifiable:
- type: object
- properties:
- qualifiers:
- type: array
- items:
- $ref: '#/components/schemas/Constraint'
- HasSemantics:
- type: object
- properties:
- semanticId:
- $ref: '#/components/schemas/Reference'
- HasDataSpecification:
- type: object
- properties:
- embeddedDataSpecifications:
- type: array
- items:
- $ref: '#/components/schemas/EmbeddedDataSpecification'
- HasExtensions:
- type: object
- properties:
- extensions:
- type: array
- items:
- $ref: '#/components/schemas/Extension'
- Extension:
- allOf:
- - $ref: '#/components/schemas/HasSemantics'
- - properties:
- name:
- type: string
- valueType:
- type: string
- enum:
- - anyUri
- - base64Binary
- - boolean
- - date
- - dateTime
- - dateTimeStamp
- - decimal
- - integer
- - long
- - int
- - short
- - byte
- - nonNegativeInteger
- - positiveInteger
- - unsignedLong
- - unsignedInt
- - unsignedShort
- - unsignedByte
- - nonPositiveInteger
- - negativeInteger
- - double
- - duration
- - dayTimeDuration
- - yearMonthDuration
- - float
- - gDay
- - gMonth
- - gMonthDay
- - gYear
- - gYearMonth
- - hexBinary
- - NOTATION
- - QName
- - string
- - normalizedString
- - token
- - language
- - Name
- - NCName
- - ENTITY
- - ID
- - IDREF
- - NMTOKEN
- - time
- value:
- type: string
- refersTo:
- $ref: '#/components/schemas/Reference'
- required:
- - name
- AssetAdministrationShell:
- allOf:
- - $ref: '#/components/schemas/Identifiable'
- - $ref: '#/components/schemas/HasDataSpecification'
- - properties:
- derivedFrom:
- $ref: '#/components/schemas/Reference'
- assetInformation:
- $ref: '#/components/schemas/AssetInformation'
- submodels:
- type: array
- items:
- $ref: '#/components/schemas/Reference'
- views:
- type: array
- items:
- $ref: '#/components/schemas/View'
- security:
- $ref: '#/components/schemas/Security'
- required:
- - assetInformation
- Identifier:
- type: object
- properties:
- id:
- type: string
- idType:
- $ref: '#/components/schemas/KeyType'
- required:
- - id
- - idType
- KeyType:
- type: string
- enum:
- - Custom
- - IRDI
- - IRI
- - IdShort
- - FragmentId
- AdministrativeInformation:
- type: object
- properties:
- version:
- type: string
- revision:
- type: string
- LangString:
- type: object
- properties:
- language:
- type: string
- text:
- type: string
- required:
- - language
- - text
- Reference:
- type: object
- properties:
- keys:
- type: array
- items:
- $ref: '#/components/schemas/Key'
- required:
- - keys
- Key:
- type: object
- properties:
- type:
- $ref: '#/components/schemas/KeyElements'
- idType:
- $ref: '#/components/schemas/KeyType'
- value:
- type: string
- required:
- - type
- - idType
- - value
- KeyElements:
- type: string
- enum:
- - Asset
- - AssetAdministrationShell
- - ConceptDescription
- - Submodel
- - AccessPermissionRule
- - AnnotatedRelationshipElement
- - BasicEvent
- - Blob
- - Capability
- - DataElement
- - File
- - Entity
- - Event
- - MultiLanguageProperty
- - Operation
- - Property
- - Range
- - ReferenceElement
- - RelationshipElement
- - SubmodelElement
- - SubmodelElementCollection
- - View
- - GlobalReference
- - FragmentReference
- ModelTypes:
- type: string
- enum:
- - Asset
- - AssetAdministrationShell
- - ConceptDescription
- - Submodel
- - AccessPermissionRule
- - AnnotatedRelationshipElement
- - BasicEvent
- - Blob
- - Capability
- - DataElement
- - File
- - Entity
- - Event
- - MultiLanguageProperty
- - Operation
- - Property
- - Range
- - ReferenceElement
- - RelationshipElement
- - SubmodelElement
- - SubmodelElementCollection
- - View
- - GlobalReference
- - FragmentReference
- - Constraint
- - Formula
- - Qualifier
- ModelType:
- type: object
- properties:
- name:
- $ref: '#/components/schemas/ModelTypes'
- required:
- - name
- EmbeddedDataSpecification:
- type: object
- properties:
- dataSpecification:
- $ref: '#/components/schemas/Reference'
- dataSpecificationContent:
- $ref: '#/components/schemas/DataSpecificationContent'
- required:
- - dataSpecification
- - dataSpecificationContent
- DataSpecificationContent:
- oneOf:
- - $ref: '#/components/schemas/DataSpecificationIEC61360Content'
- - $ref: '#/components/schemas/DataSpecificationPhysicalUnitContent'
- DataSpecificationPhysicalUnitContent:
- type: object
- properties:
- unitName:
- type: string
- unitSymbol:
- type: string
- definition:
- type: array
- items:
- $ref: '#/components/schemas/LangString'
- siNotation:
- type: string
- siName:
- type: string
- dinNotation:
- type: string
- eceName:
- type: string
- eceCode:
- type: string
- nistName:
- type: string
- sourceOfDefinition:
- type: string
- conversionFactor:
- type: string
- registrationAuthorityId:
- type: string
- supplier:
- type: string
- required:
- - unitName
- - unitSymbol
- - definition
- DataSpecificationIEC61360Content:
- allOf:
- - $ref: '#/components/schemas/ValueObject'
- - type: object
- properties:
- dataType:
- enum:
- - DATE
- - STRING
- - STRING_TRANSLATABLE
- - REAL_MEASURE
- - REAL_COUNT
- - REAL_CURRENCY
- - BOOLEAN
- - URL
- - RATIONAL
- - RATIONAL_MEASURE
- - TIME
- - TIMESTAMP
- - INTEGER_COUNT
- - INTEGER_MEASURE
- - INTEGER_CURRENCY
- definition:
- type: array
- items:
- $ref: '#/components/schemas/LangString'
- preferredName:
- type: array
- items:
- $ref: '#/components/schemas/LangString'
- shortName:
- type: array
- items:
- $ref: '#/components/schemas/LangString'
- sourceOfDefinition:
- type: string
- symbol:
- type: string
- unit:
- type: string
- unitId:
- $ref: '#/components/schemas/Reference'
- valueFormat:
- type: string
- valueList:
- $ref: '#/components/schemas/ValueList'
- levelType:
- type: array
- items:
- $ref: '#/components/schemas/LevelType'
- required:
- - preferredName
- LevelType:
- type: string
- enum:
- - Min
- - Max
- - Nom
- - Typ
- ValueList:
- type: object
- properties:
- valueReferencePairTypes:
- type: array
- minItems: 1
- items:
- $ref: '#/components/schemas/ValueReferencePairType'
- required:
- - valueReferencePairTypes
- ValueReferencePairType:
- allOf:
- - $ref: '#/components/schemas/ValueObject'
- ValueObject:
- type: object
- properties:
- value:
- type: string
- valueId:
- $ref: '#/components/schemas/Reference'
- valueType:
- type: string
- enum:
- - anyUri
- - base64Binary
- - boolean
- - date
- - dateTime
- - dateTimeStamp
- - decimal
- - integer
- - long
- - int
- - short
- - byte
- - nonNegativeInteger
- - positiveInteger
- - unsignedLong
- - unsignedInt
- - unsignedShort
- - unsignedByte
- - nonPositiveInteger
- - negativeInteger
- - double
- - duration
- - dayTimeDuration
- - yearMonthDuration
- - float
- - gDay
- - gMonth
- - gMonthDay
- - gYear
- - gYearMonth
- - hexBinary
- - NOTATION
- - QName
- - string
- - normalizedString
- - token
- - language
- - Name
- - NCName
- - ENTITY
- - ID
- - IDREF
- - NMTOKEN
- - time
- Asset:
- allOf:
- - $ref: '#/components/schemas/Identifiable'
- - $ref: '#/components/schemas/HasDataSpecification'
- AssetInformation:
- allOf:
- - properties:
- assetKind:
- $ref: '#/components/schemas/AssetKind'
- globalAssetId:
- $ref: '#/components/schemas/Reference'
- externalAssetIds:
- type: array
- items:
- $ref: '#/components/schemas/IdentifierKeyValuePair'
- billOfMaterial:
- type: array
- items:
- $ref: '#/components/schemas/Reference'
- thumbnail:
- $ref: '#/components/schemas/File'
- required:
- - assetKind
- IdentifierKeyValuePair:
- allOf:
- - $ref: '#/components/schemas/HasSemantics'
- - properties:
- key:
- type: string
- value:
- type: string
- subjectId:
- $ref: '#/components/schemas/Reference'
- required:
- - key
- - value
- - subjectId
- AssetKind:
- type: string
- enum:
- - Type
- - Instance
- ModelingKind:
- type: string
- enum:
- - Template
- - Instance
- Submodel:
- allOf:
- - $ref: '#/components/schemas/Identifiable'
- - $ref: '#/components/schemas/HasDataSpecification'
- - $ref: '#/components/schemas/Qualifiable'
- - $ref: '#/components/schemas/HasSemantics'
- - properties:
- kind:
- $ref: '#/components/schemas/ModelingKind'
- submodelElements:
- type: array
- items:
- $ref: '#/components/schemas/SubmodelElement'
- Constraint:
- type: object
- properties:
- modelType:
- $ref: '#/components/schemas/ModelType'
- required:
- - modelType
- Operation:
- allOf:
- - $ref: '#/components/schemas/SubmodelElement'
- - properties:
- inputVariable:
- type: array
- items:
- $ref: '#/components/schemas/OperationVariable'
- outputVariable:
- type: array
- items:
- $ref: '#/components/schemas/OperationVariable'
- inoutputVariable:
- type: array
- items:
- $ref: '#/components/schemas/OperationVariable'
- OperationVariable:
- type: object
- properties:
- value:
- oneOf:
- - $ref: '#/components/schemas/Blob'
- - $ref: '#/components/schemas/File'
- - $ref: '#/components/schemas/Capability'
- - $ref: '#/components/schemas/Entity'
- - $ref: '#/components/schemas/Event'
- - $ref: '#/components/schemas/BasicEvent'
- - $ref: '#/components/schemas/MultiLanguageProperty'
- - $ref: '#/components/schemas/Operation'
- - $ref: '#/components/schemas/Property'
- - $ref: '#/components/schemas/Range'
- - $ref: '#/components/schemas/ReferenceElement'
- - $ref: '#/components/schemas/RelationshipElement'
- - $ref: '#/components/schemas/SubmodelElementCollection'
- required:
- - value
- SubmodelElement:
- allOf:
- - $ref: '#/components/schemas/Referable'
- - $ref: '#/components/schemas/HasDataSpecification'
- - $ref: '#/components/schemas/HasSemantics'
- - $ref: '#/components/schemas/Qualifiable'
- - properties:
- kind:
- $ref: '#/components/schemas/ModelingKind'
- idShort:
- type: string
- required:
- - idShort
- Event:
- allOf:
- - $ref: '#/components/schemas/SubmodelElement'
- BasicEvent:
- allOf:
- - $ref: '#/components/schemas/Event'
- - properties:
- observed:
- $ref: '#/components/schemas/Reference'
- required:
- - observed
- EntityType:
- type: string
- enum:
- - CoManagedEntity
- - SelfManagedEntity
- Entity:
- allOf:
- - $ref: '#/components/schemas/SubmodelElement'
- - properties:
- statements:
- type: array
- items:
- $ref: '#/components/schemas/SubmodelElement'
- entityType:
- $ref: '#/components/schemas/EntityType'
- globalAssetId:
- $ref: '#/components/schemas/Reference'
- specificAssetIds:
- $ref: '#/components/schemas/IdentifierKeyValuePair'
- required:
- - entityType
- View:
- allOf:
- - $ref: '#/components/schemas/Referable'
- - $ref: '#/components/schemas/HasDataSpecification'
- - $ref: '#/components/schemas/HasSemantics'
- - properties:
- containedElements:
- type: array
- items:
- $ref: '#/components/schemas/Reference'
- ConceptDescription:
- allOf:
- - $ref: '#/components/schemas/Identifiable'
- - $ref: '#/components/schemas/HasDataSpecification'
- - properties:
- isCaseOf:
- type: array
- items:
- $ref: '#/components/schemas/Reference'
- Capability:
- allOf:
- - $ref: '#/components/schemas/SubmodelElement'
- Property:
- allOf:
- - $ref: '#/components/schemas/SubmodelElement'
- - $ref: '#/components/schemas/ValueObject'
- Range:
- allOf:
- - $ref: '#/components/schemas/SubmodelElement'
- - properties:
- valueType:
- type: string
- enum:
- - anyUri
- - base64Binary
- - boolean
- - date
- - dateTime
- - dateTimeStamp
- - decimal
- - integer
- - long
- - int
- - short
- - byte
- - nonNegativeInteger
- - positiveInteger
- - unsignedLong
- - unsignedInt
- - unsignedShort
- - unsignedByte
- - nonPositiveInteger
- - negativeInteger
- - double
- - duration
- - dayTimeDuration
- - yearMonthDuration
- - float
- - gDay
- - gMonth
- - gMonthDay
- - gYear
- - gYearMonth
- - hexBinary
- - NOTATION
- - QName
- - string
- - normalizedString
- - token
- - language
- - Name
- - NCName
- - ENTITY
- - ID
- - IDREF
- - NMTOKEN
- - time
- min:
- type: string
- max:
- type: string
- required:
- - valueType
- MultiLanguageProperty:
- allOf:
- - $ref: '#/components/schemas/SubmodelElement'
- - properties:
- value:
- type: array
- items:
- $ref: '#/components/schemas/LangString'
- valueId:
- $ref: '#/components/schemas/Reference'
- File:
- allOf:
- - $ref: '#/components/schemas/SubmodelElement'
- - properties:
- value:
- type: string
- mimeType:
- type: string
- required:
- - mimeType
- Blob:
- allOf:
- - $ref: '#/components/schemas/SubmodelElement'
- - properties:
- value:
- type: string
- mimeType:
- type: string
- required:
- - mimeType
- ReferenceElement:
- allOf:
- - $ref: '#/components/schemas/SubmodelElement'
- - properties:
- value:
- $ref: '#/components/schemas/Reference'
- SubmodelElementCollection:
- allOf:
- - $ref: '#/components/schemas/SubmodelElement'
- - properties:
- value:
- type: array
- items:
- oneOf:
- - $ref: '#/components/schemas/Blob'
- - $ref: '#/components/schemas/File'
- - $ref: '#/components/schemas/Capability'
- - $ref: '#/components/schemas/Entity'
- - $ref: '#/components/schemas/Event'
- - $ref: '#/components/schemas/BasicEvent'
- - $ref: '#/components/schemas/MultiLanguageProperty'
- - $ref: '#/components/schemas/Operation'
- - $ref: '#/components/schemas/Property'
- - $ref: '#/components/schemas/Range'
- - $ref: '#/components/schemas/ReferenceElement'
- - $ref: '#/components/schemas/RelationshipElement'
- - $ref: '#/components/schemas/SubmodelElementCollection'
- allowDuplicates:
- type: boolean
- ordered:
- type: boolean
- RelationshipElement:
- allOf:
- - $ref: '#/components/schemas/SubmodelElement'
- - properties:
- first:
- $ref: '#/components/schemas/Reference'
- second:
- $ref: '#/components/schemas/Reference'
- required:
- - first
- - second
- AnnotatedRelationshipElement:
- allOf:
- - $ref: '#/components/schemas/RelationshipElement'
- - properties:
- annotation:
- type: array
- items:
- oneOf:
- - $ref: '#/components/schemas/Blob'
- - $ref: '#/components/schemas/File'
- - $ref: '#/components/schemas/MultiLanguageProperty'
- - $ref: '#/components/schemas/Property'
- - $ref: '#/components/schemas/Range'
- - $ref: '#/components/schemas/ReferenceElement'
- Qualifier:
- allOf:
- - $ref: '#/components/schemas/Constraint'
- - $ref: '#/components/schemas/HasSemantics'
- - $ref: '#/components/schemas/ValueObject'
- - properties:
- type:
- type: string
- required:
- - type
- Formula:
- allOf:
- - $ref: '#/components/schemas/Constraint'
- - properties:
- dependsOn:
- type: array
- items:
- $ref: '#/components/schemas/Reference'
- Security:
- type: object
- properties:
- accessControlPolicyPoints:
- $ref: '#/components/schemas/AccessControlPolicyPoints'
- certificate:
- type: array
- items:
- oneOf:
- - $ref: '#/components/schemas/BlobCertificate'
- requiredCertificateExtension:
- type: array
- items:
- $ref: '#/components/schemas/Reference'
- required:
- - accessControlPolicyPoints
- Certificate:
- type: object
- BlobCertificate:
- allOf:
- - $ref: '#/components/schemas/Certificate'
- - properties:
- blobCertificate:
- $ref: '#/components/schemas/Blob'
- containedExtension:
- type: array
- items:
- $ref: '#/components/schemas/Reference'
- lastCertificate:
- type: boolean
- AccessControlPolicyPoints:
- type: object
- properties:
- policyAdministrationPoint:
- $ref: '#/components/schemas/PolicyAdministrationPoint'
- policyDecisionPoint:
- $ref: '#/components/schemas/PolicyDecisionPoint'
- policyEnforcementPoint:
- $ref: '#/components/schemas/PolicyEnforcementPoint'
- policyInformationPoints:
- $ref: '#/components/schemas/PolicyInformationPoints'
- required:
- - policyAdministrationPoint
- - policyDecisionPoint
- - policyEnforcementPoint
- PolicyAdministrationPoint:
- type: object
- properties:
- localAccessControl:
- $ref: '#/components/schemas/AccessControl'
- externalAccessControl:
- type: boolean
- required:
- - externalAccessControl
- PolicyInformationPoints:
- type: object
- properties:
- internalInformationPoint:
- type: array
- items:
- $ref: '#/components/schemas/Reference'
- externalInformationPoint:
- type: boolean
- required:
- - externalInformationPoint
- PolicyEnforcementPoint:
- type: object
- properties:
- externalPolicyEnforcementPoint:
- type: boolean
- required:
- - externalPolicyEnforcementPoint
- PolicyDecisionPoint:
- type: object
- properties:
- externalPolicyDecisionPoints:
- type: boolean
- required:
- - externalPolicyDecisionPoints
- AccessControl:
- type: object
- properties:
- selectableSubjectAttributes:
- $ref: '#/components/schemas/Reference'
- defaultSubjectAttributes:
- $ref: '#/components/schemas/Reference'
- selectablePermissions:
- $ref: '#/components/schemas/Reference'
- defaultPermissions:
- $ref: '#/components/schemas/Reference'
- selectableEnvironmentAttributes:
- $ref: '#/components/schemas/Reference'
- defaultEnvironmentAttributes:
- $ref: '#/components/schemas/Reference'
- accessPermissionRule:
- type: array
- items:
- $ref: '#/components/schemas/AccessPermissionRule'
- AccessPermissionRule:
- allOf:
- - $ref: '#/components/schemas/Referable'
- - $ref: '#/components/schemas/Qualifiable'
- - properties:
- targetSubjectAttributes:
- type: array
- items:
- $ref: '#/components/schemas/SubjectAttributes'
- minItems: 1
- permissionsPerObject:
- type: array
- items:
- $ref: '#/components/schemas/PermissionsPerObject'
- required:
- - targetSubjectAttributes
- SubjectAttributes:
- type: object
- properties:
- subjectAttributes:
- type: array
- items:
- $ref: '#/components/schemas/Reference'
- minItems: 1
- PermissionsPerObject:
- type: object
- properties:
- object:
- $ref: '#/components/schemas/Reference'
- targetObjectAttributes:
- $ref: '#/components/schemas/ObjectAttributes'
- permission:
- type: array
- items:
- $ref: '#/components/schemas/Permission'
- ObjectAttributes:
- type: object
- properties:
- objectAttribute:
- type: array
- items:
- $ref: '#/components/schemas/Property'
- minItems: 1
- Permission:
- type: object
- properties:
- permission:
- $ref: '#/components/schemas/Reference'
- kindOfPermission:
- type: string
- enum:
- - Allow
- - Deny
- - NotApplicable
- - Undefined
- required:
- - permission
- - kindOfPermission
diff --git a/sdk/test/adapter/http-api-oas-submodel.yaml b/sdk/test/adapter/http-api-oas-submodel.yaml
deleted file mode 100644
index 79ac905da..000000000
--- a/sdk/test/adapter/http-api-oas-submodel.yaml
+++ /dev/null
@@ -1,2017 +0,0 @@
-openapi: 3.0.0
-info:
- version: "1"
- title: PyI40AAS REST API
- description: "REST API Specification for the [PyI40AAS framework](https://git.rwth-aachen.de/acplt/pyi40aas).
-
-
- **Submodel Interface**
-
-
- Any identifier/identification objects are encoded as follows `{identifierType}:URIencode(URIencode({identifier}))`, e.g. `IRI:http:%252F%252Facplt.org%252Fasset`."
- contact:
- name: "Michael Thies, Torben Miny, Leon Möller"
- license:
- name: Use under Eclipse Public License 2.0
- url: "https://www.eclipse.org/legal/epl-2.0/"
-servers:
- - url: http://{authority}/{basePath}/{api-version}
- description: This is the Server to access the Asset Administration Shell
- variables:
- authority:
- default: localhost:8080
- description: The authority is the server url (made of IP-Address or DNS-Name, user information, and/or port information) of the hosting environment for the Asset Administration Shell
- basePath:
- default: api
- description: The basePath variable is additional path information for the hosting environment. It may contain the name of an aggregation point like 'shells' and/or API version information and/or tenant-id information, etc.
- api-version:
- default: v1
- description: The Version of the API-Specification
-paths:
- "/":
- get:
- summary: "Returns the stripped Submodel (without SubmodelElements and Constraints (property: qualifiers))"
- operationId: ReadSubmodel
- responses:
- "200":
- description: Success
- content:
- "application/json":
- schema:
- $ref: "#/components/schemas/SubmodelResult"
- "404":
- description: No Submodel found
- content:
- "application/json":
- schema:
- $ref: "#/components/schemas/BaseResult"
- tags:
- - Submodel Interface
- "/constraints/":
- get:
- summary: Returns all Constraints of the current Submodel
- operationId: ReadSubmodelConstraints
- responses:
- "200":
- description: Success
- content:
- "application/json":
- schema:
- $ref: "#/components/schemas/ConstraintListResult"
- "404":
- description: Submodel not found
- content:
- "application/json":
- schema:
- $ref: "#/components/schemas/BaseResult"
- tags:
- - Submodel Interface
- post:
- summary: Adds a new Constraint to the Submodel
- operationId: CreateSubmodelConstraint
- requestBody:
- description: The Constraint to create
- required: true
- content:
- "application/json":
- schema:
- $ref: "#/components/schemas/Constraint"
- responses:
- "201":
- description: Success
- content:
- "application/json":
- schema:
- $ref: "#/components/schemas/ConstraintResult"
- headers:
- Location:
- description: The URL of the created Constraint
- schema:
- type: string
- links:
- ReadSubmodelQualifierByType:
- $ref: "#/components/links/ReadSubmodelQualifierByType"
- UpdateSubmodelQualifierByType:
- $ref: "#/components/links/UpdateSubmodelQualifierByType"
- DeleteSubmodelQualifierByType:
- $ref: "#/components/links/DeleteSubmodelQualifierByType"
- "404":
- description: Submodel not found
- content:
- "application/json":
- schema:
- $ref: "#/components/schemas/BaseResult"
- "409":
- description: "When trying to add a qualifier: Qualifier with same type already exists"
- content:
- "application/json":
- schema:
- $ref: "#/components/schemas/BaseResult"
- "422":
- description: Request body is not a valid Qualifier
- content:
- "application/json":
- schema:
- $ref: "#/components/schemas/BaseResult"
- tags:
- - Submodel Interface
- "/constraints/{qualifier-type}/":
- parameters:
- - name: qualifier-type
- in: path
- description: The type of the Qualifier
- required: true
- schema:
- type: string
- get:
- summary: Retrieves a specific Qualifier of the Submodel's constraints (Formulas cannot be referred to yet)
- operationId: ReadSubmodelConstraint
- responses:
- "200":
- description: Success
- content:
- "application/json":
- schema:
- $ref: "#/components/schemas/ConstraintResult"
- links:
- ReadSubmodelQualifierByType:
- $ref: "#/components/links/ReadSubmodelQualifierByType"
- UpdateSubmodelQualifierByType:
- $ref: "#/components/links/UpdateSubmodelQualifierByType"
- DeleteSubmodelQualifierByType:
- $ref: "#/components/links/DeleteSubmodelQualifierByType"
- "404":
- description: Submodel or Constraint not found
- content:
- "application/json":
- schema:
- $ref: "#/components/schemas/BaseResult"
- tags:
- - Submodel Interface
- put:
- summary: Updates an existing Qualifier in the Submodel (Formulas cannot be referred to yet)
- operationId: UpdateSubmodelConstraint
- requestBody:
- description: The Qualifier used to overwrite the existing Qualifier
- required: true
- content:
- "application/json":
- schema:
- $ref: "#/components/schemas/Qualifier"
- responses:
- "200":
- description: Success
- content:
- "application/json":
- schema:
- $ref: "#/components/schemas/ConstraintResult"
- links:
- ReadSubmodelQualifierByType:
- $ref: "#/components/links/ReadSubmodelQualifierByType"
- UpdateSubmodelQualifierByType:
- $ref: "#/components/links/UpdateSubmodelQualifierByType"
- DeleteSubmodelQualifierByType:
- $ref: "#/components/links/DeleteSubmodelQualifierByType"
- "201":
- description: Success (type changed)
- content:
- "application/json":
- schema:
- $ref: "#/components/schemas/ConstraintResult"
- headers:
- Location:
- description: The new URL of the Qualifier
- schema:
- type: string
- links:
- ReadSubmodelQualifierByType:
- $ref: "#/components/links/ReadSubmodelQualifierByType"
- UpdateSubmodelQualifierByType:
- $ref: "#/components/links/UpdateSubmodelQualifierByType"
- DeleteSubmodelQualifierByType:
- $ref: "#/components/links/DeleteSubmodelQualifierByType"
- "404":
- description: Submodel or Constraint not found
- content:
- "application/json":
- schema:
- $ref: "#/components/schemas/BaseResult"
- "409":
- description: type changed and new type already exists
- content:
- "application/json":
- schema:
- $ref: "#/components/schemas/BaseResult"
- "422":
- description: Request body is not a valid Qualifier
- content:
- "application/json":
- schema:
- $ref: "#/components/schemas/BaseResult"
- tags:
- - Submodel Interface
- delete:
- summary: Deletes an existing Qualifier from the Submodel (Formulas cannot be referred to yet)
- operationId: DeleteSubmodelConstraint
- responses:
- "200":
- description: Success
- content:
- "application/json":
- schema:
- $ref: "#/components/schemas/BaseResult"
- "404":
- description: Submodel or Constraint not found
- content:
- "application/json":
- schema:
- $ref: "#/components/schemas/BaseResult"
- tags:
- - Submodel Interface
- "/submodelElements/":
- get:
- summary: Returns all SubmodelElements of the current Submodel
- operationId: ReadSubmodelSubmodelElements
- responses:
- "200":
- description: Success
- content:
- "application/json":
- schema:
- $ref: "#/components/schemas/SubmodelElementListResult"
- "404":
- description: Submodel not found
- content:
- "application/json":
- schema:
- $ref: "#/components/schemas/BaseResult"
- tags:
- - Submodel Interface
- post:
- summary: Adds a new SubmodelElement to the Submodel
- operationId: CreateSubmodelSubmodelElement
- requestBody:
- description: The SubmodelElement to create
- required: true
- content:
- "application/json":
- schema:
- $ref: "#/components/schemas/SubmodelElement"
- responses:
- "201":
- description: Success
- content:
- "application/json":
- schema:
- $ref: "#/components/schemas/SubmodelElementResult"
- headers:
- Location:
- description: The URL of the created SubmodelElement
- schema:
- type: string
- links:
- ReadSubmodelSubmodelElementByIdShortAfterPost:
- $ref: "#/components/links/ReadSubmodelSubmodelElementByIdShortAfterPost"
- UpdateSubmodelSubmodelElementByIdShortAfterPost:
- $ref: "#/components/links/UpdateSubmodelSubmodelElementByIdShortAfterPost"
- DeleteSubmodelSubmodelElementByIdShortAfterPost:
- $ref: "#/components/links/DeleteSubmodelSubmodelElementByIdShortAfterPost"
- "404":
- description: Submodel not found
- content:
- "application/json":
- schema:
- $ref: "#/components/schemas/BaseResult"
- "409":
- description: SubmodelElement with same idShort already exists
- content:
- "application/json":
- schema:
- $ref: "#/components/schemas/BaseResult"
- "422":
- description: Request body is not a valid SubmodelElement
- content:
- "application/json":
- schema:
- $ref: "#/components/schemas/BaseResult"
- tags:
- - Submodel Interface
- "/{idShort-path}/":
- parameters:
- - name: idShort-path
- in: path
- description: A /-separated concatenation of !-prefixed idShorts
- required: true
- schema:
- type: string
- get:
- summary: Returns the (stripped) (nested) SubmodelElement
- operationId: ReadSubmodelSubmodelElement
- responses:
- "200":
- description: Success
- content:
- "application/json":
- schema:
- $ref: "#/components/schemas/SubmodelElementListResult"
- "400":
- description: Invalid idShort
- content:
- "application/json":
- schema:
- $ref: "#/components/schemas/BaseResult"
- "404":
- description: Submodel or any SubmodelElement referred by idShort-path not found
- content:
- "application/json":
- schema:
- $ref: "#/components/schemas/BaseResult"
- tags:
- - Submodel Interface
- put:
- summary: Updates a nested SubmodelElement
- operationId: UpdateSubmodelSubmodelElement
- requestBody:
- description: The SubmodelElement used to overwrite the existing SubmodelElement
- required: true
- content:
- "application/json":
- schema:
- $ref: "#/components/schemas/SubmodelElement"
- responses:
- "200":
- description: Success
- content:
- "application/json":
- schema:
- $ref: "#/components/schemas/SubmodelElementResult"
- "201":
- description: Success (idShort changed)
- content:
- "application/json":
- schema:
- $ref: "#/components/schemas/SubmodelElementResult"
- headers:
- Location:
- description: The new URL of the SubmodelElement
- schema:
- type: string
- "400":
- description: Invalid idShort
- content:
- "application/json":
- schema:
- $ref: "#/components/schemas/BaseResult"
- "404":
- description: Submodel or any SubmodelElement referred by idShort-path not found
- content:
- "application/json":
- schema:
- $ref: "#/components/schemas/BaseResult"
- "409":
- description: idShort changed and new idShort already exists
- content:
- "application/json":
- schema:
- $ref: "#/components/schemas/BaseResult"
- "422":
- description: Request body is not a valid SubmodelElement **or** the type of the new SubmodelElement differs from the existing one
- content:
- "application/json":
- schema:
- $ref: "#/components/schemas/BaseResult"
- tags:
- - Submodel Interface
- delete:
- summary: Deletes a specific (nested) SubmodelElement from the Submodel
- operationId: DeleteSubmodelSubmodelElement
- responses:
- "200":
- description: Success
- content:
- "application/json":
- schema:
- $ref: "#/components/schemas/BaseResult"
- "400":
- description: Invalid idShort
- content:
- "application/json":
- schema:
- $ref: "#/components/schemas/BaseResult"
- "404":
- description: Submodel or any SubmodelElement referred by idShort-path not found
- content:
- "application/json":
- schema:
- $ref: "#/components/schemas/BaseResult"
- tags:
- - Submodel Interface
- "/{idShort-path}/value/":
- parameters:
- - name: idShort-path
- in: path
- description: A /-separated concatenation of !-prefixed idShorts
- required: true
- schema:
- type: string
- get:
- summary: If the (nested) SubmodelElement is a SubmodelElementCollection, return contained (stripped) SubmodelElements
- operationId: ReadSubmodelSubmodelElementValue
- responses:
- "200":
- description: Success
- content:
- "application/json":
- schema:
- $ref: "#/components/schemas/BaseResult"
- "400":
- description: Invalid idShort or SubmodelElement exists, but is not a SubmodelElementCollection, so /value is not possible
- content:
- "application/json":
- schema:
- $ref: "#/components/schemas/BaseResult"
- "404":
- description: Submodel or any SubmodelElement referred by idShort-path not found
- content:
- "application/json":
- schema:
- $ref: "#/components/schemas/BaseResult"
- tags:
- - Submodel Interface
- post:
- summary: If the (nested) SubmodelElement is a SubmodelElementCollection, add a SubmodelElement to its value
- operationId: CreateSubmodelSubmodelElementValue
- requestBody:
- description: The SubmodelElement to create
- required: true
- content:
- "application/json":
- schema:
- $ref: "#/components/schemas/SubmodelElement"
- responses:
- "201":
- description: Success
- content:
- "application/json":
- schema:
- $ref: "#/components/schemas/SubmodelElementResult"
- headers:
- Location:
- description: The URL of the created SubmodelElement
- schema:
- type: string
- links:
- ReadSubmodelSubmodelElementByIdShortAfterPostWithPath:
- $ref: "#/components/links/ReadSubmodelSubmodelElementByIdShortAfterPostWithPath"
- UpdateSubmodelSubmodelElementByIdShortAfterPostWithPath:
- $ref: "#/components/links/UpdateSubmodelSubmodelElementByIdShortAfterPostWithPath"
- DeleteSubmodelSubmodelElementByIdShortAfterPostWithPath:
- $ref: "#/components/links/DeleteSubmodelSubmodelElementByIdShortAfterPostWithPath"
- "400":
- description: Invalid idShort or SubmodelElement exists, but is not a SubmodelElementCollection, so /value is not possible
- content:
- "application/json":
- schema:
- $ref: "#/components/schemas/BaseResult"
- "404":
- description: Submodel or any SubmodelElement referred by idShort-path not found
- content:
- "application/json":
- schema:
- $ref: "#/components/schemas/BaseResult"
- "409":
- description: SubmodelElement with same idShort already exists
- content:
- "application/json":
- schema:
- $ref: "#/components/schemas/BaseResult"
- "422":
- description: Request body is not a valid SubmodelElement
- content:
- "application/json":
- schema:
- $ref: "#/components/schemas/BaseResult"
- tags:
- - Submodel Interface
- "/{idShort-path}/annotation/":
- parameters:
- - name: idShort-path
- in: path
- description: A /-separated concatenation of !-prefixed idShorts
- required: true
- schema:
- type: string
- get:
- summary: If the (nested) SubmodelElement is an AnnotatedRelationshipElement, return contained (stripped) SubmodelElements
- operationId: ReadSubmodelSubmodelElementAnnotation
- responses:
- "200":
- description: Success
- content:
- "application/json":
- schema:
- $ref: "#/components/schemas/SubmodelElementListResult"
- "400":
- description: Invalid idShort or SubmodelElement exists, but is not an AnnotatedRelationshipElement, so /annotation is not possible
- content:
- "application/json":
- schema:
- $ref: "#/components/schemas/BaseResult"
- "404":
- description: Submodel or any SubmodelElement referred by idShort-path not found
- content:
- "application/json":
- schema:
- $ref: "#/components/schemas/BaseResult"
- tags:
- - Submodel Interface
- post:
- summary: If the (nested) SubmodelElement is an AnnotatedRelationshipElement, add a SubmodelElement to its annotation
- operationId: CreateSubmodelSubmodelElementAnnotation
- requestBody:
- description: The SubmodelElement to create
- required: true
- content:
- "application/json":
- schema:
- $ref: "#/components/schemas/SubmodelElement"
- responses:
- "201":
- description: Success
- content:
- "application/json":
- schema:
- $ref: "#/components/schemas/SubmodelElementResult"
- headers:
- Location:
- description: The URL of the created SubmodelElement
- schema:
- type: string
- links:
- ReadSubmodelSubmodelElementByIdShortAfterPostWithPath:
- $ref: "#/components/links/ReadSubmodelSubmodelElementByIdShortAfterPostWithPath"
- UpdateSubmodelSubmodelElementByIdShortAfterPostWithPath:
- $ref: "#/components/links/UpdateSubmodelSubmodelElementByIdShortAfterPostWithPath"
- DeleteSubmodelSubmodelElementByIdShortAfterPostWithPath:
- $ref: "#/components/links/DeleteSubmodelSubmodelElementByIdShortAfterPostWithPath"
- "400":
- description: Invalid idShort or SubmodelElement exists, but is not an AnnotatedRelationshipElement, so /annotation is not possible
- content:
- "application/json":
- schema:
- $ref: "#/components/schemas/BaseResult"
- "404":
- description: Submodel or any SubmodelElement referred by idShort-path not found
- content:
- "application/json":
- schema:
- $ref: "#/components/schemas/BaseResult"
- "409":
- description: SubmodelElement with given idShort already exists
- content:
- "application/json":
- schema:
- $ref: "#/components/schemas/BaseResult"
- "422":
- description: Request body is not a valid SubmodelElement
- content:
- "application/json":
- schema:
- $ref: "#/components/schemas/BaseResult"
- tags:
- - Submodel Interface
- "/{idShort-path}/statement/":
- parameters:
- - name: idShort-path
- in: path
- description: A /-separated concatenation of !-prefixed idShorts
- required: true
- schema:
- type: string
- get:
- summary: If the (nested) SubmodelElement is an Entity, return contained (stripped) SubmodelElements
- operationId: ReadSubmodelSubmodelElementStatement
- responses:
- "200":
- description: Success
- content:
- "application/json":
- schema:
- $ref: "#/components/schemas/SubmodelElementListResult"
- "400":
- description: Invalid idShort or SubmodelElement exists, but is not an Entity, so /statement is not possible.
- content:
- "application/json":
- schema:
- $ref: "#/components/schemas/BaseResult"
- "404":
- description: Submodel or any SubmodelElement referred by idShort-path not found
- content:
- "application/json":
- schema:
- $ref: "#/components/schemas/BaseResult"
- tags:
- - Submodel Interface
- post:
- summary: If the (nested) SubmodelElement is an Entity, add a SubmodelElement to its statement
- operationId: CreateSubmodelSubmodelElementStatement
- requestBody:
- description: The SubmodelElement to create
- required: true
- content:
- "application/json":
- schema:
- $ref: "#/components/schemas/SubmodelElement"
- responses:
- "201":
- description: Success
- content:
- "application/json":
- schema:
- $ref: "#/components/schemas/SubmodelElementResult"
- headers:
- Location:
- description: The URL of the created SubmodelElement
- schema:
- type: string
- links:
- ReadSubmodelSubmodelElementByIdShortAfterPostWithPath:
- $ref: "#/components/links/ReadSubmodelSubmodelElementByIdShortAfterPostWithPath"
- UpdateSubmodelSubmodelElementByIdShortAfterPostWithPath:
- $ref: "#/components/links/UpdateSubmodelSubmodelElementByIdShortAfterPostWithPath"
- DeleteSubmodelSubmodelElementByIdShortAfterPostWithPath:
- $ref: "#/components/links/DeleteSubmodelSubmodelElementByIdShortAfterPostWithPath"
- "400":
- description: Invalid idShort or SubmodelElement exists, but is not an Entity, so /statement is not possible
- content:
- "application/json":
- schema:
- $ref: "#/components/schemas/BaseResult"
- "404":
- description: Submodel or any SubmodelElement referred by idShort-path not found
- content:
- "application/json":
- schema:
- $ref: "#/components/schemas/BaseResult"
- "409":
- description: SubmodelElement with same idShort already exists
- content:
- "application/json":
- schema:
- $ref: "#/components/schemas/BaseResult"
- "422":
- description: Request body is not a valid SubmodelElement
- content:
- "application/json":
- schema:
- $ref: "#/components/schemas/BaseResult"
- tags:
- - Submodel Interface
- "/{idShort-path}/constraints/":
- parameters:
- - name: idShort-path
- in: path
- description: A /-separated concatenation of !-prefixed idShorts
- required: true
- schema:
- type: string
- get:
- summary: Returns all Constraints of the (nested) SubmodelElement
- operationId: ReadSubmodelSubmodelElementConstraints
- responses:
- "200":
- description: Success
- content:
- "application/json":
- schema:
- $ref: "#/components/schemas/ConstraintListResult"
- "400":
- description: Invalid idShort
- content:
- "application/json":
- schema:
- $ref: "#/components/schemas/BaseResult"
- "404":
- description: Submodel or any SubmodelElement referred by idShort-path not found
- content:
- "application/json":
- schema:
- $ref: "#/components/schemas/BaseResult"
- tags:
- - Submodel Interface
- post:
- summary: Adds a new Constraint to the (nested) SubmodelElement
- operationId: CreateSubmodelSubmodelElementConstraint
- requestBody:
- description: The Constraint to create
- required: true
- content:
- "application/json":
- schema:
- $ref: "#/components/schemas/Constraint"
- responses:
- "201":
- description: Success
- content:
- "application/json":
- schema:
- $ref: "#/components/schemas/ConstraintResult"
- headers:
- Location:
- description: The URL of the created Constraint
- schema:
- type: string
- links:
- ReadSubmodelSubmodelElementQualifierByType:
- $ref: "#/components/links/ReadSubmodelSubmodelElementQualifierByType"
- UpdateSubmodelSubmodelElementQualifierByType:
- $ref: "#/components/links/UpdateSubmodelSubmodelElementQualifierByType"
- DeleteSubmodelSubmodelElementQualifierByType:
- $ref: "#/components/links/DeleteSubmodelSubmodelElementQualifierByType"
- "400":
- description: Invalid idShort
- content:
- "application/json":
- schema:
- $ref: "#/components/schemas/BaseResult"
- "404":
- description: Submodel or any SubmodelElement referred by idShort-path not found
- content:
- "application/json":
- schema:
- $ref: "#/components/schemas/BaseResult"
- "409":
- description: "When trying to add a qualifier: Qualifier with specified type already exists"
- content:
- "application/json":
- schema:
- $ref: "#/components/schemas/BaseResult"
- "422":
- description: Request body is not a valid Qualifier
- content:
- "application/json":
- schema:
- $ref: "#/components/schemas/BaseResult"
- tags:
- - Submodel Interface
- "/{idShort-path}/constraints/{qualifier-type}/":
- parameters:
- - name: idShort-path
- in: path
- description: A /-separated concatenation of !-prefixed idShorts
- required: true
- schema:
- type: string
- - name: qualifier-type
- in: path
- description: "Type of the qualifier"
- required: true
- schema:
- type: string
- get:
- summary: Retrieves a specific Qualifier of the (nested) SubmodelElements's Constraints (Formulas cannot be referred to yet)
- operationId: ReadSubmodelSubmodelElementConstraint
- responses:
- "200":
- description: Success
- content:
- "application/json":
- schema:
- $ref: "#/components/schemas/ConstraintResult"
- links:
- ReadSubmodelSubmodelElementQualifierByType:
- $ref: "#/components/links/ReadSubmodelSubmodelElementQualifierByType"
- UpdateSubmodelSubmodelElementQualifierByType:
- $ref: "#/components/links/UpdateSubmodelSubmodelElementQualifierByType"
- DeleteSubmodelSubmodelElementQualifierByType:
- $ref: "#/components/links/DeleteSubmodelSubmodelElementQualifierByType"
- "400":
- description: Invalid idShort
- content:
- "application/json":
- schema:
- $ref: "#/components/schemas/BaseResult"
- "404":
- description: Submodel, Qualifier or any SubmodelElement referred by idShort-path not found
- content:
- "application/json":
- schema:
- $ref: "#/components/schemas/BaseResult"
- tags:
- - Submodel Interface
- put:
- summary: Updates an existing Qualifier in the (nested) SubmodelElement (Formulas cannot be referred to yet)
- operationId: UpdateSubmodelSubmodelElementConstraint
- requestBody:
- description: The Qualifier used to overwrite the existing Qualifier
- required: true
- content:
- "application/json":
- schema:
- $ref: "#/components/schemas/Qualifier"
- responses:
- "200":
- description: Success
- content:
- "application/json":
- schema:
- $ref: "#/components/schemas/ConstraintResult"
- links:
- ReadSubmodelSubmodelElementQualifierByType:
- $ref: "#/components/links/ReadSubmodelSubmodelElementQualifierByType"
- UpdateSubmodelSubmodelElementQualifierByType:
- $ref: "#/components/links/UpdateSubmodelSubmodelElementQualifierByType"
- DeleteSubmodelSubmodelElementQualifierByType:
- $ref: "#/components/links/DeleteSubmodelSubmodelElementQualifierByType"
- "201":
- description: Success (type changed)
- content:
- "application/json":
- schema:
- $ref: "#/components/schemas/ConstraintResult"
- headers:
- Location:
- description: The new URL of the Qualifier
- schema:
- type: string
- links:
- ReadSubmodelSubmodelElementQualifierByType:
- $ref: "#/components/links/ReadSubmodelSubmodelElementQualifierByType"
- UpdateSubmodelSubmodelElementQualifierByType:
- $ref: "#/components/links/UpdateSubmodelSubmodelElementQualifierByType"
- DeleteSubmodelSubmodelElementQualifierByType:
- $ref: "#/components/links/DeleteSubmodelSubmodelElementQualifierByType"
- "400":
- description: Invalid idShort
- content:
- "application/json":
- schema:
- $ref: "#/components/schemas/BaseResult"
- "404":
- description: Submodel, Qualifier or any SubmodelElement referred by idShort-path not found
- content:
- "application/json":
- schema:
- $ref: "#/components/schemas/BaseResult"
- "409":
- description: type changed and new type already exists
- content:
- "application/json":
- schema:
- $ref: "#/components/schemas/BaseResult"
- "422":
- description: Request body is not a valid Qualifier
- content:
- "application/json":
- schema:
- $ref: "#/components/schemas/BaseResult"
- tags:
- - Submodel Interface
- delete:
- summary: Deletes an existing Qualifier from the (nested) SubmodelElement (Formulas cannot be referred to yet)
- operationId: DeleteSubmodelSubmodelElementConstraint
- responses:
- "200":
- description: Success
- content:
- "application/json":
- schema:
- $ref: "#/components/schemas/BaseResult"
- "400":
- description: Invalid idShort
- content:
- "application/json":
- schema:
- $ref: "#/components/schemas/BaseResult"
- "404":
- description: Submodel, Qualifier or any SubmodelElement referred by idShort-path not found
- content:
- "application/json":
- schema:
- $ref: "#/components/schemas/BaseResult"
- tags:
- - Submodel Interface
-components:
- links:
- ReadSubmodelQualifierByType:
- description: The `type` of the returned Qualifier can be used to read the Qualifier.
- operationId: ReadSubmodelConstraint
- parameters:
- qualifier-type: "$response.body#/data/type"
- UpdateSubmodelQualifierByType:
- description: The `type` of the returned Qualifier can be used to update the Qualifier.
- operationId: UpdateSubmodelConstraint
- parameters:
- qualifier-type: "$response.body#/data/type"
- DeleteSubmodelQualifierByType:
- description: The `type` of the returned Qualifier can be used to delete the Qualifier.
- operationId: DeleteSubmodelConstraint
- parameters:
- qualifier-type: "$response.body#/data/type"
- ReadSubmodelSubmodelElementByIdShortAfterPost:
- description: The `idShort` of the returned SubmodelElement can be used to read the SubmodelElement.
- operationId: ReadSubmodelSubmodelElement
- parameters:
- idShort-path: "!{$response.body#/data/idShort}"
- UpdateSubmodelSubmodelElementByIdShortAfterPost:
- description: The `idShort` of the returned SubmodelElement can be used to update the SubmodelElement.
- operationId: UpdateSubmodelSubmodelElement
- parameters:
- idShort-path: "!{$response.body#/data/idShort}"
- DeleteSubmodelSubmodelElementByIdShortAfterPost:
- description: The `idShort` of the returned SubmodelElement can be used to delete the SubmodelElement.
- operationId: DeleteSubmodelSubmodelElement
- parameters:
- idShort-path: "!{$response.body#/data/idShort}"
- ReadSubmodelSubmodelElementByIdShortAfterPostWithPath:
- description: The `idShort` of the returned SubmodelElement can be used to read the SubmodelElement.
- operationId: ReadSubmodelSubmodelElement
- parameters:
- idShort-path: "{$request.path.idShort-path}/!{$response.body#/data/idShort}"
- UpdateSubmodelSubmodelElementByIdShortAfterPostWithPath:
- description: The `idShort` of the returned SubmodelElement can be used to update the SubmodelElement.
- operationId: UpdateSubmodelSubmodelElement
- parameters:
- idShort-path: "{$request.path.idShort-path}/!{$response.body#/data/idShort}"
- DeleteSubmodelSubmodelElementByIdShortAfterPostWithPath:
- description: The `idShort` of the returned SubmodelElement can be used to delete the SubmodelElement.
- operationId: DeleteSubmodelSubmodelElement
- parameters:
- idShort-path: "{$request.path.idShort-path}/!{$response.body#/data/idShort}"
- ReadSubmodelSubmodelElementQualifierByType:
- description: The `type` of the returned Qualifier can be used to read the Qualifier.
- operationId: ReadSubmodelSubmodelElementConstraint
- parameters:
- idShort-path: "$request.path.idShort-path"
- qualifier-type: "$response.body#/type"
- UpdateSubmodelSubmodelElementQualifierByType:
- description: The `type` of the returned Qualifier can be used to update the Qualifier.
- operationId: UpdateSubmodelSubmodelElementConstraint
- parameters:
- idShort-path: "$request.path.idShort-path"
- qualifier-type: "$response.body#/type"
- DeleteSubmodelSubmodelElementQualifierByType:
- description: The `type` of the returned Qualifier can be used to delete the Qualifier.
- operationId: DeleteSubmodelSubmodelElementConstraint
- parameters:
- idShort-path: "$request.path.idShort-path"
- qualifier-type: "$response.body#/type"
- schemas:
- BaseResult:
- type: object
- properties:
- success:
- type: boolean
- error:
- type: object
- nullable: true
- properties:
- type:
- enum:
- - Unspecified
- - Debug
- - Information
- - Warning
- - Error
- - Fatal
- - Exception
- type: string
- code:
- type: string
- text:
- type: string
- data:
- nullable: true
- AssetAdministrationShellResult:
- allOf:
- - $ref: "#/components/schemas/BaseResult"
- - properties:
- data:
- $ref: "#/components/schemas/StrippedAssetAdministrationShell"
- error:
- nullable: true
- ReferenceResult:
- allOf:
- - $ref: "#/components/schemas/BaseResult"
- - properties:
- data:
- $ref: "#/components/schemas/Reference"
- error:
- nullable: true
- ReferenceListResult:
- allOf:
- - $ref: "#/components/schemas/BaseResult"
- - properties:
- data:
- type: array
- items:
- $ref: "#/components/schemas/Reference"
- error:
- nullable: true
- ViewResult:
- allOf:
- - $ref: "#/components/schemas/BaseResult"
- - properties:
- data:
- $ref: "#/components/schemas/View"
- error:
- nullable: true
- ViewListResult:
- allOf:
- - $ref: "#/components/schemas/BaseResult"
- - properties:
- data:
- type: array
- items:
- $ref: "#/components/schemas/View"
- error:
- nullable: true
- SubmodelResult:
- allOf:
- - $ref: "#/components/schemas/BaseResult"
- - properties:
- data:
- $ref: "#/components/schemas/StrippedSubmodel"
- error:
- nullable: true
- SubmodelElementResult:
- allOf:
- - $ref: "#/components/schemas/BaseResult"
- - properties:
- data:
- $ref: "#/components/schemas/StrippedSubmodelElement"
- error:
- nullable: true
- SubmodelElementListResult:
- allOf:
- - $ref: "#/components/schemas/BaseResult"
- - properties:
- data:
- type: array
- items:
- $ref: "#/components/schemas/StrippedSubmodelElement"
- error:
- nullable: true
- ConstraintResult:
- allOf:
- - $ref: "#/components/schemas/BaseResult"
- - properties:
- data:
- $ref: "#/components/schemas/Constraint"
- error:
- nullable: true
- ConstraintListResult:
- allOf:
- - $ref: "#/components/schemas/BaseResult"
- - properties:
- data:
- type: array
- items:
- $ref: "#/components/schemas/Constraint"
- error:
- nullable: true
- StrippedAssetAdministrationShell:
- allOf:
- - $ref: "#/components/schemas/AssetAdministrationShell"
- - properties:
- views:
- not: {}
- submodels:
- not: {}
- conceptDictionaries:
- not: {}
- StrippedSubmodel:
- allOf:
- - $ref: "#/components/schemas/Submodel"
- - properties:
- submodelElements:
- not: {}
- qualifiers:
- not: {}
- StrippedSubmodelElement:
- allOf:
- - $ref: "#/components/schemas/SubmodelElement"
- - properties:
- qualifiers:
- not: {}
- Referable:
- allOf:
- - $ref: '#/components/schemas/HasExtensions'
- - properties:
- idShort:
- type: string
- category:
- type: string
- displayName:
- type: string
- description:
- type: array
- items:
- $ref: '#/components/schemas/LangString'
- modelType:
- $ref: '#/components/schemas/ModelType'
- required:
- - modelType
- Identifiable:
- allOf:
- - $ref: '#/components/schemas/Referable'
- - properties:
- identification:
- $ref: '#/components/schemas/Identifier'
- administration:
- $ref: '#/components/schemas/AdministrativeInformation'
- required:
- - identification
- Qualifiable:
- type: object
- properties:
- qualifiers:
- type: array
- items:
- $ref: '#/components/schemas/Constraint'
- HasSemantics:
- type: object
- properties:
- semanticId:
- $ref: '#/components/schemas/Reference'
- HasDataSpecification:
- type: object
- properties:
- embeddedDataSpecifications:
- type: array
- items:
- $ref: '#/components/schemas/EmbeddedDataSpecification'
- HasExtensions:
- type: object
- properties:
- extensions:
- type: array
- items:
- $ref: '#/components/schemas/Extension'
- Extension:
- allOf:
- - $ref: '#/components/schemas/HasSemantics'
- - properties:
- name:
- type: string
- valueType:
- type: string
- enum:
- - anyUri
- - base64Binary
- - boolean
- - date
- - dateTime
- - dateTimeStamp
- - decimal
- - integer
- - long
- - int
- - short
- - byte
- - nonNegativeInteger
- - positiveInteger
- - unsignedLong
- - unsignedInt
- - unsignedShort
- - unsignedByte
- - nonPositiveInteger
- - negativeInteger
- - double
- - duration
- - dayTimeDuration
- - yearMonthDuration
- - float
- - gDay
- - gMonth
- - gMonthDay
- - gYear
- - gYearMonth
- - hexBinary
- - NOTATION
- - QName
- - string
- - normalizedString
- - token
- - language
- - Name
- - NCName
- - ENTITY
- - ID
- - IDREF
- - NMTOKEN
- - time
- value:
- type: string
- refersTo:
- $ref: '#/components/schemas/Reference'
- required:
- - name
- AssetAdministrationShell:
- allOf:
- - $ref: '#/components/schemas/Identifiable'
- - $ref: '#/components/schemas/HasDataSpecification'
- - properties:
- derivedFrom:
- $ref: '#/components/schemas/Reference'
- assetInformation:
- $ref: '#/components/schemas/AssetInformation'
- submodels:
- type: array
- items:
- $ref: '#/components/schemas/Reference'
- views:
- type: array
- items:
- $ref: '#/components/schemas/View'
- security:
- $ref: '#/components/schemas/Security'
- required:
- - assetInformation
- Identifier:
- type: object
- properties:
- id:
- type: string
- idType:
- $ref: '#/components/schemas/KeyType'
- required:
- - id
- - idType
- KeyType:
- type: string
- enum:
- - Custom
- - IRDI
- - IRI
- - IdShort
- - FragmentId
- AdministrativeInformation:
- type: object
- properties:
- version:
- type: string
- revision:
- type: string
- LangString:
- type: object
- properties:
- language:
- type: string
- text:
- type: string
- required:
- - language
- - text
- Reference:
- type: object
- properties:
- keys:
- type: array
- items:
- $ref: '#/components/schemas/Key'
- required:
- - keys
- Key:
- type: object
- properties:
- type:
- $ref: '#/components/schemas/KeyElements'
- idType:
- $ref: '#/components/schemas/KeyType'
- value:
- type: string
- required:
- - type
- - idType
- - value
- KeyElements:
- type: string
- enum:
- - Asset
- - AssetAdministrationShell
- - ConceptDescription
- - Submodel
- - AccessPermissionRule
- - AnnotatedRelationshipElement
- - BasicEvent
- - Blob
- - Capability
- - DataElement
- - File
- - Entity
- - Event
- - MultiLanguageProperty
- - Operation
- - Property
- - Range
- - ReferenceElement
- - RelationshipElement
- - SubmodelElement
- - SubmodelElementCollection
- - View
- - GlobalReference
- - FragmentReference
- ModelTypes:
- type: string
- enum:
- - Asset
- - AssetAdministrationShell
- - ConceptDescription
- - Submodel
- - AccessPermissionRule
- - AnnotatedRelationshipElement
- - BasicEvent
- - Blob
- - Capability
- - DataElement
- - File
- - Entity
- - Event
- - MultiLanguageProperty
- - Operation
- - Property
- - Range
- - ReferenceElement
- - RelationshipElement
- - SubmodelElement
- - SubmodelElementCollection
- - View
- - GlobalReference
- - FragmentReference
- - Constraint
- - Formula
- - Qualifier
- ModelType:
- type: object
- properties:
- name:
- $ref: '#/components/schemas/ModelTypes'
- required:
- - name
- EmbeddedDataSpecification:
- type: object
- properties:
- dataSpecification:
- $ref: '#/components/schemas/Reference'
- dataSpecificationContent:
- $ref: '#/components/schemas/DataSpecificationContent'
- required:
- - dataSpecification
- - dataSpecificationContent
- DataSpecificationContent:
- oneOf:
- - $ref: '#/components/schemas/DataSpecificationIEC61360Content'
- - $ref: '#/components/schemas/DataSpecificationPhysicalUnitContent'
- DataSpecificationPhysicalUnitContent:
- type: object
- properties:
- unitName:
- type: string
- unitSymbol:
- type: string
- definition:
- type: array
- items:
- $ref: '#/components/schemas/LangString'
- siNotation:
- type: string
- siName:
- type: string
- dinNotation:
- type: string
- eceName:
- type: string
- eceCode:
- type: string
- nistName:
- type: string
- sourceOfDefinition:
- type: string
- conversionFactor:
- type: string
- registrationAuthorityId:
- type: string
- supplier:
- type: string
- required:
- - unitName
- - unitSymbol
- - definition
- DataSpecificationIEC61360Content:
- allOf:
- - $ref: '#/components/schemas/ValueObject'
- - type: object
- properties:
- dataType:
- enum:
- - DATE
- - STRING
- - STRING_TRANSLATABLE
- - REAL_MEASURE
- - REAL_COUNT
- - REAL_CURRENCY
- - BOOLEAN
- - URL
- - RATIONAL
- - RATIONAL_MEASURE
- - TIME
- - TIMESTAMP
- - INTEGER_COUNT
- - INTEGER_MEASURE
- - INTEGER_CURRENCY
- definition:
- type: array
- items:
- $ref: '#/components/schemas/LangString'
- preferredName:
- type: array
- items:
- $ref: '#/components/schemas/LangString'
- shortName:
- type: array
- items:
- $ref: '#/components/schemas/LangString'
- sourceOfDefinition:
- type: string
- symbol:
- type: string
- unit:
- type: string
- unitId:
- $ref: '#/components/schemas/Reference'
- valueFormat:
- type: string
- valueList:
- $ref: '#/components/schemas/ValueList'
- levelType:
- type: array
- items:
- $ref: '#/components/schemas/LevelType'
- required:
- - preferredName
- LevelType:
- type: string
- enum:
- - Min
- - Max
- - Nom
- - Typ
- ValueList:
- type: object
- properties:
- valueReferencePairTypes:
- type: array
- minItems: 1
- items:
- $ref: '#/components/schemas/ValueReferencePairType'
- required:
- - valueReferencePairTypes
- ValueReferencePairType:
- allOf:
- - $ref: '#/components/schemas/ValueObject'
- ValueObject:
- type: object
- properties:
- value:
- type: string
- valueId:
- $ref: '#/components/schemas/Reference'
- valueType:
- type: string
- enum:
- - anyUri
- - base64Binary
- - boolean
- - date
- - dateTime
- - dateTimeStamp
- - decimal
- - integer
- - long
- - int
- - short
- - byte
- - nonNegativeInteger
- - positiveInteger
- - unsignedLong
- - unsignedInt
- - unsignedShort
- - unsignedByte
- - nonPositiveInteger
- - negativeInteger
- - double
- - duration
- - dayTimeDuration
- - yearMonthDuration
- - float
- - gDay
- - gMonth
- - gMonthDay
- - gYear
- - gYearMonth
- - hexBinary
- - NOTATION
- - QName
- - string
- - normalizedString
- - token
- - language
- - Name
- - NCName
- - ENTITY
- - ID
- - IDREF
- - NMTOKEN
- - time
- Asset:
- allOf:
- - $ref: '#/components/schemas/Identifiable'
- - $ref: '#/components/schemas/HasDataSpecification'
- AssetInformation:
- allOf:
- - properties:
- assetKind:
- $ref: '#/components/schemas/AssetKind'
- globalAssetId:
- $ref: '#/components/schemas/Reference'
- externalAssetIds:
- type: array
- items:
- $ref: '#/components/schemas/IdentifierKeyValuePair'
- billOfMaterial:
- type: array
- items:
- $ref: '#/components/schemas/Reference'
- thumbnail:
- $ref: '#/components/schemas/File'
- required:
- - assetKind
- IdentifierKeyValuePair:
- allOf:
- - $ref: '#/components/schemas/HasSemantics'
- - properties:
- key:
- type: string
- value:
- type: string
- subjectId:
- $ref: '#/components/schemas/Reference'
- required:
- - key
- - value
- - subjectId
- AssetKind:
- type: string
- enum:
- - Type
- - Instance
- ModelingKind:
- type: string
- enum:
- - Template
- - Instance
- Submodel:
- allOf:
- - $ref: '#/components/schemas/Identifiable'
- - $ref: '#/components/schemas/HasDataSpecification'
- - $ref: '#/components/schemas/Qualifiable'
- - $ref: '#/components/schemas/HasSemantics'
- - properties:
- kind:
- $ref: '#/components/schemas/ModelingKind'
- submodelElements:
- type: array
- items:
- $ref: '#/components/schemas/SubmodelElement'
- Constraint:
- type: object
- properties:
- modelType:
- $ref: '#/components/schemas/ModelType'
- required:
- - modelType
- Operation:
- allOf:
- - $ref: '#/components/schemas/SubmodelElement'
- - properties:
- inputVariable:
- type: array
- items:
- $ref: '#/components/schemas/OperationVariable'
- outputVariable:
- type: array
- items:
- $ref: '#/components/schemas/OperationVariable'
- inoutputVariable:
- type: array
- items:
- $ref: '#/components/schemas/OperationVariable'
- OperationVariable:
- type: object
- properties:
- value:
- oneOf:
- - $ref: '#/components/schemas/Blob'
- - $ref: '#/components/schemas/File'
- - $ref: '#/components/schemas/Capability'
- - $ref: '#/components/schemas/Entity'
- - $ref: '#/components/schemas/Event'
- - $ref: '#/components/schemas/BasicEvent'
- - $ref: '#/components/schemas/MultiLanguageProperty'
- - $ref: '#/components/schemas/Operation'
- - $ref: '#/components/schemas/Property'
- - $ref: '#/components/schemas/Range'
- - $ref: '#/components/schemas/ReferenceElement'
- - $ref: '#/components/schemas/RelationshipElement'
- - $ref: '#/components/schemas/SubmodelElementCollection'
- required:
- - value
- SubmodelElement:
- allOf:
- - $ref: '#/components/schemas/Referable'
- - $ref: '#/components/schemas/HasDataSpecification'
- - $ref: '#/components/schemas/HasSemantics'
- - $ref: '#/components/schemas/Qualifiable'
- - properties:
- kind:
- $ref: '#/components/schemas/ModelingKind'
- idShort:
- type: string
- required:
- - idShort
- Event:
- allOf:
- - $ref: '#/components/schemas/SubmodelElement'
- BasicEvent:
- allOf:
- - $ref: '#/components/schemas/Event'
- - properties:
- observed:
- $ref: '#/components/schemas/Reference'
- required:
- - observed
- EntityType:
- type: string
- enum:
- - CoManagedEntity
- - SelfManagedEntity
- Entity:
- allOf:
- - $ref: '#/components/schemas/SubmodelElement'
- - properties:
- statements:
- type: array
- items:
- $ref: '#/components/schemas/SubmodelElement'
- entityType:
- $ref: '#/components/schemas/EntityType'
- globalAssetId:
- $ref: '#/components/schemas/Reference'
- specificAssetIds:
- $ref: '#/components/schemas/IdentifierKeyValuePair'
- required:
- - entityType
- View:
- allOf:
- - $ref: '#/components/schemas/Referable'
- - $ref: '#/components/schemas/HasDataSpecification'
- - $ref: '#/components/schemas/HasSemantics'
- - properties:
- containedElements:
- type: array
- items:
- $ref: '#/components/schemas/Reference'
- ConceptDescription:
- allOf:
- - $ref: '#/components/schemas/Identifiable'
- - $ref: '#/components/schemas/HasDataSpecification'
- - properties:
- isCaseOf:
- type: array
- items:
- $ref: '#/components/schemas/Reference'
- Capability:
- allOf:
- - $ref: '#/components/schemas/SubmodelElement'
- Property:
- allOf:
- - $ref: '#/components/schemas/SubmodelElement'
- - $ref: '#/components/schemas/ValueObject'
- Range:
- allOf:
- - $ref: '#/components/schemas/SubmodelElement'
- - properties:
- valueType:
- type: string
- enum:
- - anyUri
- - base64Binary
- - boolean
- - date
- - dateTime
- - dateTimeStamp
- - decimal
- - integer
- - long
- - int
- - short
- - byte
- - nonNegativeInteger
- - positiveInteger
- - unsignedLong
- - unsignedInt
- - unsignedShort
- - unsignedByte
- - nonPositiveInteger
- - negativeInteger
- - double
- - duration
- - dayTimeDuration
- - yearMonthDuration
- - float
- - gDay
- - gMonth
- - gMonthDay
- - gYear
- - gYearMonth
- - hexBinary
- - NOTATION
- - QName
- - string
- - normalizedString
- - token
- - language
- - Name
- - NCName
- - ENTITY
- - ID
- - IDREF
- - NMTOKEN
- - time
- min:
- type: string
- max:
- type: string
- required:
- - valueType
- MultiLanguageProperty:
- allOf:
- - $ref: '#/components/schemas/SubmodelElement'
- - properties:
- value:
- type: array
- items:
- $ref: '#/components/schemas/LangString'
- valueId:
- $ref: '#/components/schemas/Reference'
- File:
- allOf:
- - $ref: '#/components/schemas/SubmodelElement'
- - properties:
- value:
- type: string
- mimeType:
- type: string
- required:
- - mimeType
- Blob:
- allOf:
- - $ref: '#/components/schemas/SubmodelElement'
- - properties:
- value:
- type: string
- mimeType:
- type: string
- required:
- - mimeType
- ReferenceElement:
- allOf:
- - $ref: '#/components/schemas/SubmodelElement'
- - properties:
- value:
- $ref: '#/components/schemas/Reference'
- SubmodelElementCollection:
- allOf:
- - $ref: '#/components/schemas/SubmodelElement'
- - properties:
- value:
- type: array
- items:
- oneOf:
- - $ref: '#/components/schemas/Blob'
- - $ref: '#/components/schemas/File'
- - $ref: '#/components/schemas/Capability'
- - $ref: '#/components/schemas/Entity'
- - $ref: '#/components/schemas/Event'
- - $ref: '#/components/schemas/BasicEvent'
- - $ref: '#/components/schemas/MultiLanguageProperty'
- - $ref: '#/components/schemas/Operation'
- - $ref: '#/components/schemas/Property'
- - $ref: '#/components/schemas/Range'
- - $ref: '#/components/schemas/ReferenceElement'
- - $ref: '#/components/schemas/RelationshipElement'
- - $ref: '#/components/schemas/SubmodelElementCollection'
- allowDuplicates:
- type: boolean
- ordered:
- type: boolean
- RelationshipElement:
- allOf:
- - $ref: '#/components/schemas/SubmodelElement'
- - properties:
- first:
- $ref: '#/components/schemas/Reference'
- second:
- $ref: '#/components/schemas/Reference'
- required:
- - first
- - second
- AnnotatedRelationshipElement:
- allOf:
- - $ref: '#/components/schemas/RelationshipElement'
- - properties:
- annotation:
- type: array
- items:
- oneOf:
- - $ref: '#/components/schemas/Blob'
- - $ref: '#/components/schemas/File'
- - $ref: '#/components/schemas/MultiLanguageProperty'
- - $ref: '#/components/schemas/Property'
- - $ref: '#/components/schemas/Range'
- - $ref: '#/components/schemas/ReferenceElement'
- Qualifier:
- allOf:
- - $ref: '#/components/schemas/Constraint'
- - $ref: '#/components/schemas/HasSemantics'
- - $ref: '#/components/schemas/ValueObject'
- - properties:
- type:
- type: string
- required:
- - type
- Formula:
- allOf:
- - $ref: '#/components/schemas/Constraint'
- - properties:
- dependsOn:
- type: array
- items:
- $ref: '#/components/schemas/Reference'
- Security:
- type: object
- properties:
- accessControlPolicyPoints:
- $ref: '#/components/schemas/AccessControlPolicyPoints'
- certificate:
- type: array
- items:
- oneOf:
- - $ref: '#/components/schemas/BlobCertificate'
- requiredCertificateExtension:
- type: array
- items:
- $ref: '#/components/schemas/Reference'
- required:
- - accessControlPolicyPoints
- Certificate:
- type: object
- BlobCertificate:
- allOf:
- - $ref: '#/components/schemas/Certificate'
- - properties:
- blobCertificate:
- $ref: '#/components/schemas/Blob'
- containedExtension:
- type: array
- items:
- $ref: '#/components/schemas/Reference'
- lastCertificate:
- type: boolean
- AccessControlPolicyPoints:
- type: object
- properties:
- policyAdministrationPoint:
- $ref: '#/components/schemas/PolicyAdministrationPoint'
- policyDecisionPoint:
- $ref: '#/components/schemas/PolicyDecisionPoint'
- policyEnforcementPoint:
- $ref: '#/components/schemas/PolicyEnforcementPoint'
- policyInformationPoints:
- $ref: '#/components/schemas/PolicyInformationPoints'
- required:
- - policyAdministrationPoint
- - policyDecisionPoint
- - policyEnforcementPoint
- PolicyAdministrationPoint:
- type: object
- properties:
- localAccessControl:
- $ref: '#/components/schemas/AccessControl'
- externalAccessControl:
- type: boolean
- required:
- - externalAccessControl
- PolicyInformationPoints:
- type: object
- properties:
- internalInformationPoint:
- type: array
- items:
- $ref: '#/components/schemas/Reference'
- externalInformationPoint:
- type: boolean
- required:
- - externalInformationPoint
- PolicyEnforcementPoint:
- type: object
- properties:
- externalPolicyEnforcementPoint:
- type: boolean
- required:
- - externalPolicyEnforcementPoint
- PolicyDecisionPoint:
- type: object
- properties:
- externalPolicyDecisionPoints:
- type: boolean
- required:
- - externalPolicyDecisionPoints
- AccessControl:
- type: object
- properties:
- selectableSubjectAttributes:
- $ref: '#/components/schemas/Reference'
- defaultSubjectAttributes:
- $ref: '#/components/schemas/Reference'
- selectablePermissions:
- $ref: '#/components/schemas/Reference'
- defaultPermissions:
- $ref: '#/components/schemas/Reference'
- selectableEnvironmentAttributes:
- $ref: '#/components/schemas/Reference'
- defaultEnvironmentAttributes:
- $ref: '#/components/schemas/Reference'
- accessPermissionRule:
- type: array
- items:
- $ref: '#/components/schemas/AccessPermissionRule'
- AccessPermissionRule:
- allOf:
- - $ref: '#/components/schemas/Referable'
- - $ref: '#/components/schemas/Qualifiable'
- - properties:
- targetSubjectAttributes:
- type: array
- items:
- $ref: '#/components/schemas/SubjectAttributes'
- minItems: 1
- permissionsPerObject:
- type: array
- items:
- $ref: '#/components/schemas/PermissionsPerObject'
- required:
- - targetSubjectAttributes
- SubjectAttributes:
- type: object
- properties:
- subjectAttributes:
- type: array
- items:
- $ref: '#/components/schemas/Reference'
- minItems: 1
- PermissionsPerObject:
- type: object
- properties:
- object:
- $ref: '#/components/schemas/Reference'
- targetObjectAttributes:
- $ref: '#/components/schemas/ObjectAttributes'
- permission:
- type: array
- items:
- $ref: '#/components/schemas/Permission'
- ObjectAttributes:
- type: object
- properties:
- objectAttribute:
- type: array
- items:
- $ref: '#/components/schemas/Property'
- minItems: 1
- Permission:
- type: object
- properties:
- permission:
- $ref: '#/components/schemas/Reference'
- kindOfPermission:
- type: string
- enum:
- - Allow
- - Deny
- - NotApplicable
- - Undefined
- required:
- - permission
- - kindOfPermission
diff --git a/sdk/test/adapter/json/test_json_deserialization.py b/sdk/test/adapter/json/test_json_deserialization.py
index 0dba6dbdb..9067d3e7b 100644
--- a/sdk/test/adapter/json/test_json_deserialization.py
+++ b/sdk/test/adapter/json/test_json_deserialization.py
@@ -1,4 +1,4 @@
-# Copyright (c) 2025 the Eclipse BaSyx Authors
+# Copyright (c) 2026 the Eclipse BaSyx Authors
#
# This program and the accompanying materials are made available under the terms of the MIT License, available in
# the LICENSE file of this project.
@@ -29,10 +29,10 @@ def test_file_format_wrong_list(self) -> None:
"submodels": [
{
"modelType": "AssetAdministrationShell",
- "id": "https://acplt.org/Test_Asset",
+ "id": "https://example.org/Test_Asset",
"assetInformation": {
"assetKind": "Instance",
- "globalAssetId": "https://acplt.org/Test_AssetId"
+ "globalAssetId": "https://example.org/Test_AssetId"
}
}
]
@@ -70,11 +70,11 @@ def test_broken_submodel(self) -> None:
},
{
"modelType": "Submodel",
- "id": ["https://acplt.org/Test_Submodel_broken_id", "IRI"]
+ "id": ["https://example.org/Test_Submodel_broken_id", "IRI"]
},
{
"modelType": "Submodel",
- "id": "https://acplt.org/Test_Submodel"
+ "id": "https://example.org/Test_Submodel"
}
]"""
# In strict mode, we should catch an exception
@@ -91,18 +91,18 @@ def test_broken_submodel(self) -> None:
self.assertIsInstance(parsed_data[0], dict)
self.assertIsInstance(parsed_data[1], dict)
self.assertIsInstance(parsed_data[2], model.Submodel)
- self.assertEqual("https://acplt.org/Test_Submodel", parsed_data[2].id)
+ self.assertEqual("https://example.org/Test_Submodel", parsed_data[2].id)
def test_wrong_submodel_element_type(self) -> None:
data = """
[
{
"modelType": "Submodel",
- "id": "http://acplt.org/Submodels/Assets/TestAsset/Identification",
+ "id": "http://example.org/Submodels/Assets/TestAsset/Identification",
"submodelElements": [
{
"modelType": "Submodel",
- "id": "https://acplt.org/Test_Submodel"
+ "id": "https://example.org/Test_Submodel"
},
{
"modelType": {
@@ -142,15 +142,15 @@ def test_duplicate_identifier(self) -> None:
{
"assetAdministrationShells": [{
"modelType": "AssetAdministrationShell",
- "id": "http://acplt.org/test_aas",
+ "id": "http://example.org/test_aas",
"assetInformation": {
"assetKind": "Instance",
- "globalAssetId": "https://acplt.org/Test_AssetId"
+ "globalAssetId": "https://example.org/Test_AssetId"
}
}],
"submodels": [{
"modelType": "Submodel",
- "id": "http://acplt.org/test_aas"
+ "id": "http://example.org/test_aas"
}],
"conceptDescriptions": []
}"""
@@ -162,11 +162,11 @@ def test_duplicate_identifier(self) -> None:
with self.assertRaisesRegex(KeyError, r"duplicate identifier"):
read_aas_json_file(string_io, failsafe=False)
- def test_duplicate_identifier_object_store(self) -> None:
- sm_id = "http://acplt.org/test_submodel"
+ def test_duplicate_identifier_identifiable_store(self) -> None:
+ sm_id = "http://example.org/test_submodel"
- def get_clean_store() -> model.DictObjectStore:
- store: model.DictObjectStore = model.DictObjectStore()
+ def get_clean_store() -> model.DictIdentifiableStore:
+ store: model.DictIdentifiableStore[model.Identifiable] = model.DictIdentifiableStore()
submodel_ = model.Submodel(sm_id, id_short="test123")
store.add(submodel_)
return store
@@ -175,7 +175,7 @@ def get_clean_store() -> model.DictObjectStore:
{
"submodels": [{
"modelType": "Submodel",
- "id": "http://acplt.org/test_submodel",
+ "id": "http://example.org/test_submodel",
"idShort": "test456"
}],
"assetAdministrationShells": [],
@@ -184,32 +184,36 @@ def get_clean_store() -> model.DictObjectStore:
string_io = io.StringIO(data)
- object_store = get_clean_store()
- identifiers = read_aas_json_file_into(object_store, string_io, replace_existing=True, ignore_existing=False)
+ identifiable_store = get_clean_store()
+ identifiers = read_aas_json_file_into(
+ identifiable_store, string_io, replace_existing=True, ignore_existing=False
+ )
self.assertEqual(identifiers.pop(), sm_id)
- submodel = object_store.pop()
+ submodel = identifiable_store.pop()
self.assertIsInstance(submodel, model.Submodel)
self.assertEqual(submodel.id_short, "test456")
string_io.seek(0)
- object_store = get_clean_store()
+ identifiable_store = get_clean_store()
with self.assertLogs(logging.getLogger(), level=logging.INFO) as log_ctx:
- identifiers = read_aas_json_file_into(object_store, string_io, replace_existing=False, ignore_existing=True)
+ identifiers = read_aas_json_file_into(
+ identifiable_store, string_io, replace_existing=False, ignore_existing=True
+ )
self.assertEqual(len(identifiers), 0)
self.assertIn("already exists in store", log_ctx.output[0]) # type: ignore
- submodel = object_store.pop()
+ submodel = identifiable_store.pop()
self.assertIsInstance(submodel, model.Submodel)
self.assertEqual(submodel.id_short, "test123")
string_io.seek(0)
- object_store = get_clean_store()
+ identifiable_store = get_clean_store()
with self.assertRaisesRegex(KeyError, r"already exists in store"):
- identifiers = read_aas_json_file_into(object_store, string_io, replace_existing=False,
+ identifiers = read_aas_json_file_into(identifiable_store, string_io, replace_existing=False,
ignore_existing=False)
self.assertEqual(len(identifiers), 0)
- submodel = object_store.pop()
+ submodel = identifiable_store.pop()
self.assertIsInstance(submodel, model.Submodel)
self.assertEqual(submodel.id_short, "test123")
@@ -230,7 +234,7 @@ def _construct_submodel(cls, dct, object_class=EnhancedSubmodel):
[
{
"modelType": "Submodel",
- "id": "https://acplt.org/Test_Submodel"
+ "id": "https://example.org/Test_Submodel"
}
]"""
parsed_data = json.loads(data, cls=EnhancedAASDecoder)
@@ -244,7 +248,7 @@ def test_stripped_qualifiable(self) -> None:
data = """
{
"modelType": "Submodel",
- "id": "http://acplt.org/test_stripped_submodel",
+ "id": "http://example.org/test_stripped_submodel",
"submodelElements": [{
"modelType": "Operation",
"idShort": "test_operation",
@@ -285,7 +289,7 @@ def test_stripped_annotated_relationship_element(self) -> None:
"keys": [
{
"type": "Submodel",
- "value": "http://acplt.org/Test_Submodel"
+ "value": "http://example.org/Test_Submodel"
},
{
"type": "AnnotatedRelationshipElement",
@@ -298,7 +302,7 @@ def test_stripped_annotated_relationship_element(self) -> None:
"keys": [
{
"type": "Submodel",
- "value": "http://acplt.org/Test_Submodel"
+ "value": "http://example.org/Test_Submodel"
},
{
"type": "AnnotatedRelationshipElement",
@@ -325,6 +329,31 @@ def test_stripped_annotated_relationship_element(self) -> None:
assert isinstance(are, model.AnnotatedRelationshipElement)
self.assertEqual(len(are.annotation), 0)
+ def test_optional_first_second_relationship_element(self) -> None:
+ data = """
+ {
+ "modelType": "RelationshipElement",
+ "idShort": "test_optional_second_relationship_element",
+ "category": "PARAMETER",
+ "first": {
+ "type": "ModelReference",
+ "keys": [
+ {
+ "type": "Submodel",
+ "value": "http://example.org/Test_Submodel"
+ },
+ {
+ "type": "AnnotatedRelationshipElement",
+ "value": "test_ref"
+ }
+ ]
+ }
+ }"""
+
+ re = json.loads(data, cls=StrictAASFromJsonDecoder)
+ self.assertIsInstance(re, model.RelationshipElement)
+ self.assertIsNone(re.second)
+
def test_stripped_entity(self) -> None:
data = """
{
@@ -377,7 +406,7 @@ def test_stripped_asset_administration_shell(self) -> None:
data = """
{
"modelType": "AssetAdministrationShell",
- "id": "http://acplt.org/test_aas",
+ "id": "http://example.org/test_aas",
"assetInformation": {
"assetKind": "Instance",
"globalAssetId": "test_asset"
@@ -386,7 +415,7 @@ def test_stripped_asset_administration_shell(self) -> None:
"type": "ModelReference",
"keys": [{
"type": "Submodel",
- "value": "http://acplt.org/test_submodel"
+ "value": "http://example.org/test_submodel"
}]
}]
}"""
diff --git a/sdk/test/adapter/json/test_json_serialization.py b/sdk/test/adapter/json/test_json_serialization.py
index 8e9bc8d01..136c04110 100644
--- a/sdk/test/adapter/json/test_json_serialization.py
+++ b/sdk/test/adapter/json/test_json_serialization.py
@@ -1,4 +1,4 @@
-# Copyright (c) 2025 the Eclipse BaSyx Authors
+# Copyright (c) 2026 the Eclipse BaSyx Authors
#
# This program and the accompanying materials are made available under the terms of the MIT License, available in
# the LICENSE file of this project.
@@ -63,7 +63,7 @@ def test_random_object_serialization(self) -> None:
# must be a Reference. (This seems to be a bug in the JSONSchema.)
submodel = model.Submodel(submodel_identifier,
semantic_id=model.ExternalReference((model.Key(model.KeyTypes.GLOBAL_REFERENCE,
- "http://acplt.org/TestSemanticId"),)))
+ "http://example.org/TestSemanticId"),)))
test_aas = model.AssetAdministrationShell(model.AssetInformation(global_asset_id="test"),
aas_identifier, submodel={submodel_reference})
@@ -96,7 +96,7 @@ def test_aas_example_serialization(self) -> None:
validate(instance=json_data, schema=aas_json_schema)
def test_submodel_template_serialization(self) -> None:
- data: model.DictObjectStore[model.Identifiable] = model.DictObjectStore()
+ data: model.DictIdentifiableStore[model.Identifiable] = model.DictIdentifiableStore()
data.add(example_submodel_template.create_example_submodel_template())
file = io.StringIO()
write_aas_json_file(file=file, data=data)
@@ -139,7 +139,7 @@ def test_missing_serialization(self) -> None:
validate(instance=json_data, schema=aas_json_schema)
def test_concept_description_serialization(self) -> None:
- data: model.DictObjectStore[model.Identifiable] = model.DictObjectStore()
+ data: model.DictIdentifiableStore[model.Identifiable] = model.DictIdentifiableStore()
data.add(example_aas.create_example_concept_description())
file = io.StringIO()
write_aas_json_file(file=file, data=data)
@@ -185,7 +185,7 @@ def test_stripped_qualifiable(self) -> None:
qualifier2 = model.Qualifier("test_qualifier2", str)
operation = model.Operation("test_operation", qualifier={qualifier})
submodel = model.Submodel(
- "http://acplt.org/test_submodel",
+ "http://example.org/test_submodel",
submodel_element=[operation],
qualifier={qualifier2}
)
@@ -196,7 +196,7 @@ def test_stripped_qualifiable(self) -> None:
def test_stripped_annotated_relationship_element(self) -> None:
mlp = model.MultiLanguageProperty("test_multi_language_property", category="PARAMETER")
ref = model.ModelReference(
- (model.Key(model.KeyTypes.SUBMODEL, "http://acplt.org/test_ref"),),
+ (model.Key(model.KeyTypes.SUBMODEL, "http://example.org/test_ref"),),
model.Submodel
)
are = model.AnnotatedRelationshipElement(
@@ -208,6 +208,12 @@ def test_stripped_annotated_relationship_element(self) -> None:
self._checkNormalAndStripped("annotations", are)
+ def test_relationship_element_omits_none_first_second(self) -> None:
+ re = model.RelationshipElement("test_re")
+ data = json.loads(json.dumps(re, cls=AASToJsonEncoder))
+ self.assertNotIn("first", data)
+ self.assertNotIn("second", data)
+
def test_stripped_entity(self) -> None:
mlp = model.MultiLanguageProperty("test_multi_language_property", category="PARAMETER")
entity = model.Entity("test_entity", model.EntityType.CO_MANAGED_ENTITY, statement=[mlp])
@@ -222,12 +228,12 @@ def test_stripped_submodel_element_collection(self) -> None:
def test_stripped_asset_administration_shell(self) -> None:
submodel_ref = model.ModelReference(
- (model.Key(model.KeyTypes.SUBMODEL, "http://acplt.org/test_ref"),),
+ (model.Key(model.KeyTypes.SUBMODEL, "http://example.org/test_ref"),),
model.Submodel
)
aas = model.AssetAdministrationShell(
- model.AssetInformation(global_asset_id="http://acplt.org/test_ref"),
- "http://acplt.org/test_aas",
+ model.AssetInformation(global_asset_id="http://example.org/test_ref"),
+ "http://example.org/test_aas",
submodel={submodel_ref}
)
diff --git a/sdk/test/adapter/json/test_json_serialization_deserialization.py b/sdk/test/adapter/json/test_json_serialization_deserialization.py
index e33921a21..48beb4c27 100644
--- a/sdk/test/adapter/json/test_json_serialization_deserialization.py
+++ b/sdk/test/adapter/json/test_json_serialization_deserialization.py
@@ -1,4 +1,4 @@
-# Copyright (c) 2025 the Eclipse BaSyx Authors
+# Copyright (c) 2026 the Eclipse BaSyx Authors
#
# This program and the accompanying materials are made available under the terms of the MIT License, available in
# the LICENSE file of this project.
@@ -39,8 +39,8 @@ def test_random_object_serialization_deserialization(self) -> None:
}, cls=AASToJsonEncoder)
json_data_new = json.loads(json_data)
- # try deserializing the json string into a DictObjectStore of AAS objects with help of the json module
- json_object_store = read_aas_json_file(io.StringIO(json_data), failsafe=False)
+ # try deserializing the json string into a DictIdentifiableStore of AAS objects with help of the json module
+ json_identifiable_store = read_aas_json_file(io.StringIO(json_data), failsafe=False)
def test_example_serialization_deserialization(self) -> None:
# test with TextIO and BinaryIO, which should both be supported
@@ -49,11 +49,11 @@ def test_example_serialization_deserialization(self) -> None:
data = example_aas.create_full_example()
write_aas_json_file(file=file, data=data)
- # try deserializing the json string into a DictObjectStore of AAS objects with help of the json module
+ # try deserializing the json string into a DictIdentifiableStore of AAS objects with help of the json module
file.seek(0)
- json_object_store = read_aas_json_file(file, failsafe=False)
+ json_identifiable_store = read_aas_json_file(file, failsafe=False)
checker = AASDataChecker(raise_immediately=True)
- example_aas.check_full_example(checker, json_object_store)
+ example_aas.check_full_example(checker, json_identifiable_store)
class JsonSerializationDeserializationTest2(unittest.TestCase):
@@ -62,11 +62,11 @@ def test_example_mandatory_attributes_serialization_deserialization(self) -> Non
file = io.StringIO()
write_aas_json_file(file=file, data=data)
- # try deserializing the json string into a DictObjectStore of AAS objects with help of the json module
+ # try deserializing the json string into a DictIdentifiableStore of AAS objects with help of the json module
file.seek(0)
- json_object_store = read_aas_json_file(file, failsafe=False)
+ json_identifiable_store = read_aas_json_file(file, failsafe=False)
checker = AASDataChecker(raise_immediately=True)
- example_aas_mandatory_attributes.check_full_example(checker, json_object_store)
+ example_aas_mandatory_attributes.check_full_example(checker, json_identifiable_store)
class JsonSerializationDeserializationTest3(unittest.TestCase):
@@ -74,33 +74,33 @@ def test_example_missing_attributes_serialization_deserialization(self) -> None:
data = example_aas_missing_attributes.create_full_example()
file = io.StringIO()
write_aas_json_file(file=file, data=data)
- # try deserializing the json string into a DictObjectStore of AAS objects with help of the json module
+ # try deserializing the json string into a DictIdentifiableStore of AAS objects with help of the json module
file.seek(0)
- json_object_store = read_aas_json_file(file, failsafe=False)
+ json_identifiable_store = read_aas_json_file(file, failsafe=False)
checker = AASDataChecker(raise_immediately=True)
- example_aas_missing_attributes.check_full_example(checker, json_object_store)
+ example_aas_missing_attributes.check_full_example(checker, json_identifiable_store)
class JsonSerializationDeserializationTest4(unittest.TestCase):
def test_example_submodel_template_serialization_deserialization(self) -> None:
- data: model.DictObjectStore[model.Identifiable] = model.DictObjectStore()
+ data: model.DictIdentifiableStore[model.Identifiable] = model.DictIdentifiableStore()
data.add(example_submodel_template.create_example_submodel_template())
file = io.StringIO()
write_aas_json_file(file=file, data=data)
- # try deserializing the json string into a DictObjectStore of AAS objects with help of the json module
+ # try deserializing the json string into a DictIdentifiableStore of AAS objects with help of the json module
file.seek(0)
- json_object_store = read_aas_json_file(file, failsafe=False)
+ json_identifiable_store = read_aas_json_file(file, failsafe=False)
checker = AASDataChecker(raise_immediately=True)
- example_submodel_template.check_full_example(checker, json_object_store)
+ example_submodel_template.check_full_example(checker, json_identifiable_store)
class JsonSerializationDeserializationTest5(unittest.TestCase):
def test_example_all_examples_serialization_deserialization(self) -> None:
- data: model.DictObjectStore[model.Identifiable] = create_example()
+ data: model.DictIdentifiableStore[model.Identifiable] = create_example()
file = io.StringIO()
write_aas_json_file(file=file, data=data)
- # try deserializing the json string into a DictObjectStore of AAS objects with help of the json module
+ # try deserializing the json string into a DictIdentifiableStore of AAS objects with help of the json module
file.seek(0)
- json_object_store = read_aas_json_file(file, failsafe=False)
+ json_identifiable_store = read_aas_json_file(file, failsafe=False)
checker = AASDataChecker(raise_immediately=True)
- checker.check_object_store(json_object_store, data)
+ checker.check_identifiable_store(json_identifiable_store, data)
diff --git a/sdk/test/adapter/test_load_directory.py b/sdk/test/adapter/test_load_directory.py
new file mode 100644
index 000000000..46b87f5d0
--- /dev/null
+++ b/sdk/test/adapter/test_load_directory.py
@@ -0,0 +1,100 @@
+import unittest
+import tempfile
+from pathlib import Path
+
+from basyx.aas import model
+from basyx.aas import adapter
+
+
+class LoadDirectoryTest(unittest.TestCase):
+ def test_reading_all_files(self):
+ # ----- Arange ----
+ # Create an AAS per file type
+ json_aas = model.AssetAdministrationShell(
+ id_="http://example.org/JSON_AAS",
+ asset_information=model.AssetInformation(
+ global_asset_id="http://example.org/JSON_Asset"
+ )
+ )
+
+ xml_aas = model.AssetAdministrationShell(
+ id_="http://example.org/XML_AAS",
+ asset_information=model.AssetInformation(
+ global_asset_id="http://example.org/XML_Asset"
+ )
+ )
+
+ aasx_aas = model.AssetAdministrationShell(
+ id_="http://example.org/aasx_AAS",
+ asset_information=model.AssetInformation(
+ global_asset_id="http://example.org/aasx_Asset"
+ )
+ )
+
+ # load TestFile.pdf to save into aasx
+ file_container = adapter.aasx.DictSupplementaryFileContainer()
+ with open(Path(__file__).parent / "aasx" / "TestFile.pdf", "rb") as pdf:
+ resulting_file_name = file_container.add_file(
+ "/aasx/suppl/file.pdf", pdf, "application/json")
+
+ # create submodel for aasx_aas that refers to pdf
+ sm_with_file = model.Submodel(
+ id_="http://example.org/tmp_Submodel",
+ submodel_element={
+ model.File(
+ id_short="SampleFile",
+ content_type="application/json",
+ value=resulting_file_name
+ )
+ }
+ )
+ aasx_aas.submodel.add(model.ModelReference.from_referable(sm_with_file))
+
+ with tempfile.TemporaryDirectory() as temp_dir:
+ temp_dir_path = Path(temp_dir)
+
+ # save to json file
+ adapter.json.write_aas_json_file(temp_dir_path / "testAAS.json",
+ model.DictIdentifiableStore([json_aas]))
+ # save to xml file
+ adapter.xml.write_aas_xml_file(temp_dir_path / "testAAS.xml",
+ model.DictIdentifiableStore([xml_aas]))
+ # save to aasx file
+ with adapter.aasx.AASXWriter(temp_dir_path / "testAAS.aasx") as writer:
+ writer.write_aas(
+ aas_ids=["http://example.org/aasx_AAS"],
+ object_store=model.DictIdentifiableStore([aasx_aas, sm_with_file]),
+ file_store=file_container
+ )
+
+ # ---- Act ----
+ new_object_store, new_file_store = adapter.load_directory(temp_dir_path)
+
+ # ---- Assert -----
+ # check for all three AAS
+ self.assertIn("http://example.org/JSON_AAS", new_object_store)
+ self.assertIn("http://example.org/XML_AAS", new_object_store)
+ self.assertIn("http://example.org/aasx_AAS", new_object_store)
+
+ # check pdf is loaded
+ self.assertIn(resulting_file_name, new_file_store)
+
+ def test_skipping_other_files(self):
+ with tempfile.TemporaryDirectory() as tmp_dir:
+ # ---- Arange ----
+ tmp_dir_path = Path(tmp_dir)
+
+ # create empty file
+ open(tmp_dir_path / "test.txt", "a").close()
+
+ # create directory
+ (tmp_dir_path / "empty").mkdir()
+
+ # ---- Act ----
+ # assert no exception is occurring
+ object_store, file_store = adapter.load_directory(tmp_dir_path)
+
+ # ---- Assert ----
+ # check stores are empty
+ self.assertEqual(len(object_store), 0)
+ self.assertEqual(len(file_store), 0)
diff --git a/sdk/test/adapter/xml/test_xml_deserialization.py b/sdk/test/adapter/xml/test_xml_deserialization.py
index 331ad98c5..395d23ed7 100644
--- a/sdk/test/adapter/xml/test_xml_deserialization.py
+++ b/sdk/test/adapter/xml/test_xml_deserialization.py
@@ -1,4 +1,4 @@
-# Copyright (c) 2025 the Eclipse BaSyx Authors
+# Copyright (c) 2026 the Eclipse BaSyx Authors
#
# This program and the accompanying materials are made available under the terms of the MIT License, available in
# the LICENSE file of this project.
@@ -77,9 +77,9 @@ def test_missing_asset_kind(self) -> None:
xml = _xml_wrap("""
- http://acplt.org/test_aas
+ http://example.org/test_aas
- http://acplt.org/TestAsset/
+ http://example.org/TestAsset/
@@ -90,10 +90,10 @@ def test_missing_asset_kind_text(self) -> None:
xml = _xml_wrap("""
- http://acplt.org/test_aas
+ http://example.org/test_aas
- http://acplt.org/TestAsset/
+ http://example.org/TestAsset/
@@ -104,10 +104,10 @@ def test_invalid_asset_kind_text(self) -> None:
xml = _xml_wrap("""
- http://acplt.org/test_aas
+ http://example.org/test_aas
invalidKind
- http://acplt.org/TestAsset/
+ http://example.org/TestAsset/
@@ -118,7 +118,7 @@ def test_invalid_boolean(self) -> None:
xml = _xml_wrap("""
- http://acplt.org/test_submodel
+ http://example.org/test_submodel
False
@@ -135,14 +135,14 @@ def test_no_modelling_kind(self) -> None:
xml = _xml_wrap("""
- http://acplt.org/test_submodel
+ http://example.org/test_submodel
""")
# should get parsed successfully
- object_store = read_aas_xml_file(io.StringIO(xml), failsafe=False)
+ identifiable_store = read_aas_xml_file(io.StringIO(xml), failsafe=False)
# modelling kind should default to INSTANCE
- submodel = object_store.pop()
+ submodel = identifiable_store.pop()
self.assertIsInstance(submodel, model.Submodel)
assert isinstance(submodel, model.Submodel) # to make mypy happy
self.assertEqual(submodel.kind, model.ModellingKind.INSTANCE)
@@ -151,17 +151,17 @@ def test_reference_kind_mismatch(self) -> None:
xml = _xml_wrap("""
- http://acplt.org/test_aas
+ http://example.org/test_aas
Instance
- http://acplt.org/TestAsset/
+ http://example.org/TestAsset/
ModelReference
Submodel
- http://acplt.org/test_ref
+ http://example.org/test_ref
@@ -170,14 +170,14 @@ def test_reference_kind_mismatch(self) -> None:
""")
with self.assertLogs(logging.getLogger(), level=logging.WARNING) as context:
read_aas_xml_file(io.StringIO(xml), failsafe=False)
- for s in ("SUBMODEL", "http://acplt.org/test_ref", "AssetAdministrationShell"):
+ for s in ("SUBMODEL", "http://example.org/test_ref", "AssetAdministrationShell"):
self.assertIn(s, context.output[0])
def test_invalid_submodel_element(self) -> None:
xml = _xml_wrap("""
- http://acplt.org/test_submodel
+ http://example.org/test_submodel
@@ -190,7 +190,7 @@ def test_empty_qualifier(self) -> None:
xml = _xml_wrap("""
- http://acplt.org/test_submodel
+ http://example.org/test_submodel
@@ -203,7 +203,7 @@ def test_operation_variable_no_submodel_element(self) -> None:
xml = _xml_wrap("""
- http://acplt.org/test_submodel
+ http://example.org/test_submodel
test_operation
@@ -223,7 +223,7 @@ def test_operation_variable_too_many_submodel_elements(self) -> None:
xml = _xml_wrap("""
- http://acplt.org/test_submodel
+ http://example.org/test_submodel
test_operation
@@ -256,26 +256,26 @@ def test_duplicate_identifier(self) -> None:
xml = _xml_wrap("""
- http://acplt.org/test_aas
+ http://example.org/test_aas
Instance
- http://acplt.org/TestAsset/
+ http://example.org/TestAsset/
- http://acplt.org/test_aas
+ http://example.org/test_aas
""")
self._assertInExceptionAndLog(xml, "duplicate identifier", KeyError, logging.ERROR)
- def test_duplicate_identifier_object_store(self) -> None:
- sm_id = "http://acplt.org/test_submodel"
+ def test_duplicate_identifier_identifiable_store(self) -> None:
+ sm_id = "http://example.org/test_submodel"
- def get_clean_store() -> model.DictObjectStore:
- store: model.DictObjectStore = model.DictObjectStore()
+ def get_clean_store() -> model.DictIdentifiableStore:
+ store: model.DictIdentifiableStore[model.Identifiable] = model.DictIdentifiableStore()
submodel_ = model.Submodel(sm_id, id_short="test123")
store.add(submodel_)
return store
@@ -283,43 +283,49 @@ def get_clean_store() -> model.DictObjectStore:
xml = _xml_wrap("""
- http://acplt.org/test_submodel
+ http://example.org/test_submodel
test456
""")
string_io = io.StringIO(xml)
- object_store = get_clean_store()
- identifiers = read_aas_xml_file_into(object_store, string_io, replace_existing=True, ignore_existing=False)
+ identifiable_store = get_clean_store()
+ identifiers = read_aas_xml_file_into(
+ identifiable_store, string_io, replace_existing=True, ignore_existing=False
+ )
self.assertEqual(identifiers.pop(), sm_id)
- submodel = object_store.pop()
+ submodel = identifiable_store.pop()
self.assertIsInstance(submodel, model.Submodel)
self.assertEqual(submodel.id_short, "test456")
- object_store = get_clean_store()
+ identifiable_store = get_clean_store()
with self.assertLogs(logging.getLogger(), level=logging.INFO) as log_ctx:
- identifiers = read_aas_xml_file_into(object_store, string_io, replace_existing=False, ignore_existing=True)
+ identifiers = read_aas_xml_file_into(
+ identifiable_store, string_io, replace_existing=False, ignore_existing=True
+ )
self.assertEqual(len(identifiers), 0)
self.assertIn("already exists in the object store", log_ctx.output[0])
- submodel = object_store.pop()
+ submodel = identifiable_store.pop()
self.assertIsInstance(submodel, model.Submodel)
self.assertEqual(submodel.id_short, "test123")
- object_store = get_clean_store()
+ identifiable_store = get_clean_store()
with self.assertRaises(KeyError) as err_ctx:
- identifiers = read_aas_xml_file_into(object_store, string_io, replace_existing=False, ignore_existing=False)
+ identifiers = read_aas_xml_file_into(
+ identifiable_store, string_io, replace_existing=False, ignore_existing=False
+ )
self.assertEqual(len(identifiers), 0)
cause = _root_cause(err_ctx.exception)
self.assertIn("already exists in the object store", str(cause))
- submodel = object_store.pop()
+ submodel = identifiable_store.pop()
self.assertIsInstance(submodel, model.Submodel)
self.assertEqual(submodel.id_short, "test123")
def test_read_aas_xml_element(self) -> None:
xml = f"""
- http://acplt.org/test_submodel
+ http://example.org/test_submodel
"""
string_io = io.StringIO(xml)
@@ -348,7 +354,7 @@ class XmlDeserializationStrippedObjectsTest(unittest.TestCase):
def test_stripped_qualifiable(self) -> None:
xml = f"""
- http://acplt.org/test_stripped_submodel
+ http://example.org/test_stripped_submodel
test_operation
@@ -388,10 +394,10 @@ def test_stripped_qualifiable(self) -> None:
def test_stripped_asset_administration_shell(self) -> None:
xml = f"""
- http://acplt.org/test_aas
+ http://example.org/test_aas
Instance
- http://acplt.org/TestAsset/
+ http://example.org/TestAsset/
@@ -399,7 +405,7 @@ def test_stripped_asset_administration_shell(self) -> None:
Submodel
- http://acplt.org/test_ref
+ http://example.org/test_ref
@@ -422,6 +428,85 @@ def test_stripped_asset_administration_shell(self) -> None:
self.assertEqual(len(aas.submodel), 0)
+class XmlDeserializationDataSpecTest(unittest.TestCase):
+ def test_data_spec_iec61360_value_without_value_format(self) -> None:
+ xml = _xml_wrap(f"""
+
+
+ http://example.org/test_cd
+
+
+
+ ExternalReference
+
+
+ GlobalReference
+ https://admin-shell.io/DataSpecificationTemplates/DataSpecificationIec61360/3/0
+
+
+
+
+
+
+
+ en
+ Test
+
+
+ test_value
+
+
+
+
+
+
+ """)
+ object_store = read_aas_xml_file(io.StringIO(xml), failsafe=False)
+ cd = object_store.get_item("http://example.org/test_cd")
+ self.assertIsInstance(cd, model.ConceptDescription)
+ assert isinstance(cd, model.ConceptDescription)
+ ds_content = list(cd.embedded_data_specifications)[0].data_specification_content
+ self.assertIsInstance(ds_content, model.DataSpecificationIEC61360)
+ assert isinstance(ds_content, model.DataSpecificationIEC61360)
+ self.assertEqual("test_value", ds_content.value)
+ self.assertIsNone(ds_content.value_format)
+
+ def test_optional_first_second_relationship_element(self) -> None:
+ xml = _xml_wrap("""
+
+
+ http://example.org/test_submodel
+
+
+ test_optional_second_relationship_element
+ PARAMETER
+
+ ModelReference
+
+
+ Submodel
+ http://example.org/Test_Submodel
+
+
+ AnnotatedRelationshipElement
+ test_ref
+
+
+
+
+
+
+
+ """)
+ object_store = read_aas_xml_file(io.StringIO(xml), failsafe=False)
+ submodel = object_store.get_item("http://example.org/test_submodel")
+ assert isinstance(submodel, model.Submodel)
+ re = submodel.get_referable("test_optional_second_relationship_element")
+ self.assertIsInstance(re, model.RelationshipElement)
+ assert isinstance(re, model.RelationshipElement)
+ self.assertIsNone(re.second)
+
+
class XmlDeserializationDerivingTest(unittest.TestCase):
def test_submodel_constructor_overriding(self) -> None:
class EnhancedSubmodel(model.Submodel):
@@ -437,7 +522,7 @@ def construct_submodel(cls, element: etree._Element, object_class=EnhancedSubmod
xml = f"""
- http://acplt.org/test_stripped_submodel
+ http://example.org/test_stripped_submodel
"""
string_io = io.StringIO(xml)
@@ -450,22 +535,22 @@ def construct_submodel(cls, element: etree._Element, object_class=EnhancedSubmod
class TestTagReplaceNamespace(unittest.TestCase):
def test_known_namespace(self):
- tag = '{https://admin-shell.io/aas/3/0}tag'
+ tag = '{https://admin-shell.io/aas/3/1}tag'
expected = 'aas:tag'
self.assertEqual(_tag_replace_namespace(tag, XML_NS_MAP), expected)
def test_empty_prefix(self):
# Empty prefix should not be replaced as otherwise it would apply everywhere
- tag = '{https://admin-shell.io/aas/3/0}tag'
- nsmap = {"": "https://admin-shell.io/aas/3/0"}
- expected = '{https://admin-shell.io/aas/3/0}tag'
+ tag = '{https://admin-shell.io/aas/3/1}tag'
+ nsmap = {"": "https://admin-shell.io/aas/3/1"}
+ expected = '{https://admin-shell.io/aas/3/1}tag'
self.assertEqual(_tag_replace_namespace(tag, nsmap), expected)
def test_empty_namespace(self):
# Empty namespaces should also have no effect
- tag = '{https://admin-shell.io/aas/3/0}tag'
+ tag = '{https://admin-shell.io/aas/3/1}tag'
nsmap = {"aas": ""}
- expected = '{https://admin-shell.io/aas/3/0}tag'
+ expected = '{https://admin-shell.io/aas/3/1}tag'
self.assertEqual(_tag_replace_namespace(tag, nsmap), expected)
def test_unknown_namespace(self):
diff --git a/sdk/test/adapter/xml/test_xml_serialization.py b/sdk/test/adapter/xml/test_xml_serialization.py
index e07e10255..9d0d6a6dd 100644
--- a/sdk/test/adapter/xml/test_xml_serialization.py
+++ b/sdk/test/adapter/xml/test_xml_serialization.py
@@ -1,4 +1,4 @@
-# Copyright (c) 2025 the Eclipse BaSyx Authors
+# Copyright (c) 2026 the Eclipse BaSyx Authors
#
# This program and the accompanying materials are made available under the terms of the MIT License, available in
# the LICENSE file of this project.
@@ -39,7 +39,7 @@ def test_random_object_serialization(self) -> None:
test_aas = model.AssetAdministrationShell(model.AssetInformation(global_asset_id="Test"),
aas_identifier, submodel={submodel_reference})
- test_data: model.DictObjectStore[model.Identifiable] = model.DictObjectStore()
+ test_data: model.DictIdentifiableStore[model.Identifiable] = model.DictIdentifiableStore()
test_data.add(test_aas)
test_data.add(submodel)
@@ -61,11 +61,12 @@ def test_random_object_serialization(self) -> None:
submodel_reference = model.ModelReference(submodel_key, model.Submodel)
submodel = model.Submodel(submodel_identifier,
semantic_id=model.ExternalReference((model.Key(model.KeyTypes.GLOBAL_REFERENCE,
- "http://acplt.org/TestSemanticId"),)))
+ "http://example.org/"
+ "TestSemanticId"),)))
test_aas = model.AssetAdministrationShell(model.AssetInformation(global_asset_id="Test"),
aas_identifier, submodel={submodel_reference})
# serialize object to xml
- test_data: model.DictObjectStore[model.Identifiable] = model.DictObjectStore()
+ test_data: model.DictIdentifiableStore[model.Identifiable] = model.DictIdentifiableStore()
test_data.add(test_aas)
test_data.add(submodel)
@@ -94,7 +95,7 @@ def test_full_example_serialization(self) -> None:
root = etree.parse(file, parser=parser)
def test_submodel_template_serialization(self) -> None:
- data: model.DictObjectStore[model.Identifiable] = model.DictObjectStore()
+ data: model.DictIdentifiableStore[model.Identifiable] = model.DictIdentifiableStore()
data.add(example_submodel_template.create_example_submodel_template())
file = io.BytesIO()
write_aas_xml_file(file=file, data=data)
@@ -134,7 +135,7 @@ def test_missing_serialization(self) -> None:
root = etree.parse(file, parser=parser)
def test_concept_description(self) -> None:
- data: model.DictObjectStore[model.Identifiable] = model.DictObjectStore()
+ data: model.DictIdentifiableStore[model.Identifiable] = model.DictIdentifiableStore()
data.add(example_aas.create_example_concept_description())
file = io.BytesIO()
write_aas_xml_file(file=file, data=data)
diff --git a/sdk/test/adapter/xml/test_xml_serialization_deserialization.py b/sdk/test/adapter/xml/test_xml_serialization_deserialization.py
index 7a4f5c12d..e38aefd68 100644
--- a/sdk/test/adapter/xml/test_xml_serialization_deserialization.py
+++ b/sdk/test/adapter/xml/test_xml_serialization_deserialization.py
@@ -1,4 +1,4 @@
-# Copyright (c) 2025 the Eclipse BaSyx Authors
+# Copyright (c) 2026 the Eclipse BaSyx Authors
#
# This program and the accompanying materials are made available under the terms of the MIT License, available in
# the LICENSE file of this project.
@@ -17,43 +17,43 @@
from basyx.aas.examples.data._helper import AASDataChecker
-def _serialize_and_deserialize(data: model.DictObjectStore) -> model.DictObjectStore:
+def _serialize_and_deserialize(data: model.DictIdentifiableStore) -> model.DictIdentifiableStore:
file = io.BytesIO()
write_aas_xml_file(file=file, data=data)
- # try deserializing the xml document into a DictObjectStore of AAS objects with help of the xml module
+ # try deserializing the xml document into a DictIdentifiableStore of AAS objects with help of the xml module
file.seek(0)
return read_aas_xml_file(file, failsafe=False)
class XMLSerializationDeserializationTest(unittest.TestCase):
def test_example_serialization_deserialization(self) -> None:
- object_store = _serialize_and_deserialize(example_aas.create_full_example())
+ identifiable_store = _serialize_and_deserialize(example_aas.create_full_example())
checker = AASDataChecker(raise_immediately=True)
- example_aas.check_full_example(checker, object_store)
+ example_aas.check_full_example(checker, identifiable_store)
def test_example_mandatory_attributes_serialization_deserialization(self) -> None:
- object_store = _serialize_and_deserialize(example_aas_mandatory_attributes.create_full_example())
+ identifiable_store = _serialize_and_deserialize(example_aas_mandatory_attributes.create_full_example())
checker = AASDataChecker(raise_immediately=True)
- example_aas_mandatory_attributes.check_full_example(checker, object_store)
+ example_aas_mandatory_attributes.check_full_example(checker, identifiable_store)
def test_example_missing_attributes_serialization_deserialization(self) -> None:
- object_store = _serialize_and_deserialize(example_aas_missing_attributes.create_full_example())
+ identifiable_store = _serialize_and_deserialize(example_aas_missing_attributes.create_full_example())
checker = AASDataChecker(raise_immediately=True)
- example_aas_missing_attributes.check_full_example(checker, object_store)
+ example_aas_missing_attributes.check_full_example(checker, identifiable_store)
def test_example_submodel_template_serialization_deserialization(self) -> None:
- data: model.DictObjectStore[model.Identifiable] = model.DictObjectStore()
+ data: model.DictIdentifiableStore[model.Identifiable] = model.DictIdentifiableStore()
data.add(example_submodel_template.create_example_submodel_template())
- object_store = _serialize_and_deserialize(data)
+ identifiable_store = _serialize_and_deserialize(data)
checker = AASDataChecker(raise_immediately=True)
- example_submodel_template.check_full_example(checker, object_store)
+ example_submodel_template.check_full_example(checker, identifiable_store)
def test_example_all_examples_serialization_deserialization(self) -> None:
- data: model.DictObjectStore[model.Identifiable] = create_example()
- object_store = _serialize_and_deserialize(data)
+ data: model.DictIdentifiableStore[model.Identifiable] = create_example()
+ identifiable_store = _serialize_and_deserialize(data)
checker = AASDataChecker(raise_immediately=True)
- checker.check_object_store(object_store, data)
+ checker.check_identifiable_store(identifiable_store, data)
class XMLSerializationDeserializationSingleObjectTest(unittest.TestCase):
diff --git a/sdk/test/backend/test_couchdb.py b/sdk/test/backend/test_couchdb.py
index 36e5ef039..16f607047 100644
--- a/sdk/test/backend/test_couchdb.py
+++ b/sdk/test/backend/test_couchdb.py
@@ -1,4 +1,4 @@
-# Copyright (c) 2025 the Eclipse BaSyx Authors
+# Copyright (c) 2026 the Eclipse BaSyx Authors
#
# This program and the accompanying materials are made available under the terms of the MIT License, available in
# the LICENSE file of this project.
@@ -23,65 +23,67 @@
COUCHDB_ERROR))
class CouchDBBackendTest(unittest.TestCase):
def setUp(self) -> None:
- self.object_store = couchdb.CouchDBObjectStore(TEST_CONFIG['couchdb']['url'],
- TEST_CONFIG['couchdb']['database'])
+ self.couch_identifiable_store = couchdb.CouchDBIdentifiableStore(TEST_CONFIG['couchdb']['url'],
+ TEST_CONFIG['couchdb']['database'])
couchdb.register_credentials(TEST_CONFIG["couchdb"]["url"],
TEST_CONFIG["couchdb"]["user"],
TEST_CONFIG["couchdb"]["password"])
- self.object_store.check_database()
+ self.couch_identifiable_store.check_database()
def tearDown(self) -> None:
- self.object_store.clear()
+ self.couch_identifiable_store.clear()
- def test_object_store_add(self):
+ def test_identifiable_store_add(self):
test_object = create_example_submodel()
- self.object_store.add(test_object)
+ self.couch_identifiable_store.add(test_object)
# Note that this test is only checking that there are no errors during adding.
# The actual logic is tested together with retrieval in `test_retrieval`.
def test_retrieval(self):
test_object = create_example_submodel()
- self.object_store.add(test_object)
+ self.couch_identifiable_store.add(test_object)
# When retrieving the object, we should get the *same* instance as we added
- test_object_retrieved = self.object_store.get_identifiable('https://acplt.org/Test_Submodel')
+ test_object_retrieved = self.couch_identifiable_store.get_item('https://example.org/Test_Submodel')
self.assertIs(test_object, test_object_retrieved)
# When retrieving it again, we should still get the same object
del test_object
- test_object_retrieved_again = self.object_store.get_identifiable('https://acplt.org/Test_Submodel')
+ test_object_retrieved_again = self.couch_identifiable_store.get_item('https://example.org/Test_Submodel')
self.assertIs(test_object_retrieved, test_object_retrieved_again)
def test_example_submodel_storing(self) -> None:
example_submodel = create_example_submodel()
# Add exmaple submodel
- self.object_store.add(example_submodel)
- self.assertEqual(1, len(self.object_store))
- self.assertIn(example_submodel, self.object_store)
+ self.couch_identifiable_store.add(example_submodel)
+ self.assertEqual(1, len(self.couch_identifiable_store))
+ self.assertIn(example_submodel, self.couch_identifiable_store)
# Restore example submodel and check data
- submodel_restored = self.object_store.get_identifiable('https://acplt.org/Test_Submodel')
+ submodel_restored = self.couch_identifiable_store.get_item('https://example.org/Test_Submodel')
assert (isinstance(submodel_restored, model.Submodel))
checker = AASDataChecker(raise_immediately=True)
check_example_submodel(checker, submodel_restored)
# Delete example submodel
- self.object_store.discard(submodel_restored)
- self.assertNotIn(example_submodel, self.object_store)
+ self.couch_identifiable_store.discard(submodel_restored)
+ self.assertNotIn(example_submodel, self.couch_identifiable_store)
def test_iterating(self) -> None:
example_data = create_full_example()
# Add all objects
for item in example_data:
- self.object_store.add(item)
+ self.couch_identifiable_store.add(item)
- self.assertEqual(5, len(self.object_store))
+ self.assertEqual(5, len(self.couch_identifiable_store))
- # Iterate objects, add them to a DictObjectStore and check them
- retrieved_data_store: model.provider.DictObjectStore[model.Identifiable] = model.provider.DictObjectStore()
- for item in self.object_store:
+ # Iterate objects, add them to a DictIdentifiableStore and check them
+ retrieved_data_store: model.provider.DictIdentifiableStore[model.Identifiable] = (
+ model.provider.DictIdentifiableStore()
+ )
+ for item in self.couch_identifiable_store:
retrieved_data_store.add(item)
checker = AASDataChecker(raise_immediately=True)
check_full_example(checker, retrieved_data_store)
@@ -89,22 +91,22 @@ def test_iterating(self) -> None:
def test_key_errors(self) -> None:
# Double adding an object should raise a KeyError
example_submodel = create_example_submodel()
- self.object_store.add(example_submodel)
+ self.couch_identifiable_store.add(example_submodel)
with self.assertRaises(KeyError) as cm:
- self.object_store.add(example_submodel)
- self.assertEqual("'Identifiable with id https://acplt.org/Test_Submodel already exists in "
+ self.couch_identifiable_store.add(example_submodel)
+ self.assertEqual("'Identifiable with id https://example.org/Test_Submodel already exists in "
"CouchDB database'", str(cm.exception))
# Querying a deleted object should raise a KeyError
- retrieved_submodel = self.object_store.get_identifiable('https://acplt.org/Test_Submodel')
- self.object_store.discard(example_submodel)
+ retrieved_submodel = self.couch_identifiable_store.get_item('https://example.org/Test_Submodel')
+ self.couch_identifiable_store.discard(example_submodel)
with self.assertRaises(KeyError) as cm:
- self.object_store.get_identifiable('https://acplt.org/Test_Submodel')
- self.assertEqual("'No Identifiable with id https://acplt.org/Test_Submodel found in CouchDB database'",
+ self.couch_identifiable_store.get_item('https://example.org/Test_Submodel')
+ self.assertEqual("'No Identifiable with id https://example.org/Test_Submodel found in CouchDB database'",
str(cm.exception))
# Double deleting should also raise a KeyError
with self.assertRaises(KeyError) as cm:
- self.object_store.discard(retrieved_submodel)
- self.assertEqual("'No AAS object with id https://acplt.org/Test_Submodel exists in "
+ self.couch_identifiable_store.discard(retrieved_submodel)
+ self.assertEqual("'No AAS object with id https://example.org/Test_Submodel exists in "
"CouchDB database'", str(cm.exception))
diff --git a/sdk/test/backend/test_local_file.py b/sdk/test/backend/test_local_file.py
index 7d96d8713..e71f4f27a 100644
--- a/sdk/test/backend/test_local_file.py
+++ b/sdk/test/backend/test_local_file.py
@@ -1,13 +1,14 @@
-# Copyright (c) 2025 the Eclipse BaSyx Authors
+# Copyright (c) 2026 the Eclipse BaSyx Authors
#
# This program and the accompanying materials are made available under the terms of the MIT License, available in
# the LICENSE file of this project.
#
# SPDX-License-Identifier: MIT
+import gc
import os.path
import shutil
-import unittest
-import unittest.mock
+
+from unittest import TestCase
from basyx.aas.backend import local_file
from basyx.aas.examples.data.example_aas import *
@@ -17,66 +18,87 @@
source_core: str = "file://localhost/{}/".format(store_path)
-class LocalFileBackendTest(unittest.TestCase):
+class LocalFileBackendTest(TestCase):
def setUp(self) -> None:
- self.object_store = local_file.LocalFileObjectStore(store_path)
- self.object_store.check_directory(create=True)
+ self.identifiable_store = local_file.LocalFileIdentifiableStore(store_path)
+ self.identifiable_store.check_directory(create=True)
def tearDown(self) -> None:
try:
- self.object_store.clear()
+ self.identifiable_store.clear()
finally:
shutil.rmtree(store_path)
- def test_object_store_add(self):
+ def test_identifiable_store_add(self):
test_object = create_example_submodel()
- self.object_store.add(test_object)
+ self.identifiable_store.add(test_object)
# Note that this test is only checking that there are no errors during adding.
# The actual logic is tested together with retrieval in `test_retrieval`.
def test_retrieval(self):
test_object = create_example_submodel()
- self.object_store.add(test_object)
+ self.identifiable_store.add(test_object)
# When retrieving the object, we should get the *same* instance as we added
- test_object_retrieved = self.object_store.get_identifiable('https://acplt.org/Test_Submodel')
+ test_object_retrieved = self.identifiable_store.get_item('https://example.org/Test_Submodel')
self.assertIs(test_object, test_object_retrieved)
# When retrieving it again, we should still get the same object
del test_object
- test_object_retrieved_again = self.object_store.get_identifiable('https://acplt.org/Test_Submodel')
+ test_object_retrieved_again = self.identifiable_store.get_item('https://example.org/Test_Submodel')
self.assertIs(test_object_retrieved, test_object_retrieved_again)
def test_example_submodel_storing(self) -> None:
example_submodel = create_example_submodel()
# Add exmaple submodel
- self.object_store.add(example_submodel)
- self.assertEqual(1, len(self.object_store))
- self.assertIn(example_submodel, self.object_store)
+ self.identifiable_store.add(example_submodel)
+ self.assertEqual(1, len(self.identifiable_store))
+ self.assertIn(example_submodel, self.identifiable_store)
# Restore example submodel and check data
- submodel_restored = self.object_store.get_identifiable('https://acplt.org/Test_Submodel')
+ submodel_restored = self.identifiable_store.get_item('https://example.org/Test_Submodel')
assert (isinstance(submodel_restored, model.Submodel))
checker = AASDataChecker(raise_immediately=True)
check_example_submodel(checker, submodel_restored)
# Delete example submodel
- self.object_store.discard(submodel_restored)
- self.assertNotIn(example_submodel, self.object_store)
+ self.identifiable_store.discard(submodel_restored)
+ self.assertNotIn(example_submodel, self.identifiable_store)
+
+ def test_check_directory(self) -> None:
+ # Make sure the test directory does not exist at the beginning of the test
+ if os.path.exists(store_path):
+ shutil.rmtree(store_path)
+
+ # If create=False, check_directory should raise a FileNotFoundError,
+ # if the directory does not exist
+ with self.assertRaises(FileNotFoundError) as cm:
+ self.identifiable_store.check_directory(create=False)
+ expected_error = "The given directory ({}) does not exist".format(store_path)
+ self.assertEqual(expected_error, str(cm.exception))
+
+ # If create=True, check_directory should create the directory if it does not exist
+ self.identifiable_store.check_directory(create=True)
+ self.assertTrue(os.path.exists(store_path))
+
+ # If the directory exists, create=False should not raise an error
+ self.identifiable_store.check_directory(create=False)
def test_iterating(self) -> None:
example_data = create_full_example()
# Add all objects
for item in example_data:
- self.object_store.add(item)
+ self.identifiable_store.add(item)
- self.assertEqual(5, len(self.object_store))
+ self.assertEqual(5, len(self.identifiable_store))
- # Iterate objects, add them to a DictObjectStore and check them
- retrieved_data_store: model.provider.DictObjectStore[model.Identifiable] = model.provider.DictObjectStore()
- for item in self.object_store:
+ # Iterate objects, add them to a DictIdentifiableStore and check them
+ retrieved_data_store: model.provider.DictIdentifiableStore[model.Identifiable] = (
+ model.provider.DictIdentifiableStore()
+ )
+ for item in self.identifiable_store:
retrieved_data_store.add(item)
checker = AASDataChecker(raise_immediately=True)
check_full_example(checker, retrieved_data_store)
@@ -84,23 +106,86 @@ def test_iterating(self) -> None:
def test_key_errors(self) -> None:
# Double adding an object should raise a KeyError
example_submodel = create_example_submodel()
- self.object_store.add(example_submodel)
+ self.identifiable_store.add(example_submodel)
with self.assertRaises(KeyError) as cm:
- self.object_store.add(example_submodel)
- self.assertEqual("'Identifiable with id https://acplt.org/Test_Submodel already exists in "
+ self.identifiable_store.add(example_submodel)
+ self.assertEqual("'Identifiable with id https://example.org/Test_Submodel already exists in "
"local file database'", str(cm.exception))
# Querying a deleted object should raise a KeyError
- retrieved_submodel = self.object_store.get_identifiable('https://acplt.org/Test_Submodel')
- self.object_store.discard(example_submodel)
+ retrieved_submodel = self.identifiable_store.get_item('https://example.org/Test_Submodel')
+ self.identifiable_store.discard(example_submodel)
with self.assertRaises(KeyError) as cm:
- self.object_store.get_identifiable('https://acplt.org/Test_Submodel')
- self.assertEqual("'No Identifiable with id https://acplt.org/Test_Submodel "
+ self.identifiable_store.get_item('https://example.org/Test_Submodel')
+ self.assertEqual("'No Identifiable with id https://example.org/Test_Submodel "
"found in local file database'",
str(cm.exception))
# Double deleting should also raise a KeyError
with self.assertRaises(KeyError) as cm:
- self.object_store.discard(retrieved_submodel)
- self.assertEqual("'No AAS object with id https://acplt.org/Test_Submodel exists in "
+ self.identifiable_store.discard(retrieved_submodel)
+ self.assertEqual("'No AAS object with id https://example.org/Test_Submodel exists in "
"local file database'", str(cm.exception))
+
+ def test_add_and_len_consistent(self) -> None:
+ # Each add() must increment len() by exactly 1
+ example_data = list(create_full_example())
+ for i, item in enumerate(example_data):
+ self.identifiable_store.add(item)
+ self.assertEqual(i + 1, len(self.identifiable_store))
+
+ # Stray non-json file must not be counted
+ stray = os.path.join(store_path, ".DS_Store")
+ with open(stray, "w") as f:
+ f.write("stray")
+ self.assertEqual(len(example_data), len(self.identifiable_store))
+ os.remove(stray)
+
+ def test_iter_ignores_non_json_files(self) -> None:
+ example_data = create_full_example()
+ for item in example_data:
+ self.identifiable_store.add(item)
+
+ # Stray files must not crash the iterator or be yielded
+ stray = os.path.join(store_path, ".DS_Store")
+ with open(stray, "w") as f:
+ f.write("stray")
+ items = list(self.identifiable_store)
+ self.assertEqual(5, len(items))
+ os.remove(stray)
+
+ def test_mutation_persistence(self) -> None:
+ submodel = model.Submodel(
+ id_='https://example.org/MutationTest',
+ submodel_element={
+ model.Property(id_short='Prop', value_type=model.datatypes.String, value='before')
+ }
+ )
+ self.identifiable_store.add(submodel)
+
+ retrieved = self.identifiable_store.get_item('https://example.org/MutationTest')
+ assert isinstance(retrieved, model.Submodel)
+ prop = retrieved.get_referable(['Prop'])
+ assert isinstance(prop, model.Property)
+ prop.update_from(model.Property(id_short='Prop', value_type=model.datatypes.String, value='after'))
+ self.identifiable_store.commit(retrieved)
+
+ # Drop all strong references to evict the WeakValueDictionary cache
+ del submodel, retrieved, prop
+ gc.collect()
+
+ fresh = self.identifiable_store.get_item('https://example.org/MutationTest')
+ assert isinstance(fresh, model.Submodel)
+ fresh_prop = fresh.get_referable(['Prop'])
+ assert isinstance(fresh_prop, model.Property)
+ self.assertEqual('after', fresh_prop.value)
+
+ def test_reload_discard(self) -> None:
+ # Load example submodel
+ example_submodel = create_example_submodel()
+ self.identifiable_store.add(example_submodel)
+
+ # Reload the DictIdentifiableStore and discard the example submodel
+ self.identifiable_store = local_file.LocalFileIdentifiableStore(store_path)
+ self.identifiable_store.discard(example_submodel)
+ self.assertNotIn(example_submodel, self.identifiable_store)
diff --git a/sdk/test/examples/test__init__.py b/sdk/test/examples/test__init__.py
index ea5eba30d..1f1e26234 100644
--- a/sdk/test/examples/test__init__.py
+++ b/sdk/test/examples/test__init__.py
@@ -1,4 +1,4 @@
-# Copyright (c) 2024 the Eclipse BaSyx Authors
+# Copyright (c) 2026 the Eclipse BaSyx Authors
#
# This program and the accompanying materials are made available under the terms of the MIT License, available in
# the LICENSE file of this project.
@@ -13,34 +13,34 @@
class TestExampleFunctions(unittest.TestCase):
def test_create_example(self):
- obj_store = create_example()
+ identifiable_store = create_example()
# Check that the object store is not empty
- self.assertGreater(len(obj_store), 0)
+ self.assertGreater(len(identifiable_store), 0)
# Check that the object store contains expected elements
expected_ids = [
- 'https://acplt.org/Test_AssetAdministrationShell',
- 'https://acplt.org/Test_Submodel_Template',
- 'https://acplt.org/Test_ConceptDescription_Mandatory'
+ 'https://example.org/Test_AssetAdministrationShell',
+ 'https://example.org/Test_Submodel_Template',
+ 'https://example.org/Test_ConceptDescription_Mandatory'
]
for id in expected_ids:
- self.assertIsNotNone(obj_store.get_identifiable(id))
+ self.assertIsNotNone(identifiable_store.get_item(id))
def test_create_example_aas_binding(self):
- obj_store = create_example_aas_binding()
+ identifiable_store = create_example_aas_binding()
# Check that the object store is not empty
- self.assertGreater(len(obj_store), 0)
+ self.assertGreater(len(identifiable_store), 0)
# Check that the object store contains expected elements
- aas_id = 'https://acplt.org/Test_AssetAdministrationShell'
- sm_id = 'https://acplt.org/Test_Submodel_Template'
- cd_id = 'https://acplt.org/Test_ConceptDescription_Mandatory'
+ aas_id = 'https://example.org/Test_AssetAdministrationShell'
+ sm_id = 'https://example.org/Test_Submodel_Template'
+ cd_id = 'https://example.org/Test_ConceptDescription_Mandatory'
- aas = obj_store.get_identifiable(aas_id)
- sm = obj_store.get_identifiable(sm_id)
- cd = obj_store.get_identifiable(cd_id)
+ aas = identifiable_store.get_item(aas_id)
+ sm = identifiable_store.get_item(sm_id)
+ cd = identifiable_store.get_item(cd_id)
self.assertIsNotNone(aas)
self.assertIsNotNone(sm)
diff --git a/sdk/test/examples/test_examples.py b/sdk/test/examples/test_examples.py
index 5695e3b94..7f17a5db6 100644
--- a/sdk/test/examples/test_examples.py
+++ b/sdk/test/examples/test_examples.py
@@ -1,4 +1,4 @@
-# Copyright (c) 2025 the Eclipse BaSyx Authors
+# Copyright (c) 2026 the Eclipse BaSyx Authors
#
# This program and the accompanying materials are made available under the terms of the MIT License, available in
# the LICENSE file of this project.
@@ -40,50 +40,50 @@ def test_example_submodel(self):
def test_full_example(self):
checker = AASDataChecker(raise_immediately=True)
- obj_store = model.DictObjectStore()
+ identifiable_store = model.DictIdentifiableStore()
with self.assertRaises(AssertionError) as cm:
- example_aas.check_full_example(checker, obj_store)
- self.assertIn("AssetAdministrationShell[https://acplt.org/Test_AssetAdministrationShell]",
+ example_aas.check_full_example(checker, identifiable_store)
+ self.assertIn("AssetAdministrationShell[https://example.org/Test_AssetAdministrationShell]",
str(cm.exception))
- obj_store = example_aas.create_full_example()
- example_aas.check_full_example(checker, obj_store)
+ identifiable_store = example_aas.create_full_example()
+ example_aas.check_full_example(checker, identifiable_store)
failed_shell = model.AssetAdministrationShell(
asset_information=model.AssetInformation(global_asset_id='test'),
id_='test'
)
- obj_store.add(failed_shell)
+ identifiable_store.add(failed_shell)
with self.assertRaises(AssertionError) as cm:
- example_aas.check_full_example(checker, obj_store)
+ example_aas.check_full_example(checker, identifiable_store)
self.assertIn("AssetAdministrationShell[test]", str(cm.exception))
- obj_store.discard(failed_shell)
+ identifiable_store.discard(failed_shell)
failed_submodel = model.Submodel(id_='test')
- obj_store.add(failed_submodel)
+ identifiable_store.add(failed_submodel)
with self.assertRaises(AssertionError) as cm:
- example_aas.check_full_example(checker, obj_store)
+ example_aas.check_full_example(checker, identifiable_store)
self.assertIn("Submodel[test]", str(cm.exception))
- obj_store.discard(failed_submodel)
+ identifiable_store.discard(failed_submodel)
failed_cd = model.ConceptDescription(id_='test')
- obj_store.add(failed_cd)
+ identifiable_store.add(failed_cd)
with self.assertRaises(AssertionError) as cm:
- example_aas.check_full_example(checker, obj_store)
+ example_aas.check_full_example(checker, identifiable_store)
self.assertIn("ConceptDescription[test]", str(cm.exception))
- obj_store.discard(failed_cd)
+ identifiable_store.discard(failed_cd)
class DummyIdentifiable(model.Identifiable):
def __init__(self, id_: model.Identifier):
super().__init__()
self.id = id_
failed_identifiable = DummyIdentifiable(id_='test')
- obj_store.add(failed_identifiable)
+ identifiable_store.add(failed_identifiable)
with self.assertRaises(KeyError) as cm:
- example_aas.check_full_example(checker, obj_store)
+ example_aas.check_full_example(checker, identifiable_store)
self.assertIn("Check for DummyIdentifiable[test] not implemented", str(cm.exception))
- obj_store.discard(failed_identifiable)
- example_aas.check_full_example(checker, obj_store)
+ identifiable_store.discard(failed_identifiable)
+ example_aas.check_full_example(checker, identifiable_store)
class ExampleAASMandatoryTest(unittest.TestCase):
@@ -109,17 +109,17 @@ def test_example_asset_administration_shell(self):
def test_full_example(self):
checker = AASDataChecker(raise_immediately=True)
- obj_store = example_aas_mandatory_attributes.create_full_example()
- example_aas_mandatory_attributes.check_full_example(checker, obj_store)
+ identifiable_store = example_aas_mandatory_attributes.create_full_example()
+ example_aas_mandatory_attributes.check_full_example(checker, identifiable_store)
failed_submodel = model.Submodel(id_='test')
- obj_store.add(failed_submodel)
+ identifiable_store.add(failed_submodel)
with self.assertRaises(AssertionError) as cm:
- example_aas_mandatory_attributes.check_full_example(checker, obj_store)
+ example_aas_mandatory_attributes.check_full_example(checker, identifiable_store)
self.assertIn("Given submodel list must not have extra submodels", str(cm.exception))
self.assertIn("Submodel[test]", str(cm.exception))
- obj_store.discard(failed_submodel)
- example_aas_mandatory_attributes.check_full_example(checker, obj_store)
+ identifiable_store.discard(failed_submodel)
+ example_aas_mandatory_attributes.check_full_example(checker, identifiable_store)
class ExampleAASMissingTest(unittest.TestCase):
@@ -140,17 +140,17 @@ def test_example_asset_administration_shell(self):
def test_full_example(self):
checker = AASDataChecker(raise_immediately=True)
- obj_store = example_aas_missing_attributes.create_full_example()
- example_aas_missing_attributes.check_full_example(checker, obj_store)
+ identifiable_store = example_aas_missing_attributes.create_full_example()
+ example_aas_missing_attributes.check_full_example(checker, identifiable_store)
failed_submodel = model.Submodel(id_='test')
- obj_store.add(failed_submodel)
+ identifiable_store.add(failed_submodel)
with self.assertRaises(AssertionError) as cm:
- example_aas_missing_attributes.check_full_example(checker, obj_store)
+ example_aas_missing_attributes.check_full_example(checker, identifiable_store)
self.assertIn("Given submodel list must not have extra submodels", str(cm.exception))
self.assertIn("Submodel[test]", str(cm.exception))
- obj_store.discard(failed_submodel)
- example_aas_missing_attributes.check_full_example(checker, obj_store)
+ identifiable_store.discard(failed_submodel)
+ example_aas_missing_attributes.check_full_example(checker, identifiable_store)
class ExampleSubmodelTemplate(unittest.TestCase):
@@ -161,16 +161,16 @@ def test_example_submodel_template(self):
def test_full_example(self):
checker = AASDataChecker(raise_immediately=True)
- obj_store: model.DictObjectStore[model.Identifiable] = model.DictObjectStore()
- obj_store.add(example_submodel_template.create_example_submodel_template())
- example_submodel_template.check_full_example(checker, obj_store)
+ identifiable_store: model.DictIdentifiableStore[model.Identifiable] = model.DictIdentifiableStore()
+ identifiable_store.add(example_submodel_template.create_example_submodel_template())
+ example_submodel_template.check_full_example(checker, identifiable_store)
failed_submodel = model.Submodel(id_='test')
- obj_store.add(failed_submodel)
+ identifiable_store.add(failed_submodel)
with self.assertRaises(AssertionError) as cm:
- example_submodel_template.check_full_example(checker, obj_store)
+ example_submodel_template.check_full_example(checker, identifiable_store)
self.assertIn("Given submodel list must not have extra submodels", str(cm.exception))
self.assertIn("Submodel[test]", str(cm.exception))
- obj_store.discard(failed_submodel)
+ identifiable_store.discard(failed_submodel)
- example_submodel_template.check_full_example(checker, obj_store)
+ example_submodel_template.check_full_example(checker, identifiable_store)
diff --git a/sdk/test/examples/test_helpers.py b/sdk/test/examples/test_helpers.py
index 0257b8bca..74aebb518 100644
--- a/sdk/test/examples/test_helpers.py
+++ b/sdk/test/examples/test_helpers.py
@@ -1,4 +1,4 @@
-# Copyright (c) 2025 the Eclipse BaSyx Authors
+# Copyright (c) 2026 the Eclipse BaSyx Authors
#
# This program and the accompanying materials are made available under the terms of the MIT License, available in
# the LICENSE file of this project.
@@ -253,14 +253,14 @@ def test_annotated_relationship_element(self):
rel1 = model.AnnotatedRelationshipElement(id_short='test',
first=model.ModelReference((
model.Key(type_=model.KeyTypes.SUBMODEL,
- value='http://acplt.org/Test_Submodel'),
+ value='http://example.org/Test_Submodel'),
model.Key(
type_=model.KeyTypes.PROPERTY,
value='ExampleProperty'),),
model.Property),
second=model.ModelReference((
model.Key(type_=model.KeyTypes.SUBMODEL,
- value='http://acplt.org/Test_Submodel'),
+ value='http://example.org/Test_Submodel'),
model.Key(type_=model.KeyTypes.PROPERTY,
value='ExampleProperty'),),
model.Property),
@@ -268,13 +268,13 @@ def test_annotated_relationship_element(self):
rel2 = model.AnnotatedRelationshipElement(id_short='test',
first=model.ModelReference((
model.Key(type_=model.KeyTypes.SUBMODEL,
- value='http://acplt.org/Test_Submodel'),
+ value='http://example.org/Test_Submodel'),
model.Key(type_=model.KeyTypes.PROPERTY,
value='ExampleProperty'),),
model.Property),
second=model.ModelReference((
model.Key(type_=model.KeyTypes.SUBMODEL,
- value='http://acplt.org/Test_Submodel'),
+ value='http://example.org/Test_Submodel'),
model.Key(type_=model.KeyTypes.PROPERTY,
value='ExampleProperty'),),
model.Property),
diff --git a/sdk/test/examples/test_tutorials.py b/sdk/test/examples/test_tutorials.py
index 2b5dbe54e..04f201e53 100644
--- a/sdk/test/examples/test_tutorials.py
+++ b/sdk/test/examples/test_tutorials.py
@@ -1,4 +1,4 @@
-# Copyright (c) 2025 the Eclipse BaSyx Authors
+# Copyright (c) 2026 the Eclipse BaSyx Authors
#
# This program and the accompanying materials are made available under the terms of the MIT License, available in
# the LICENSE file of this project.
@@ -22,7 +22,7 @@ class TutorialTest(unittest.TestCase):
def test_tutorial_create_simple_aas(self):
from basyx.aas.examples import tutorial_create_simple_aas
self.assertEqual(tutorial_create_simple_aas.submodel.get_referable('ExampleProperty').value, 'exampleValue')
- store = model.DictObjectStore({tutorial_create_simple_aas.submodel})
+ store = model.DictIdentifiableStore({tutorial_create_simple_aas.submodel})
next(iter(tutorial_create_simple_aas.aas.submodel)).resolve(store)
def test_tutorial_storage(self):
@@ -47,6 +47,10 @@ def test_tutorial_aasx(self):
pass
# The tutorial already includes assert statements for the relevant points. So no further checks are required.
+ def test_tutorial_navigate_aas(self):
+ from basyx.aas.examples import tutorial_navigate_aas
+ # The tutorial already includes assert statements for the relevant points. So no further checks are required
+
@contextmanager
def temporary_workingdirectory():
diff --git a/sdk/test/model/test_aas.py b/sdk/test/model/test_aas.py
index 3b974c04f..ed0e5bf6e 100644
--- a/sdk/test/model/test_aas.py
+++ b/sdk/test/model/test_aas.py
@@ -1,4 +1,4 @@
-# Copyright (c) 2025 the Eclipse BaSyx Authors
+# Copyright (c) 2026 the Eclipse BaSyx Authors
#
# This program and the accompanying materials are made available under the terms of the MIT License, available in
# the LICENSE file of this project.
@@ -16,14 +16,14 @@ def test_aasd_131_init(self) -> None:
model.AssetInformation(model.AssetKind.INSTANCE)
self.assertEqual("An AssetInformation has to have a globalAssetId or a specificAssetId (Constraint AASd-131)",
str(cm.exception))
- model.AssetInformation(model.AssetKind.INSTANCE, global_asset_id="https://acplt.org/TestAsset")
+ model.AssetInformation(model.AssetKind.INSTANCE, global_asset_id="https://example.org/TestAsset")
model.AssetInformation(model.AssetKind.INSTANCE, specific_asset_id=(model.SpecificAssetId("test", "test"),))
- model.AssetInformation(model.AssetKind.INSTANCE, global_asset_id="https://acplt.org/TestAsset",
+ model.AssetInformation(model.AssetKind.INSTANCE, global_asset_id="https://example.org/TestAsset",
specific_asset_id=(model.SpecificAssetId("test", "test"),))
def test_aasd_131_set(self) -> None:
asset_information = model.AssetInformation(model.AssetKind.INSTANCE,
- global_asset_id="https://acplt.org/TestAsset",
+ global_asset_id="https://example.org/TestAsset",
specific_asset_id=(model.SpecificAssetId("test", "test"),))
asset_information.global_asset_id = None
with self.assertRaises(model.AASConstraintViolation) as cm:
@@ -32,7 +32,7 @@ def test_aasd_131_set(self) -> None:
str(cm.exception))
asset_information = model.AssetInformation(model.AssetKind.INSTANCE,
- global_asset_id="https://acplt.org/TestAsset",
+ global_asset_id="https://example.org/TestAsset",
specific_asset_id=(model.SpecificAssetId("test", "test"),))
asset_information.specific_asset_id = model.ConstrainedList(())
with self.assertRaises(model.AASConstraintViolation) as cm:
@@ -42,7 +42,7 @@ def test_aasd_131_set(self) -> None:
def test_aasd_131_specific_asset_id_add(self) -> None:
asset_information = model.AssetInformation(model.AssetKind.INSTANCE,
- global_asset_id="https://acplt.org/TestAsset")
+ global_asset_id="https://example.org/TestAsset")
specific_asset_id1 = model.SpecificAssetId("test", "test")
specific_asset_id2 = model.SpecificAssetId("test", "test")
asset_information.specific_asset_id.append(specific_asset_id1)
diff --git a/sdk/test/model/test_base.py b/sdk/test/model/test_base.py
index 460bce563..826d6ed08 100644
--- a/sdk/test/model/test_base.py
+++ b/sdk/test/model/test_base.py
@@ -1,4 +1,4 @@
-# Copyright (c) 2025 the Eclipse BaSyx Authors
+# Copyright (c) 2026 the Eclipse BaSyx Authors
#
# This program and the accompanying materials are made available under the terms of the MIT License, available in
# the LICENSE file of this project.
@@ -100,10 +100,15 @@ def test_id_short_constraint_aasd_002(self):
test_object = ExampleReferable()
test_object.id_short = "Test"
self.assertEqual("Test", test_object.id_short)
- test_object.id_short = "asdASd123_"
- self.assertEqual("asdASd123_", test_object.id_short)
+ test_object.id_short = "asdASd-123_"
+ self.assertEqual("asdASd-123_", test_object.id_short)
test_object.id_short = "AAs12_"
self.assertEqual("AAs12_", test_object.id_short)
+ test_object.id_short = "A"
+ self.assertEqual("A", test_object.id_short)
+ with self.assertRaises(model.AASConstraintViolation) as cm:
+ test_object.id_short = "Test-"
+ self.assertEqual("The id_short must not end with a hyphen (Constraint AASd-002)", str(cm.exception))
with self.assertRaises(model.AASConstraintViolation) as cm:
test_object.id_short = "98sdsfdAS"
self.assertEqual("The id_short must start with a letter (Constraint AASd-002)", str(cm.exception))
@@ -113,12 +118,12 @@ def test_id_short_constraint_aasd_002(self):
with self.assertRaises(model.AASConstraintViolation) as cm:
test_object.id_short = "asdlujSAD8348@S"
self.assertEqual(
- "The id_short must contain only letters, digits and underscore (Constraint AASd-002)",
+ "The id_short must contain only letters, digits underscore and hyphen (Constraint AASd-002)",
str(cm.exception))
with self.assertRaises(model.AASConstraintViolation) as cm:
test_object.id_short = "abc\n"
self.assertEqual(
- "The id_short must contain only letters, digits and underscore (Constraint AASd-002)",
+ "The id_short must contain only letters, digits underscore and hyphen (Constraint AASd-002)",
str(cm.exception))
def test_representation(self):
@@ -247,7 +252,7 @@ def test_update_from(self):
self.assertIs(example_relel.parent, example_submodel)
def test_update_commit_qualifier_extension_semantic_id(self):
- submodel = model.Submodel("https://acplt.org/Test_Submodel")
+ submodel = model.Submodel("https://example.org/Test_Submodel")
qualifier = model.Qualifier("test", model.datatypes.String)
extension = model.Extension("test")
collection = model.SubmodelElementCollection("test")
@@ -308,11 +313,11 @@ class ModelNamespaceTest(unittest.TestCase):
def setUp(self):
self.propSemanticID = model.ExternalReference((model.Key(type_=model.KeyTypes.GLOBAL_REFERENCE,
- value='http://acplt.org/Test1'),))
+ value='http://example.org/Test1'),))
self.propSemanticID2 = model.ExternalReference((model.Key(type_=model.KeyTypes.GLOBAL_REFERENCE,
- value='http://acplt.org/Test2'),))
+ value='http://example.org/Test2'),))
self.propSemanticID3 = model.ExternalReference((model.Key(type_=model.KeyTypes.GLOBAL_REFERENCE,
- value='http://acplt.org/Test3'),))
+ value='http://example.org/Test3'),))
self.prop1 = model.Property("Prop1", model.datatypes.Int, semantic_id=self.propSemanticID)
self.prop2 = model.Property("Prop2", model.datatypes.Int, semantic_id=self.propSemanticID)
self.prop3 = model.Property("Prop2", model.datatypes.Int, semantic_id=self.propSemanticID2)
@@ -333,6 +338,18 @@ def setUp(self):
self.namespace = self._namespace_class()
self.namespace3 = self._namespace_class_qualifier()
+ def test_namespaceset_pop_removes_from_all_backends(self) -> None:
+ # set1 has two backends: id_short and semantic_id
+ self.namespace.set1.add(self.prop1)
+ popped = self.namespace.set1.pop()
+ self.assertIs(self.prop1, popped)
+ self.assertEqual(0, len(self.namespace.set1))
+ # After pop, adding a new item with the same semantic_id must NOT raise AASConstraintViolation —
+ # it would if the popped item's semantic_id entry were still in the backend
+ new_prop = model.Property("NewProp", model.datatypes.Int, semantic_id=self.propSemanticID)
+ self.namespace.set1.add(new_prop)
+ self.assertEqual(1, len(self.namespace.set1))
+
def test_NamespaceSet(self) -> None:
self.namespace.set1.add(self.prop1)
self.assertEqual(1, len(self.namespace.set1))
@@ -340,7 +357,7 @@ def test_NamespaceSet(self) -> None:
self.namespace.set1.add(self.prop2)
self.assertEqual(
"Object with attribute (name='semantic_id', value='ExternalReference(key=(Key("
- "type=GLOBAL_REFERENCE, value=http://acplt.org/Test1),))') is already present in this set of objects "
+ "type=GLOBAL_REFERENCE, value=http://example.org/Test1),))') is already present in this set of objects "
"(Constraint AASd-000)",
str(cm.exception))
self.namespace.set2.add(self.prop5)
@@ -355,7 +372,7 @@ def test_NamespaceSet(self) -> None:
self.namespace.set2.add(self.prop4)
self.assertEqual(
"Object with attribute (name='semantic_id', value='"
- "ExternalReference(key=(Key(type=GLOBAL_REFERENCE, value=http://acplt.org/Test1),))')"
+ "ExternalReference(key=(Key(type=GLOBAL_REFERENCE, value=http://example.org/Test1),))')"
" is already present in another set in the same namespace (Constraint AASd-000)",
str(cm.exception))
@@ -722,6 +739,62 @@ def test_OrderedNamespace(self) -> None:
f"{self._namespace_class.__name__}[{self.namespace.id}]'", # type: ignore[has-type]
str(cm2.exception))
+ def test_ordered_namespaceset_int_setitem_preserves_index(self) -> None:
+ # __setitem__ int must place the new item at the exact index of the replaced item.
+ # Items before and after the replaced index must not shift.
+ ns = ExampleOrderedNamespace()
+ sid1 = model.ExternalReference((model.Key(model.KeyTypes.GLOBAL_REFERENCE, "http://example.org/s1"),))
+ sid2 = model.ExternalReference((model.Key(model.KeyTypes.GLOBAL_REFERENCE, "http://example.org/s2"),))
+ sid3 = model.ExternalReference((model.Key(model.KeyTypes.GLOBAL_REFERENCE, "http://example.org/s3"),))
+ sid4 = model.ExternalReference((model.Key(model.KeyTypes.GLOBAL_REFERENCE, "http://example.org/s4"),))
+ p0 = model.Property("PA", model.datatypes.Int, semantic_id=sid1)
+ old = model.Property("PB", model.datatypes.Int, semantic_id=sid2)
+ p2 = model.Property("PC", model.datatypes.Int, semantic_id=sid3)
+ new = model.Property("PB", model.datatypes.Int, semantic_id=sid4) # same id_short as old
+ ns.set1.add(p0)
+ ns.set1.add(old)
+ ns.set1.add(p2)
+ # set1 is [p0, old, p2] at indices [0, 1, 2]
+
+ # Replace middle item (index 1) — same id_short must not raise AASConstraintViolation
+ ns.set1[1] = new
+
+ # p0 stays at 0, new is at 1, p2 stays at 2 — no index shift
+ self.assertIs(p0, ns.set1[0])
+ self.assertIs(new, ns.set1[1])
+ self.assertIs(p2, ns.set1[2])
+ self.assertIs(ns, new.parent)
+ self.assertIsNone(old.parent)
+
+ def test_ordered_namespaceset_slice_setitem_preserves_order(self) -> None:
+ # Replace a slice of items; the new items must appear in the correct positions after replacement
+ ns = ExampleOrderedNamespace()
+ sid1 = model.ExternalReference((model.Key(model.KeyTypes.GLOBAL_REFERENCE, "http://example.org/sid1"),))
+ sid2 = model.ExternalReference((model.Key(model.KeyTypes.GLOBAL_REFERENCE, "http://example.org/sid2"),))
+ sid3 = model.ExternalReference((model.Key(model.KeyTypes.GLOBAL_REFERENCE, "http://example.org/sid3"),))
+ sid4 = model.ExternalReference((model.Key(model.KeyTypes.GLOBAL_REFERENCE, "http://example.org/sid4"),))
+ sid5 = model.ExternalReference((model.Key(model.KeyTypes.GLOBAL_REFERENCE, "http://example.org/sid5"),))
+ p1 = model.Property("PA", model.datatypes.Int, semantic_id=sid1)
+ p2 = model.Property("PB", model.datatypes.Int, semantic_id=sid2)
+ p3 = model.Property("PC", model.datatypes.Int, semantic_id=sid3)
+ ns.set1.add(p1)
+ ns.set1.add(p2)
+ ns.set1.add(p3)
+ self.assertEqual([p1, p2, p3], list(ns.set1))
+
+ # Replace slice [0:2] (p1, p2) with two new items
+ new1 = model.Property("PX", model.datatypes.Int, semantic_id=sid4)
+ new2 = model.Property("PY", model.datatypes.Int, semantic_id=sid5)
+ ns.set1[0:2] = [new1, new2]
+
+ # After replacement: [new1, new2, p3]
+ result = list(ns.set1)
+ self.assertEqual([new1, new2, p3], result)
+ self.assertIsNone(p1.parent)
+ self.assertIsNone(p2.parent)
+ self.assertIs(ns, new1.parent)
+ self.assertIs(ns, new2.parent)
+
class ExternalReferenceTest(unittest.TestCase):
def test_constraints(self):
@@ -854,12 +927,12 @@ def test_equality(self):
def test_reference_typing(self) -> None:
dummy_submodel = model.Submodel("urn:x-test:x")
- class DummyObjectProvider(model.AbstractObjectProvider):
- def get_identifiable(self, identifier: Identifier) -> Identifiable:
+ class DummyIdentifiableProvider(model.AbstractObjectProvider[model.Identifier, model.Identifiable]):
+ def get_item(self, identifier: Identifier) -> Identifiable:
return dummy_submodel
x = model.ModelReference((model.Key(model.KeyTypes.SUBMODEL, "urn:x-test:x"),), model.Submodel)
- submodel: model.Submodel = x.resolve(DummyObjectProvider())
+ submodel: model.Submodel = x.resolve(DummyIdentifiableProvider())
self.assertIs(submodel, submodel)
def test_resolve(self) -> None:
@@ -868,8 +941,8 @@ def test_resolve(self) -> None:
list_ = model.SubmodelElementList("list", model.SubmodelElementCollection, {collection})
submodel = model.Submodel("urn:x-test:submodel", {list_})
- class DummyObjectProvider(model.AbstractObjectProvider):
- def get_identifiable(self, identifier: Identifier) -> Identifiable:
+ class DummyIdentifiableProvider(model.AbstractObjectProvider[model.Identifier, model.Identifiable]):
+ def get_item(self, identifier: Identifier) -> Identifiable:
if identifier == submodel.id:
return submodel
else:
@@ -881,7 +954,7 @@ def get_identifiable(self, identifier: Identifier) -> Identifiable:
model.Key(model.KeyTypes.PROPERTY, "prop")),
model.Property)
with self.assertRaises(KeyError) as cm:
- ref1.resolve(DummyObjectProvider())
+ ref1.resolve(DummyIdentifiableProvider())
self.assertEqual("'Referable with id_short lst not found in Submodel[urn:x-test:submodel]'", str(cm.exception))
ref2 = model.ModelReference((model.Key(model.KeyTypes.SUBMODEL, "urn:x-test:submodel"),
@@ -890,7 +963,7 @@ def get_identifiable(self, identifier: Identifier) -> Identifiable:
model.Key(model.KeyTypes.PROPERTY, "prop")),
model.Property)
with self.assertRaises(KeyError) as cm_2:
- ref2.resolve(DummyObjectProvider())
+ ref2.resolve(DummyIdentifiableProvider())
self.assertEqual("'Referable with index 99 not found in SubmodelElementList[urn:x-test:submodel / list]'",
str(cm_2.exception))
@@ -899,7 +972,7 @@ def get_identifiable(self, identifier: Identifier) -> Identifiable:
model.Key(model.KeyTypes.SUBMODEL_ELEMENT_COLLECTION, "0"),
model.Key(model.KeyTypes.PROPERTY, "prop")),
model.Property)
- self.assertIs(prop, ref3.resolve(DummyObjectProvider()))
+ self.assertIs(prop, ref3.resolve(DummyIdentifiableProvider()))
ref4 = model.ModelReference((model.Key(model.KeyTypes.SUBMODEL, "urn:x-test:submodel"),
model.Key(model.KeyTypes.SUBMODEL_ELEMENT_LIST, "list"),
@@ -908,7 +981,7 @@ def get_identifiable(self, identifier: Identifier) -> Identifiable:
model.Key(model.KeyTypes.PROPERTY, "prop")),
model.Property)
with self.assertRaises(TypeError) as cm_3:
- ref4.resolve(DummyObjectProvider())
+ ref4.resolve(DummyIdentifiableProvider())
self.assertEqual("Cannot resolve id_short or index 'prop' at Property[urn:x-test:submodel / list[0].prop], "
"because it is not a UniqueIdShortNamespace!", str(cm_3.exception))
@@ -919,14 +992,14 @@ def get_identifiable(self, identifier: Identifier) -> Identifiable:
ref5 = model.ModelReference((model.Key(model.KeyTypes.SUBMODEL, "urn:x-test:sub"),), model.Property)
# Oh no, yet another typo!
with self.assertRaises(KeyError) as cm_5:
- ref5.resolve(DummyObjectProvider())
+ ref5.resolve(DummyIdentifiableProvider())
self.assertEqual("'Could not resolve identifier urn:x-test:sub'", str(cm_5.exception))
ref6 = model.ModelReference((model.Key(model.KeyTypes.SUBMODEL, "urn:x-test:submodel"),), model.Property)
# Okay, typo is fixed, but the type is not what we expect. However, we should get the submodel via the
# exception's value attribute
with self.assertRaises(model.UnexpectedTypeError) as cm_6:
- ref6.resolve(DummyObjectProvider())
+ ref6.resolve(DummyIdentifiableProvider())
self.assertIs(submodel, cm_6.exception.value)
with self.assertRaises(ValueError) as cm_7:
@@ -939,7 +1012,7 @@ def get_identifiable(self, identifier: Identifier) -> Identifiable:
model.Key(model.KeyTypes.PROPERTY, "prop_false")), model.Property)
with self.assertRaises(KeyError) as cm_8:
- ref8.resolve(DummyObjectProvider())
+ ref8.resolve(DummyIdentifiableProvider())
self.assertEqual("'Referable with id_short prop_false not found in "
"SubmodelElementCollection[urn:x-test:submodel / list[0]]'", str(cm_8.exception))
@@ -949,7 +1022,7 @@ def get_identifiable(self, identifier: Identifier) -> Identifiable:
model.SubmodelElementCollection)
with self.assertRaises(ValueError) as cm_9:
- ref9.resolve(DummyObjectProvider())
+ ref9.resolve(DummyIdentifiableProvider())
self.assertEqual("Cannot resolve 'collection' at SubmodelElementList[urn:x-test:submodel / list], "
"because it is not a numeric index!", str(cm_9.exception))
@@ -1230,20 +1303,28 @@ def hook(itm: int, _list: List[int]) -> None:
class LangStringSetTest(unittest.TestCase):
def test_language_tag_constraints(self) -> None:
+ with self.assertRaises(ValueError):
+ model.LangStringSet({"": "bar"})
+
with self.assertRaises(ValueError) as cm:
- model.LangStringSet({"foo": "bar"})
- self.assertEqual("The language code of the language tag must consist of exactly two lower-case letters! "
- "Given language tag and language code: 'foo', 'foo'", str(cm.exception))
+ model.LangStringSet({"x": "bar"})
+ self.assertEqual(f"The language tag must follow the format defined in BCP 47. "
+ f"Given language tag: x", cm.exception.args[0])
- lss = model.LangStringSet({"fo-OO": "bar"})
with self.assertRaises(ValueError) as cm:
- lss["foo"] = "bar"
- self.assertEqual("The language code of the language tag must consist of exactly two lower-case letters! "
- "Given language tag and language code: 'foo', 'foo'", str(cm.exception))
- self.assertNotIn("foo", lss)
- self.assertNotIn("fo", lss)
- lss["fo"] = "bar"
- self.assertIn("fo", lss)
+ model.LangStringSet({"foo-oo1": "bar"})
+ self.assertEqual(f"The language tag must follow the format defined in BCP 47. "
+ f"Given language tag: foo-oo1", cm.exception.args[0])
+
+ lss = model.LangStringSet({"fo-OO": "bar"})
+ self.assertIn("fo-OO", lss)
+ with self.assertRaises(ValueError):
+ lss["foo-oo1"] = "bar"
+ self.assertNotIn("foo-oo1", lss)
+
+ self.assertNotIn("foo-ASDF-OO", lss)
+ lss["foo-ASDF-OO"] = "bar"
+ self.assertIn("foo-ASDF-OO", lss)
def test_empty(self) -> None:
lss = model.LangStringSet({"fo": "bar", "fo-OO": "baz"})
@@ -1261,15 +1342,15 @@ def test_empty(self) -> None:
def test_text_constraints(self) -> None:
with self.assertRaises(ValueError) as cm:
- model.MultiLanguageNameType({"fo": "o" * 65})
+ model.MultiLanguageNameType({"fo": "o" * 129})
self.assertEqual("The text for the language tag 'fo' is invalid: MultiLanguageNameType has a maximum length of "
- "64! (length: 65)", str(cm.exception))
- mlnt = model.MultiLanguageNameType({"fo": "o" * 64})
+ "128! (length: 129)", str(cm.exception))
+ mlnt = model.MultiLanguageNameType({"fo": "o" * 128})
with self.assertRaises(ValueError) as cm:
mlnt["fo"] = ""
self.assertEqual("The text for the language tag 'fo' is invalid: MultiLanguageNameType has a minimum length of "
"1! (length: 0)", str(cm.exception))
- self.assertEqual(mlnt["fo"], "o" * 64)
+ self.assertEqual(mlnt["fo"], "o" * 128)
mlnt["fo"] = "o"
self.assertEqual(mlnt["fo"], "o")
diff --git a/sdk/test/model/test_datatypes.py b/sdk/test/model/test_datatypes.py
index b83c5e5fb..aaab73aa9 100644
--- a/sdk/test/model/test_datatypes.py
+++ b/sdk/test/model/test_datatypes.py
@@ -1,4 +1,4 @@
-# Copyright (c) 2025 the Eclipse BaSyx Authors
+# Copyright (c) 2026 the Eclipse BaSyx Authors
#
# This program and the accompanying materials are made available under the terms of the MIT License, available in
# the LICENSE file of this project.
@@ -143,6 +143,8 @@ def test_parse_date(self) -> None:
with self.assertRaises(ValueError) as cm:
model.datatypes.from_xsd("2020-01-24+11", model.datatypes.Date)
self.assertEqual("Value is not a valid XSD date string", str(cm.exception))
+ with self.assertRaises(NotImplementedError):
+ model.datatypes.from_xsd("-2020-01-24", model.datatypes.Date)
def test_serialize_date(self) -> None:
self.assertEqual("2020-01-24", model.datatypes.xsd_repr(model.datatypes.Date(2020, 1, 24)))
@@ -154,10 +156,18 @@ def test_serialize_date(self) -> None:
def test_parse_partial_dates(self) -> None:
self.assertEqual(model.datatypes.GYear(2019),
model.datatypes.from_xsd("2019", model.datatypes.GYear))
+ self.assertEqual(model.datatypes.GYear(-2001),
+ model.datatypes.from_xsd("-2001", model.datatypes.GYear))
+ self.assertEqual(model.datatypes.GYear(20000),
+ model.datatypes.from_xsd("20000", model.datatypes.GYear))
self.assertEqual(model.datatypes.GMonth(7),
model.datatypes.from_xsd("--07", model.datatypes.GMonth))
self.assertEqual(model.datatypes.GYearMonth(2020, 5),
model.datatypes.from_xsd("2020-05", model.datatypes.GYearMonth))
+ self.assertEqual(model.datatypes.GYearMonth(-2001, 10),
+ model.datatypes.from_xsd("-2001-10", model.datatypes.GYearMonth))
+ self.assertEqual(model.datatypes.GYearMonth(20000, 5),
+ model.datatypes.from_xsd("20000-05", model.datatypes.GYearMonth))
self.assertEqual(model.datatypes.GMonthDay(12, 6),
model.datatypes.from_xsd("--12-06", model.datatypes.GMonthDay))
self.assertEqual(model.datatypes.GDay(23),
@@ -178,6 +188,14 @@ def test_parse_partial_dates(self) -> None:
model.datatypes.from_xsd("10-10", model.datatypes.GYearMonth)
self.assertEqual("Value is not a valid XSD GYearMonth string", str(cm.exception))
+ def test_partial_dates_negative_year_into_date(self) -> None:
+ # Python's `datetime` library does not support negative years. Converting a G-Class with a negative year into a
+ # `Date` must therefore fail with a clear error message instead of the cryptic "year -2001 is out of range".
+ for value in (model.datatypes.GYear(-2001), model.datatypes.GYearMonth(-2001, 5)):
+ with self.assertRaises(ValueError) as cm:
+ value.into_date()
+ self.assertEqual("Negative years are not supported by Python's `datetime` library.", str(cm.exception))
+
def test_copy_date(self) -> None:
date = model.datatypes.Date(2020, 1, 24)
date_copy_shallow = copy.copy(date)
@@ -210,7 +228,9 @@ def test_parse_datetime(self) -> None:
model.datatypes.from_xsd("2020-01-24T15:25:17-00:20", model.datatypes.DateTime))
with self.assertRaises(ValueError) as cm:
model.datatypes.from_xsd("--2020-01-24T15:25:17-00:20", model.datatypes.DateTime)
- self.assertEqual("Value is not a valid XSD datetime string", str(cm.exception))
+ self.assertEqual("--2020-01-24T15:25:17-00:20 is not a valid XSD datetime string", str(cm.exception))
+ with self.assertRaises(NotImplementedError):
+ model.datatypes.from_xsd("-2020-01-24T15:25:17+01:00", model.datatypes.DateTime)
def test_serialize_datetime(self) -> None:
self.assertEqual("2020-01-24T15:25:17",
@@ -244,6 +264,31 @@ def test_serialize_time(self) -> None:
self.assertEqual("15:25:17.250000+01:00", model.datatypes.xsd_repr(
datetime.time(15, 25, 17, 250000, tzinfo=datetime.timezone(datetime.timedelta(hours=1)))))
+ def test_parse_datetime_midnight_24(self) -> None:
+ res = model.datatypes.from_xsd("2020-01-24T24:00:00", model.datatypes.DateTime)
+ self.assertEqual(datetime.datetime(2020, 1, 25, 0, 0, 0), res)
+ res_tz = model.datatypes.from_xsd("2020-01-24T24:00:00Z", model.datatypes.DateTime)
+ self.assertEqual(datetime.datetime(2020, 1, 25, 0, 0, 0, tzinfo=datetime.timezone.utc), res_tz)
+
+ def test_parse_datetime_midnight_24_invalid(self) -> None:
+ with self.assertRaises(ValueError) as cm:
+ model.datatypes.from_xsd("2020-01-24T24:01:00", model.datatypes.DateTime)
+ self.assertEqual(
+ "2020-01-24T24:01:00 is not a valid xsd:datetime.",
+ str(cm.exception))
+
+ def test_parse_time_midnight_24(self) -> None:
+ res = model.datatypes.from_xsd("24:00:00", model.datatypes.Time)
+ self.assertEqual(datetime.time(0, 0, 0), res)
+
+ def test_parse_time_midnight_24_invalid(self) -> None:
+ with self.assertRaises(ValueError) as cm:
+ model.datatypes.from_xsd("24:00:01", model.datatypes.Time)
+ self.assertEqual(
+ "24:00:01 is not a valid xsd:time.",
+ str(cm.exception)
+ )
+
def test_trivial_cast(self) -> None:
val = model.datatypes.trivial_cast(datetime.date(2017, 11, 13), model.datatypes.Date)
self.assertEqual(model.datatypes.Date(2017, 11, 13), val)
diff --git a/sdk/test/model/test_provider.py b/sdk/test/model/test_provider.py
index 68fb01cff..50e19c0da 100644
--- a/sdk/test/model/test_provider.py
+++ b/sdk/test/model/test_provider.py
@@ -1,4 +1,4 @@
-# Copyright (c) 2025 the Eclipse BaSyx Authors
+# Copyright (c) 2026 the Eclipse BaSyx Authors
#
# This program and the accompanying materials are made available under the terms of the MIT License, available in
# the LICENSE file of this project.
@@ -11,61 +11,103 @@
class ProvidersTest(unittest.TestCase):
+ _STORE_CLASSES = (model.DictIdentifiableStore, model.SetIdentifiableStore)
+
def setUp(self) -> None:
self.aas1 = model.AssetAdministrationShell(
- model.AssetInformation(global_asset_id="http://acplt.org/TestAsset1/"), "urn:x-test:aas1")
+ model.AssetInformation(global_asset_id="http://example.org/TestAsset1/"), "urn:x-test:aas1")
self.aas2 = model.AssetAdministrationShell(
- model.AssetInformation(global_asset_id="http://acplt.org/TestAsset2/"), "urn:x-test:aas2")
+ model.AssetInformation(global_asset_id="http://example.org/TestAsset2/"), "urn:x-test:aas2")
self.submodel1 = model.Submodel("urn:x-test:submodel1")
self.submodel2 = model.Submodel("urn:x-test:submodel2")
def test_store_retrieve(self) -> None:
- object_store: model.DictObjectStore[model.AssetAdministrationShell] = model.DictObjectStore()
- object_store.add(self.aas1)
- object_store.add(self.aas2)
- self.assertIn(self.aas1, object_store)
- property = model.Property('test', model.datatypes.String)
- self.assertFalse(property in object_store)
- aas3 = model.AssetAdministrationShell(model.AssetInformation(global_asset_id="http://acplt.org/TestAsset/"),
- "urn:x-test:aas1")
- with self.assertRaises(KeyError) as cm:
- object_store.add(aas3)
- self.assertEqual("'Identifiable object with same id urn:x-test:aas1 is already "
- "stored in this store'", str(cm.exception))
- self.assertEqual(2, len(object_store))
- self.assertIs(self.aas1,
- object_store.get_identifiable("urn:x-test:aas1"))
- self.assertIs(self.aas1,
- object_store.get("urn:x-test:aas1"))
- object_store.discard(self.aas1)
- object_store.discard(self.aas1)
- with self.assertRaises(KeyError) as cm:
- object_store.get_identifiable("urn:x-test:aas1")
- self.assertIsNone(object_store.get("urn:x-test:aas1"))
- self.assertEqual("'urn:x-test:aas1'", str(cm.exception))
- self.assertIs(self.aas2, object_store.pop())
- self.assertEqual(0, len(object_store))
+ for store_class in self._STORE_CLASSES:
+ with self.subTest(store=store_class.__name__):
+ store: model.AbstractObjectStore[model.Identifier, model.Identifiable] = store_class([self.aas1])
+ store.add(self.aas2)
+
+ store.add(self.aas1)
+ self.assertEqual(2, len(store))
+
+ self.assertIn(self.aas1, store)
+ self.assertIn("urn:x-test:aas1", store)
+ self.assertNotIn("urn:x-test:missing", store)
+ property = model.Property('test', model.datatypes.String)
+ self.assertFalse(property in store)
+ aas3 = model.AssetAdministrationShell(
+ model.AssetInformation(global_asset_id="http://example.org/TestAsset/"), "urn:x-test:aas1")
+ with self.assertRaises(KeyError) as cm:
+ store.add(aas3)
+ self.assertEqual("'Identifiable object with same id urn:x-test:aas1 is already "
+ "stored in this store'", str(cm.exception))
+ self.assertEqual(2, len(store))
+ self.assertIs(self.aas1, store.get_item("urn:x-test:aas1"))
+ self.assertIs(self.aas1, store.get("urn:x-test:aas1"))
+ store.discard(self.aas1)
+ store.discard(self.aas1)
+ with self.assertRaises(KeyError) as cm:
+ store.get_item("urn:x-test:aas1")
+ self.assertIsNone(store.get("urn:x-test:aas1"))
+ self.assertEqual("'urn:x-test:aas1'", str(cm.exception))
+ self.assertIs(self.aas2, store.pop())
+ self.assertEqual(0, len(store))
def test_store_update(self) -> None:
- object_store1: model.DictObjectStore[model.AssetAdministrationShell] = model.DictObjectStore()
- object_store1.add(self.aas1)
- object_store2: model.DictObjectStore[model.AssetAdministrationShell] = model.DictObjectStore()
- object_store2.add(self.aas2)
- object_store1.update(object_store2)
- self.assertIsInstance(object_store1, model.DictObjectStore)
- self.assertIn(self.aas2, object_store1)
+ for store_class in self._STORE_CLASSES:
+ with self.subTest(store=store_class.__name__):
+ store1: model.AbstractObjectStore[model.Identifier, model.Identifiable] = store_class()
+ store1.add(self.aas1)
+ store2: model.AbstractObjectStore[model.Identifier, model.Identifiable] = store_class()
+ store2.add(self.aas2)
+ store1.update(store2)
+ self.assertIsInstance(store1, store_class)
+ self.assertIn(self.aas2, store1)
+
+ def test_store_sync(self) -> None:
+ for store_class in self._STORE_CLASSES:
+ with self.subTest(store=store_class.__name__):
+ store: model.AbstractObjectStore[model.Identifier, model.Identifiable] = store_class()
+ self.assertEqual(store.sync([self.aas1, self.aas2], overwrite=False), (2, 0, 0))
+ self.assertIn(self.aas1, store)
+ self.assertIn(self.aas2, store)
+
+ self.assertEqual(store.sync([self.aas1], overwrite=False), (0, 0, 1))
+
+ self.assertEqual(store.sync([self.aas1], overwrite=True), (0, 1, 0))
+ self.assertIn(self.aas1, store)
+
+ self.assertEqual(store.sync([self.aas1, self.submodel1], overwrite=True), (1, 1, 0))
+
+ self.assertEqual(store.sync([self.aas1, self.submodel2], overwrite=False), (1, 0, 1))
+
+ self.assertEqual(store.sync([], overwrite=False), (0, 0, 0))
+
+ def test_store_remove(self) -> None:
+ for store_class in self._STORE_CLASSES:
+ with self.subTest(store=store_class.__name__):
+ store: model.AbstractObjectStore[model.Identifier, model.Identifiable] = store_class()
+ store.add(self.aas1)
+ store.remove(self.aas1)
+ self.assertEqual(0, len(store))
+ with self.assertRaises(KeyError):
+ store.remove(self.aas1)
def test_provider_multiplexer(self) -> None:
- aas_object_store: model.DictObjectStore[model.AssetAdministrationShell] = model.DictObjectStore()
- aas_object_store.add(self.aas1)
- aas_object_store.add(self.aas2)
- submodel_object_store: model.DictObjectStore[model.Submodel] = model.DictObjectStore()
- submodel_object_store.add(self.submodel1)
- submodel_object_store.add(self.submodel2)
-
- multiplexer = model.ObjectProviderMultiplexer([aas_object_store, submodel_object_store])
- self.assertIs(self.aas1, multiplexer.get_identifiable("urn:x-test:aas1"))
- self.assertIs(self.submodel1, multiplexer.get_identifiable("urn:x-test:submodel1"))
+ aas_identifiable_store: model.DictIdentifiableStore[model.Identifiable] = (
+ model.DictIdentifiableStore()
+ )
+ aas_identifiable_store.add(self.aas1)
+ aas_identifiable_store.add(self.aas2)
+ submodel_identifiable_store: model.DictIdentifiableStore[model.Identifiable] = model.DictIdentifiableStore()
+ submodel_identifiable_store.add(self.submodel1)
+ submodel_identifiable_store.add(self.submodel2)
+
+ multiplexer: model.ObjectProviderMultiplexer[model.Identifier, model.Identifiable] = (
+ model.ObjectProviderMultiplexer([aas_identifiable_store, submodel_identifiable_store])
+ )
+ self.assertIs(self.aas1, multiplexer.get_item("urn:x-test:aas1"))
+ self.assertIs(self.submodel1, multiplexer.get_item("urn:x-test:submodel1"))
with self.assertRaises(KeyError) as cm:
- multiplexer.get_identifiable("urn:x-test:submodel3")
- self.assertEqual("'Identifier could not be found in any of the 2 consulted registries.'", str(cm.exception))
+ multiplexer.get_item("urn:x-test:submodel3")
+ self.assertEqual("'Key could not be found in any of the 2 consulted registries.'", str(cm.exception))
diff --git a/sdk/test/model/test_string_constraints.py b/sdk/test/model/test_string_constraints.py
index 55d5789f5..4fe5c9f8b 100644
--- a/sdk/test/model/test_string_constraints.py
+++ b/sdk/test/model/test_string_constraints.py
@@ -1,4 +1,4 @@
-# Copyright (c) 2025 the Eclipse BaSyx Authors
+# Copyright (c) 2026 the Eclipse BaSyx Authors
#
# This program and the accompanying materials are made available under the terms of the MIT License, available in
# the LICENSE file of this project.
@@ -17,11 +17,11 @@ def test_identifier(self) -> None:
with self.assertRaises(ValueError) as cm:
_string_constraints.check_identifier(identifier)
self.assertEqual("Identifier has a minimum length of 1! (length: 0)", cm.exception.args[0])
- identifier = "a" * 2001
+ identifier = "a" * 2049
with self.assertRaises(ValueError) as cm:
_string_constraints.check_identifier(identifier)
- self.assertEqual("Identifier has a maximum length of 2000! (length: 2001)", cm.exception.args[0])
- identifier = "a" * 2000
+ self.assertEqual("Identifier has a maximum length of 2048! (length: 2049)", cm.exception.args[0])
+ identifier = "a" * 2048
_string_constraints.check_identifier(identifier)
def test_version_type(self) -> None:
@@ -73,8 +73,8 @@ def test_path_type_decoration(self) -> None:
self.assertEqual("PathType has a minimum length of 1! (length: 0)", cm.exception.args[0])
dc = self.DummyClass("a")
with self.assertRaises(ValueError) as cm:
- dc.some_attr = "a" * 2001
- self.assertEqual("PathType has a maximum length of 2000! (length: 2001)", cm.exception.args[0])
+ dc.some_attr = "a" * 2049
+ self.assertEqual("PathType has a maximum length of 2048! (length: 2049)", cm.exception.args[0])
self.assertEqual(dc.some_attr, "a")
def test_ignore_none_values(self) -> None:
diff --git a/sdk/test/model/test_submodel.py b/sdk/test/model/test_submodel.py
index 37a5792df..b5ee0d7dd 100644
--- a/sdk/test/model/test_submodel.py
+++ b/sdk/test/model/test_submodel.py
@@ -1,4 +1,4 @@
-# Copyright (c) 2025 the Eclipse BaSyx Authors
+# Copyright (c) 2026 the Eclipse BaSyx Authors
#
# This program and the accompanying materials are made available under the terms of the MIT License, available in
# the LICENSE file of this project.
@@ -17,17 +17,19 @@ def test_aasd_014_init_self_managed(self) -> None:
model.Entity("TestEntity", model.EntityType.SELF_MANAGED_ENTITY)
self.assertEqual("A self-managed entity has to have a globalAssetId or a specificAssetId (Constraint AASd-014)",
str(cm.exception))
- model.Entity("TestEntity", model.EntityType.SELF_MANAGED_ENTITY, global_asset_id="https://acplt.org/TestAsset")
+ model.Entity("TestEntity", model.EntityType.SELF_MANAGED_ENTITY,
+ global_asset_id="https://example.org/TestAsset")
model.Entity("TestEntity", model.EntityType.SELF_MANAGED_ENTITY,
specific_asset_id=(model.SpecificAssetId("test", "test"),))
- model.Entity("TestEntity", model.EntityType.SELF_MANAGED_ENTITY, global_asset_id="https://acplt.org/TestAsset",
+ model.Entity("TestEntity", model.EntityType.SELF_MANAGED_ENTITY,
+ global_asset_id="https://example.org/TestAsset",
specific_asset_id=(model.SpecificAssetId("test", "test"),))
def test_aasd_014_init_co_managed(self) -> None:
model.Entity("TestEntity", model.EntityType.CO_MANAGED_ENTITY)
with self.assertRaises(model.AASConstraintViolation) as cm:
model.Entity("TestEntity", model.EntityType.CO_MANAGED_ENTITY,
- global_asset_id="https://acplt.org/TestAsset")
+ global_asset_id="https://example.org/TestAsset")
self.assertEqual("A co-managed entity has to have neither a globalAssetId nor a specificAssetId "
"(Constraint AASd-014)", str(cm.exception))
with self.assertRaises(model.AASConstraintViolation) as cm:
@@ -37,14 +39,14 @@ def test_aasd_014_init_co_managed(self) -> None:
"(Constraint AASd-014)", str(cm.exception))
with self.assertRaises(model.AASConstraintViolation) as cm:
model.Entity("TestEntity", model.EntityType.CO_MANAGED_ENTITY,
- global_asset_id="https://acplt.org/TestAsset",
+ global_asset_id="https://example.org/TestAsset",
specific_asset_id=(model.SpecificAssetId("test", "test"),))
self.assertEqual("A co-managed entity has to have neither a globalAssetId nor a specificAssetId "
"(Constraint AASd-014)", str(cm.exception))
def test_aasd_014_set_self_managed(self) -> None:
entity = model.Entity("TestEntity", model.EntityType.SELF_MANAGED_ENTITY,
- global_asset_id="https://acplt.org/TestAsset",
+ global_asset_id="https://example.org/TestAsset",
specific_asset_id=(model.SpecificAssetId("test", "test"),))
entity.global_asset_id = None
with self.assertRaises(model.AASConstraintViolation) as cm:
@@ -53,7 +55,7 @@ def test_aasd_014_set_self_managed(self) -> None:
str(cm.exception))
entity = model.Entity("TestEntity", model.EntityType.SELF_MANAGED_ENTITY,
- global_asset_id="https://acplt.org/TestAsset",
+ global_asset_id="https://example.org/TestAsset",
specific_asset_id=(model.SpecificAssetId("test", "test"),))
entity.specific_asset_id = model.ConstrainedList(())
with self.assertRaises(model.AASConstraintViolation) as cm:
@@ -64,7 +66,7 @@ def test_aasd_014_set_self_managed(self) -> None:
def test_aasd_014_set_co_managed(self) -> None:
entity = model.Entity("TestEntity", model.EntityType.CO_MANAGED_ENTITY)
with self.assertRaises(model.AASConstraintViolation) as cm:
- entity.global_asset_id = "https://acplt.org/TestAsset"
+ entity.global_asset_id = "https://example.org/TestAsset"
self.assertEqual("A co-managed entity has to have neither a globalAssetId nor a specificAssetId "
"(Constraint AASd-014)", str(cm.exception))
with self.assertRaises(model.AASConstraintViolation) as cm:
@@ -74,7 +76,7 @@ def test_aasd_014_set_co_managed(self) -> None:
def test_aasd_014_specific_asset_id_add_self_managed(self) -> None:
entity = model.Entity("TestEntity", model.EntityType.SELF_MANAGED_ENTITY,
- global_asset_id="https://acplt.org/TestAsset")
+ global_asset_id="https://example.org/TestAsset")
specific_asset_id1 = model.SpecificAssetId("test", "test")
specific_asset_id2 = model.SpecificAssetId("test", "test")
entity.specific_asset_id.append(specific_asset_id1)
@@ -232,19 +234,6 @@ def test_constraints(self):
mlp2.semantic_id = semantic_id1
model.SubmodelElementList("test_list", model.MultiLanguageProperty, [mlp1, mlp2])
- # AASd-120
- mlp = model.MultiLanguageProperty("mlp")
- with self.assertRaises(model.AASConstraintViolation) as cm:
- model.SubmodelElementList("test_list", model.MultiLanguageProperty, [mlp])
- self.assertEqual("Objects with an id_short may not be added to a SubmodelElementList, got "
- "MultiLanguageProperty[mlp] with id_short=mlp (Constraint AASd-120)", str(cm.exception))
- mlp.id_short = None
- model.SubmodelElementList("test_list", model.MultiLanguageProperty, [mlp])
- with self.assertRaises(model.AASConstraintViolation) as cm:
- mlp.id_short = "mlp"
- self.assertEqual("id_short of MultiLanguageProperty[test_list[0]] cannot be set, because it is "
- "contained in a SubmodelElementList[test_list] (Constraint AASd-120)", str(cm.exception))
-
def test_aasd_108_add_set(self):
prop = model.Property(None, model.datatypes.Int)
mlp1 = model.MultiLanguageProperty(None)
diff --git a/sdk/test/util/test_identification.py b/sdk/test/util/test_identification.py
index ebfed8606..36019ede2 100644
--- a/sdk/test/util/test_identification.py
+++ b/sdk/test/util/test_identification.py
@@ -1,4 +1,4 @@
-# Copyright (c) 2025 the Eclipse BaSyx Authors
+# Copyright (c) 2026 the Eclipse BaSyx Authors
#
# This program and the accompanying materials are made available under the terms of the MIT License, available in
# the LICENSE file of this project.
@@ -24,7 +24,7 @@ def test_generate_uuid_identifier(self):
ids.add(identification)
def test_generate_iri_identifier(self):
- provider = model.DictObjectStore()
+ provider = model.DictIdentifiableStore()
# Check expected Errors when Namespaces are not valid
with self.assertRaises(ValueError) as cm:
@@ -34,24 +34,24 @@ def test_generate_iri_identifier(self):
generator = NamespaceIRIGenerator("http", provider)
self.assertEqual('Namespace must be a valid IRI, ending with #, / or =', str(cm.exception))
- generator = NamespaceIRIGenerator("http://acplt.org/AAS/", provider)
- self.assertEqual("http://acplt.org/AAS/", generator.namespace)
+ generator = NamespaceIRIGenerator("http://example.org/AAS/", provider)
+ self.assertEqual("http://example.org/AAS/", generator.namespace)
identification = generator.generate_id()
- self.assertEqual(identification, "http://acplt.org/AAS/0000")
+ self.assertEqual(identification, "http://example.org/AAS/0000")
provider.add(model.Submodel(identification))
for i in range(10):
identification = generator.generate_id()
self.assertNotIn(identification, provider)
provider.add(model.Submodel(identification))
- self.assertEqual(identification, "http://acplt.org/AAS/0010")
+ self.assertEqual(identification, "http://example.org/AAS/0010")
identification = generator.generate_id("Spülmaschine")
- self.assertEqual(identification, "http://acplt.org/AAS/Spülmaschine")
+ self.assertEqual(identification, "http://example.org/AAS/Spülmaschine")
provider.add(model.Submodel(identification))
for i in range(10):
identification = generator.generate_id("Spülmaschine")
self.assertNotIn(identification, provider)
- self.assertNotEqual(identification, "http://acplt.org/AAS/Spülmaschine")
+ self.assertNotEqual(identification, "http://example.org/AAS/Spülmaschine")
diff --git a/sdk/test/util/test_traversal.py b/sdk/test/util/test_traversal.py
new file mode 100644
index 000000000..881d7f7dc
--- /dev/null
+++ b/sdk/test/util/test_traversal.py
@@ -0,0 +1,114 @@
+# Copyright (c) 2026 the Eclipse BaSyx Authors
+#
+# This program and the accompanying materials are made available under the terms of the MIT License, available in
+# the LICENSE file of this project.
+#
+# SPDX-License-Identifier: MIT
+
+import unittest
+
+from basyx.aas import model
+from basyx.aas.util.traversal import walk_submodel, walk_submodel_element
+
+
+class TestWalkSubmodel(unittest.TestCase):
+ def _submodel(self, *elements: model.SubmodelElement) -> model.Submodel:
+ return model.Submodel("test-submodel", submodel_element=list(elements))
+
+ def test_flat_submodel(self):
+ prop = model.Property("prop", model.datatypes.String)
+ sm = self._submodel(prop)
+ result = list(walk_submodel(sm))
+ self.assertCountEqual([prop], result)
+
+ def test_collection_traversal(self):
+ child1 = model.Property("child1", model.datatypes.String)
+ child2 = model.Property("child2", model.datatypes.String)
+ coll = model.SubmodelElementCollection("coll", value=[child1, child2])
+ sm = self._submodel(coll)
+ result = list(walk_submodel(sm))
+ self.assertCountEqual([child1, child2, coll], result)
+
+ def test_list_traversal(self):
+ child = model.Property(None, model.datatypes.String)
+ sml = model.SubmodelElementList("sml", type_value_list_element=model.Property,
+ value_type_list_element=model.datatypes.String, value=[child])
+ sm = self._submodel(sml)
+ result = list(walk_submodel(sm))
+ self.assertCountEqual([child, sml], result)
+
+ def test_entity_statement_traversal(self):
+ stmt = model.Property("stmt", model.datatypes.String)
+ entity = model.Entity("entity", model.EntityType.CO_MANAGED_ENTITY, statement=[stmt])
+ sm = self._submodel(entity)
+ result = list(walk_submodel(sm))
+ self.assertCountEqual([stmt, entity], result)
+
+ def test_entity_empty_statement(self):
+ entity = model.Entity("entity", model.EntityType.CO_MANAGED_ENTITY)
+ sm = self._submodel(entity)
+ result = list(walk_submodel(sm))
+ self.assertCountEqual([entity], result)
+
+ def test_operation_variables_traversal(self):
+ in_var = model.Property("in_var", model.datatypes.String)
+ out_var = model.Property("out_var", model.datatypes.String)
+ inout_var = model.Property("inout_var", model.datatypes.String)
+ op = model.Operation("op", input_variable=[in_var], output_variable=[out_var],
+ in_output_variable=[inout_var])
+ sm = self._submodel(op)
+ result = list(walk_submodel(sm))
+ self.assertCountEqual([in_var, out_var, inout_var, op], result)
+
+ def test_operation_empty_variables(self):
+ op = model.Operation("op")
+ sm = self._submodel(op)
+ result = list(walk_submodel(sm))
+ self.assertCountEqual([op], result)
+
+ def test_collection_inside_entity_statement(self):
+ inner = model.Property("inner", model.datatypes.String)
+ coll = model.SubmodelElementCollection("coll", value=[inner])
+ entity = model.Entity("entity", model.EntityType.CO_MANAGED_ENTITY, statement=[coll])
+ sm = self._submodel(entity)
+ result = list(walk_submodel(sm))
+ self.assertCountEqual([inner, coll, entity], result)
+
+ def test_entity_inside_operation_input_variable(self):
+ stmt = model.Property("stmt", model.datatypes.String)
+ entity = model.Entity("entity", model.EntityType.CO_MANAGED_ENTITY, statement=[stmt])
+ op = model.Operation("op", input_variable=[entity])
+ sm = self._submodel(op)
+ result = list(walk_submodel(sm))
+ self.assertCountEqual([stmt, entity, op], result)
+
+ def test_walk_from_collection(self):
+ prop = model.Property("prop", model.datatypes.String)
+ entity = model.Entity("entity", model.EntityType.CO_MANAGED_ENTITY, statement=[prop])
+ coll = model.SubmodelElementCollection("coll", value=[entity])
+ # walk_submodel_element yields descendants of coll, not coll itself
+ result = list(walk_submodel_element(coll))
+ self.assertCountEqual([prop, entity], result)
+
+ def test_walk_from_list(self):
+ op = model.Operation(None)
+ sml = model.SubmodelElementList("sml", type_value_list_element=model.Operation, value=[op])
+ # walk_submodel_element yields descendants of sml, not sml itself
+ result = list(walk_submodel_element(sml))
+ self.assertCountEqual([op], result)
+
+ def test_file_inside_entity_is_found(self):
+ """Regression test for issue #423: File inside Entity.statement must be yielded."""
+ f = model.File("file", content_type="application/pdf", value="/some/file.pdf")
+ entity = model.Entity("entity", model.EntityType.CO_MANAGED_ENTITY, statement=[f])
+ sm = self._submodel(entity)
+ files = [e for e in walk_submodel(sm) if isinstance(e, model.File)]
+ self.assertIn(f, files)
+
+ def test_file_inside_operation_variable_is_found(self):
+ """Regression test for issue #423: File inside Operation variable must be yielded."""
+ f = model.File("file", content_type="application/pdf", value="/some/file.pdf")
+ op = model.Operation("op", input_variable=[f])
+ sm = self._submodel(op)
+ files = [e for e in walk_submodel(sm) if isinstance(e, model.File)]
+ self.assertIn(f, files)
diff --git a/server/README.md b/server/README.md
index d368d4ae5..1a3e42173 100644
--- a/server/README.md
+++ b/server/README.md
@@ -5,6 +5,9 @@ The server currently implements the following interfaces:
- [Asset Administration Shell Repository Service][4]
- [Submodel Repository Service][5]
+- [Asset Administration Shell Registry Service][12]
+- [Submodel Registry Service][14]
+- [Discovery Service][13]
It uses the [HTTP API][1] and the [*AASX*][7], [*JSON*][8], and [*XML*][9] Adapters of the [BaSyx Python SDK][3], to serve regarding files from a given directory.
The files are only read, changes won't persist.
@@ -12,23 +15,36 @@ The files are only read, changes won't persist.
Alternatively, the container can also be told to use the [Local-File Backend][2] instead, which stores Asset Administration Shells (AAS) and Submodels as individual *JSON* files and allows for persistent changes (except supplementary files, i.e. files referenced by `File` SubmodelElements).
See [below](#options) on how to configure this.
+## Docker Hub
+
+Pre-built images are published to [Docker Hub][11] on every release.
+Pull the latest version via:
+```
+$ docker pull eclipsebasyx/basyx-python-server:latest
+```
+
+Or pin to a specific release by replacing `` with the desired release number:
+```
+$ docker pull eclipsebasyx/basyx-python-server:
+```
+
## Building
-The container image can be built via:
+If you need to build the image locally (e.g. for development), run:
```
$ docker build -t basyx-python-server -f Dockerfile ..
```
-Note that when cloning this repository on Windows, Git may convert the line separators to CRLF. This breaks [`entrypoint.sh`](entrypoint.sh) and [`stop-supervisor.sh`](stop-supervisor.sh). Ensure both files use LF line separators (`\n`) before building.
+Note that when cloning this repository on Windows, Git may convert the line separators to CRLF. This breaks [`entrypoint.sh`](docker/repository/entrypoint.sh) and [`stop-supervisor.sh`](docker/common/stop-supervisor.sh). Ensure both files use LF line separators (`\n`) before building.
## Running
### Storage
-The server makes use of two directories:
+The repository and registry servers make use of two directories:
-- **`/input`** - *start-up data*: Directory from which the server loads AAS and Submodel files in *AASX*, *JSON* or *XML* format during start-up. The server will not modify these files.
-- **`/storage`** - *persistent store*: Directory where all AAS and Submodels are stored as individual *JSON* files if the server is [configured](#options) for persistence. The server will modify these files.
+- **`/input`** - *start-up data*: Directory from which the server loads its start-up data during start-up. The repository server loads AAS and Submodel files in *AASX*, *JSON* or *XML* format, while the registry server loads AAS and Submodel descriptors from *JSON* files only. The server will not modify these files.
+- **`/storage`** - *persistent store*: Directory where the server's data is stored as individual *JSON* files if the server is [configured](#options) for persistence. The repository server stores AAS and Submodels, while the registry server stores AAS and Submodel descriptors. The server will modify these files.
The directories can be mapped via the `-v` option from another image or a local directory.
To mount the host directories into the container, `-v ./input:/input -v ./storage:/storage` can be used.
@@ -43,12 +59,12 @@ To expose it on the host on port 8080, use the option `-p 8080:80` when running
The container can be configured via environment variables. The most important ones are summarised below:
-| Variable | Description | Default |
-|-----------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|--------------|
-| `API_BASE_PATH` | Base path under which the API is served. | `/api/v3.0/` |
-| `INPUT` | Path inside the container pointing to the directory from which the server takes its start-up data (*AASX*, *JSON*, *XML*). | `/input` |
-| `STORAGE` | Path inside the container pointing to the directory used by the server to persistently store data (*JSON*). | `/storage` |
-| `STORAGE_PERSISTENCY` | Flag to enable data persistence via the [LocalFileBackend][2]. AAS/Submodels are stored as *JSON* files in the directory specified by `STORAGE`. Supplementary files, i.e. files referenced by `File` SubmodelElements, are not stored. If disabled, any changes made via the API are only stored in memory. | `False` |
+| Variable | Description | Default |
+|-----------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|--------------|
+| `API_BASE_PATH` | Base path under which the API is served. | `/api/v3.1/` |
+| `INPUT` | Path inside the container pointing to the directory from which the server takes its start-up data. The repository server takes *AASX*, *JSON* and *XML* files, while the registry server takes AAS/Submodel descriptors from *JSON* files only. | `/input` |
+| `STORAGE` | Path inside the container pointing to the directory used by the repository or registry server to persistently store data (*JSON*). | `/storage` |
+| `STORAGE_PERSISTENCY` | Flag to enable data persistence via the [LocalFileBackend][2]. AAS/Submodels (repository server) or AAS/Submodel descriptors (registry server) are stored as *JSON* files in the directory specified by `STORAGE`. Supplementary files, i.e. files referenced by `File` SubmodelElements, are not stored. If disabled, any changes made via the API are only stored in memory. | `False` |
| `STORAGE_OVERWRITE` | Flag to enable storage overwrite if `STORAGE_PERSISTENCY` is enabled. Any AAS/Submodel from the `INPUT` directory already present in the LocalFileBackend replaces its existing version. If disabled, the existing version is kept. | `False` |
@@ -60,51 +76,15 @@ This implies the following start-up behaviour:
- Any AAS/Submodel *already present* is skipped, unless `STORAGE_OVERWRITE = True`, in which case it is replaced.
- Supplementary files (e.g., `File` SubmodelElements) are never persisted by the LocalFileBackend.
-### Running Examples
-
-Putting it all together, the container can be started via the following command:
-```
-$ docker run -p 8080:80 -v ./input:/input -v ./storage:/storage basyx-python-server
-```
-
-Since Windows uses backslashes instead of forward slashes in paths, you'll have to adjust the path to the storage directory there:
-```
-> docker run -p 8080:80 -v .\input:/input -v .\storage:/storage basyx-python-server
-```
-
-By default, the server will use the standard settings described [above](#options). Those settings can be adapted in the following way:
-```
-$ docker run -p 8080:80 -v ./input:/input2 -v ./storage:/storage2 -e API_BASE_PATH=/api/v3.1/ -e INPUT=/input2 -e STORAGE=/storage2 -e STORAGE_PERSISTENCY=True -e STORAGE_OVERWRITE=True basyx-python-server
-```
-
## Building and Running the Image with Docker Compose
-The container image can also be built and run via:
-```
-$ docker compose up
-```
-
-An exemplary [`compose.yml`](compose.yml) file for the server is given [here](compose.yml):
-```yaml
-name: basyx-python-server
-services:
- app:
- build:
- context: ..
- dockerfile: server/Dockerfile
- ports:
- - "8080:80"
- volumes:
- - ./input:/input
- - ./storage:/storage
- environment:
- STORAGE_PERSISTENCY: True
-```
+Example configurations can be found in the `./example_configurations` directory.
-Input files are read from `./input` and stored persistently under `./storage` on your host system. The server can be accessed at http://localhost:8080/api/v3.0/ from your host system.
-To get a different setup, the [`compose.yml`](compose.yml) file can be adapted using the options described [above](#options), similar to the third [running example](#running-examples).
+Currently, we offer:
-Note that the `Dockerfile` has to be specified explicitly via `dockerfile: server/Dockerfile`, as the build context must be set to the parent directory of `/server` to allow access to the local `/sdk`.
+- [repository_standalone](example_configurations/repository_standalone/README.md): Standalone repository server
+- [registry_standalone](example_configurations/registry_standalone/README.md): Standalone registry server
+- [discovery_standalone](example_configurations/discovery_standalone/README.md): Standalone discovery server
## Running without Docker (Debugging Only)
@@ -124,7 +104,7 @@ The server can also be run directly on the host system without Docker, NGINX and
$ python -m app.interfaces.repository
```
-The server can be accessed at http://localhost:8080/api/v3.0/ from your host system.
+The server can be accessed at http://localhost:8080/api/v3.1/ from your host system.
## Currently Unimplemented
Several features and routes are currently not supported:
@@ -161,10 +141,14 @@ This Dockerfile is inspired by the [tiangolo/uwsgi-nginx-docker][10] repository.
[1]: https://github.com/eclipse-basyx/basyx-python-sdk/pull/238
[2]: https://basyx-python-sdk.readthedocs.io/en/latest/backend/local_file.html
[3]: https://github.com/eclipse-basyx/basyx-python-sdk
-[4]: https://app.swaggerhub.com/apis/Plattform_i40/AssetAdministrationShellRepositoryServiceSpecification/V3.0.1_SSP-001
-[5]: https://app.swaggerhub.com/apis/Plattform_i40/SubmodelRepositoryServiceSpecification/V3.0.1_SSP-001
-[6]: https://industrialdigitaltwin.io/aas-specifications/IDTA-01002/v3.0/index.html
+[4]: https://app.swaggerhub.com/apis/Plattform_i40/AssetAdministrationShellRepositoryServiceSpecification/V3.1.1_SSP-001
+[5]: https://app.swaggerhub.com/apis/Plattform_i40/SubmodelRepositoryServiceSpecification/V3.1.1_SSP-001
+[6]: https://industrialdigitaltwin.io/aas-specifications/IDTA-01002/v3.1.2/index.html
[7]: https://basyx-python-sdk.readthedocs.io/en/latest/adapter/aasx.html#adapter-aasx
[8]: https://basyx-python-sdk.readthedocs.io/en/latest/adapter/json.html
[9]: https://basyx-python-sdk.readthedocs.io/en/latest/adapter/xml.html
[10]: https://github.com/tiangolo/uwsgi-nginx-docker
+[11]: https://hub.docker.com/r/eclipsebasyx/basyx-python-server
+[12]: https://app.swaggerhub.com/apis/Plattform_i40/AssetAdministrationShellRegistryServiceSpecification/V3.1.1_SSP-001
+[13]: https://app.swaggerhub.com/apis/Plattform_i40/DiscoveryServiceSpecification/V3.1.1_SSP-001
+[14]: https://app.swaggerhub.com/apis/Plattform_i40/SubmodelRegistryServiceSpecification/V3.1.1_SSP-001
diff --git a/1.0.0 b/server/app/__init__.py
similarity index 100%
rename from 1.0.0
rename to server/app/__init__.py
diff --git a/server/app/adapter/__init__.py b/server/app/adapter/__init__.py
new file mode 100644
index 000000000..10c99aa69
--- /dev/null
+++ b/server/app/adapter/__init__.py
@@ -0,0 +1 @@
+from .jsonization import *
diff --git a/server/app/adapter/jsonization.py b/server/app/adapter/jsonization.py
new file mode 100644
index 000000000..897590c77
--- /dev/null
+++ b/server/app/adapter/jsonization.py
@@ -0,0 +1,319 @@
+import logging
+from typing import Callable, Dict, Type
+
+from basyx.aas import model
+from basyx.aas.adapter._generic import ASSET_KIND, ASSET_KIND_INVERSE, JSON_AAS_TOP_LEVEL_KEYS_TO_TYPES
+from basyx.aas.adapter.json import AASToJsonEncoder
+from basyx.aas.adapter.json.json_deserialization import AASFromJsonDecoder, _get_ts
+
+import app.model as server_model
+
+logger = logging.getLogger(__name__)
+
+JSON_SERVER_AAS_TOP_LEVEL_KEYS_TO_TYPES = JSON_AAS_TOP_LEVEL_KEYS_TO_TYPES + (
+ ("assetAdministrationShellDescriptors", server_model.AssetAdministrationShellDescriptor),
+ ("submodelDescriptors", server_model.SubmodelDescriptor),
+)
+
+
+class ServerAASFromJsonDecoder(AASFromJsonDecoder):
+ @classmethod
+ def _get_aas_class_parsers(cls) -> Dict[str, Callable[[Dict[str, object]], object]]:
+ aas_class_parsers = super()._get_aas_class_parsers()
+ aas_class_parsers.update(
+ {
+ "AssetAdministrationShellDescriptor": cls._construct_asset_administration_shell_descriptor,
+ "SubmodelDescriptor": cls._construct_submodel_descriptor,
+ "AssetLink": cls._construct_asset_link,
+ "ProtocolInformation": cls._construct_protocol_information,
+ "Endpoint": cls._construct_endpoint,
+ }
+ )
+ return aas_class_parsers
+
+ # ##################################################################################################
+ # Utility Methods used in constructor methods to add general attributes (from abstract base classes)
+ # ##################################################################################################
+
+ @classmethod
+ def _amend_abstract_attributes(cls, obj: object, dct: Dict[str, object]) -> None:
+ super()._amend_abstract_attributes(obj, dct)
+
+ if isinstance(obj, server_model.Descriptor):
+ if "description" in dct:
+ obj.description = cls._construct_lang_string_set(
+ _get_ts(dct, "description", list), model.MultiLanguageTextType
+ )
+ if "displayName" in dct:
+ obj.display_name = cls._construct_lang_string_set(
+ _get_ts(dct, "displayName", list), model.MultiLanguageNameType
+ )
+ if "extensions" in dct:
+ for extension in _get_ts(dct, "extensions", list):
+ obj.extension.add(cls._construct_extension(extension))
+
+ @classmethod
+ def _construct_asset_administration_shell_descriptor(
+ cls, dct: Dict[str, object], object_class=server_model.AssetAdministrationShellDescriptor
+ ) -> server_model.AssetAdministrationShellDescriptor:
+ ret = object_class(id_=_get_ts(dct, "id", str))
+ cls._amend_abstract_attributes(ret, dct)
+ if "administration" in dct:
+ ret.administration = cls._construct_administrative_information(_get_ts(dct, "administration", dict))
+ if "assetKind" in dct:
+ ret.asset_kind = ASSET_KIND_INVERSE[_get_ts(dct, "assetKind", str)]
+ if "assetType" in dct:
+ ret.asset_type = _get_ts(dct, "assetType", str)
+ global_asset_id = None
+ if "globalAssetId" in dct:
+ ret.global_asset_id = _get_ts(dct, "globalAssetId", str)
+ specific_asset_id = set()
+ if "specificAssetIds" in dct:
+ for desc_data in _get_ts(dct, "specificAssetIds", list):
+ specific_asset_id.add(cls._construct_specific_asset_id(desc_data, model.SpecificAssetId))
+ if "endpoints" in dct:
+ for endpoint_dct in _get_ts(dct, "endpoints", list):
+ if "protocolInformation" in endpoint_dct:
+ ret.endpoints.append(cls._construct_endpoint(endpoint_dct, server_model.Endpoint))
+ elif "href" in endpoint_dct:
+ protocol_info = server_model.ProtocolInformation(
+ href=_get_ts(endpoint_dct["href"], "href", str),
+ endpoint_protocol=(
+ _get_ts(endpoint_dct["href"], "endpointProtocol", str)
+ if "endpointProtocol" in endpoint_dct["href"]
+ else None
+ ),
+ endpoint_protocol_version=(
+ _get_ts(endpoint_dct["href"], "endpointProtocolVersion", list)
+ if "endpointProtocolVersion" in endpoint_dct["href"]
+ else None
+ ),
+ )
+ ret.endpoints.append(
+ server_model.Endpoint(
+ protocol_information=protocol_info, interface=_get_ts(endpoint_dct, "interface", str)
+ )
+ )
+ if "idShort" in dct:
+ ret.id_short = _get_ts(dct, "idShort", str)
+ if "submodelDescriptors" in dct:
+ for sm_dct in _get_ts(dct, "submodelDescriptors", list):
+ ret.submodel_descriptors.append(
+ cls._construct_submodel_descriptor(sm_dct, server_model.SubmodelDescriptor)
+ )
+ return ret
+
+ @classmethod
+ def _construct_protocol_information(
+ cls, dct: Dict[str, object], object_class=server_model.ProtocolInformation
+ ) -> server_model.ProtocolInformation:
+ ret = object_class(
+ href=_get_ts(dct, "href", str),
+ endpoint_protocol=_get_ts(dct, "endpointProtocol", str) if "endpointProtocol" in dct else None,
+ endpoint_protocol_version=(
+ _get_ts(dct, "endpointProtocolVersion", list) if "endpointProtocolVersion" in dct else None
+ ),
+ subprotocol=_get_ts(dct, "subprotocol", str) if "subprotocol" in dct else None,
+ subprotocol_body=_get_ts(dct, "subprotocolBody", str) if "subprotocolBody" in dct else None,
+ subprotocol_body_encoding=(
+ _get_ts(dct, "subprotocolBodyEncoding", str) if "subprotocolBodyEncoding" in dct else None
+ ),
+ )
+ return ret
+
+ @classmethod
+ def _construct_endpoint(cls, dct: Dict[str, object], object_class=server_model.Endpoint) -> server_model.Endpoint:
+ ret = object_class(
+ protocol_information=cls._construct_protocol_information(
+ _get_ts(dct, "protocolInformation", dict), server_model.ProtocolInformation
+ ),
+ interface=_get_ts(dct, "interface", str),
+ )
+ cls._amend_abstract_attributes(ret, dct)
+ return ret
+
+ @classmethod
+ def _construct_submodel_descriptor(
+ cls, dct: Dict[str, object], object_class=server_model.SubmodelDescriptor
+ ) -> server_model.SubmodelDescriptor:
+ ret = object_class(id_=_get_ts(dct, "id", str), endpoints=[])
+ cls._amend_abstract_attributes(ret, dct)
+ for endpoint_dct in _get_ts(dct, "endpoints", list):
+ if "protocolInformation" in endpoint_dct:
+ ret.endpoints.append(cls._construct_endpoint(endpoint_dct, server_model.Endpoint))
+ elif "href" in endpoint_dct:
+ protocol_info = server_model.ProtocolInformation(
+ href=_get_ts(endpoint_dct["href"], "href", str),
+ endpoint_protocol=(
+ _get_ts(endpoint_dct["href"], "endpointProtocol", str)
+ if "endpointProtocol" in endpoint_dct["href"]
+ else None
+ ),
+ endpoint_protocol_version=(
+ _get_ts(endpoint_dct["href"], "endpointProtocolVersion", list)
+ if "endpointProtocolVersion" in endpoint_dct["href"]
+ else None
+ ),
+ )
+ ret.endpoints.append(
+ server_model.Endpoint(
+ protocol_information=protocol_info, interface=_get_ts(endpoint_dct, "interface", str)
+ )
+ )
+ if "administration" in dct:
+ ret.administration = cls._construct_administrative_information(_get_ts(dct, "administration", dict))
+ if "idShort" in dct:
+ ret.id_short = _get_ts(dct, "idShort", str)
+ if "semanticId" in dct:
+ ret.semantic_id = cls._construct_reference(_get_ts(dct, "semanticId", dict))
+ if "supplementalSemanticIds" in dct:
+ for ref in _get_ts(dct, "supplementalSemanticIds", list):
+ ret.supplemental_semantic_id.append(cls._construct_reference(ref))
+ return ret
+
+ @classmethod
+ def _construct_asset_link(
+ cls, dct: Dict[str, object], object_class=server_model.AssetLink
+ ) -> server_model.AssetLink:
+ ret = object_class(name=_get_ts(dct, "name", str), value=_get_ts(dct, "value", str))
+ return ret
+
+
+class ServerStrictAASFromJsonDecoder(ServerAASFromJsonDecoder):
+ """
+ A strict version of the AASFromJsonDecoder class for deserializing Asset Administration Shell data from the
+ official JSON format
+
+ This version has set ``failsafe = False``, which will lead to Exceptions raised for every missing attribute or wrong
+ object type.
+ """
+
+ failsafe = False
+
+
+class ServerStrippedAASFromJsonDecoder(ServerAASFromJsonDecoder):
+ """
+ Decoder for stripped JSON objects. Used in the HTTP adapter.
+ """
+
+ stripped = True
+
+
+class ServerStrictStrippedAASFromJsonDecoder(ServerStrictAASFromJsonDecoder, ServerStrippedAASFromJsonDecoder):
+ """
+ Non-failsafe decoder for stripped JSON objects.
+ """
+
+ pass
+
+
+class ServerAASToJsonEncoder(AASToJsonEncoder):
+
+ @classmethod
+ def _get_aas_class_serializers(cls) -> Dict[Type, Callable]:
+ serializers = super()._get_aas_class_serializers()
+ serializers.update(
+ {
+ server_model.AssetAdministrationShellDescriptor: cls._asset_administration_shell_descriptor_to_json,
+ server_model.SubmodelDescriptor: cls._submodel_descriptor_to_json,
+ server_model.Endpoint: cls._endpoint_to_json,
+ server_model.ProtocolInformation: cls._protocol_information_to_json,
+ server_model.AssetLink: cls._asset_link_to_json,
+ }
+ )
+ return serializers
+
+ @classmethod
+ def _abstract_classes_to_json(cls, obj: object) -> Dict[str, object]:
+ data: Dict[str, object] = super()._abstract_classes_to_json(obj)
+ if isinstance(obj, server_model.Descriptor):
+ if obj.description:
+ data["description"] = obj.description
+ if obj.display_name:
+ data["displayName"] = obj.display_name
+ if obj.extension:
+ data["extensions"] = list(obj.extension)
+ return data
+
+ @classmethod
+ def _asset_administration_shell_descriptor_to_json(
+ cls, obj: server_model.AssetAdministrationShellDescriptor
+ ) -> Dict[str, object]:
+ """
+ serialization of an object from class AssetAdministrationShell to json
+
+ :param obj: object of class AssetAdministrationShell
+ :return: dict with the serialized attributes of this object
+ """
+ data = cls._abstract_classes_to_json(obj)
+ data.update(cls._namespace_to_json(obj))
+ data["id"] = obj.id
+ if obj.administration:
+ data["administration"] = obj.administration
+ if obj.asset_kind:
+ data["assetKind"] = ASSET_KIND[obj.asset_kind]
+ if obj.asset_type:
+ data["assetType"] = obj.asset_type
+ if obj.global_asset_id:
+ data["globalAssetId"] = obj.global_asset_id
+ if obj.specific_asset_id:
+ data["specificAssetIds"] = list(obj.specific_asset_id)
+ if obj.endpoints:
+ data["endpoints"] = list(obj.endpoints)
+ if obj.id_short:
+ data["idShort"] = obj.id_short
+ if obj.submodel_descriptors:
+ data["submodelDescriptors"] = list(obj.submodel_descriptors)
+ return data
+
+ @classmethod
+ def _protocol_information_to_json(cls, obj: server_model.ProtocolInformation) -> Dict[str, object]:
+ data = cls._abstract_classes_to_json(obj)
+
+ data["href"] = obj.href
+ if obj.endpoint_protocol:
+ data["endpointProtocol"] = obj.endpoint_protocol
+ if obj.endpoint_protocol_version:
+ data["endpointProtocolVersion"] = obj.endpoint_protocol_version
+ if obj.subprotocol:
+ data["subprotocol"] = obj.subprotocol
+ if obj.subprotocol_body:
+ data["subprotocolBody"] = obj.subprotocol_body
+ if obj.subprotocol_body_encoding:
+ data["subprotocolBodyEncoding"] = obj.subprotocol_body_encoding
+ return data
+
+ @classmethod
+ def _endpoint_to_json(cls, obj: server_model.Endpoint) -> Dict[str, object]:
+ data = cls._abstract_classes_to_json(obj)
+ data["protocolInformation"] = cls._protocol_information_to_json(obj.protocol_information)
+ data["interface"] = obj.interface
+ return data
+
+ @classmethod
+ def _submodel_descriptor_to_json(cls, obj: server_model.SubmodelDescriptor) -> Dict[str, object]:
+ """
+ serialization of an object from class Submodel to json
+
+ :param obj: object of class Submodel
+ :return: dict with the serialized attributes of this object
+ """
+ data = cls._abstract_classes_to_json(obj)
+ data["id"] = obj.id
+ data["endpoints"] = [cls._endpoint_to_json(ep) for ep in obj.endpoints]
+ if obj.id_short:
+ data["idShort"] = obj.id_short
+ if obj.administration:
+ data["administration"] = obj.administration
+ if obj.semantic_id:
+ data["semanticId"] = obj.semantic_id
+ if obj.supplemental_semantic_id:
+ data["supplementalSemanticIds"] = list(obj.supplemental_semantic_id)
+ return data
+
+ @classmethod
+ def _asset_link_to_json(cls, obj: server_model.AssetLink) -> Dict[str, object]:
+ data = cls._abstract_classes_to_json(obj)
+ data["name"] = obj.name
+ data["value"] = obj.value
+ return data
diff --git a/server/app/backend/__init__.py b/server/app/backend/__init__.py
new file mode 100644
index 000000000..a58825fbb
--- /dev/null
+++ b/server/app/backend/__init__.py
@@ -0,0 +1 @@
+from .local_file import *
diff --git a/server/app/backend/local_file.py b/server/app/backend/local_file.py
new file mode 100644
index 000000000..e71a0e984
--- /dev/null
+++ b/server/app/backend/local_file.py
@@ -0,0 +1,186 @@
+import hashlib
+import json
+import logging
+import os
+import threading
+import weakref
+from typing import Dict, Iterator, Type, Union
+
+from basyx.aas import model
+from basyx.aas.model import provider as sdk_provider
+
+from app.adapter import jsonization
+from app.model import AssetAdministrationShellDescriptor, SubmodelDescriptor, descriptor
+
+logger = logging.getLogger(__name__)
+
+_DESCRIPTOR_TYPE = Union[descriptor.AssetAdministrationShellDescriptor, descriptor.SubmodelDescriptor]
+_DESCRIPTOR_CLASSES = (descriptor.AssetAdministrationShellDescriptor, descriptor.SubmodelDescriptor)
+
+# We need to resolve the Descriptor type in order to deserialize it again from JSON
+DESCRIPTOR_TYPE_TO_STRING: Dict[Type[Union[AssetAdministrationShellDescriptor, SubmodelDescriptor]], str] = {
+ AssetAdministrationShellDescriptor: "AssetAdministrationShellDescriptor",
+ SubmodelDescriptor: "SubmodelDescriptor",
+}
+
+
+class LocalFileDescriptorStore(sdk_provider.AbstractObjectStore[model.Identifier, _DESCRIPTOR_TYPE]):
+ """
+ An ObjectStore implementation for :class:`~app.model.descriptor.Descriptor` BaSyx Python SDK objects backed
+ by a local file based local backend
+ """
+
+ def __init__(self, directory_path: str):
+ """
+ Initializer of class LocalFileDescriptorStore
+
+ :param directory_path: Path to the local file backend (the path where you want to store your AAS JSON files)
+ """
+ self.directory_path: str = directory_path.rstrip("/")
+
+ # A dictionary of weak references to local replications of stored objects. Objects are kept in this cache as
+ # long as there is any other reference in the Python application to them. We use this to make sure that only one
+ # local replication of each object is kept in the application and retrieving an object from the store always
+ # returns the **same** (not only equal) object. Still, objects are forgotten, when they are not referenced
+ # anywhere else to save memory.
+ self._object_cache: weakref.WeakValueDictionary[model.Identifier, _DESCRIPTOR_TYPE] = (
+ weakref.WeakValueDictionary()
+ )
+ self._object_cache_lock = threading.Lock()
+
+ def check_directory(self, create=False):
+ """
+ Check if the directory exists and created it if not (and requested to do so)
+
+ :param create: If True and the database does not exist, try to create it
+ """
+ if not os.path.exists(self.directory_path):
+ if not create:
+ raise FileNotFoundError("The given directory ({}) does not exist".format(self.directory_path))
+ # Create directory
+ os.mkdir(self.directory_path)
+ logger.info("Creating directory {}".format(self.directory_path))
+
+ def get_descriptor_by_hash(self, hash_: str) -> _DESCRIPTOR_TYPE:
+ """
+ Retrieve an AAS Descriptor object from the local file by its identifier hash
+
+ :raises KeyError: If the respective file could not be found
+ """
+ try:
+ with open("{}/{}.json".format(self.directory_path, hash_), "r") as file:
+ obj = json.load(file, cls=jsonization.ServerAASFromJsonDecoder)
+ except FileNotFoundError as e:
+ raise KeyError("No Descriptor with hash {} found in local file database".format(hash_)) from e
+ with self._object_cache_lock:
+ if obj.id in self._object_cache:
+ return self._object_cache[obj.id]
+ self._object_cache[obj.id] = obj
+ return obj
+
+ def get_item(self, identifier: model.Identifier) -> _DESCRIPTOR_TYPE:
+ """
+ Retrieve an AAS Descriptor object from the local file by its :class:`~basyx.aas.model.base.Identifier`
+
+ :raises KeyError: If the respective file could not be found
+ """
+ with self._object_cache_lock:
+ if identifier in self._object_cache:
+ return self._object_cache[identifier]
+ try:
+ return self.get_descriptor_by_hash(self._transform_id(identifier))
+ except KeyError as e:
+ raise KeyError("No Identifiable with id {} found in local file database".format(identifier)) from e
+
+ def add(self, x: _DESCRIPTOR_TYPE) -> None:
+ """
+ Add a Descriptor object to the store
+
+ :raises KeyError: If an object with the same id exists already in the object store
+ """
+ logger.debug("Adding object %s to Local File Store ...", repr(x))
+ if os.path.exists("{}/{}.json".format(self.directory_path, self._transform_id(x.id))):
+ raise KeyError("Descriptor with id {} already exists in local file database".format(x.id))
+ with open("{}/{}.json".format(self.directory_path, self._transform_id(x.id)), "w") as file:
+ # Usually, we don't need to serialize the modelType, since during HTTP requests, we know exactly if this
+ # is an AASDescriptor or SubmodelDescriptor. However, here we cannot distinguish them, so to deserialize
+ # them successfully, we hack the `modelType` into the JSON.
+ serialized = json.loads(json.dumps(x, cls=jsonization.ServerAASToJsonEncoder))
+ serialized["modelType"] = DESCRIPTOR_TYPE_TO_STRING[type(x)]
+ json.dump(serialized, file, indent=4)
+ with self._object_cache_lock:
+ self._object_cache[x.id] = x
+
+ def commit(self, x: _DESCRIPTOR_TYPE) -> None:
+ """
+ Write the current in-memory state of a stored descriptor back to its file.
+
+ :param x: The descriptor to persist
+ :raises KeyError: If the descriptor is not present in the store
+ """
+ if not os.path.exists("{}/{}.json".format(self.directory_path, self._transform_id(x.id))):
+ raise KeyError("No AAS Descriptor object with id {} exists in local file database".format(x.id))
+ with open("{}/{}.json".format(self.directory_path, self._transform_id(x.id)), "w") as file:
+ serialized = json.loads(json.dumps(x, cls=jsonization.ServerAASToJsonEncoder))
+ serialized["modelType"] = DESCRIPTOR_TYPE_TO_STRING[type(x)]
+ json.dump(serialized, file, indent=4)
+
+ def discard(self, x: _DESCRIPTOR_TYPE) -> None:
+ """
+ Delete an :class:`~app.model.descriptor.Descriptor` AAS object from the local file store
+
+ :param x: The object to be deleted
+ :raises KeyError: If the object does not exist in the database
+ """
+ logger.debug("Deleting object %s from Local File Store database ...", repr(x))
+ try:
+ os.remove("{}/{}.json".format(self.directory_path, self._transform_id(x.id)))
+ except FileNotFoundError as e:
+ raise KeyError("No AAS Descriptor object with id {} exists in local file database".format(x.id)) from e
+ with self._object_cache_lock:
+ self._object_cache.pop(x.id, None)
+
+ def __contains__(self, x: object) -> bool:
+ """
+ Check if an object with the given :class:`~basyx.aas.model.base.Identifier` or the same
+ :class:`~basyx.aas.model.base.Identifier` as the given object is contained in the local file database
+
+ :param x: AAS object :class:`~basyx.aas.model.base.Identifier` or :class:`~app.model.descriptor.Descriptor`
+ AAS object
+ :return: ``True`` if such an object exists in the database, ``False`` otherwise
+ """
+ if isinstance(x, model.Identifier):
+ identifier = x
+ elif isinstance(x, _DESCRIPTOR_CLASSES):
+ identifier = x.id
+ else:
+ return False
+ logger.debug("Checking existence of Descriptor object with id %s in database ...", repr(x))
+ return os.path.exists("{}/{}.json".format(self.directory_path, self._transform_id(identifier)))
+
+ def __len__(self) -> int:
+ """
+ Retrieve the number of objects in the local file database
+
+ :return: The number of objects (determined from the number of documents)
+ """
+ logger.debug("Fetching number of documents from database ...")
+ return len(os.listdir(self.directory_path))
+
+ def __iter__(self) -> Iterator[_DESCRIPTOR_TYPE]:
+ """
+ Iterate all :class:`~app.model.descriptor.Descriptor` objects in the local folder.
+
+ This method returns an iterator, containing only a list of all identifiers in the database and retrieving
+ the identifiable objects on the fly.
+ """
+ logger.debug("Iterating over objects in database ...")
+ for name in os.listdir(self.directory_path):
+ yield self.get_descriptor_by_hash(name.rstrip(".json"))
+
+ @staticmethod
+ def _transform_id(identifier: model.Identifier) -> str:
+ """
+ Helper method to represent an ASS Identifier as a string to be used as Local file document id
+ """
+ return hashlib.sha256(identifier.encode("utf-8")).hexdigest()
diff --git a/server/app/interfaces/_string_constraints.py b/server/app/interfaces/_string_constraints.py
new file mode 100644
index 000000000..8489f4a03
--- /dev/null
+++ b/server/app/interfaces/_string_constraints.py
@@ -0,0 +1,64 @@
+# Copyright (c) 2026 the Eclipse BaSyx Authors
+#
+# This program and the accompanying materials are made available under the terms of the MIT License, available in
+# the LICENSE file of this project.
+#
+# SPDX-License-Identifier: MIT
+"""
+This module implements constraint functions for the listed constrained string types.
+All types are constrained in length (min and max).
+
+.. warning::
+ This module is intended for internal use only.
+
+The following types aliased in the :mod:`~server.app.interfaces.base` module are constrained:
+
+- :class:`~server.app.interfaces.base.CodeType`
+- :class:`~server.app.interfaces.base.ShortIdType`
+- :class:`~server.app.interfaces.base.LocatorType`
+- :class:`~server.app.interfaces.base.TextType`
+- :class:`~server.app.interfaces.base.SchemeType`
+"""
+
+from typing import Callable, Type
+from basyx.aas.model._string_constraints import check, constrain_attr, _T
+
+
+def check_code_type(value: str, type_name: str = "CodeType") -> None:
+ return check(value, type_name, 1, 32)
+
+
+def check_short_id_type(value: str, type_name: str = "ShortIdType") -> None:
+ return check(value, type_name, 1, 128)
+
+
+def check_locator_type(value: str, type_name: str = "LocatorType") -> None:
+ return check(value, type_name, 1, 2048)
+
+
+def check_text_type(value: str, type_name: str = "TextType") -> None:
+ return check(value, type_name, 1, 2048)
+
+
+def check_scheme_type(value: str, type_name: str = "SchemeType") -> None:
+ return check(value, type_name, 1, 128)
+
+
+def constrain_code_type(pub_attr_name: str) -> Callable[[Type[_T]], Type[_T]]:
+ return constrain_attr(pub_attr_name, check_code_type)
+
+
+def constrain_short_id_type(pub_attr_name: str) -> Callable[[Type[_T]], Type[_T]]:
+ return constrain_attr(pub_attr_name, check_short_id_type)
+
+
+def constrain_locator_type(pub_attr_name: str) -> Callable[[Type[_T]], Type[_T]]:
+ return constrain_attr(pub_attr_name, check_locator_type)
+
+
+def constrain_text_type(pub_attr_name: str) -> Callable[[Type[_T]], Type[_T]]:
+ return constrain_attr(pub_attr_name, check_text_type)
+
+
+def constrain_scheme_type(pub_attr_name: str) -> Callable[[Type[_T]], Type[_T]]:
+ return constrain_attr(pub_attr_name, check_scheme_type)
diff --git a/server/app/interfaces/base.py b/server/app/interfaces/base.py
index 2f23b5622..d32312370 100644
--- a/server/app/interfaces/base.py
+++ b/server/app/interfaces/base.py
@@ -1,4 +1,4 @@
-# Copyright (c) 2025 the Eclipse BaSyx Authors
+# Copyright (c) 2026 the Eclipse BaSyx Authors
#
# This program and the accompanying materials are made available under the terms of the MIT License, available in
# the LICENSE file of this project.
@@ -10,23 +10,34 @@
import io
import itertools
import json
-from typing import Iterable, Type, Iterator, Tuple, Optional, List, Union, Dict, Callable, TypeVar, Any
+from typing import Any, Callable, Dict, Iterable, Iterator, List, Optional, Tuple, Type, TypeVar, Union
import werkzeug.exceptions
import werkzeug.routing
import werkzeug.utils
-from lxml import etree
-from werkzeug import Response, Request
-from werkzeug.exceptions import NotFound, BadRequest
-from werkzeug.routing import MapAdapter
-
from basyx.aas import model
from basyx.aas.adapter._generic import XML_NS_MAP
-from basyx.aas.adapter.json import StrictStrippedAASFromJsonDecoder, StrictAASFromJsonDecoder, AASToJsonEncoder
-from basyx.aas.adapter.xml import xml_serialization, XMLConstructables, read_aas_xml_element
+from basyx.aas.adapter.xml import XMLConstructables, read_aas_xml_element, xml_serialization
from basyx.aas.model import AbstractObjectStore
-from util.converters import base64url_decode
+from basyx.aas.model.datatypes import NonNegativeInteger
+from lxml import etree
+from werkzeug import Request, Response
+from werkzeug.exceptions import BadRequest, NotFound
+from werkzeug.routing import MapAdapter
+
+import app.model
+from app.adapter import ServerAASToJsonEncoder, ServerStrictAASFromJsonDecoder, ServerStrictStrippedAASFromJsonDecoder
+from app.model import AssetAdministrationShellDescriptor, AssetLink, SubmodelDescriptor
+from app.util.converters import base64url_decode
+from . import _string_constraints
+# The following string aliases are constrained by the decorator functions defined in the string_constraints module,
+# wherever they are used for an instances attributes.
+CodeType = str
+ShortIdType = str
+LocatorType = str
+TextType = str
+SchemeType = str
T = TypeVar("T")
@@ -43,14 +54,21 @@ def __str__(self):
return self.name.capitalize()
+@_string_constraints.constrain_code_type("code")
class Message:
- def __init__(self, code: str, text: str, message_type: MessageType = MessageType.UNDEFINED,
- timestamp: Optional[datetime.datetime] = None):
- self.code: str = code
+ def __init__(
+ self,
+ code: CodeType,
+ text: str,
+ message_type: MessageType = MessageType.UNDEFINED,
+ timestamp: Optional[datetime.datetime] = None,
+ ):
+ self.code: CodeType = code
self.text: str = text
self.message_type: MessageType = message_type
- self.timestamp: datetime.datetime = timestamp if timestamp is not None \
- else datetime.datetime.now(datetime.timezone.utc)
+ self.timestamp: datetime.datetime = (
+ timestamp if timestamp is not None else datetime.datetime.now(datetime.timezone.utc)
+ )
class Result:
@@ -64,18 +82,25 @@ def __init__(self, success: bool, messages: Optional[List[Message]] = None):
ResponseData = Union[Result, object, List[object]]
+class PagingMetadata:
+ def __init__(self, cursor: Optional[str] = None):
+ self.cursor = cursor
+
+
class APIResponse(abc.ABC, Response):
@abc.abstractmethod
- def __init__(self, obj: Optional[ResponseData] = None, cursor: Optional[int] = None,
- stripped: bool = False, *args, **kwargs):
+ def __init__(
+ self, obj: Optional[ResponseData] = None, paging_metadata: Optional[PagingMetadata] = None,
+ stripped: bool = False, *args, **kwargs
+ ):
super().__init__(*args, **kwargs)
if obj is None:
self.status_code = 204
else:
- self.data = self.serialize(obj, cursor, stripped)
+ self.data = self.serialize(obj, paging_metadata, stripped)
@abc.abstractmethod
- def serialize(self, obj: ResponseData, cursor: Optional[int], stripped: bool) -> str:
+ def serialize(self, obj: ResponseData, paging_metadata: Optional[PagingMetadata], stripped: bool) -> str:
pass
@@ -83,18 +108,13 @@ class JsonResponse(APIResponse):
def __init__(self, *args, content_type="application/json", **kwargs):
super().__init__(*args, **kwargs, content_type=content_type)
- def serialize(self, obj: ResponseData, cursor: Optional[int], stripped: bool) -> str:
- if cursor is None:
+ def serialize(self, obj: ResponseData, paging_metadata: Optional[PagingMetadata], stripped: bool) -> str:
+ if paging_metadata is None:
data = obj
else:
- data = {
- "paging_metadata": {"cursor": str(cursor)},
- "result": obj
- }
+ data = {"paging_metadata": paging_metadata, "result": obj}
return json.dumps(
- data,
- cls=StrippedResultToJsonEncoder if stripped else ResultToJsonEncoder,
- separators=(",", ":")
+ data, cls=StrippedResultToJsonEncoder if stripped else ResultToJsonEncoder, separators=(",", ":")
)
@@ -102,10 +122,10 @@ class XmlResponse(APIResponse):
def __init__(self, *args, content_type="application/xml", **kwargs):
super().__init__(*args, **kwargs, content_type=content_type)
- def serialize(self, obj: ResponseData, cursor: Optional[int], stripped: bool) -> str:
+ def serialize(self, obj: ResponseData, paging_metadata: Optional[PagingMetadata], stripped: bool) -> str:
root_elem = etree.Element("response", nsmap=XML_NS_MAP)
- if cursor is not None:
- root_elem.set("cursor", str(cursor))
+ if paging_metadata is not None:
+ root_elem.set("cursor", str(paging_metadata.cursor))
if isinstance(obj, Result):
result_elem = self.result_to_xml(obj, **XML_NS_MAP)
for child in result_elem:
@@ -159,13 +179,10 @@ def __init__(self, *args, content_type="text/xml", **kwargs):
super().__init__(*args, **kwargs, content_type=content_type)
-class ResultToJsonEncoder(AASToJsonEncoder):
+class ResultToJsonEncoder(ServerAASToJsonEncoder):
@classmethod
def _result_to_json(cls, result: Result) -> Dict[str, object]:
- return {
- "success": result.success,
- "messages": result.messages
- }
+ return {"success": result.success, "messages": result.messages}
@classmethod
def _message_to_json(cls, message: Message) -> Dict[str, object]:
@@ -173,9 +190,16 @@ def _message_to_json(cls, message: Message) -> Dict[str, object]:
"messageType": message.message_type,
"text": message.text,
"code": message.code,
- "timestamp": message.timestamp.isoformat()
+ "timestamp": message.timestamp.isoformat(),
}
+ @classmethod
+ def _paging_metadata_to_json(cls, metadata: PagingMetadata) -> Dict[str, object]:
+ json_result: Dict[str, object] = dict()
+ if metadata.cursor is not None:
+ json_result["cursor"] = str(metadata.cursor)
+ return json_result
+
def default(self, obj: object) -> object:
if isinstance(obj, Result):
return self._result_to_json(obj)
@@ -183,6 +207,8 @@ def default(self, obj: object) -> object:
return self._message_to_json(obj)
if isinstance(obj, MessageType):
return str(obj)
+ if isinstance(obj, PagingMetadata):
+ return self._paging_metadata_to_json(obj)
return super().default(obj)
@@ -199,19 +225,23 @@ def __call__(self, environ, start_response) -> Iterable[bytes]:
return response(environ, start_response)
@classmethod
- def _get_slice(cls, request: Request, iterator: Iterable[T]) -> Tuple[Iterator[T], int]:
- limit_str = request.args.get('limit', default="10")
- cursor_str = request.args.get('cursor', default="1")
+ def _get_slice(cls, request: Request, iterator: Iterable[T]) -> Tuple[Iterator[T], Optional[PagingMetadata]]:
+ limit_str = request.args.get("limit", default="100")
+ cursor_str = request.args.get("cursor", default="1")
try:
- limit, cursor = int(limit_str), int(cursor_str) - 1 # cursor is 1-indexed
- if limit < 0 or cursor < 0:
- raise ValueError
+ limit, cursor = (NonNegativeInteger(int(limit_str)),
+ NonNegativeInteger(int(cursor_str) - 1)) # cursor is 1-indexed
except ValueError:
raise BadRequest("Limit can not be negative, cursor must be positive!")
start_index = cursor
end_index = cursor + limit
- paginated_slice = itertools.islice(iterator, start_index, end_index)
- return paginated_slice, end_index
+ items = list(itertools.islice(iterator, start_index, end_index + 1))
+ has_more = len(items) > limit
+ paginated_slice = iter(items[:limit])
+ next_cursor = str(cursor + limit + 1) if has_more else None
+
+ paging_metadata = PagingMetadata(cursor=next_cursor)
+ return paginated_slice, paging_metadata
def handle_request(self, request: Request):
map_adapter: MapAdapter = self.url_map.bind_to_environ(request.environ)
@@ -234,27 +264,31 @@ def get_response_type(request: Request) -> Type[APIResponse]:
response_types: Dict[str, Type[APIResponse]] = {
"application/json": JsonResponse,
"application/xml": XmlResponse,
- "text/xml": XmlResponseAlt
+ "text/xml": XmlResponseAlt,
}
if len(request.accept_mimetypes) == 0 or request.accept_mimetypes.best in (None, "*/*"):
return JsonResponse
mime_type = request.accept_mimetypes.best_match(response_types)
if mime_type is None:
- raise werkzeug.exceptions.NotAcceptable("This server supports the following content types: "
- + ", ".join(response_types.keys()))
+ raise werkzeug.exceptions.NotAcceptable(
+ "This server supports the following content types: " + ", ".join(response_types.keys())
+ )
return response_types[mime_type]
@staticmethod
- def http_exception_to_response(exception: werkzeug.exceptions.HTTPException, response_type: Type[APIResponse]) \
- -> APIResponse:
+ def http_exception_to_response(
+ exception: werkzeug.exceptions.HTTPException, response_type: Type[APIResponse]
+ ) -> APIResponse:
headers = exception.get_headers()
location = exception.get_response().location
if location is not None:
headers.append(("Location", location))
if exception.code and exception.code >= 400:
- message = Message(type(exception).__name__,
- exception.description if exception.description is not None else "",
- MessageType.ERROR)
+ message = Message(
+ type(exception).__name__,
+ exception.description if exception.description is not None else "",
+ MessageType.ERROR,
+ )
result = Result(False, [message])
else:
result = Result(False)
@@ -264,12 +298,12 @@ def http_exception_to_response(exception: werkzeug.exceptions.HTTPException, res
class ObjectStoreWSGIApp(BaseWSGIApp):
object_store: AbstractObjectStore
- def _get_all_obj_of_type(self, type_: Type[model.provider._IT]) -> Iterator[model.provider._IT]:
+ def _get_all_obj_of_type(self, type_: Type[T]) -> Iterator[T]:
for obj in self.object_store:
if isinstance(obj, type_):
yield obj
- def _get_obj_ts(self, identifier: model.Identifier, type_: Type[model.provider._IT]) -> model.provider._IT:
+ def _get_obj_ts(self, identifier: model.Identifier, type_: Type[T]) -> T:
identifiable = self.object_store.get(identifier)
if not isinstance(identifiable, type_):
raise NotFound(f"No {type_.__name__} with {identifier} found!")
@@ -292,7 +326,12 @@ class HTTPApiDecoder:
@classmethod
def check_type_support(cls, type_: type):
- if type_ not in cls.type_constructables_map:
+ tolerated_types = (
+ AssetAdministrationShellDescriptor,
+ SubmodelDescriptor,
+ AssetLink,
+ )
+ if type_ not in cls.type_constructables_map and type_ not in tolerated_types:
raise TypeError(f"Parsing {type_} is not supported!")
@classmethod
@@ -304,8 +343,9 @@ def assert_type(cls, obj: object, type_: Type[T]) -> T:
@classmethod
def json_list(cls, data: Union[str, bytes], expect_type: Type[T], stripped: bool, expect_single: bool) -> List[T]:
cls.check_type_support(expect_type)
- decoder: Type[StrictAASFromJsonDecoder] = StrictStrippedAASFromJsonDecoder if stripped \
- else StrictAASFromJsonDecoder
+ decoder: Type[ServerStrictAASFromJsonDecoder] = (
+ ServerStrictStrippedAASFromJsonDecoder if stripped else ServerStrictAASFromJsonDecoder
+ )
try:
parsed = json.loads(data, cls=decoder)
if isinstance(parsed, list) and expect_single:
@@ -324,6 +364,9 @@ def json_list(cls, data: Union[str, bytes], expect_type: Type[T], stripped: bool
model.SpecificAssetId: decoder._construct_specific_asset_id,
model.Reference: decoder._construct_reference,
model.Qualifier: decoder._construct_qualifier,
+ app.model.AssetAdministrationShellDescriptor: decoder._construct_asset_administration_shell_descriptor,
+ app.model.SubmodelDescriptor: decoder._construct_submodel_descriptor,
+ app.model.AssetLink: decoder._construct_asset_link,
}
constructor: Optional[Callable[..., T]] = mapping.get(expect_type) # type: ignore[assignment]
@@ -359,8 +402,9 @@ def xml(cls, data: bytes, expect_type: Type[T], stripped: bool) -> T:
cls.check_type_support(expect_type)
try:
xml_data = io.BytesIO(data)
- rv = read_aas_xml_element(xml_data, cls.type_constructables_map[expect_type],
- stripped=stripped, failsafe=False)
+ rv = read_aas_xml_element(
+ xml_data, cls.type_constructables_map[expect_type], stripped=stripped, failsafe=False
+ )
except (KeyError, ValueError) as e:
# xml deserialization creates an error chain. since we only return one error, return the root cause
f: BaseException = e
@@ -385,8 +429,8 @@ def request_body(cls, request: Request, expect_type: Type[T], stripped: bool) ->
if request.mimetype not in valid_content_types:
raise werkzeug.exceptions.UnsupportedMediaType(
- f"Invalid content-type: {request.mimetype}! Supported types: "
- + ", ".join(valid_content_types))
+ f"Invalid content-type: {request.mimetype}! Supported types: " + ", ".join(valid_content_types)
+ )
if request.mimetype == "application/json":
return cls.json(request.get_data(), expect_type, stripped)
diff --git a/server/app/interfaces/discovery.py b/server/app/interfaces/discovery.py
new file mode 100644
index 000000000..a340a0e12
--- /dev/null
+++ b/server/app/interfaces/discovery.py
@@ -0,0 +1,239 @@
+"""
+This module implements the Discovery interface defined in the
+'Specification of the Asset Administration Shell Part 2
+– Application Programming Interface'.
+"""
+
+import json
+import os
+from typing import Dict, List, Set, Type
+
+import werkzeug.exceptions
+from basyx.aas import model
+from werkzeug.routing import Rule, Submount
+from werkzeug.wrappers import Request, Response
+
+from app import model as server_model
+from app.adapter import jsonization
+from app.interfaces.base import BaseWSGIApp, HTTPApiDecoder, APIResponse
+from app.util.converters import IdentifierToBase64URLConverter, base64url_decode
+from app.model import ServiceSpecificationProfileEnum, ServiceDescription
+
+SUPPORTED_PROFILES: ServiceDescription = ServiceDescription([
+ ServiceSpecificationProfileEnum.DISCOVERY_FULL,
+ ServiceSpecificationProfileEnum.DISCOVERY_READ,
+])
+
+
+class DiscoveryStore:
+ def __init__(self) -> None:
+ self.aas_id_to_asset_ids: Dict[model.Identifier, Set[model.SpecificAssetId]] = {}
+ self.asset_id_to_aas_ids: Dict[model.SpecificAssetId, Set[model.Identifier]] = {}
+
+ def get_all_specific_asset_ids_by_aas_id(self, aas_id: model.Identifier) -> List[model.SpecificAssetId]:
+ return list(self.aas_id_to_asset_ids.get(aas_id, set()))
+
+ def add_specific_asset_ids_to_aas(self, aas_id: model.Identifier, asset_ids: List[model.SpecificAssetId]) -> None:
+
+ if aas_id not in self.aas_id_to_asset_ids:
+ self.aas_id_to_asset_ids[aas_id] = set()
+
+ for asset in asset_ids:
+ self.aas_id_to_asset_ids[aas_id].add(asset)
+
+ def delete_specific_asset_ids_by_aas_id(self, aas_id: model.Identifier) -> None:
+ key = aas_id
+ if key in self.aas_id_to_asset_ids:
+ del self.aas_id_to_asset_ids[key]
+
+ def search_aas_ids_by_asset_link(self, asset_link: server_model.AssetLink) -> List[model.Identifier]:
+ result = []
+ for asset_key, aas_ids in self.asset_id_to_aas_ids.items():
+ if asset_key.name == asset_link.name and asset_key.value == asset_link.value:
+ result.extend(list(aas_ids))
+ return result
+
+ def _add_aas_id_to_specific_asset_id(self, asset_id: model.SpecificAssetId, aas_id: model.Identifier) -> None:
+ if asset_id in self.asset_id_to_aas_ids:
+ self.asset_id_to_aas_ids[asset_id].add(aas_id)
+ else:
+ self.asset_id_to_aas_ids[asset_id] = {aas_id}
+
+ def _delete_aas_id_from_specific_asset_ids(self, asset_id: model.SpecificAssetId, aas_id: model.Identifier) -> None:
+ if asset_id in self.asset_id_to_aas_ids:
+ self.asset_id_to_aas_ids[asset_id].discard(aas_id)
+
+ @classmethod
+ def from_file(cls, filename: str) -> "DiscoveryStore":
+ """
+ Load a persisted discovery store from JSON.
+
+ The file stores the AAS-to-asset-id mapping as the source of truth.
+ While loading, the reverse asset-id-to-AAS index is rebuilt in memory so
+ lookup by asset ID works without persisting duplicate state.
+ """
+ with open(filename, "r") as file:
+ data = json.load(file, cls=jsonization.ServerAASFromJsonDecoder)
+
+ discovery_store = DiscoveryStore()
+
+ for aas_id, asset_ids in data.get("aas_id_to_asset_ids", {}).items():
+ parsed_asset_ids = set()
+
+ for asset_id in asset_ids:
+ if isinstance(asset_id, model.SpecificAssetId):
+ parsed_asset_id = asset_id
+ else:
+ parsed_asset_id = model.SpecificAssetId(
+ name=asset_id["name"],
+ value=asset_id["value"],
+ )
+
+ parsed_asset_ids.add(parsed_asset_id)
+ discovery_store._add_aas_id_to_specific_asset_id(parsed_asset_id, aas_id)
+
+ discovery_store.aas_id_to_asset_ids[aas_id] = parsed_asset_ids
+
+ return discovery_store
+
+ def to_file(self, filename: str) -> None:
+ """
+ Persist the discovery store as JSON.
+
+ Only the AAS-to-asset-id mapping is written because the reverse lookup
+ index can be rebuilt when the store is loaded. The data is written to a
+ temporary file first and then atomically moved into place to avoid
+ corrupting the existing store if serialization fails.
+ """
+ data = {
+ "aas_id_to_asset_ids": {
+ aas_id: list(asset_ids)
+ for aas_id, asset_ids in self.aas_id_to_asset_ids.items()
+ }
+ }
+
+ temp_filename = f"{filename}.tmp"
+ with open(temp_filename, "w") as file:
+ json.dump(data, file, cls=jsonization.ServerAASToJsonEncoder, indent=4)
+
+ os.replace(temp_filename, filename)
+
+
+class DiscoveryAPI(BaseWSGIApp):
+ def __init__(self, persistent_store: DiscoveryStore, base_path: str = "/api/v3.1"):
+ self.persistent_store: DiscoveryStore = persistent_store
+ self.url_map = werkzeug.routing.Map(
+ [
+ Submount(
+ base_path,
+ [
+ Rule("/description", methods=["GET"], endpoint=self.get_description),
+ Rule(
+ "/lookup/shellsByAssetLink",
+ methods=["POST"],
+ endpoint=self.search_all_aas_ids_by_asset_link,
+ ),
+ Submount(
+ "/lookup/shells",
+ [
+ # Todo: This route is deprecated in the specification, but needed for interoperability
+ # with the BaSyx UI https://github.com/eclipse-basyx/basyx-aas-web-ui.
+ # Once this route is no longer needed, we should consider removing it.
+ Rule("/", methods=["GET"], endpoint=self.get_all_aas_ids_by_asset_link),
+ Rule(
+ "/",
+ methods=["GET"],
+ endpoint=self.get_all_specific_asset_ids_by_aas_id,
+ ),
+ Rule("/", methods=["POST"], endpoint=self.post_all_asset_links_by_id),
+ Rule(
+ "/",
+ methods=["DELETE"],
+ endpoint=self.delete_all_asset_links_by_id,
+ ),
+ ],
+ ),
+ ],
+ )
+ ],
+ converters={"base64url": IdentifierToBase64URLConverter},
+ strict_slashes=False,
+ )
+
+ def get_description(self, request: Request, url_args: Dict, response_t: Type[APIResponse], **_kwargs) -> Response:
+ return response_t(SUPPORTED_PROFILES.to_dict())
+
+ def get_all_aas_ids_by_asset_link(
+ self, request: Request, url_args: dict, response_t: Type[APIResponse], **_kwargs
+ ) -> Response:
+ asset_ids_param = request.args.get("assetIds", "")
+ if not asset_ids_param:
+ raise werkzeug.exceptions.BadRequest("Missing query parameter 'assetIds'")
+
+ try:
+ decoded_str = base64url_decode(asset_ids_param)
+ payload = json.loads(decoded_str)
+ except (ValueError, json.JSONDecodeError) as exc:
+ raise werkzeug.exceptions.BadRequest(f"Invalid query parameter 'assetIds': {exc}") from exc
+
+ if isinstance(payload, dict):
+ payload = [payload]
+
+ if not isinstance(payload, list):
+ raise werkzeug.exceptions.BadRequest("Decoded assetIds payload must be a JSON object or list")
+
+ matching_aas_keys = set()
+ for item in payload:
+ if not isinstance(item, dict):
+ raise werkzeug.exceptions.BadRequest("Each asset link must be a JSON object")
+
+ asset_link = server_model.AssetLink(item["name"], item["value"])
+ aas_keys = self.persistent_store.search_aas_ids_by_asset_link(asset_link)
+ matching_aas_keys.update(aas_keys)
+
+ paginated_slice, paging_metadata = self._get_slice(request, list(matching_aas_keys))
+ return response_t(list(paginated_slice), paging_metadata=paging_metadata)
+
+ def search_all_aas_ids_by_asset_link(
+ self, request: Request, url_args: dict, response_t: Type[APIResponse], **_kwargs
+ ) -> Response:
+ asset_links = HTTPApiDecoder.request_body_list(request, server_model.AssetLink, False)
+ matching_aas_keys = set()
+ for asset_link in asset_links:
+ aas_keys = self.persistent_store.search_aas_ids_by_asset_link(asset_link)
+ matching_aas_keys.update(aas_keys)
+ paginated_slice, paging_metadata = self._get_slice(request, list(matching_aas_keys))
+ return response_t(list(paginated_slice), paging_metadata=paging_metadata)
+
+ def get_all_specific_asset_ids_by_aas_id(
+ self, request: Request, url_args: dict, response_t: Type[APIResponse], **_kwargs
+ ) -> Response:
+ aas_identifier = str(url_args["aas_id"])
+ asset_ids = self.persistent_store.get_all_specific_asset_ids_by_aas_id(aas_identifier)
+ return response_t(asset_ids)
+
+ def post_all_asset_links_by_id(
+ self, request: Request, url_args: dict, response_t: Type[APIResponse], **_kwargs
+ ) -> Response:
+ aas_identifier = str(url_args["aas_id"])
+ specific_asset_ids = HTTPApiDecoder.request_body_list(request, model.SpecificAssetId, False)
+ self.persistent_store.add_specific_asset_ids_to_aas(aas_identifier, specific_asset_ids)
+ for asset_id in specific_asset_ids:
+ self.persistent_store._add_aas_id_to_specific_asset_id(asset_id, aas_identifier)
+ updated = {aas_identifier: self.persistent_store.get_all_specific_asset_ids_by_aas_id(aas_identifier)}
+ return response_t(updated)
+
+ def delete_all_asset_links_by_id(
+ self, request: Request, url_args: dict, response_t: Type[APIResponse], **_kwargs
+ ) -> Response:
+ aas_identifier = str(url_args["aas_id"])
+ self.persistent_store.delete_specific_asset_ids_by_aas_id(aas_identifier)
+ for key in list(self.persistent_store.asset_id_to_aas_ids.keys()):
+ self.persistent_store.asset_id_to_aas_ids[key].discard(aas_identifier)
+ return response_t()
+
+
+if __name__ == "__main__":
+ from werkzeug.serving import run_simple
+
+ run_simple("localhost", 8084, DiscoveryAPI(DiscoveryStore()), use_debugger=True, use_reloader=True)
diff --git a/server/app/interfaces/registry.py b/server/app/interfaces/registry.py
new file mode 100644
index 000000000..274602fff
--- /dev/null
+++ b/server/app/interfaces/registry.py
@@ -0,0 +1,361 @@
+"""
+This module implements the Registry interface defined in the
+'Specification of the Asset Administration Shell Part 2
+– Application Programming Interface'.
+"""
+
+from typing import Dict, Iterator, Tuple, Type, Optional
+
+import werkzeug.exceptions
+import werkzeug.routing
+import werkzeug.urls
+import werkzeug.utils
+from basyx.aas import model
+from werkzeug.exceptions import BadRequest, Conflict, NotFound
+from werkzeug.routing import MapAdapter, Rule, Submount
+from werkzeug.wrappers import Request, Response
+
+import app.model as server_model
+from app.interfaces.base import APIResponse, HTTPApiDecoder, ObjectStoreWSGIApp, is_stripped_request, PagingMetadata
+from app.model import DictDescriptorStore, ServiceSpecificationProfileEnum, ServiceDescription
+from app.util.converters import IdentifierToBase64URLConverter, base64url_decode
+
+SUPPORTED_PROFILES: ServiceDescription = ServiceDescription([
+ ServiceSpecificationProfileEnum.AAS_REGISTRY_FULL,
+ ServiceSpecificationProfileEnum.SUBMODEL_REGISTRY_FULL,
+ ServiceSpecificationProfileEnum.AAS_REGISTRY_READ,
+ ServiceSpecificationProfileEnum.SUBMODEL_REGISTRY_READ,
+])
+
+
+class RegistryAPI(ObjectStoreWSGIApp):
+ def __init__(self, object_store: model.AbstractObjectStore, base_path: str = "/api/v3.1"):
+ self.object_store: model.AbstractObjectStore = object_store
+ self.url_map = werkzeug.routing.Map(
+ [
+ Submount(
+ base_path,
+ [
+ Rule("/description", methods=["GET"], endpoint=self.get_self_description),
+ Rule("/shell-descriptors", methods=["GET"], endpoint=self.get_all_aas_descriptors),
+ Rule("/shell-descriptors", methods=["POST"], endpoint=self.post_aas_descriptor),
+ Submount(
+ "/shell-descriptors",
+ [
+ Rule("/", methods=["GET"], endpoint=self.get_aas_descriptor_by_id),
+ Rule("/", methods=["PUT"], endpoint=self.put_aas_descriptor_by_id),
+ Rule(
+ "/", methods=["DELETE"], endpoint=self.delete_aas_descriptor_by_id
+ ),
+ Submount(
+ "/",
+ [
+ Rule(
+ "/submodel-descriptors",
+ methods=["GET"],
+ endpoint=self.get_all_submodel_descriptors_through_superpath,
+ ),
+ Rule(
+ "/submodel-descriptors",
+ methods=["POST"],
+ endpoint=self.post_submodel_descriptor_through_superpath,
+ ),
+ Submount(
+ "/submodel-descriptors",
+ [
+ Rule(
+ "/",
+ methods=["GET"],
+ endpoint=self.get_submodel_descriptor_by_id_through_superpath,
+ ),
+ Rule(
+ "/",
+ methods=["PUT"],
+ endpoint=self.put_submodel_descriptor_by_id_through_superpath,
+ ),
+ Rule(
+ "/",
+ methods=["DELETE"],
+ endpoint=self.delete_submodel_descriptor_by_id_through_superpath,
+ ),
+ ],
+ ),
+ ],
+ ),
+ ],
+ ),
+ Rule("/submodel-descriptors", methods=["GET"], endpoint=self.get_all_submodel_descriptors),
+ Rule("/submodel-descriptors", methods=["POST"], endpoint=self.post_submodel_descriptor),
+ Submount(
+ "/submodel-descriptors",
+ [
+ Rule(
+ "/",
+ methods=["GET"],
+ endpoint=self.get_submodel_descriptor_by_id,
+ ),
+ Rule(
+ "/",
+ methods=["PUT"],
+ endpoint=self.put_submodel_descriptor_by_id,
+ ),
+ Rule(
+ "/",
+ methods=["DELETE"],
+ endpoint=self.delete_submodel_descriptor_by_id,
+ ),
+ ],
+ ),
+ ],
+ )
+ ],
+ converters={"base64url": IdentifierToBase64URLConverter},
+ strict_slashes=False,
+ )
+
+ def _get_all_aas_descriptors(
+ self, request: "Request"
+ ) -> Tuple[Iterator[server_model.AssetAdministrationShellDescriptor], Optional[PagingMetadata]]:
+
+ descriptors: Iterator[server_model.AssetAdministrationShellDescriptor] = self._get_all_obj_of_type(
+ server_model.AssetAdministrationShellDescriptor
+ )
+
+ asset_kind_str = request.args.get("assetKind")
+ if asset_kind_str is not None:
+ try:
+ asset_kind = model.AssetKind[asset_kind_str]
+ except KeyError:
+ raise BadRequest(
+ f"Invalid assetKind '{asset_kind_str}', "
+ f"must be one of {list(model.AssetKind.__members__)}"
+ )
+ descriptors = filter(lambda desc: desc.asset_kind == asset_kind, descriptors)
+
+ asset_type = request.args.get("assetType")
+ if asset_type is not None:
+ asset_type = base64url_decode(asset_type)
+ try:
+ asset_type = model.Identifier(asset_type)
+ except Exception:
+ raise BadRequest(f"Invalid assetType: '{asset_type}'")
+ descriptors = filter(lambda desc: desc.asset_type == asset_type, descriptors)
+
+ paginated_descriptors, paging_metadata = self._get_slice(request, descriptors)
+ return paginated_descriptors, paging_metadata
+
+ def _get_aas_descriptor(self, url_args: Dict) -> server_model.AssetAdministrationShellDescriptor:
+ return self._get_obj_ts(url_args["aas_id"], server_model.AssetAdministrationShellDescriptor)
+
+ def _get_all_submodel_descriptors(self, request: Request) -> Tuple[
+ Iterator[server_model.SubmodelDescriptor], Optional[PagingMetadata]
+ ]:
+ submodel_descriptors: Iterator[server_model.SubmodelDescriptor] = self._get_all_obj_of_type(
+ server_model.SubmodelDescriptor
+ )
+ paginated_submodel_descriptors, paging_metadata = self._get_slice(request, submodel_descriptors)
+ return paginated_submodel_descriptors, paging_metadata
+
+ def _get_submodel_descriptor(self, url_args: Dict) -> server_model.SubmodelDescriptor:
+ return self._get_obj_ts(url_args["submodel_id"], server_model.SubmodelDescriptor)
+
+ # ------ COMMON ROUTES -------
+ def get_self_description(
+ self, request: Request, url_args: Dict, response_t: Type[APIResponse], **_kwargs
+ ) -> Response:
+ return response_t(SUPPORTED_PROFILES.to_dict())
+
+ # ------ AAS REGISTRY ROUTES -------
+ def get_all_aas_descriptors(
+ self, request: Request, url_args: Dict, response_t: Type[APIResponse], **_kwargs
+ ) -> Response:
+ aas_descriptors, paging_metadata = self._get_all_aas_descriptors(request)
+ return response_t(list(aas_descriptors), paging_metadata=paging_metadata)
+
+ def post_aas_descriptor(
+ self, request: Request, url_args: Dict, response_t: Type[APIResponse], map_adapter: MapAdapter
+ ) -> Response:
+ descriptor = HTTPApiDecoder.request_body(request, server_model.AssetAdministrationShellDescriptor, False)
+ try:
+ self.object_store.add(descriptor)
+ except KeyError as e:
+ raise Conflict(f"AssetAdministrationShellDescriptor with Identifier {descriptor.id} already exists!") from e
+ self.object_store.commit(descriptor)
+ created_resource_url = map_adapter.build(
+ self.get_aas_descriptor_by_id, {"aas_id": descriptor.id}, force_external=True
+ )
+ return response_t(descriptor, status=201, headers={"Location": created_resource_url})
+
+ def get_aas_descriptor_by_id(
+ self, request: Request, url_args: Dict, response_t: Type[APIResponse], **_kwargs
+ ) -> Response:
+ descriptor = self._get_aas_descriptor(url_args)
+ return response_t(descriptor)
+
+ def put_aas_descriptor_by_id(
+ self, request: Request, url_args: Dict, response_t: Type[APIResponse], map_adapter: MapAdapter, **_kwargs
+ ) -> Response:
+ try:
+ descriptor = self._get_aas_descriptor(url_args)
+ descriptor.update_from(
+ HTTPApiDecoder.request_body(
+ request, server_model.AssetAdministrationShellDescriptor, is_stripped_request(request)
+ )
+ )
+ self.object_store.commit(descriptor)
+ return response_t()
+ except NotFound:
+ descriptor = HTTPApiDecoder.request_body(request, server_model.AssetAdministrationShellDescriptor, False)
+ self.object_store.add(descriptor)
+ self.object_store.commit(descriptor)
+ created_resource_url = map_adapter.build(
+ self.get_aas_descriptor_by_id, {"aas_id": descriptor.id}, force_external=True
+ )
+ return response_t(descriptor, status=201, headers={"Location": created_resource_url})
+
+ def delete_aas_descriptor_by_id(
+ self, request: Request, url_args: Dict, response_t: Type[APIResponse], **_kwargs
+ ) -> Response:
+ descriptor = self._get_aas_descriptor(url_args)
+ self.object_store.remove(descriptor)
+ return response_t()
+
+ def get_all_submodel_descriptors_through_superpath(
+ self, request: Request, url_args: Dict, response_t: Type[APIResponse], **_kwargs
+ ) -> Response:
+ aas_descriptor = self._get_aas_descriptor(url_args)
+ submodel_descriptors, paging_metadata = self._get_slice(request, aas_descriptor.submodel_descriptors)
+ return response_t(list(submodel_descriptors), paging_metadata=paging_metadata)
+
+ def get_submodel_descriptor_by_id_through_superpath(
+ self, request: Request, url_args: Dict, response_t: Type[APIResponse], **_kwargs
+ ) -> Response:
+ aas_descriptor = self._get_aas_descriptor(url_args)
+ submodel_id = url_args["submodel_id"]
+ submodel_descriptor = next((sd for sd in aas_descriptor.submodel_descriptors if sd.id == submodel_id), None)
+ if submodel_descriptor is None:
+ raise NotFound(f"Submodel Descriptor with Identifier {submodel_id} not found in AssetAdministrationShell!")
+ return response_t(submodel_descriptor)
+
+ def post_submodel_descriptor_through_superpath(
+ self, request: Request, url_args: Dict, response_t: Type[APIResponse], map_adapter: MapAdapter
+ ) -> Response:
+ aas_descriptor = self._get_aas_descriptor(url_args)
+ submodel_descriptor = HTTPApiDecoder.request_body(
+ request, server_model.SubmodelDescriptor, is_stripped_request(request)
+ )
+ if any(sd.id == submodel_descriptor.id for sd in aas_descriptor.submodel_descriptors):
+ raise Conflict(f"Submodel Descriptor with Identifier {submodel_descriptor.id} already exists!")
+ aas_descriptor.submodel_descriptors.append(submodel_descriptor)
+ self.object_store.commit(aas_descriptor)
+ created_resource_url = map_adapter.build(
+ self.get_submodel_descriptor_by_id_through_superpath,
+ {"aas_id": aas_descriptor.id, "submodel_id": submodel_descriptor.id},
+ force_external=True,
+ )
+ return response_t(submodel_descriptor, status=201, headers={"Location": created_resource_url})
+
+ def put_submodel_descriptor_by_id_through_superpath(
+ self, request: Request, url_args: Dict, response_t: Type[APIResponse], map_adapter: MapAdapter, **_kwargs
+ ) -> Response:
+ aas_descriptor = self._get_aas_descriptor(url_args)
+ try:
+ submodel_id = url_args["submodel_id"]
+ submodel_descriptor = next((sd for sd in aas_descriptor.submodel_descriptors if sd.id == submodel_id), None)
+ if submodel_descriptor is None:
+ raise NotFound(
+ f"Submodel Descriptor with Identifier {submodel_id} not found in AssetAdministrationShell!"
+ )
+ submodel_descriptor.update_from(
+ HTTPApiDecoder.request_body(request, server_model.SubmodelDescriptor, is_stripped_request(request))
+ )
+ self.object_store.commit(aas_descriptor)
+ return response_t()
+ except NotFound:
+ submodel_descriptor = HTTPApiDecoder.request_body(
+ request, server_model.SubmodelDescriptor, is_stripped_request(request)
+ )
+ aas_descriptor.submodel_descriptors.append(submodel_descriptor)
+ self.object_store.commit(aas_descriptor)
+ created_resource_url = map_adapter.build(
+ self.get_submodel_descriptor_by_id_through_superpath,
+ {"aas_id": aas_descriptor.id, "submodel_id": submodel_descriptor.id},
+ force_external=True,
+ )
+ return response_t(submodel_descriptor, status=201, headers={"Location": created_resource_url})
+
+ def delete_submodel_descriptor_by_id_through_superpath(
+ self, request: Request, url_args: Dict, response_t: Type[APIResponse], **_kwargs
+ ) -> Response:
+ aas_descriptor = self._get_aas_descriptor(url_args)
+ submodel_id = url_args["submodel_id"]
+ submodel_descriptor = next((sd for sd in aas_descriptor.submodel_descriptors if sd.id == submodel_id), None)
+ if submodel_descriptor is None:
+ raise NotFound(f"Submodel Descriptor with Identifier {submodel_id} not found in AssetAdministrationShell!")
+ aas_descriptor.submodel_descriptors.remove(submodel_descriptor)
+ self.object_store.commit(aas_descriptor)
+ return response_t()
+
+ # ------ Submodel REGISTRY ROUTES -------
+ def get_all_submodel_descriptors(
+ self, request: Request, url_args: Dict, response_t: Type[APIResponse], **_kwargs
+ ) -> Response:
+ submodel_descriptors, paging_metadata = self._get_all_submodel_descriptors(request)
+ return response_t(
+ list(submodel_descriptors), paging_metadata=paging_metadata, stripped=is_stripped_request(request)
+ )
+
+ def get_submodel_descriptor_by_id(
+ self, request: Request, url_args: Dict, response_t: Type[APIResponse], **_kwargs
+ ) -> Response:
+ submodel_descriptor = self._get_submodel_descriptor(url_args)
+ return response_t(submodel_descriptor, stripped=is_stripped_request(request))
+
+ def post_submodel_descriptor(
+ self, request: Request, url_args: Dict, response_t: Type[APIResponse], map_adapter: MapAdapter
+ ) -> Response:
+ submodel_descriptor = HTTPApiDecoder.request_body(
+ request, server_model.SubmodelDescriptor, is_stripped_request(request)
+ )
+ try:
+ self.object_store.add(submodel_descriptor)
+ except KeyError as e:
+ raise Conflict(f"Submodel Descriptor with Identifier {submodel_descriptor.id} already exists!") from e
+ self.object_store.commit(submodel_descriptor)
+ created_resource_url = map_adapter.build(
+ self.get_submodel_descriptor_by_id, {"submodel_id": submodel_descriptor.id}, force_external=True
+ )
+ return response_t(submodel_descriptor, status=201, headers={"Location": created_resource_url})
+
+ def put_submodel_descriptor_by_id(
+ self, request: Request, url_args: Dict, response_t: Type[APIResponse], map_adapter: MapAdapter, **_kwargs
+ ) -> Response:
+ try:
+ submodel_descriptor = self._get_submodel_descriptor(url_args)
+ submodel_descriptor.update_from(
+ HTTPApiDecoder.request_body(request, server_model.SubmodelDescriptor, is_stripped_request(request))
+ )
+ self.object_store.commit(submodel_descriptor)
+ return response_t()
+ except NotFound:
+ submodel_descriptor = HTTPApiDecoder.request_body(
+ request, server_model.SubmodelDescriptor, is_stripped_request(request)
+ )
+ self.object_store.add(submodel_descriptor)
+ self.object_store.commit(submodel_descriptor)
+ created_resource_url = map_adapter.build(
+ self.get_submodel_descriptor_by_id, {"submodel_id": submodel_descriptor.id}, force_external=True
+ )
+ return response_t(submodel_descriptor, status=201, headers={"Location": created_resource_url})
+
+ def delete_submodel_descriptor_by_id(
+ self, request: Request, url_args: Dict, response_t: Type[APIResponse], **_kwargs
+ ) -> Response:
+ self.object_store.remove(self._get_obj_ts(url_args["submodel_id"], server_model.SubmodelDescriptor))
+ return response_t()
+
+
+if __name__ == "__main__":
+ from werkzeug.serving import run_simple
+
+ run_simple("localhost", 8083, RegistryAPI(DictDescriptorStore()), use_debugger=True, use_reloader=True)
diff --git a/server/app/interfaces/repository.py b/server/app/interfaces/repository.py
index 713023d0c..c604408e9 100644
--- a/server/app/interfaces/repository.py
+++ b/server/app/interfaces/repository.py
@@ -1,4 +1,4 @@
-# Copyright (c) 2025 the Eclipse BaSyx Authors
+# Copyright (c) 2026 the Eclipse BaSyx Authors
#
# This program and the accompanying materials are made available under the terms of the MIT License, available in
# the LICENSE file of this project.
@@ -10,170 +10,357 @@
import io
import json
-from typing import Type, Iterator, List, Dict, Union, Callable, Tuple, Optional, Iterable
+from typing import Callable, Dict, Iterable, Iterator, List, Optional, Tuple, Type, Union
import werkzeug.exceptions
import werkzeug.routing
import werkzeug.utils
-from werkzeug import Response, Request
-from werkzeug.datastructures import FileStorage
-from werkzeug.exceptions import NotFound, BadRequest, Conflict
-from werkzeug.routing import Submount, Rule, MapAdapter
-
from basyx.aas import model
from basyx.aas.adapter import aasx
-from util.converters import IdentifierToBase64URLConverter, IdShortPathConverter, base64url_decode
+from werkzeug import Request, Response
+from werkzeug.datastructures import FileStorage
+from werkzeug.exceptions import BadRequest, Conflict, NotFound
+from werkzeug.routing import MapAdapter, Rule, Submount
+
+from app.interfaces.base import PagingMetadata
+from app.util.converters import IdentifierToBase64URLConverter, IdShortPathConverter, base64url_decode
from .base import ObjectStoreWSGIApp, APIResponse, is_stripped_request, HTTPApiDecoder, T
+from app.model import ServiceSpecificationProfileEnum, ServiceDescription
+
+SUPPORTED_PROFILES: ServiceDescription = ServiceDescription([
+ ServiceSpecificationProfileEnum.AAS_REPOSITORY_FULL,
+ ServiceSpecificationProfileEnum.SUBMODEL_REPOSITORY_FULL,
+ ServiceSpecificationProfileEnum.AAS_REPOSITORY_READ,
+ ServiceSpecificationProfileEnum.SUBMODEL_REPOSITORY_READ,
+])
class WSGIApp(ObjectStoreWSGIApp):
- def __init__(self, object_store: model.AbstractObjectStore, file_store: aasx.AbstractSupplementaryFileContainer,
- base_path: str = "/api/v3.0"):
+ def __init__(
+ self,
+ object_store: model.AbstractObjectStore,
+ file_store: aasx.AbstractSupplementaryFileContainer,
+ base_path: str = "/api/v3.1",
+ ):
self.object_store: model.AbstractObjectStore = object_store
self.file_store: aasx.AbstractSupplementaryFileContainer = file_store
- self.url_map = werkzeug.routing.Map([
- Submount(base_path, [
- Rule("/serialization", methods=["GET"], endpoint=self.not_implemented),
- Rule("/description", methods=["GET"], endpoint=self.not_implemented),
- Rule("/shells", methods=["GET"], endpoint=self.get_aas_all),
- Rule("/shells", methods=["POST"], endpoint=self.post_aas),
- Submount("/shells", [
- Rule("/$reference", methods=["GET"], endpoint=self.get_aas_all_reference),
- Rule("/", methods=["GET"], endpoint=self.get_aas),
- Rule("/", methods=["PUT"], endpoint=self.put_aas),
- Rule("/", methods=["DELETE"], endpoint=self.delete_aas),
- Submount("/", [
- Rule("/$reference", methods=["GET"], endpoint=self.get_aas_reference),
- Rule("/asset-information", methods=["GET"], endpoint=self.get_aas_asset_information),
- Rule("/asset-information", methods=["PUT"], endpoint=self.put_aas_asset_information),
- Rule("/asset-information/thumbnail", methods=["GET", "PUT", "DELETE"],
- endpoint=self.not_implemented),
- Rule("/submodel-refs", methods=["GET"], endpoint=self.get_aas_submodel_refs),
- Rule("/submodel-refs", methods=["POST"], endpoint=self.post_aas_submodel_refs),
- Rule("/submodel-refs/", methods=["DELETE"],
- endpoint=self.delete_aas_submodel_refs_specific),
- Submount("/submodels", [
- Rule("/", methods=["PUT"],
- endpoint=self.put_aas_submodel_refs_submodel),
- Rule("/", methods=["DELETE"],
- endpoint=self.delete_aas_submodel_refs_submodel),
- Rule("/", endpoint=self.aas_submodel_refs_redirect),
- Rule("//", endpoint=self.aas_submodel_refs_redirect)
- ])
- ])
- ]),
- Rule("/submodels", methods=["GET"], endpoint=self.get_submodel_all),
- Rule("/submodels", methods=["POST"], endpoint=self.post_submodel),
- Submount("/submodels", [
- Rule("/$metadata", methods=["GET"], endpoint=self.get_submodel_all_metadata),
- Rule("/$reference", methods=["GET"], endpoint=self.get_submodel_all_reference),
- Rule("/$value", methods=["GET"], endpoint=self.not_implemented),
- Rule("/$path", methods=["GET"], endpoint=self.not_implemented),
- Rule("/", methods=["GET"], endpoint=self.get_submodel),
- Rule("/", methods=["PUT"], endpoint=self.put_submodel),
- Rule("/", methods=["DELETE"], endpoint=self.delete_submodel),
- Rule("/", methods=["PATCH"], endpoint=self.not_implemented),
- Submount("/", [
- Rule("/$metadata", methods=["GET"], endpoint=self.get_submodels_metadata),
- Rule("/$metadata", methods=["PATCH"], endpoint=self.not_implemented),
- Rule("/$value", methods=["GET"], endpoint=self.not_implemented),
- Rule("/$value", methods=["PATCH"], endpoint=self.not_implemented),
- Rule("/$reference", methods=["GET"], endpoint=self.get_submodels_reference),
- Rule("/$path", methods=["GET"], endpoint=self.not_implemented),
- Rule("/submodel-elements", methods=["GET"], endpoint=self.get_submodel_submodel_elements),
- Rule("/submodel-elements", methods=["POST"],
- endpoint=self.post_submodel_submodel_elements_id_short_path),
- Submount("/submodel-elements", [
- Rule("/$metadata", methods=["GET"], endpoint=self.get_submodel_submodel_elements_metadata),
- Rule("/$reference", methods=["GET"],
- endpoint=self.get_submodel_submodel_elements_reference),
- Rule("/$value", methods=["GET"], endpoint=self.not_implemented),
- Rule("/$path", methods=["GET"], endpoint=self.not_implemented),
- Rule("/", methods=["GET"],
- endpoint=self.get_submodel_submodel_elements_id_short_path),
- Rule("/", methods=["POST"],
- endpoint=self.post_submodel_submodel_elements_id_short_path),
- Rule("/", methods=["PUT"],
- endpoint=self.put_submodel_submodel_elements_id_short_path),
- Rule("/", methods=["DELETE"],
- endpoint=self.delete_submodel_submodel_elements_id_short_path),
- Rule("/", methods=["PATCH"], endpoint=self.not_implemented),
- Submount("/", [
- Rule("/$metadata", methods=["GET"],
- endpoint=self.get_submodel_submodel_elements_id_short_path_metadata),
- Rule("/$metadata", methods=["PATCH"], endpoint=self.not_implemented),
- Rule("/$reference", methods=["GET"],
- endpoint=self.get_submodel_submodel_elements_id_short_path_reference),
+ self.url_map = werkzeug.routing.Map(
+ [
+ Submount(
+ base_path,
+ [
+ Rule("/serialization", methods=["GET"], endpoint=self.not_implemented),
+ Rule("/description", methods=["GET"], endpoint=self.get_description),
+ Rule("/shells", methods=["GET"], endpoint=self.get_aas_all),
+ Rule("/shells", methods=["POST"], endpoint=self.post_aas),
+ Submount(
+ "/shells",
+ [
+ Rule("/$reference", methods=["GET"], endpoint=self.get_aas_all_reference),
+ Rule("/", methods=["GET"], endpoint=self.get_aas),
+ Rule("/", methods=["PUT"], endpoint=self.put_aas),
+ Rule("/", methods=["DELETE"], endpoint=self.delete_aas),
+ Submount(
+ "/",
+ [
+ Rule("/$reference", methods=["GET"], endpoint=self.get_aas_reference),
+ Rule(
+ "/asset-information",
+ methods=["GET"],
+ endpoint=self.get_aas_asset_information,
+ ),
+ Rule(
+ "/asset-information",
+ methods=["PUT"],
+ endpoint=self.put_aas_asset_information,
+ ),
+ Rule(
+ "/asset-information/thumbnail",
+ methods=["GET", "PUT", "DELETE"],
+ endpoint=self.not_implemented,
+ ),
+ Rule("/submodel-refs", methods=["GET"], endpoint=self.get_aas_submodel_refs),
+ Rule("/submodel-refs", methods=["POST"], endpoint=self.post_aas_submodel_refs),
+ Rule(
+ "/submodel-refs/",
+ methods=["DELETE"],
+ endpoint=self.delete_aas_submodel_refs_specific,
+ ),
+ Submount(
+ "/submodels",
+ [
+ Rule(
+ "/",
+ methods=["PUT"],
+ endpoint=self.put_aas_submodel_refs_submodel,
+ ),
+ Rule(
+ "/",
+ methods=["DELETE"],
+ endpoint=self.delete_aas_submodel_refs_submodel,
+ ),
+ Rule(
+ "/", endpoint=self.aas_submodel_refs_redirect
+ ),
+ Rule(
+ "//",
+ endpoint=self.aas_submodel_refs_redirect,
+ ),
+ ],
+ ),
+ ],
+ ),
+ ],
+ ),
+ Rule("/submodels", methods=["GET"], endpoint=self.get_submodel_all),
+ Rule("/submodels", methods=["POST"], endpoint=self.post_submodel),
+ Submount(
+ "/submodels",
+ [
+ Rule("/$metadata", methods=["GET"], endpoint=self.get_submodel_all_metadata),
+ Rule("/$reference", methods=["GET"], endpoint=self.get_submodel_all_reference),
Rule("/$value", methods=["GET"], endpoint=self.not_implemented),
- Rule("/$value", methods=["PATCH"], endpoint=self.not_implemented),
Rule("/$path", methods=["GET"], endpoint=self.not_implemented),
- Rule("/attachment", methods=["GET"],
- endpoint=self.get_submodel_submodel_element_attachment),
- Rule("/attachment", methods=["PUT"],
- endpoint=self.put_submodel_submodel_element_attachment),
- Rule("/attachment", methods=["DELETE"],
- endpoint=self.delete_submodel_submodel_element_attachment),
- Rule("/invoke", methods=["POST"], endpoint=self.not_implemented),
- Rule("/invoke/$value", methods=["POST"], endpoint=self.not_implemented),
- Rule("/invoke-async", methods=["POST"], endpoint=self.not_implemented),
- Rule("/invoke-async/$value", methods=["POST"], endpoint=self.not_implemented),
- Rule("/operation-status/", methods=["GET"],
- endpoint=self.not_implemented),
- Submount("/operation-results", [
- Rule("/", methods=["GET"], endpoint=self.not_implemented),
- Rule("//$value", methods=["GET"], endpoint=self.not_implemented)
- ]),
- Rule("/qualifiers", methods=["GET"],
- endpoint=self.get_submodel_submodel_element_qualifiers),
- Rule("/qualifiers", methods=["POST"],
- endpoint=self.post_submodel_submodel_element_qualifiers),
- Submount("/qualifiers", [
- Rule("/", methods=["GET"],
- endpoint=self.get_submodel_submodel_element_qualifiers),
- Rule("/", methods=["PUT"],
- endpoint=self.put_submodel_submodel_element_qualifiers),
- Rule("/", methods=["DELETE"],
- endpoint=self.delete_submodel_submodel_element_qualifiers)
- ])
- ])
- ]),
- Rule("/qualifiers", methods=["GET"], endpoint=self.get_submodel_submodel_element_qualifiers),
- Rule("/qualifiers", methods=["POST"], endpoint=self.post_submodel_submodel_element_qualifiers),
- Submount("/qualifiers", [
- Rule("/", methods=["GET"],
- endpoint=self.get_submodel_submodel_element_qualifiers),
- Rule("/", methods=["PUT"],
- endpoint=self.put_submodel_submodel_element_qualifiers),
- Rule("/", methods=["DELETE"],
- endpoint=self.delete_submodel_submodel_element_qualifiers)
- ])
- ])
- ]),
- Rule("/concept-descriptions", methods=["GET"], endpoint=self.get_concept_description_all),
- Rule("/concept-descriptions", methods=["POST"], endpoint=self.post_concept_description),
- Submount("/concept-descriptions", [
- Rule("/", methods=["GET"], endpoint=self.get_concept_description),
- Rule("/", methods=["PUT"], endpoint=self.put_concept_description),
- Rule("/", methods=["DELETE"], endpoint=self.delete_concept_description),
- ]),
- ])
- ], converters={
- "base64url": IdentifierToBase64URLConverter,
- "id_short_path": IdShortPathConverter
- }, strict_slashes=False)
+ Rule("/", methods=["GET"], endpoint=self.get_submodel),
+ Rule("/", methods=["PUT"], endpoint=self.put_submodel),
+ Rule("/", methods=["DELETE"], endpoint=self.delete_submodel),
+ Rule("/", methods=["PATCH"], endpoint=self.not_implemented),
+ Submount(
+ "/",
+ [
+ Rule("/$metadata", methods=["GET"], endpoint=self.get_submodels_metadata),
+ Rule("/$metadata", methods=["PATCH"], endpoint=self.not_implemented),
+ Rule("/$value", methods=["GET"], endpoint=self.not_implemented),
+ Rule("/$value", methods=["PATCH"], endpoint=self.not_implemented),
+ Rule("/$reference", methods=["GET"], endpoint=self.get_submodels_reference),
+ Rule("/$path", methods=["GET"], endpoint=self.not_implemented),
+ Rule(
+ "/submodel-elements",
+ methods=["GET"],
+ endpoint=self.get_submodel_submodel_elements,
+ ),
+ Rule(
+ "/submodel-elements",
+ methods=["POST"],
+ endpoint=self.post_submodel_submodel_elements_id_short_path,
+ ),
+ Submount(
+ "/submodel-elements",
+ [
+ Rule(
+ "/$metadata",
+ methods=["GET"],
+ endpoint=self.get_submodel_submodel_elements_metadata,
+ ),
+ Rule(
+ "/$reference",
+ methods=["GET"],
+ endpoint=self.get_submodel_submodel_elements_reference,
+ ),
+ Rule("/$value", methods=["GET"], endpoint=self.not_implemented),
+ Rule("/$path", methods=["GET"], endpoint=self.not_implemented),
+ Rule(
+ "/",
+ methods=["GET"],
+ endpoint=self.get_submodel_submodel_elements_id_short_path,
+ ),
+ Rule(
+ "/",
+ methods=["POST"],
+ endpoint=self.post_submodel_submodel_elements_id_short_path,
+ ),
+ Rule(
+ "/",
+ methods=["PUT"],
+ endpoint=self.put_submodel_submodel_elements_id_short_path,
+ ),
+ Rule(
+ "/",
+ methods=["DELETE"],
+ endpoint=self.delete_submodel_submodel_elements_id_short_path,
+ ),
+ Rule(
+ "/",
+ methods=["PATCH"],
+ endpoint=self.not_implemented,
+ ),
+ Submount(
+ "/",
+ [
+ Rule(
+ "/$metadata",
+ methods=["GET"],
+ endpoint=self.get_submodel_submodel_elements_id_short_path_metadata, # noqa: E501
+ ),
+ Rule(
+ "/$metadata",
+ methods=["PATCH"],
+ endpoint=self.not_implemented,
+ ),
+ Rule(
+ "/$reference",
+ methods=["GET"],
+ endpoint=self.get_submodel_submodel_elements_id_short_path_reference, # noqa: E501
+ ),
+ Rule("/$value", methods=["GET"], endpoint=self.not_implemented),
+ Rule(
+ "/$value", methods=["PATCH"], endpoint=self.not_implemented
+ ),
+ Rule("/$path", methods=["GET"], endpoint=self.not_implemented),
+ Rule(
+ "/attachment",
+ methods=["GET"],
+ endpoint=self.get_submodel_submodel_element_attachment,
+ ),
+ Rule(
+ "/attachment",
+ methods=["PUT"],
+ endpoint=self.put_submodel_submodel_element_attachment,
+ ),
+ Rule(
+ "/attachment",
+ methods=["DELETE"],
+ endpoint=self.delete_submodel_submodel_element_attachment,
+ ),
+ Rule(
+ "/invoke", methods=["POST"], endpoint=self.not_implemented
+ ),
+ Rule(
+ "/invoke/$value",
+ methods=["POST"],
+ endpoint=self.not_implemented,
+ ),
+ Rule(
+ "/invoke-async",
+ methods=["POST"],
+ endpoint=self.not_implemented,
+ ),
+ Rule(
+ "/invoke-async/$value",
+ methods=["POST"],
+ endpoint=self.not_implemented,
+ ),
+ Rule(
+ "/operation-status/",
+ methods=["GET"],
+ endpoint=self.not_implemented,
+ ),
+ Submount(
+ "/operation-results",
+ [
+ Rule(
+ "/",
+ methods=["GET"],
+ endpoint=self.not_implemented,
+ ),
+ Rule(
+ "//$value",
+ methods=["GET"],
+ endpoint=self.not_implemented,
+ ),
+ ],
+ ),
+ Rule(
+ "/qualifiers",
+ methods=["GET"],
+ endpoint=self.get_submodel_submodel_element_qualifiers,
+ ),
+ Rule(
+ "/qualifiers",
+ methods=["POST"],
+ endpoint=self.post_submodel_submodel_element_qualifiers,
+ ),
+ Submount(
+ "/qualifiers",
+ [
+ Rule(
+ "/",
+ methods=["GET"],
+ endpoint=self.get_submodel_submodel_element_qualifiers, # noqa: E501
+ ),
+ Rule(
+ "/",
+ methods=["PUT"],
+ endpoint=self.put_submodel_submodel_element_qualifiers, # noqa: E501
+ ),
+ Rule(
+ "/",
+ methods=["DELETE"],
+ endpoint=self.delete_submodel_submodel_element_qualifiers, # noqa: E501
+ ),
+ ],
+ ),
+ ],
+ ),
+ ],
+ ),
+ Rule(
+ "/qualifiers",
+ methods=["GET"],
+ endpoint=self.get_submodel_submodel_element_qualifiers,
+ ),
+ Rule(
+ "/qualifiers",
+ methods=["POST"],
+ endpoint=self.post_submodel_submodel_element_qualifiers,
+ ),
+ Submount(
+ "/qualifiers",
+ [
+ Rule(
+ "/",
+ methods=["GET"],
+ endpoint=self.get_submodel_submodel_element_qualifiers,
+ ),
+ Rule(
+ "/",
+ methods=["PUT"],
+ endpoint=self.put_submodel_submodel_element_qualifiers,
+ ),
+ Rule(
+ "/",
+ methods=["DELETE"],
+ endpoint=self.delete_submodel_submodel_element_qualifiers,
+ ),
+ ],
+ ),
+ ],
+ ),
+ ],
+ ),
+ Rule("/concept-descriptions", methods=["GET"], endpoint=self.get_concept_description_all),
+ Rule("/concept-descriptions", methods=["POST"], endpoint=self.post_concept_description),
+ Submount(
+ "/concept-descriptions",
+ [
+ Rule("/", methods=["GET"], endpoint=self.get_concept_description),
+ Rule("/", methods=["PUT"], endpoint=self.put_concept_description),
+ Rule(
+ "/",
+ methods=["DELETE"],
+ endpoint=self.delete_concept_description,
+ ),
+ ],
+ ),
+ ],
+ )
+ ],
+ converters={"base64url": IdentifierToBase64URLConverter, "id_short_path": IdShortPathConverter},
+ strict_slashes=False,
+ )
# TODO: the parameters can be typed via builtin wsgiref with Python 3.11+
def __call__(self, environ, start_response) -> Iterable[bytes]:
response: Response = self.handle_request(Request(environ))
return response(environ, start_response)
- def _get_obj_ts(self, identifier: model.Identifier, type_: Type[model.provider._IT]) -> model.provider._IT:
+ def _get_obj_ts(self, identifier: model.Identifier, type_: Type[T]) -> T:
identifiable = self.object_store.get(identifier)
if not isinstance(identifiable, type_):
raise NotFound(f"No {type_.__name__} with {identifier} found!")
return identifiable
- def _get_all_obj_of_type(self, type_: Type[model.provider._IT]) -> Iterator[model.provider._IT]:
+ def _get_all_obj_of_type(self, type_: Type[T]) -> Iterator[T]:
for obj in self.object_store:
if isinstance(obj, type_):
yield obj
@@ -185,8 +372,9 @@ def _resolve_reference(self, reference: model.ModelReference[model.base._RT]) ->
raise werkzeug.exceptions.InternalServerError(str(e)) from e
@classmethod
- def _get_nested_submodel_element(cls, namespace: model.UniqueIdShortNamespace, id_shorts: List[str]) \
- -> model.SubmodelElement:
+ def _get_nested_submodel_element(
+ cls, namespace: model.UniqueIdShortNamespace, id_shorts: List[str]
+ ) -> model.SubmodelElement:
if not id_shorts:
raise ValueError("No id_shorts specified!")
@@ -216,8 +404,9 @@ def _expect_namespace(cls, obj: object, needle: str) -> model.UniqueIdShortNames
return obj
@classmethod
- def _namespace_submodel_element_op(cls, namespace: model.UniqueIdShortNamespace, op: Callable[[str], T], arg: str) \
- -> T:
+ def _namespace_submodel_element_op(
+ cls, namespace: model.UniqueIdShortNamespace, op: Callable[[str], T], arg: str
+ ) -> T:
try:
return op(arg)
except KeyError as e:
@@ -231,8 +420,9 @@ def _qualifiable_qualifier_op(cls, qualifiable: model.Qualifiable, op: Callable[
raise NotFound(f"Qualifier with type {arg!r} not found in {qualifiable!r}") from e
@classmethod
- def _get_submodel_reference(cls, aas: model.AssetAdministrationShell, submodel_id: model.NameType) \
- -> model.ModelReference[model.Submodel]:
+ def _get_submodel_reference(
+ cls, aas: model.AssetAdministrationShell, submodel_id: model.NameType
+ ) -> model.ModelReference[model.Submodel]:
# TODO: this is currently O(n), could be O(1) as aas.submodel, but keys would have to precisely match, as they
# are hashed including their KeyType
for ref in aas.submodel:
@@ -240,7 +430,9 @@ def _get_submodel_reference(cls, aas: model.AssetAdministrationShell, submodel_i
return ref
raise NotFound(f"The AAS {aas!r} doesn't have a submodel reference to {submodel_id!r}!")
- def _get_shells(self, request: Request) -> Tuple[Iterator[model.AssetAdministrationShell], int]:
+ def _get_shells(
+ self, request: Request
+ ) -> Tuple[Iterator[model.AssetAdministrationShell], Optional[PagingMetadata]]:
aas: Iterator[model.AssetAdministrationShell] = self._get_all_obj_of_type(model.AssetAdministrationShell)
id_short = request.args.get("idShort")
@@ -256,31 +448,40 @@ def _get_shells(self, request: Request) -> Tuple[Iterator[model.AssetAdministrat
for asset_id in asset_ids:
asset_id_json = base64url_decode(asset_id)
asset_dict = json.loads(asset_id_json)
- name = asset_dict["name"]
- value = asset_dict["value"]
+ try:
+ name = asset_dict["name"]
+ value = asset_dict["value"]
+ except KeyError as e:
+ raise BadRequest(f"Invalid assetId format: missing field {e}") from e
if name == "specificAssetId":
- decoded_specific_id = HTTPApiDecoder.json_list(value, model.SpecificAssetId,
- False, True)[0]
+ decoded_specific_id = HTTPApiDecoder.json_list(value, model.SpecificAssetId, False, True)[0]
specific_asset_ids.append(decoded_specific_id)
elif name == "globalAssetId":
global_asset_ids.append(value)
# Filter AAS based on both SpecificAssetIds and globalAssetIds
- aas = filter(lambda shell: (
- (not specific_asset_ids or all(specific_asset_id in shell.asset_information.specific_asset_id
- for specific_asset_id in specific_asset_ids)) and
- (len(global_asset_ids) <= 1 and
- (not global_asset_ids or shell.asset_information.global_asset_id in global_asset_ids))
- ), aas)
-
- paginated_aas, end_index = self._get_slice(request, aas)
- return paginated_aas, end_index
+ aas = filter(
+ lambda shell: (
+ (
+ not specific_asset_ids
+ or all(
+ specific_asset_id in shell.asset_information.specific_asset_id
+ for specific_asset_id in specific_asset_ids
+ )
+ )
+ and (not global_asset_ids or shell.asset_information.global_asset_id in global_asset_ids)
+ ),
+ aas,
+ )
+
+ paginated_aas, paging_metadata = self._get_slice(request, aas)
+ return paginated_aas, paging_metadata
def _get_shell(self, url_args: Dict) -> model.AssetAdministrationShell:
return self._get_obj_ts(url_args["aas_id"], model.AssetAdministrationShell)
- def _get_submodels(self, request: Request) -> Tuple[Iterator[model.Submodel], int]:
+ def _get_submodels(self, request: Request) -> Tuple[Iterator[model.Submodel], Optional[PagingMetadata]]:
submodels: Iterator[model.Submodel] = self._get_all_obj_of_type(model.Submodel)
id_short = request.args.get("idShort")
if id_short is not None:
@@ -288,20 +489,22 @@ def _get_submodels(self, request: Request) -> Tuple[Iterator[model.Submodel], in
semantic_id = request.args.get("semanticId")
if semantic_id is not None:
spec_semantic_id = HTTPApiDecoder.base64url_json(
- semantic_id, model.Reference, False) # type: ignore[type-abstract]
+ semantic_id, model.Reference, False # type: ignore[type-abstract]
+ )
submodels = filter(lambda sm: sm.semantic_id == spec_semantic_id, submodels)
- paginated_submodels, end_index = self._get_slice(request, submodels)
- return paginated_submodels, end_index
+ paginated_submodels, paging_metadata = self._get_slice(request, submodels)
+ return paginated_submodels, paging_metadata
def _get_submodel(self, url_args: Dict) -> model.Submodel:
return self._get_obj_ts(url_args["submodel_id"], model.Submodel)
- def _get_submodel_submodel_elements(self, request: Request, url_args: Dict) -> \
- Tuple[Iterator[model.SubmodelElement], int]:
+ def _get_submodel_submodel_elements(
+ self, request: Request, url_args: Dict
+ ) -> Tuple[Iterator[model.SubmodelElement], Optional[PagingMetadata]]:
submodel = self._get_submodel(url_args)
paginated_submodel_elements: Iterator[model.SubmodelElement]
- paginated_submodel_elements, end_index = self._get_slice(request, submodel.submodel_element)
- return paginated_submodel_elements, end_index
+ paginated_submodel_elements, paging_metadata = self._get_slice(request, submodel.submodel_element)
+ return paginated_submodel_elements, paging_metadata
def _get_submodel_submodel_elements_id_short_path(self, url_args: Dict) -> model.SubmodelElement:
submodel = self._get_submodel(url_args)
@@ -315,29 +518,31 @@ def _get_concept_description(self, url_args):
def not_implemented(self, request: Request, url_args: Dict, **_kwargs) -> Response:
raise werkzeug.exceptions.NotImplemented("This route is not implemented!")
+ def get_description(self, request: Request, url_args: Dict, response_t: Type[APIResponse], **_kwargs) -> Response:
+ return response_t(SUPPORTED_PROFILES.to_dict())
+
# ------ AAS REPO ROUTES -------
def get_aas_all(self, request: Request, url_args: Dict, response_t: Type[APIResponse], **_kwargs) -> Response:
- aashells, cursor = self._get_shells(request)
- return response_t(list(aashells), cursor=cursor)
+ aashells, paging_metadata = self._get_shells(request)
+ return response_t(list(aashells), paging_metadata=paging_metadata)
- def post_aas(self, request: Request, url_args: Dict, response_t: Type[APIResponse],
- map_adapter: MapAdapter) -> Response:
+ def post_aas(
+ self, request: Request, url_args: Dict, response_t: Type[APIResponse], map_adapter: MapAdapter
+ ) -> Response:
aas = HTTPApiDecoder.request_body(request, model.AssetAdministrationShell, False)
try:
self.object_store.add(aas)
except KeyError as e:
raise Conflict(f"AssetAdministrationShell with Identifier {aas.id} already exists!") from e
- created_resource_url = map_adapter.build(self.get_aas, {
- "aas_id": aas.id
- }, force_external=True)
+ created_resource_url = map_adapter.build(self.get_aas, {"aas_id": aas.id}, force_external=True)
return response_t(aas, status=201, headers={"Location": created_resource_url})
- def get_aas_all_reference(self, request: Request, url_args: Dict, response_t: Type[APIResponse],
- **_kwargs) -> Response:
- aashells, cursor = self._get_shells(request)
- references: list[model.ModelReference] = [model.ModelReference.from_referable(aas)
- for aas in aashells]
- return response_t(references, cursor=cursor)
+ def get_aas_all_reference(
+ self, request: Request, url_args: Dict, response_t: Type[APIResponse], **_kwargs
+ ) -> Response:
+ aashells, paging_metadata = self._get_shells(request)
+ references: list[model.ModelReference] = [model.ModelReference.from_referable(aas) for aas in aashells]
+ return response_t(references, paging_metadata=paging_metadata)
# --------- AAS ROUTES ---------
def get_aas(self, request: Request, url_args: Dict, response_t: Type[APIResponse], **_kwargs) -> Response:
@@ -351,8 +556,10 @@ def get_aas_reference(self, request: Request, url_args: Dict, response_t: Type[A
def put_aas(self, request: Request, url_args: Dict, response_t: Type[APIResponse], **_kwargs) -> Response:
aas = self._get_shell(url_args)
- aas.update_from(HTTPApiDecoder.request_body(request, model.AssetAdministrationShell,
- is_stripped_request(request)))
+ aas.update_from(
+ HTTPApiDecoder.request_body(request, model.AssetAdministrationShell, is_stripped_request(request))
+ )
+ self.object_store.commit(aas)
return response_t()
def delete_aas(self, request: Request, url_args: Dict, response_t: Type[APIResponse], **_kwargs) -> Response:
@@ -360,41 +567,54 @@ def delete_aas(self, request: Request, url_args: Dict, response_t: Type[APIRespo
self.object_store.remove(aas)
return response_t()
- def get_aas_asset_information(self, request: Request, url_args: Dict, response_t: Type[APIResponse],
- **_kwargs) -> Response:
+ def get_aas_asset_information(
+ self, request: Request, url_args: Dict, response_t: Type[APIResponse], **_kwargs
+ ) -> Response:
aas = self._get_shell(url_args)
return response_t(aas.asset_information)
- def put_aas_asset_information(self, request: Request, url_args: Dict, response_t: Type[APIResponse],
- **_kwargs) -> Response:
+ def put_aas_asset_information(
+ self, request: Request, url_args: Dict, response_t: Type[APIResponse], **_kwargs
+ ) -> Response:
aas = self._get_shell(url_args)
aas.asset_information = HTTPApiDecoder.request_body(request, model.AssetInformation, False)
+ self.object_store.commit(aas)
return response_t()
- def get_aas_submodel_refs(self, request: Request, url_args: Dict, response_t: Type[APIResponse],
- **_kwargs) -> Response:
+ def get_aas_submodel_refs(
+ self, request: Request, url_args: Dict, response_t: Type[APIResponse], **_kwargs
+ ) -> Response:
aas = self._get_shell(url_args)
submodel_refs: Iterator[model.ModelReference[model.Submodel]]
- submodel_refs, cursor = self._get_slice(request, aas.submodel)
- return response_t(list(submodel_refs), cursor=cursor)
+ sorted_submodel_refs = sorted(aas.submodel, key=lambda ref: ref.key[0].value)
+ submodel_refs, paging_metadata = self._get_slice(request, sorted_submodel_refs)
+ return response_t(list(submodel_refs), paging_metadata=paging_metadata)
def post_aas_submodel_refs(self, request: Request, url_args: Dict, response_t: Type[APIResponse],
- **_kwargs) -> Response:
+ map_adapter: MapAdapter, **_kwargs) -> Response:
aas = self._get_shell(url_args)
sm_ref = HTTPApiDecoder.request_body(request, model.ModelReference, False)
if sm_ref in aas.submodel:
raise Conflict(f"{sm_ref!r} already exists!")
aas.submodel.add(sm_ref)
- return response_t(sm_ref, status=201)
+ self.object_store.commit(aas)
+ created_resource_url = map_adapter.build(self.delete_aas_submodel_refs_specific, {
+ "aas_id": aas.id,
+ "submodel_id": sm_ref.key[0].value
+ }, force_external=True)
+ return response_t(sm_ref, status=201, headers={"Location": created_resource_url})
- def delete_aas_submodel_refs_specific(self, request: Request, url_args: Dict, response_t: Type[APIResponse],
- **_kwargs) -> Response:
+ def delete_aas_submodel_refs_specific(
+ self, request: Request, url_args: Dict, response_t: Type[APIResponse], **_kwargs
+ ) -> Response:
aas = self._get_shell(url_args)
aas.submodel.remove(self._get_submodel_reference(aas, url_args["submodel_id"]))
+ self.object_store.commit(aas)
return response_t()
- def put_aas_submodel_refs_submodel(self, request: Request, url_args: Dict, response_t: Type[APIResponse],
- **_kwargs) -> Response:
+ def put_aas_submodel_refs_submodel(
+ self, request: Request, url_args: Dict, response_t: Type[APIResponse], **_kwargs
+ ) -> Response:
aas = self._get_shell(url_args)
sm_ref = self._get_submodel_reference(aas, url_args["submodel_id"])
submodel = self._resolve_reference(sm_ref)
@@ -403,28 +623,33 @@ def put_aas_submodel_refs_submodel(self, request: Request, url_args: Dict, respo
id_changed: bool = submodel.id != new_submodel.id
# TODO: https://github.com/eclipse-basyx/basyx-python-sdk/issues/216
submodel.update_from(new_submodel)
+ self.object_store.commit(submodel)
if id_changed:
aas.submodel.remove(sm_ref)
aas.submodel.add(model.ModelReference.from_referable(submodel))
+ self.object_store.commit(aas)
return response_t()
- def delete_aas_submodel_refs_submodel(self, request: Request, url_args: Dict, response_t: Type[APIResponse],
- **_kwargs) -> Response:
+ def delete_aas_submodel_refs_submodel(
+ self, request: Request, url_args: Dict, response_t: Type[APIResponse], **_kwargs
+ ) -> Response:
aas = self._get_shell(url_args)
sm_ref = self._get_submodel_reference(aas, url_args["submodel_id"])
submodel = self._resolve_reference(sm_ref)
self.object_store.remove(submodel)
aas.submodel.remove(sm_ref)
+ self.object_store.commit(aas)
return response_t()
- def aas_submodel_refs_redirect(self, request: Request, url_args: Dict, map_adapter: MapAdapter, response_t=None,
- **_kwargs) -> Response:
+ def aas_submodel_refs_redirect(
+ self, request: Request, url_args: Dict, map_adapter: MapAdapter, response_t=None, **_kwargs
+ ) -> Response:
aas = self._get_shell(url_args)
# the following makes sure the reference exists
self._get_submodel_reference(aas, url_args["submodel_id"])
- redirect_url = map_adapter.build(self.get_submodel, {
- "submodel_id": url_args["submodel_id"]
- }, force_external=True)
+ redirect_url = map_adapter.build(
+ self.get_submodel, {"submodel_id": url_args["submodel_id"]}, force_external=True
+ )
if "path" in url_args:
redirect_url += "/" + url_args["path"]
if request.query_string:
@@ -433,32 +658,35 @@ def aas_submodel_refs_redirect(self, request: Request, url_args: Dict, map_adapt
# ------ SUBMODEL REPO ROUTES -------
def get_submodel_all(self, request: Request, url_args: Dict, response_t: Type[APIResponse], **_kwargs) -> Response:
- submodels, cursor = self._get_submodels(request)
- return response_t(list(submodels), cursor=cursor, stripped=is_stripped_request(request))
+ submodels, paging_metadata = self._get_submodels(request)
+ return response_t(list(submodels), paging_metadata=paging_metadata, stripped=is_stripped_request(request))
- def post_submodel(self, request: Request, url_args: Dict, response_t: Type[APIResponse],
- map_adapter: MapAdapter) -> Response:
+ def post_submodel(
+ self, request: Request, url_args: Dict, response_t: Type[APIResponse], map_adapter: MapAdapter
+ ) -> Response:
submodel = HTTPApiDecoder.request_body(request, model.Submodel, is_stripped_request(request))
try:
self.object_store.add(submodel)
except KeyError as e:
raise Conflict(f"Submodel with Identifier {submodel.id} already exists!") from e
- created_resource_url = map_adapter.build(self.get_submodel, {
- "submodel_id": submodel.id
- }, force_external=True)
+ created_resource_url = map_adapter.build(self.get_submodel, {"submodel_id": submodel.id}, force_external=True)
return response_t(submodel, status=201, headers={"Location": created_resource_url})
def get_submodel_all_metadata(self, request: Request, url_args: Dict, response_t: Type[APIResponse],
**_kwargs) -> Response:
- submodels, cursor = self._get_submodels(request)
- return response_t(list(submodels), cursor=cursor, stripped=True)
-
- def get_submodel_all_reference(self, request: Request, url_args: Dict, response_t: Type[APIResponse],
- **_kwargs) -> Response:
- submodels, cursor = self._get_submodels(request)
- references: list[model.ModelReference] = [model.ModelReference.from_referable(submodel)
- for submodel in submodels]
- return response_t(references, cursor=cursor, stripped=is_stripped_request(request))
+ if "level" in request.args:
+ raise BadRequest(f"level cannot be used when retrieving metadata!")
+ submodels, paging_metadata = self._get_submodels(request)
+ return response_t(list(submodels), paging_metadata=paging_metadata, stripped=True)
+
+ def get_submodel_all_reference(
+ self, request: Request, url_args: Dict, response_t: Type[APIResponse], **_kwargs
+ ) -> Response:
+ submodels, paging_metadata = self._get_submodels(request)
+ references: list[model.ModelReference] = [
+ model.ModelReference.from_referable(submodel) for submodel in submodels
+ ]
+ return response_t(references, paging_metadata=paging_metadata, stripped=is_stripped_request(request))
# --------- SUBMODEL ROUTES ---------
@@ -472,11 +700,14 @@ def get_submodel(self, request: Request, url_args: Dict, response_t: Type[APIRes
def get_submodels_metadata(self, request: Request, url_args: Dict, response_t: Type[APIResponse],
**_kwargs) -> Response:
+ if "level" in request.args:
+ raise BadRequest(f"level cannot be used when retrieving metadata!")
submodel = self._get_submodel(url_args)
return response_t(submodel, stripped=True)
- def get_submodels_reference(self, request: Request, url_args: Dict, response_t: Type[APIResponse],
- **_kwargs) -> Response:
+ def get_submodels_reference(
+ self, request: Request, url_args: Dict, response_t: Type[APIResponse], **_kwargs
+ ) -> Response:
submodel = self._get_submodel(url_args)
reference = model.ModelReference.from_referable(submodel)
return response_t(reference, stripped=is_stripped_request(request))
@@ -484,88 +715,104 @@ def get_submodels_reference(self, request: Request, url_args: Dict, response_t:
def put_submodel(self, request: Request, url_args: Dict, response_t: Type[APIResponse], **_kwargs) -> Response:
submodel = self._get_submodel(url_args)
submodel.update_from(HTTPApiDecoder.request_body(request, model.Submodel, is_stripped_request(request)))
+ self.object_store.commit(submodel)
return response_t()
- def get_submodel_submodel_elements(self, request: Request, url_args: Dict, response_t: Type[APIResponse],
- **_kwargs) -> Response:
- submodel_elements, cursor = self._get_submodel_submodel_elements(request, url_args)
- return response_t(list(submodel_elements), cursor=cursor, stripped=is_stripped_request(request))
+ def get_submodel_submodel_elements(
+ self, request: Request, url_args: Dict, response_t: Type[APIResponse], **_kwargs
+ ) -> Response:
+ submodel_elements, paging_metadata = self._get_submodel_submodel_elements(request, url_args)
+ return response_t(
+ list(submodel_elements), paging_metadata=paging_metadata, stripped=is_stripped_request(request)
+ )
def get_submodel_submodel_elements_metadata(self, request: Request, url_args: Dict, response_t: Type[APIResponse],
**_kwargs) -> Response:
- submodel_elements, cursor = self._get_submodel_submodel_elements(request, url_args)
- return response_t(list(submodel_elements), cursor=cursor, stripped=True)
-
- def get_submodel_submodel_elements_reference(self, request: Request, url_args: Dict, response_t: Type[APIResponse],
- **_kwargs) -> Response:
- submodel_elements, cursor = self._get_submodel_submodel_elements(request, url_args)
- references: list[model.ModelReference] = [model.ModelReference.from_referable(element) for element in
- list(submodel_elements)]
- return response_t(references, cursor=cursor, stripped=is_stripped_request(request))
-
- def get_submodel_submodel_elements_id_short_path(self, request: Request, url_args: Dict,
- response_t: Type[APIResponse],
- **_kwargs) -> Response:
+ if "level" in request.args:
+ raise BadRequest(f"level cannot be used when retrieving metadata!")
+ submodel_elements, paging_metadata = self._get_submodel_submodel_elements(request, url_args)
+ return response_t(list(submodel_elements), paging_metadata=paging_metadata, stripped=True)
+
+ def get_submodel_submodel_elements_reference(
+ self, request: Request, url_args: Dict, response_t: Type[APIResponse], **_kwargs
+ ) -> Response:
+ submodel_elements, paging_metadata = self._get_submodel_submodel_elements(request, url_args)
+ references: list[model.ModelReference] = [
+ model.ModelReference.from_referable(element) for element in list(submodel_elements)
+ ]
+ return response_t(references, paging_metadata=paging_metadata, stripped=is_stripped_request(request))
+
+ def get_submodel_submodel_elements_id_short_path(
+ self, request: Request, url_args: Dict, response_t: Type[APIResponse], **_kwargs
+ ) -> Response:
submodel_element = self._get_submodel_submodel_elements_id_short_path(url_args)
return response_t(submodel_element, stripped=is_stripped_request(request))
def get_submodel_submodel_elements_id_short_path_metadata(self, request: Request, url_args: Dict,
response_t: Type[APIResponse], **_kwargs) -> Response:
+ if "level" in request.args:
+ raise BadRequest(f"level cannot be used when retrieving metadata!")
submodel_element = self._get_submodel_submodel_elements_id_short_path(url_args)
if isinstance(submodel_element, model.Capability) or isinstance(submodel_element, model.Operation):
raise BadRequest(f"{submodel_element.id_short} does not allow the content modifier metadata!")
return response_t(submodel_element, stripped=True)
- def get_submodel_submodel_elements_id_short_path_reference(self, request: Request, url_args: Dict,
- response_t: Type[APIResponse], **_kwargs) -> Response:
+ def get_submodel_submodel_elements_id_short_path_reference(
+ self, request: Request, url_args: Dict, response_t: Type[APIResponse], **_kwargs
+ ) -> Response:
submodel_element = self._get_submodel_submodel_elements_id_short_path(url_args)
reference = model.ModelReference.from_referable(submodel_element)
return response_t(reference, stripped=is_stripped_request(request))
- def post_submodel_submodel_elements_id_short_path(self, request: Request, url_args: Dict,
- response_t: Type[APIResponse],
- map_adapter: MapAdapter):
+ def post_submodel_submodel_elements_id_short_path(
+ self, request: Request, url_args: Dict, response_t: Type[APIResponse], map_adapter: MapAdapter
+ ):
parent = self._get_submodel_or_nested_submodel_element(url_args)
if not isinstance(parent, model.UniqueIdShortNamespace):
raise BadRequest(f"{parent!r} is not a namespace, can't add child submodel element!")
# TODO: remove the following type: ignore comment when mypy supports abstract types for Type[T]
# see https://github.com/python/mypy/issues/5374
- new_submodel_element = HTTPApiDecoder.request_body(request,
- model.SubmodelElement, # type: ignore[type-abstract]
- is_stripped_request(request))
+ new_submodel_element = HTTPApiDecoder.request_body(
+ request, model.SubmodelElement, is_stripped_request(request) # type: ignore[type-abstract]
+ )
try:
parent.add_referable(new_submodel_element)
except model.AASConstraintViolation as e:
if e.constraint_id != 22:
raise
- raise Conflict(f"SubmodelElement with idShort {new_submodel_element.id_short} already exists "
- f"within {parent}!")
+ raise Conflict(
+ f"SubmodelElement with idShort {new_submodel_element.id_short} already exists " f"within {parent}!"
+ )
+ self.object_store.commit(self._get_submodel(url_args))
submodel = self._get_submodel(url_args)
id_short_path = url_args.get("id_shorts", [])
- created_resource_url = map_adapter.build(self.get_submodel_submodel_elements_id_short_path, {
- "submodel_id": submodel.id,
- "id_shorts": id_short_path + [new_submodel_element.id_short]
- }, force_external=True)
+ created_resource_url = map_adapter.build(
+ self.get_submodel_submodel_elements_id_short_path,
+ {"submodel_id": submodel.id, "id_shorts": id_short_path + [new_submodel_element.id_short]},
+ force_external=True,
+ )
return response_t(new_submodel_element, status=201, headers={"Location": created_resource_url})
- def put_submodel_submodel_elements_id_short_path(self, request: Request, url_args: Dict,
- response_t: Type[APIResponse],
- **_kwargs) -> Response:
+ def put_submodel_submodel_elements_id_short_path(
+ self, request: Request, url_args: Dict, response_t: Type[APIResponse], **_kwargs
+ ) -> Response:
submodel_element = self._get_submodel_submodel_elements_id_short_path(url_args)
# TODO: remove the following type: ignore comment when mypy supports abstract types for Type[T]
# see https://github.com/python/mypy/issues/5374
- new_submodel_element = HTTPApiDecoder.request_body(request,
- model.SubmodelElement, # type: ignore[type-abstract]
- is_stripped_request(request))
+ new_submodel_element = HTTPApiDecoder.request_body(
+ request, model.SubmodelElement, is_stripped_request(request) # type: ignore[type-abstract]
+ )
submodel_element.update_from(new_submodel_element)
+ self.object_store.commit(self._get_submodel(url_args))
return response_t()
- def delete_submodel_submodel_elements_id_short_path(self, request: Request, url_args: Dict,
- response_t: Type[APIResponse],
- **_kwargs) -> Response:
+ def delete_submodel_submodel_elements_id_short_path(
+ self, request: Request, url_args: Dict, response_t: Type[APIResponse], **_kwargs
+ ) -> Response:
sm_or_se = self._get_submodel_or_nested_submodel_element(url_args)
parent: model.UniqueIdShortNamespace = self._expect_namespace(sm_or_se.parent, sm_or_se.id_short)
self._namespace_submodel_element_op(parent, parent.remove_referable, sm_or_se.id_short)
+ self.object_store.commit(self._get_submodel(url_args))
return response_t()
def get_submodel_submodel_element_attachment(self, request: Request, url_args: Dict, **_kwargs) -> Response:
@@ -591,8 +838,9 @@ def get_submodel_submodel_element_attachment(self, request: Request, url_args: D
# Blob and File both have the content_type attribute
return Response(value, content_type=submodel_element.content_type) # type: ignore[attr-defined]
- def put_submodel_submodel_element_attachment(self, request: Request, url_args: Dict, response_t: Type[APIResponse],
- **_kwargs) -> Response:
+ def put_submodel_submodel_element_attachment(
+ self, request: Request, url_args: Dict, response_t: Type[APIResponse], **_kwargs
+ ) -> Response:
submodel_element = self._get_submodel_submodel_elements_id_short_path(url_args)
# spec allows PUT only for File, not for Blob
@@ -601,26 +849,28 @@ def put_submodel_submodel_element_attachment(self, request: Request, url_args: D
elif submodel_element.value is not None:
raise Conflict(f"{submodel_element!r} already references a file!")
- filename = request.form.get('fileName')
+ filename = request.form.get("fileName")
if filename is None:
raise BadRequest("No 'fileName' specified!")
elif not filename.startswith("/"):
raise BadRequest(f"Given 'fileName' doesn't start with a slash (/): {filename}")
- file_storage: Optional[FileStorage] = request.files.get('file')
+ file_storage: Optional[FileStorage] = request.files.get("file")
if file_storage is None:
raise BadRequest("Missing file to upload")
elif file_storage.mimetype != submodel_element.content_type:
raise werkzeug.exceptions.UnsupportedMediaType(
f"Request body is of type {file_storage.mimetype!r}, "
- f"while {submodel_element!r} has content_type {submodel_element.content_type!r}!")
+ f"while {submodel_element!r} has content_type {submodel_element.content_type!r}!"
+ )
submodel_element.value = self.file_store.add_file(filename, file_storage.stream, submodel_element.content_type)
+ self.object_store.commit(self._get_submodel(url_args))
return response_t()
- def delete_submodel_submodel_element_attachment(self, request: Request, url_args: Dict,
- response_t: Type[APIResponse],
- **_kwargs) -> Response:
+ def delete_submodel_submodel_element_attachment(
+ self, request: Request, url_args: Dict, response_t: Type[APIResponse], **_kwargs
+ ) -> Response:
submodel_element = self._get_submodel_submodel_elements_id_short_path(url_args)
if not isinstance(submodel_element, (model.Blob, model.File)):
raise BadRequest(f"{submodel_element!r} is not a Blob or File, no file content to delete!")
@@ -638,32 +888,41 @@ def delete_submodel_submodel_element_attachment(self, request: Request, url_args
pass
submodel_element.value = None
+ self.object_store.commit(self._get_submodel(url_args))
return response_t()
- def get_submodel_submodel_element_qualifiers(self, request: Request, url_args: Dict, response_t: Type[APIResponse],
- **_kwargs) -> Response:
+ def get_submodel_submodel_element_qualifiers(
+ self, request: Request, url_args: Dict, response_t: Type[APIResponse], **_kwargs
+ ) -> Response:
sm_or_se = self._get_submodel_or_nested_submodel_element(url_args)
qualifier_type = url_args.get("qualifier_type")
if qualifier_type is None:
return response_t(list(sm_or_se.qualifier))
return response_t(self._qualifiable_qualifier_op(sm_or_se, sm_or_se.get_qualifier_by_type, qualifier_type))
- def post_submodel_submodel_element_qualifiers(self, request: Request, url_args: Dict, response_t: Type[APIResponse],
- map_adapter: MapAdapter) -> Response:
+ def post_submodel_submodel_element_qualifiers(
+ self, request: Request, url_args: Dict, response_t: Type[APIResponse], map_adapter: MapAdapter
+ ) -> Response:
sm_or_se = self._get_submodel_or_nested_submodel_element(url_args)
qualifier = HTTPApiDecoder.request_body(request, model.Qualifier, is_stripped_request(request))
if sm_or_se.qualifier.contains_id("type", qualifier.type):
raise Conflict(f"Qualifier with type {qualifier.type} already exists!")
sm_or_se.qualifier.add(qualifier)
- created_resource_url = map_adapter.build(self.get_submodel_submodel_element_qualifiers, {
- "submodel_id": url_args["submodel_id"],
- "id_shorts": url_args.get("id_shorts") or None,
- "qualifier_type": qualifier.type
- }, force_external=True)
+ self.object_store.commit(self._get_submodel(url_args))
+ created_resource_url = map_adapter.build(
+ self.get_submodel_submodel_element_qualifiers,
+ {
+ "submodel_id": url_args["submodel_id"],
+ "id_shorts": url_args.get("id_shorts") or None,
+ "qualifier_type": qualifier.type,
+ },
+ force_external=True,
+ )
return response_t(qualifier, status=201, headers={"Location": created_resource_url})
- def put_submodel_submodel_element_qualifiers(self, request: Request, url_args: Dict, response_t: Type[APIResponse],
- map_adapter: MapAdapter) -> Response:
+ def put_submodel_submodel_element_qualifiers(
+ self, request: Request, url_args: Dict, response_t: Type[APIResponse], map_adapter: MapAdapter
+ ) -> Response:
sm_or_se = self._get_submodel_or_nested_submodel_element(url_args)
new_qualifier = HTTPApiDecoder.request_body(request, model.Qualifier, is_stripped_request(request))
qualifier_type = url_args["qualifier_type"]
@@ -673,64 +932,84 @@ def put_submodel_submodel_element_qualifiers(self, request: Request, url_args: D
raise Conflict(f"A qualifier of type {new_qualifier.type!r} already exists for {sm_or_se!r}")
sm_or_se.remove_qualifier_by_type(qualifier.type)
sm_or_se.qualifier.add(new_qualifier)
+ self.object_store.commit(self._get_submodel(url_args))
if qualifier_type_changed:
- created_resource_url = map_adapter.build(self.get_submodel_submodel_element_qualifiers, {
- "submodel_id": url_args["submodel_id"],
- "id_shorts": url_args.get("id_shorts") or None,
- "qualifier_type": new_qualifier.type
- }, force_external=True)
+ created_resource_url = map_adapter.build(
+ self.get_submodel_submodel_element_qualifiers,
+ {
+ "submodel_id": url_args["submodel_id"],
+ "id_shorts": url_args.get("id_shorts") or None,
+ "qualifier_type": new_qualifier.type,
+ },
+ force_external=True,
+ )
return response_t(new_qualifier, status=201, headers={"Location": created_resource_url})
return response_t(new_qualifier)
- def delete_submodel_submodel_element_qualifiers(self, request: Request, url_args: Dict,
- response_t: Type[APIResponse],
- **_kwargs) -> Response:
+ def delete_submodel_submodel_element_qualifiers(
+ self, request: Request, url_args: Dict, response_t: Type[APIResponse], **_kwargs
+ ) -> Response:
sm_or_se = self._get_submodel_or_nested_submodel_element(url_args)
qualifier_type = url_args["qualifier_type"]
self._qualifiable_qualifier_op(sm_or_se, sm_or_se.remove_qualifier_by_type, qualifier_type)
+ self.object_store.commit(self._get_submodel(url_args))
return response_t()
# --------- CONCEPT DESCRIPTION ROUTES ---------
- def get_concept_description_all(self, request: Request, url_args: Dict, response_t: Type[APIResponse],
- **_kwargs) -> Response:
+ def get_concept_description_all(
+ self, request: Request, url_args: Dict, response_t: Type[APIResponse], **_kwargs
+ ) -> Response:
concept_descriptions: Iterator[model.ConceptDescription] = self._get_all_obj_of_type(model.ConceptDescription)
- concept_descriptions, cursor = self._get_slice(request, concept_descriptions)
- return response_t(list(concept_descriptions), cursor=cursor, stripped=is_stripped_request(request))
-
- def post_concept_description(self, request: Request, url_args: Dict, response_t: Type[APIResponse],
- map_adapter: MapAdapter) -> Response:
- concept_description = HTTPApiDecoder.request_body(request, model.ConceptDescription,
- is_stripped_request(request))
+ concept_descriptions, paging_metadata = self._get_slice(request, concept_descriptions)
+ return response_t(list(concept_descriptions), paging_metadata=paging_metadata,
+ stripped=is_stripped_request(request))
+
+ def post_concept_description(
+ self, request: Request, url_args: Dict, response_t: Type[APIResponse], map_adapter: MapAdapter
+ ) -> Response:
+ concept_description = HTTPApiDecoder.request_body(
+ request, model.ConceptDescription, is_stripped_request(request)
+ )
try:
self.object_store.add(concept_description)
except KeyError as e:
raise Conflict(f"ConceptDescription with Identifier {concept_description.id} already exists!") from e
- created_resource_url = map_adapter.build(self.get_concept_description, {
- "concept_id": concept_description.id
- }, force_external=True)
+ created_resource_url = map_adapter.build(
+ self.get_concept_description, {"concept_id": concept_description.id}, force_external=True
+ )
return response_t(concept_description, status=201, headers={"Location": created_resource_url})
- def get_concept_description(self, request: Request, url_args: Dict, response_t: Type[APIResponse],
- **_kwargs) -> Response:
+ def get_concept_description(
+ self, request: Request, url_args: Dict, response_t: Type[APIResponse], **_kwargs
+ ) -> Response:
concept_description = self._get_concept_description(url_args)
return response_t(concept_description, stripped=is_stripped_request(request))
- def put_concept_description(self, request: Request, url_args: Dict, response_t: Type[APIResponse],
- **_kwargs) -> Response:
+ def put_concept_description(
+ self, request: Request, url_args: Dict, response_t: Type[APIResponse], **_kwargs
+ ) -> Response:
concept_description = self._get_concept_description(url_args)
- concept_description.update_from(HTTPApiDecoder.request_body(request, model.ConceptDescription,
- is_stripped_request(request)))
+ concept_description.update_from(
+ HTTPApiDecoder.request_body(request, model.ConceptDescription, is_stripped_request(request))
+ )
+ self.object_store.commit(concept_description)
return response_t()
- def delete_concept_description(self, request: Request, url_args: Dict, response_t: Type[APIResponse],
- **_kwargs) -> Response:
+ def delete_concept_description(
+ self, request: Request, url_args: Dict, response_t: Type[APIResponse], **_kwargs
+ ) -> Response:
self.object_store.remove(self._get_concept_description(url_args))
return response_t()
if __name__ == "__main__":
- from werkzeug.serving import run_simple
from basyx.aas.examples.data.example_aas import create_full_example
+ from werkzeug.serving import run_simple
- run_simple("localhost", 8080, WSGIApp(create_full_example(), aasx.DictSupplementaryFileContainer()),
- use_debugger=True, use_reloader=True)
+ run_simple(
+ "localhost",
+ 8080,
+ WSGIApp(create_full_example(), aasx.DictSupplementaryFileContainer()),
+ use_debugger=True,
+ use_reloader=True,
+ )
diff --git a/server/app/model/__init__.py b/server/app/model/__init__.py
new file mode 100644
index 000000000..684379e2c
--- /dev/null
+++ b/server/app/model/__init__.py
@@ -0,0 +1,4 @@
+from .descriptor import *
+from .endpoint import *
+from .provider import *
+from .service_specification import *
diff --git a/server/app/model/descriptor.py b/server/app/model/descriptor.py
new file mode 100644
index 000000000..13fdf8d00
--- /dev/null
+++ b/server/app/model/descriptor.py
@@ -0,0 +1,128 @@
+from __future__ import absolute_import
+
+import abc
+from typing import Iterable, List, Optional
+
+from basyx.aas import model
+
+from app.model.endpoint import Endpoint
+
+
+class Descriptor(model.HasExtension, metaclass=abc.ABCMeta):
+ @abc.abstractmethod
+ def __init__(
+ self,
+ description: Optional[model.MultiLanguageTextType] = None,
+ display_name: Optional[model.MultiLanguageNameType] = None,
+ extension: Iterable[model.Extension] = (),
+ ):
+ super().__init__()
+ self.description: Optional[model.MultiLanguageTextType] = description
+ self.display_name: Optional[model.MultiLanguageNameType] = display_name
+ self.extension = model.NamespaceSet(self, [("name", True)], extension)
+
+ def commit(self):
+ pass
+
+ def update(self):
+ pass
+
+ def update_from(self, other: "Descriptor", update_source: bool = False):
+ """
+ Updates the descriptor's attributes from another descriptor.
+
+ :param other: The descriptor to update from.
+ :param update_source: Placeholder for compatibility; not used in this context.
+ """
+ for attr in vars(other):
+ if attr == "id":
+ continue # Skip updating the unique identifier of the AAS
+ setattr(self, attr, getattr(other, attr))
+
+
+class SubmodelDescriptor(Descriptor):
+
+ def __init__(
+ self,
+ id_: model.Identifier,
+ endpoints: List[Endpoint],
+ administration: Optional[model.AdministrativeInformation] = None,
+ id_short: Optional[model.NameType] = None,
+ semantic_id: Optional[model.Reference] = None,
+ supplemental_semantic_id: Iterable[model.Reference] = (),
+ ):
+ super().__init__()
+ self.id: model.Identifier = id_
+ self.endpoints: List[Endpoint] = endpoints
+ self.administration: Optional[model.AdministrativeInformation] = administration
+ self.id_short: Optional[model.NameType] = id_short
+ self.semantic_id: Optional[model.Reference] = semantic_id
+ self.supplemental_semantic_id: model.ConstrainedList[model.Reference] = model.ConstrainedList(
+ supplemental_semantic_id
+ )
+
+
+class AssetAdministrationShellDescriptor(Descriptor):
+
+ def __init__(
+ self,
+ id_: model.Identifier,
+ administration: Optional[model.AdministrativeInformation] = None,
+ asset_kind: Optional[model.AssetKind] = None,
+ asset_type: Optional[model.Identifier] = None,
+ endpoints: Optional[List[Endpoint]] = None,
+ global_asset_id: Optional[model.Identifier] = None,
+ id_short: Optional[model.NameType] = None,
+ specific_asset_id: Iterable[model.SpecificAssetId] = (),
+ submodel_descriptors: Optional[List[SubmodelDescriptor]] = None,
+ description: Optional[model.MultiLanguageTextType] = None,
+ display_name: Optional[model.MultiLanguageNameType] = None,
+ extension: Iterable[model.Extension] = (),
+ ):
+ """AssetAdministrationShellDescriptor -
+
+ Nur das 'id'-Feld (id_) ist zwingend erforderlich. Alle anderen Felder erhalten Defaultwerte.
+ """
+ super().__init__()
+ self.administration: Optional[model.AdministrativeInformation] = administration
+ self.asset_kind: Optional[model.AssetKind] = asset_kind
+ self.asset_type: Optional[model.Identifier] = asset_type
+ self.endpoints: Optional[List[Endpoint]] = (
+ endpoints if endpoints is not None else []
+ ) # leere Liste, falls nicht gesetzt
+ self.global_asset_id: Optional[model.Identifier] = global_asset_id
+ self.id_short: Optional[model.NameType] = id_short
+ self.id: model.Identifier = id_
+ self._specific_asset_id: model.ConstrainedList[model.SpecificAssetId] = model.ConstrainedList(
+ specific_asset_id,
+ item_set_hook=self._check_constraint_set_spec_asset_id,
+ item_del_hook=self._check_constraint_del_spec_asset_id,
+ )
+ self.submodel_descriptors = submodel_descriptors if submodel_descriptors is not None else []
+ self.description: Optional[model.MultiLanguageTextType] = description
+ self.display_name: Optional[model.MultiLanguageNameType] = display_name
+ self.extension = model.NamespaceSet(self, [("name", True)], extension)
+
+ @property
+ def specific_asset_id(self) -> model.ConstrainedList[model.SpecificAssetId]:
+ return self._specific_asset_id
+
+ @specific_asset_id.setter
+ def specific_asset_id(self, specific_asset_id: Iterable[model.SpecificAssetId]) -> None:
+ # constraints are checked via _check_constraint_set_spec_asset_id() in this case
+ self._specific_asset_id[:] = specific_asset_id
+
+ def _check_constraint_set_spec_asset_id(
+ self,
+ items_to_replace: List[model.SpecificAssetId],
+ new_items: List[model.SpecificAssetId],
+ old_list: List[model.SpecificAssetId],
+ ) -> None:
+ model.AssetInformation._validate_aasd_131(
+ self.global_asset_id, len(old_list) - len(items_to_replace) + len(new_items) > 0
+ )
+
+ def _check_constraint_del_spec_asset_id(
+ self, _item_to_del: model.SpecificAssetId, old_list: List[model.SpecificAssetId]
+ ) -> None:
+ model.AssetInformation._validate_aasd_131(self.global_asset_id, len(old_list) > 1)
diff --git a/server/app/model/endpoint.py b/server/app/model/endpoint.py
new file mode 100644
index 000000000..06301e9a1
--- /dev/null
+++ b/server/app/model/endpoint.py
@@ -0,0 +1,117 @@
+from __future__ import absolute_import
+
+import re
+from enum import Enum
+from typing import List, Optional
+
+from basyx.aas.model import base
+
+
+class AssetLink:
+ def __init__(self, name: base.LabelType, value: base.Identifier):
+ if not name:
+ raise ValueError("AssetLink 'name' must be a non-empty string.")
+ if not value:
+ raise ValueError("AssetLink 'value' must be a non-empty string.")
+ self.name = name
+ self.value = value
+
+
+class SecurityTypeEnum(Enum):
+ NONE = "NONE"
+ RFC_TLSA = "RFC_TLSA"
+ W3C_DID = "W3C_DID"
+
+
+class SecurityAttributeObject:
+ def __init__(self, type_: SecurityTypeEnum, key: str, value: str):
+
+ if not isinstance(type_, SecurityTypeEnum):
+ raise ValueError(f"Invalid security type: {type_}. Must be one of {list(SecurityTypeEnum)}")
+ if not key or not isinstance(key, str):
+ raise ValueError("Key must be a non-empty string.")
+ if not value or not isinstance(value, str):
+ raise ValueError("Value must be a non-empty string.")
+ self.type = type_
+ self.key = key
+ self.value = value
+
+
+class ProtocolInformation:
+
+ def __init__(
+ self,
+ href: str,
+ endpoint_protocol: Optional[str] = None,
+ endpoint_protocol_version: Optional[List[str]] = None,
+ subprotocol: Optional[str] = None,
+ subprotocol_body: Optional[str] = None,
+ subprotocol_body_encoding: Optional[str] = None,
+ security_attributes: Optional[List[SecurityAttributeObject]] = None,
+ ):
+ if not href or not isinstance(href, str):
+ raise ValueError("href must be a non-empty string representing a valid URL.")
+
+ self.href = href
+ self.endpoint_protocol = endpoint_protocol
+ self.endpoint_protocol_version = endpoint_protocol_version or []
+ self.subprotocol = subprotocol
+ self.subprotocol_body = subprotocol_body
+ self.subprotocol_body_encoding = subprotocol_body_encoding
+ self.security_attributes = security_attributes or []
+
+
+class Endpoint:
+ INTERFACE_SHORTNAMES = {
+ "AAS",
+ "SUBMODEL",
+ "SERIALIZE",
+ "AASX-FILE",
+ "AAS-REGISTRY",
+ "SUBMODEL-REGISTRY",
+ "AAS-REPOSITORY",
+ "SUBMODEL-REPOSITORY",
+ "CD-REPOSITORY",
+ "AAS-DISCOVERY",
+ }
+ VERSION_PATTERN = re.compile(r"^\d+(\.\d+)*$")
+
+ def __init__(self, interface: base.NameType, protocol_information: ProtocolInformation): # noqa: E501
+
+ self.interface = interface
+ self.protocol_information = protocol_information
+
+ @property
+ def interface(self) -> str:
+ return self._interface
+
+ @interface.setter
+ def interface(self, interface: base.NameType):
+ if interface is None:
+ raise ValueError("Invalid value for `interface`, must not be `None`")
+ if not self.is_valid_interface(interface):
+ raise ValueError(f"Invalid interface format: {interface}. Expected format: '-', ")
+
+ self._interface = interface
+
+ @classmethod
+ def is_valid_interface(cls, interface: base.NameType) -> bool:
+ parts = interface.split("-", 1)
+ if len(parts) != 2:
+ return False
+ short_name, version = parts
+ if short_name in cls.INTERFACE_SHORTNAMES and cls.VERSION_PATTERN.match(version):
+ return True
+ else:
+ return False
+
+ @property
+ def protocol_information(self) -> ProtocolInformation:
+ return self._protocol_information
+
+ @protocol_information.setter
+ def protocol_information(self, protocol_information: ProtocolInformation):
+ if protocol_information is None:
+ raise ValueError("Invalid value for `protocol_information`, must not be `None`") # noqa: E501
+
+ self._protocol_information = protocol_information
diff --git a/server/app/model/provider.py b/server/app/model/provider.py
new file mode 100644
index 000000000..472f09979
--- /dev/null
+++ b/server/app/model/provider.py
@@ -0,0 +1,83 @@
+import json
+from pathlib import Path
+from typing import IO, Dict, Iterable, Iterator, Union
+
+from basyx.aas import model
+from basyx.aas.model import provider as sdk_provider
+
+from app import adapter
+from app.model import descriptor
+
+PathOrIO = Union[Path, IO]
+
+
+_DESCRIPTOR_TYPE = Union[descriptor.AssetAdministrationShellDescriptor, descriptor.SubmodelDescriptor]
+_DESCRIPTOR_CLASSES = (descriptor.AssetAdministrationShellDescriptor, descriptor.SubmodelDescriptor)
+
+
+class DictDescriptorStore(sdk_provider.AbstractObjectStore[model.Identifier, _DESCRIPTOR_TYPE]):
+ """
+ A local in-memory object store for :class:`~app.model.descriptor.Descriptor` objects, backed by a dict, mapping
+ :class:`~basyx.aas.model.base.Identifier` → :class:`~app.model.descriptor.Descriptor`
+ """
+
+ def __init__(self, descriptors: Iterable[_DESCRIPTOR_TYPE] = ()) -> None:
+ self._backend: Dict[model.Identifier, _DESCRIPTOR_TYPE] = {}
+ for x in descriptors:
+ self.add(x)
+
+ def get_item(self, identifier: model.Identifier) -> _DESCRIPTOR_TYPE:
+ return self._backend[identifier]
+
+ def add(self, x: _DESCRIPTOR_TYPE) -> None:
+ if x.id in self._backend and self._backend.get(x.id) is not x:
+ raise KeyError("Descriptor object with same id {} is already stored in this store".format(x.id))
+ self._backend[x.id] = x
+
+ def discard(self, x: _DESCRIPTOR_TYPE) -> None:
+ if self._backend.get(x.id) is x:
+ del self._backend[x.id]
+
+ def __contains__(self, x: object) -> bool:
+ if isinstance(x, model.Identifier):
+ return x in self._backend
+ if not isinstance(x, _DESCRIPTOR_CLASSES):
+ return False
+ return self._backend.get(x.id) is x
+
+ def __len__(self) -> int:
+ return len(self._backend)
+
+ def __iter__(self) -> Iterator[_DESCRIPTOR_TYPE]:
+ return iter(self._backend.values())
+
+
+def load_directory(directory: Union[Path, str]) -> DictDescriptorStore:
+ """
+ Load AAS/Submodel descriptor JSON files from a directory into a :class:`DictDescriptorStore`.
+
+ :param directory: Path to the directory containing JSON descriptor files
+ :return: Populated :class:`DictDescriptorStore`
+ """
+ store = DictDescriptorStore()
+ directory = Path(directory)
+
+ for file in directory.iterdir():
+ if not file.is_file() or file.suffix.lower() != ".json":
+ continue
+ with open(file) as f:
+ data = json.load(f, cls=adapter.ServerAASFromJsonDecoder)
+ for item in data.get("assetAdministrationShellDescriptors", []):
+ if isinstance(item, descriptor.AssetAdministrationShellDescriptor):
+ try:
+ store.add(item)
+ except KeyError:
+ pass
+ for item in data.get("submodelDescriptors", []):
+ if isinstance(item, descriptor.SubmodelDescriptor):
+ try:
+ store.add(item)
+ except KeyError:
+ pass
+
+ return store
diff --git a/server/app/model/service_specification.py b/server/app/model/service_specification.py
new file mode 100644
index 000000000..5181901ad
--- /dev/null
+++ b/server/app/model/service_specification.py
@@ -0,0 +1,82 @@
+from typing import List
+import enum
+
+
+class ServiceSpecificationProfileEnum(str, enum.Enum):
+ """
+ Enumeration of all standardized Service Specification Profiles
+ from the AAS Part 2 API Specification (IDTA-01002-3-1-2).
+ Each profile is uniquely identified by its semantic URI.
+
+ Reference: https://industrialdigitaltwin.io/aas-specifications/IDTA-01002/v3.1.2/
+ http-rest-api/service-specifications-and-profiles.html
+ """
+
+ # --- Asset Administration Shell (AAS) ---
+ AAS_FULL = "https://admin-shell.io/aas/API/3/1/AssetAdministrationShellServiceSpecification/SSP-001"
+ AAS_READ = "https://admin-shell.io/aas/API/3/1/AssetAdministrationShellServiceSpecification/SSP-002"
+
+ # --- Submodel ---
+ SUBMODEL_FULL = "https://admin-shell.io/aas/API/3/1/SubmodelServiceSpecification/SSP-001"
+ SUBMODEL_READ = "https://admin-shell.io/aas/API/3/1/SubmodelServiceSpecification/SSP-002"
+ SUBMODEL_VALUE = "https://admin-shell.io/aas/API/3/1/SubmodelServiceSpecification/SSP-003"
+
+ # --- AASX File Server ---
+ AASX_FILESERVER_FULL = "https://admin-shell.io/aas/API/3/1/AasxFileServerServiceSpecification/SSP-001"
+
+ # --- AAS Registry ---
+ AAS_REGISTRY_FULL = \
+ "https://admin-shell.io/aas/API/3/1/AssetAdministrationShellRegistryServiceSpecification/SSP-001"
+ AAS_REGISTRY_READ = \
+ "https://admin-shell.io/aas/API/3/1/AssetAdministrationShellRegistryServiceSpecification/SSP-002"
+ AAS_REGISTRY_BULK = \
+ "https://admin-shell.io/aas/API/3/1/AssetAdministrationShellRegistryServiceSpecification/SSP-003"
+ AAS_REGISTRY_QUERY = \
+ "https://admin-shell.io/aas/API/3/1/AssetAdministrationShellRegistryServiceSpecification/SSP-004"
+ AAS_REGISTRY_MINIMAL_READ = \
+ "https://admin-shell.io/aas/API/3/1/AssetAdministrationShellRegistryServiceSpecification/SSP-005"
+
+ # --- Submodel Registry ---
+ SUBMODEL_REGISTRY_FULL = "https://admin-shell.io/aas/API/3/1/SubmodelRegistryServiceSpecification/SSP-001"
+ SUBMODEL_REGISTRY_READ = "https://admin-shell.io/aas/API/3/1/SubmodelRegistryServiceSpecification/SSP-002"
+ SUBMODEL_REGISTRY_BULK = "https://admin-shell.io/aas/API/3/1/SubmodelRegistryServiceSpecification/SSP-003"
+ SUBMODEL_REGISTRY_QUERY = "https://admin-shell.io/aas/API/3/1/SubmodelRegistryServiceSpecification/SSP-004"
+
+ # --- AAS Repository ---
+ AAS_REPOSITORY_FULL = \
+ "https://admin-shell.io/aas/API/3/1/AssetAdministrationShellRepositoryServiceSpecification/SSP-001"
+ AAS_REPOSITORY_READ = \
+ "https://admin-shell.io/aas/API/3/1/AssetAdministrationShellRepositoryServiceSpecification/SSP-002"
+ AAS_REPOSITORY_QUERY = \
+ "https://admin-shell.io/aas/API/3/1/AssetAdministrationShellRepositoryServiceSpecification/SSP-003"
+
+ # --- Submodel Repository ---
+ SUBMODEL_REPOSITORY_FULL = "https://admin-shell.io/aas/API/3/1/SubmodelRepositoryServiceSpecification/SSP-001"
+ SUBMODEL_REPOSITORY_READ = "https://admin-shell.io/aas/API/3/1/SubmodelRepositoryServiceSpecification/SSP-002"
+ SUBMODEL_REPOSITORY_TEMPLATE = \
+ "https://admin-shell.io/aas/API/3/1/SubmodelRepositoryServiceSpecification/SSP-003"
+ SUBMODEL_REPOSITORY_TEMPLATE_READ = \
+ "https://admin-shell.io/aas/API/3/1/SubmodelRepositoryServiceSpecification/SSP-004"
+ SUBMODEL_REPOSITORY_QUERY = \
+ "https://admin-shell.io/aas/API/3/1/SubmodelRepositoryServiceSpecification/SSP-005"
+
+ # --- Concept Description Repository ---
+ CONCEPT_DESCRIPTION_REPOSITORY_FULL = \
+ "https://admin-shell.io/aas/API/3/1/ConceptDescriptionRepositoryServiceSpecification/SSP-001"
+ CONCEPT_DESCRIPTION_REPOSITORY_QUERY = \
+ "https://admin-shell.io/aas/API/3/1/ConceptDescriptionRepositoryServiceSpecification/SSP-002"
+
+ # --- Discovery ---
+ DISCOVERY_FULL = "https://admin-shell.io/aas/API/3/1/DiscoveryServiceSpecification/SSP-001"
+ DISCOVERY_READ = "https://admin-shell.io/aas/API/3/1/DiscoveryServiceSpecification/SSP-002"
+
+
+# TODO: Maybe remove this in spite of spec? Too complicated structure
+class ServiceDescription:
+ def __init__(self, profiles: List[ServiceSpecificationProfileEnum]):
+ if not profiles:
+ raise ValueError("At least one profile must be specified")
+ self.profiles: List[ServiceSpecificationProfileEnum] = profiles
+
+ def to_dict(self):
+ return {"profiles": [p.value for p in self.profiles]}
diff --git a/compliance_tool/test/files/test_demo_full_example_json_aasx/aasx/aasx-origin b/server/app/services/__init__.py
similarity index 100%
rename from compliance_tool/test/files/test_demo_full_example_json_aasx/aasx/aasx-origin
rename to server/app/services/__init__.py
diff --git a/server/app/services/run_discovery.py b/server/app/services/run_discovery.py
new file mode 100644
index 000000000..deead79af
--- /dev/null
+++ b/server/app/services/run_discovery.py
@@ -0,0 +1,38 @@
+import atexit
+import os
+
+from app.interfaces.discovery import DiscoveryAPI, DiscoveryStore
+
+storage_path = os.getenv("storage_path", None)
+base_path = os.getenv("API_BASE_PATH")
+
+wsgi_optparams = {}
+
+
+if base_path is not None:
+ wsgi_optparams["base_path"] = base_path
+
+
+# Load DiscoveryStore from disk, if `storage_path` is set
+if storage_path:
+ # If the storage file exists, we load it
+ if os.path.exists(storage_path) and os.path.getsize(storage_path) > 0:
+ discovery_store = DiscoveryStore.from_file(storage_path)
+
+ # If the file doesn't exist at the given path, we initiate a new file
+ else:
+ os.makedirs(os.path.dirname(storage_path), exist_ok=True)
+ discovery_store = DiscoveryStore()
+ discovery_store.to_file(storage_path)
+else:
+ discovery_store = DiscoveryStore()
+
+
+def persist_store():
+ if storage_path:
+ discovery_store.to_file(storage_path)
+
+
+atexit.register(persist_store)
+
+application = DiscoveryAPI(discovery_store, **wsgi_optparams)
diff --git a/server/app/services/run_registry.py b/server/app/services/run_registry.py
new file mode 100644
index 000000000..3161971b8
--- /dev/null
+++ b/server/app/services/run_registry.py
@@ -0,0 +1,112 @@
+# Copyright (c) 2026 the Eclipse BaSyx Authors
+#
+# This program and the accompanying materials are made available under the terms of the MIT License, available in
+# the LICENSE file of this project.
+#
+# SPDX-License-Identifier: MIT
+"""
+This module provides the WSGI entry point for the Asset Administration Shell Registry Server.
+"""
+
+import logging
+import os
+from typing import Union
+
+from app.backend import LocalFileDescriptorStore
+from app.interfaces.registry import RegistryAPI
+from app.model import DictDescriptorStore, load_directory
+
+# -------- Helper methods --------
+
+
+def setup_logger() -> logging.Logger:
+ """
+ Configure a custom :class:`~logging.Logger` for the start-up sequence of the server.
+
+ :return: Configured :class:`~logging.Logger`
+ """
+
+ logger = logging.getLogger(__name__)
+ if not logger.handlers:
+ logger.setLevel(logging.INFO)
+ handler = logging.StreamHandler()
+ handler.setLevel(logging.INFO)
+ handler.setFormatter(logging.Formatter("%(levelname)s [Server Start-up] %(message)s"))
+ logger.addHandler(handler)
+ logger.propagate = False
+ return logger
+
+
+def build_storage(
+ env_input: str, env_storage: str, env_storage_persistency: bool, env_storage_overwrite: bool, logger: logging.Logger
+) -> Union[DictDescriptorStore, LocalFileDescriptorStore]:
+ """
+ Configure the server's storage according to the given start-up settings.
+
+ :param env_input: ``str`` pointing to the input directory of the server
+ :param env_storage: ``str`` pointing to the :class:`~basyx.aas.backend.local_file.LocalFileIdentifiableStore`
+ storage directory of the server if persistent storage is enabled
+ :param env_storage_persistency: Flag to enable persistent storage
+ :param env_storage_overwrite: Flag to overwrite existing :class:`Identifiables `
+ in the :class:`~basyx.aas.backend.local_file.LocalFileIdentifiableStore` if persistent storage is enabled
+ :param logger: :class:`~logging.Logger` used for start-up diagnostics
+ :return: Tuple consisting of a :class:`~basyx.aas.model.provider.DictIdentifiableStore` if persistent storage is
+ disabled or a :class:`~basyx.aas.backend.local_file.LocalFileIdentifiableStore` if persistent storage is
+ enabled and a :class:`~basyx.aas.adapter.aasx.DictSupplementaryFileContainer` as storage for
+ :class:`~interfaces.repository.WSGIApp`
+ """
+
+ if env_storage_persistency:
+ storage_files = LocalFileDescriptorStore(env_storage)
+ storage_files.check_directory(create=True)
+ if os.path.isdir(env_input):
+ input_files = load_directory(env_input)
+ added, overwritten, skipped = storage_files.sync(input_files, env_storage_overwrite)
+ logger.info('Loaded %d descriptors(s) from "%s"', len(input_files), env_input)
+ logger.info(
+ "Synced INPUT to STORAGE with %d added and %d %s",
+ added,
+ overwritten if env_storage_overwrite else skipped,
+ "overwritten" if env_storage_overwrite else "skipped",
+ )
+ return storage_files
+ else:
+ logger.warning('INPUT directory "%s" not found, starting empty', env_input)
+ return storage_files
+
+ if os.path.isdir(env_input):
+ input_files = load_directory(env_input)
+ logger.info('Loaded %d descriptors(s) from "%s"', len(input_files), env_input)
+ return input_files
+ else:
+ logger.warning('INPUT directory "%s" not found, starting empty', env_input)
+ return DictDescriptorStore()
+
+
+# -------- WSGI entrypoint --------
+
+logger = setup_logger()
+
+env_input = os.getenv("INPUT", "/input")
+env_storage = os.getenv("STORAGE", "/storage")
+env_storage_persistency = os.getenv("STORAGE_PERSISTENCY", "false").lower() in {"1", "true", "yes"}
+env_storage_overwrite = os.getenv("STORAGE_OVERWRITE", "false").lower() in {"1", "true", "yes"}
+env_api_base_path = os.getenv("API_BASE_PATH")
+
+wsgi_optparams = {"base_path": env_api_base_path} if env_api_base_path else {}
+
+logger.info(
+ 'Loaded settings API_BASE_PATH="%s", INPUT="%s", STORAGE="%s", PERSISTENCY=%s, OVERWRITE=%s',
+ env_api_base_path or "",
+ env_input,
+ env_storage,
+ env_storage_persistency,
+ env_storage_overwrite,
+)
+
+storage_files = build_storage(env_input, env_storage, env_storage_persistency, env_storage_overwrite, logger)
+
+application = RegistryAPI(storage_files, **wsgi_optparams)
+
+if __name__ == "__main__":
+ logger.info("WSGI entrypoint created. Serve this module with uWSGI/Gunicorn/etc.")
diff --git a/server/app/main.py b/server/app/services/run_repository.py
similarity index 65%
rename from server/app/main.py
rename to server/app/services/run_repository.py
index 49920f628..04e6c7443 100644
--- a/server/app/main.py
+++ b/server/app/services/run_repository.py
@@ -1,4 +1,4 @@
-# Copyright (c) 2025 the Eclipse BaSyx Authors
+# Copyright (c) 2026 the Eclipse BaSyx Authors
#
# This program and the accompanying materials are made available under the terms of the MIT License, available in
# the LICENSE file of this project.
@@ -10,16 +10,18 @@
import logging
import os
+from typing import Tuple, Union
+
from basyx.aas.adapter import load_directory
from basyx.aas.adapter.aasx import DictSupplementaryFileContainer
-from basyx.aas.backend.local_file import LocalFileObjectStore
-from basyx.aas.model.provider import DictObjectStore
-from interfaces.repository import WSGIApp
-from typing import Tuple, Union
+from basyx.aas.backend.local_file import LocalFileIdentifiableStore
+from basyx.aas.model.provider import DictIdentifiableStore
+from app.interfaces.repository import WSGIApp
# -------- Helper methods --------
+
def setup_logger() -> logging.Logger:
"""
Configure a custom :class:`~logging.Logger` for the start-up sequence of the server.
@@ -39,59 +41,59 @@ def setup_logger() -> logging.Logger:
def build_storage(
- env_input: str,
- env_storage: str,
- env_storage_persistency: bool,
- env_storage_overwrite: bool,
- logger: logging.Logger
-) -> Tuple[Union[DictObjectStore, LocalFileObjectStore], DictSupplementaryFileContainer]:
+ env_input: str, env_storage: str, env_storage_persistency: bool, env_storage_overwrite: bool, logger: logging.Logger
+) -> Tuple[Union[DictIdentifiableStore, LocalFileIdentifiableStore], DictSupplementaryFileContainer]:
"""
Configure the server's storage according to the given start-up settings.
:param env_input: ``str`` pointing to the input directory of the server
- :param env_storage: ``str`` pointing to the :class:`~basyx.aas.backend.local_file.LocalFileObjectStore` storage
- directory of the server if persistent storage is enabled
+ :param env_storage: ``str`` pointing to the :class:`~basyx.aas.backend.local_file.LocalFileIdentifiableStore`
+ storage directory of the server if persistent storage is enabled
:param env_storage_persistency: Flag to enable persistent storage
:param env_storage_overwrite: Flag to overwrite existing :class:`Identifiables `
- in the :class:`~basyx.aas.backend.local_file.LocalFileObjectStore` if persistent storage is enabled
+ in the :class:`~basyx.aas.backend.local_file.LocalFileIdentifiableStore` if persistent storage is enabled
:param logger: :class:`~logging.Logger` used for start-up diagnostics
- :return: Tuple consisting of a :class:`~basyx.aas.model.provider.DictObjectStore` if persistent storage is disabled
- or a :class:`~basyx.aas.backend.local_file.LocalFileObjectStore` if persistent storage is enabled and a
- :class:`~basyx.aas.adapter.aasx.DictSupplementaryFileContainer` as storage for
+ :return: Tuple consisting of a :class:`~basyx.aas.model.provider.DictIdentifiableStore` if persistent storage is
+ disabled or a :class:`~basyx.aas.backend.local_file.LocalFileIdentifiableStore` if persistent storage is
+ enabled and a :class:`~basyx.aas.adapter.aasx.DictSupplementaryFileContainer` as storage for
:class:`~interfaces.repository.WSGIApp`
"""
if env_storage_persistency:
- storage_files = LocalFileObjectStore(env_storage)
+ storage_files = LocalFileIdentifiableStore(env_storage)
storage_files.check_directory(create=True)
if os.path.isdir(env_input):
input_files, input_supp_files = load_directory(env_input)
added, overwritten, skipped = storage_files.sync(input_files, env_storage_overwrite)
logger.info(
- "Loaded %d identifiable(s) and %d supplementary file(s) from \"%s\"",
- len(input_files), len(input_supp_files), env_input
+ 'Loaded %d identifiable(s) and %d supplementary file(s) from "%s"',
+ len(input_files),
+ len(input_supp_files),
+ env_input,
)
logger.info(
"Synced INPUT to STORAGE with %d added and %d %s",
added,
overwritten if env_storage_overwrite else skipped,
- "overwritten" if env_storage_overwrite else "skipped"
+ "overwritten" if env_storage_overwrite else "skipped",
)
return storage_files, input_supp_files
else:
- logger.warning("INPUT directory \"%s\" not found, starting empty", env_input)
+ logger.warning('INPUT directory "%s" not found, starting empty', env_input)
return storage_files, DictSupplementaryFileContainer()
if os.path.isdir(env_input):
input_files, input_supp_files = load_directory(env_input)
logger.info(
- "Loaded %d identifiable(s) and %d supplementary file(s) from \"%s\"",
- len(input_files), len(input_supp_files), env_input
+ 'Loaded %d identifiable(s) and %d supplementary file(s) from "%s"',
+ len(input_files),
+ len(input_supp_files),
+ env_input,
)
return input_files, input_supp_files
else:
- logger.warning("INPUT directory \"%s\" not found, starting empty", env_input)
- return DictObjectStore(), DictSupplementaryFileContainer()
+ logger.warning('INPUT directory "%s" not found, starting empty', env_input)
+ return DictIdentifiableStore(), DictSupplementaryFileContainer()
# -------- WSGI entrypoint --------
@@ -107,20 +109,19 @@ def build_storage(
wsgi_optparams = {"base_path": env_api_base_path} if env_api_base_path else {}
logger.info(
- "Loaded settings API_BASE_PATH=\"%s\", INPUT=\"%s\", STORAGE=\"%s\", PERSISTENCY=%s, OVERWRITE=%s",
- env_api_base_path or "", env_input, env_storage, env_storage_persistency, env_storage_overwrite
-)
-
-storage_files, supp_files = build_storage(
+ 'Loaded settings API_BASE_PATH="%s", INPUT="%s", STORAGE="%s", PERSISTENCY=%s, OVERWRITE=%s',
+ env_api_base_path or "",
env_input,
env_storage,
env_storage_persistency,
env_storage_overwrite,
- logger
)
-application = WSGIApp(storage_files, supp_files, **wsgi_optparams)
+storage_files, supp_files = build_storage(
+ env_input, env_storage, env_storage_persistency, env_storage_overwrite, logger
+)
+application = WSGIApp(storage_files, supp_files, **wsgi_optparams)
if __name__ == "__main__":
logger.info("WSGI entrypoint created. Serve this module with uWSGI/Gunicorn/etc.")
diff --git a/server/app/util/converters.py b/server/app/util/converters.py
index 4e37c4702..3b98cd5d4 100644
--- a/server/app/util/converters.py
+++ b/server/app/util/converters.py
@@ -1,4 +1,4 @@
-# Copyright (c) 2025 the Eclipse BaSyx Authors
+# Copyright (c) 2026 the Eclipse BaSyx Authors
#
# This program and the accompanying materials are made available under the terms of the MIT License, available in
# the LICENSE file of this project.
@@ -13,14 +13,12 @@
import base64
import binascii
+from typing import List
import werkzeug.routing
import werkzeug.utils
-from werkzeug.exceptions import BadRequest
-
from basyx.aas import model
-
-from typing import List
+from werkzeug.exceptions import BadRequest
BASE64URL_ENCODING = "utf-8"
@@ -46,9 +44,10 @@ def base64url_encode(data: str) -> str:
class IdentifierToBase64URLConverter(werkzeug.routing.UnicodeConverter):
"""
- A custom URL converter for Werkzeug routing that encodes and decodes
- Identifiers using Base64 URL-safe encoding.
+ A custom URL converter for Werkzeug routing that encodes and decodes
+ Identifiers using Base64 URL-safe encoding.
"""
+
def to_url(self, value: model.Identifier) -> str:
return super().to_url(base64url_encode(value))
@@ -60,12 +59,12 @@ def to_python(self, value: str) -> model.Identifier:
class IdShortPathConverter(werkzeug.routing.UnicodeConverter):
"""
- A custom Werkzeug URL converter for handling dot-separated idShort paths and indexes.
+ A custom Werkzeug URL converter for handling dot-separated idShort paths and indexes.
- This converter joins a list of idShort strings into an id_short_sep-separated path for URLs
- (e.g., ["submodel", "element", "1"] -> "submodel.element[1]") and parses incoming URL paths
- back into a list, validating each idShort.
- """
+ This converter joins a list of idShort strings into an id_short_sep-separated path for URLs
+ (e.g., ["submodel", "element", "1"] -> "submodel.element[1]") and parses incoming URL paths
+ back into a list, validating each idShort.
+ """
def to_url(self, value: List[str]) -> str:
id_short_path = model.Referable.build_id_short_path(value)
diff --git a/server/entrypoint.sh b/server/docker/common/entrypoint.sh
similarity index 70%
rename from server/entrypoint.sh
rename to server/docker/common/entrypoint.sh
index 722394409..7aecbcd05 100644
--- a/server/entrypoint.sh
+++ b/server/docker/common/entrypoint.sh
@@ -55,6 +55,20 @@ else
content_server='server {\n'
content_server=$content_server" listen ${USE_LISTEN_PORT};\n"
content_server=$content_server' location / {\n'
+ content_server=$content_server' if ($request_method = OPTIONS) {\n'
+ content_server=$content_server' add_header Access-Control-Allow-Origin *;\n'
+ content_server=$content_server' add_header Access-Control-Allow-Methods "GET, POST, PUT, PATCH, DELETE, OPTIONS, HEAD";\n'
+ content_server=$content_server' add_header Access-Control-Allow-Headers "Content-Type, Authorization, Accept, Origin";\n'
+ content_server=$content_server' add_header Access-Control-Max-Age 86400;\n'
+ content_server=$content_server' add_header Content-Length 0;\n'
+ content_server=$content_server' add_header Content-Type text/plain;\n'
+ content_server=$content_server' return 204;\n'
+ content_server=$content_server' }\n'
+ content_server=$content_server'\n'
+ content_server=$content_server' add_header Access-Control-Allow-Origin * always;\n'
+ content_server=$content_server' add_header Access-Control-Allow-Methods "GET, POST, PUT, PATCH, DELETE, OPTIONS, HEAD" always;\n'
+ content_server=$content_server' add_header Access-Control-Allow-Headers "Content-Type, Authorization, Accept, Origin" always;\n'
+ content_server=$content_server'\n'
content_server=$content_server' include uwsgi_params;\n'
content_server=$content_server' uwsgi_pass unix:///tmp/uwsgi.sock;\n'
content_server=$content_server' }\n'
@@ -62,10 +76,9 @@ else
# Save generated server /etc/nginx/conf.d/nginx.conf
printf "$content_server" > /etc/nginx/conf.d/nginx.conf
- # # Generate additional configuration
+ # Generate additional configuration
printf "client_max_body_size $USE_NGINX_MAX_UPLOAD;\n" > /etc/nginx/conf.d/upload.conf
printf "client_body_buffer_size $USE_CLIENT_BODY_BUFFER_SIZE;\n" > /etc/nginx/conf.d/body-buffer-size.conf
- printf "add_header Access-Control-Allow-Origin *;\n" > /etc/nginx/conf.d/cors-header.conf
fi
exec "$@"
diff --git a/server/stop-supervisor.sh b/server/docker/common/stop-supervisor.sh
similarity index 100%
rename from server/stop-supervisor.sh
rename to server/docker/common/stop-supervisor.sh
diff --git a/server/supervisord.ini b/server/docker/common/supervisord.ini
similarity index 100%
rename from server/supervisord.ini
rename to server/docker/common/supervisord.ini
diff --git a/server/docker/discovery/Dockerfile b/server/docker/discovery/Dockerfile
new file mode 100644
index 000000000..66c9618e8
--- /dev/null
+++ b/server/docker/discovery/Dockerfile
@@ -0,0 +1,50 @@
+FROM python:3.11-alpine
+
+LABEL org.label-schema.name="Eclipse BaSyx" \
+ org.label-schema.version="1.0" \
+ org.label-schema.description="Docker image for the basyx-python-sdk discovery server application" \
+ org.label-schema.maintainer="Eclipse BaSyx"
+
+ENV PYTHONDONTWRITEBYTECODE=1
+ENV PYTHONUNBUFFERED=1
+
+# If we have more dependencies for the server it would make sense
+# to refactor uswgi to the pyproject.toml
+RUN apk update && \
+ apk add --no-cache nginx supervisor gcc musl-dev linux-headers python3-dev git bash && \
+ pip install uwsgi && \
+ apk del git bash
+
+COPY ./sdk /sdk
+COPY ./server/app /server/app
+COPY ./server/pyproject.toml /server/pyproject.toml
+COPY ./server/docker/discovery/uwsgi.ini /etc/uwsgi/
+COPY ./server/docker/common/supervisord.ini /etc/supervisor/conf.d/supervisord.ini
+COPY ./server/docker/common/stop-supervisor.sh /etc/supervisor/stop-supervisor.sh
+RUN chmod +x /etc/supervisor/stop-supervisor.sh
+
+# Makes it possible to use a different configuration
+ENV UWSGI_INI=/etc/uwsgi/uwsgi.ini
+# object stores aren't thread-safe yet
+# https://github.com/eclipse-basyx/basyx-python-sdk/issues/205
+ENV UWSGI_CHEAPER=0
+ENV UWSGI_PROCESSES=1
+ENV NGINX_MAX_UPLOAD=1M
+ENV NGINX_WORKER_PROCESSES=1
+ENV LISTEN_PORT=80
+ENV CLIENT_BODY_BUFFER_SIZE=1M
+ENV API_BASE_PATH=/api/v3.1/
+
+# Copy the entrypoint that will generate Nginx additional configs
+COPY server/docker/common/entrypoint.sh /entrypoint.sh
+RUN chmod +x /entrypoint.sh
+
+ENTRYPOINT ["/entrypoint.sh"]
+
+ENV SETUPTOOLS_SCM_PRETEND_VERSION=1.0.0
+
+WORKDIR /server/app
+RUN pip install ../../sdk
+RUN pip install ..
+
+CMD ["/usr/bin/supervisord", "-c", "/etc/supervisor/conf.d/supervisord.ini"]
\ No newline at end of file
diff --git a/server/docker/discovery/uwsgi.ini b/server/docker/discovery/uwsgi.ini
new file mode 100644
index 000000000..250ea4b23
--- /dev/null
+++ b/server/docker/discovery/uwsgi.ini
@@ -0,0 +1,9 @@
+[uwsgi]
+wsgi-file = /server/app/services/run_discovery.py
+socket = /tmp/uwsgi.sock
+chown-socket = nginx:nginx
+chmod-socket = 664
+hook-master-start = unix_signal:15 gracefully_kill_them_all
+need-app = true
+die-on-term = true
+show-config = false
diff --git a/server/Dockerfile b/server/docker/registry/Dockerfile
similarity index 70%
rename from server/Dockerfile
rename to server/docker/registry/Dockerfile
index 7ad70bc66..df367f2d6 100644
--- a/server/Dockerfile
+++ b/server/docker/registry/Dockerfile
@@ -2,7 +2,7 @@ FROM python:3.11-alpine
LABEL org.label-schema.name="Eclipse BaSyx" \
org.label-schema.version="1.0" \
- org.label-schema.description="Docker image for the basyx-python-sdk server application" \
+ org.label-schema.description="Docker image for the basyx-python-sdk registry server application" \
org.label-schema.maintainer="Eclipse BaSyx"
ENV PYTHONDONTWRITEBYTECODE=1
@@ -16,14 +16,17 @@ RUN apk update && \
apk del git bash
-COPY server/uwsgi.ini /etc/uwsgi/
-COPY server/supervisord.ini /etc/supervisor/conf.d/supervisord.ini
-COPY server/stop-supervisor.sh /etc/supervisor/stop-supervisor.sh
+COPY ./sdk /sdk
+COPY ./server/app /server/app
+COPY ./server/pyproject.toml /server/pyproject.toml
+COPY ./server/docker/registry/uwsgi.ini /etc/uwsgi/
+COPY ./server/docker/common/supervisord.ini /etc/supervisor/conf.d/supervisord.ini
+COPY ./server/docker/common/stop-supervisor.sh /etc/supervisor/stop-supervisor.sh
RUN chmod +x /etc/supervisor/stop-supervisor.sh
# Makes it possible to use a different configuration
ENV UWSGI_INI=/etc/uwsgi/uwsgi.ini
-# Object stores aren't thread-safe yet
+# object stores aren't thread-safe yet
# https://github.com/eclipse-basyx/basyx-python-sdk/issues/205
ENV UWSGI_CHEAPER=0
ENV UWSGI_PROCESSES=1
@@ -31,7 +34,7 @@ ENV NGINX_MAX_UPLOAD=1M
ENV NGINX_WORKER_PROCESSES=1
ENV LISTEN_PORT=80
ENV CLIENT_BODY_BUFFER_SIZE=1M
-ENV API_BASE_PATH=/api/v3.0/
+ENV API_BASE_PATH=/api/v3.1/
# Default values for the storage envs
ENV INPUT=/input
@@ -41,17 +44,15 @@ ENV STORAGE_OVERWRITE=False
VOLUME ["/input", "/storage"]
# Copy the entrypoint that will generate Nginx additional configs
-COPY server/entrypoint.sh /entrypoint.sh
+COPY server/docker/common/entrypoint.sh /entrypoint.sh
RUN chmod +x /entrypoint.sh
ENTRYPOINT ["/entrypoint.sh"]
ENV SETUPTOOLS_SCM_PRETEND_VERSION=1.0.0
-COPY ./sdk /sdk
-COPY ./server/app /app
-WORKDIR /app
-RUN pip install ../sdk
-RUN pip install .
+WORKDIR /server/app
+RUN pip install ../../sdk
+RUN pip install ..
-CMD ["/usr/bin/supervisord", "-c", "/etc/supervisor/conf.d/supervisord.ini"]
+CMD ["/usr/bin/supervisord", "-c", "/etc/supervisor/conf.d/supervisord.ini"]
\ No newline at end of file
diff --git a/server/uwsgi.ini b/server/docker/registry/uwsgi.ini
similarity index 79%
rename from server/uwsgi.ini
rename to server/docker/registry/uwsgi.ini
index 9c54ae1cc..70488f0e2 100644
--- a/server/uwsgi.ini
+++ b/server/docker/registry/uwsgi.ini
@@ -1,5 +1,5 @@
[uwsgi]
-wsgi-file = /app/main.py
+wsgi-file = /server/app/services/run_registry.py
socket = /tmp/uwsgi.sock
chown-socket = nginx:nginx
chmod-socket = 664
diff --git a/server/docker/repository/Dockerfile b/server/docker/repository/Dockerfile
new file mode 100644
index 000000000..bc58e3e65
--- /dev/null
+++ b/server/docker/repository/Dockerfile
@@ -0,0 +1,59 @@
+FROM python:3.11-alpine
+
+LABEL org.label-schema.name="Eclipse BaSyx" \
+ org.label-schema.version="1.0" \
+ org.label-schema.description="Docker image for the basyx-python-sdk server application" \
+ org.label-schema.maintainer="Eclipse BaSyx"
+
+ENV PYTHONDONTWRITEBYTECODE=1
+ENV PYTHONUNBUFFERED=1
+
+# If we have more dependencies for the server it would make sense
+# to refactor uswgi to the pyproject.toml
+RUN apk update && \
+ apk add --no-cache nginx supervisor gcc musl-dev linux-headers python3-dev git bash && \
+ pip install uwsgi && \
+ apk del git bash
+
+# Copy only the files we need to run the container
+# The argumentation for this is to keep the containers as slim as possible.
+COPY ./sdk /sdk
+COPY ./server/app /server/app
+COPY ./server/pyproject.toml /server/pyproject.toml
+COPY ./server/docker/repository/uwsgi.ini /etc/uwsgi/
+COPY ./server/docker/common/supervisord.ini /etc/supervisor/conf.d/supervisord.ini
+COPY ./server/docker/common/stop-supervisor.sh /etc/supervisor/stop-supervisor.sh
+RUN chmod +x /etc/supervisor/stop-supervisor.sh
+
+# Makes it possible to use a different configuration
+ENV UWSGI_INI=/etc/uwsgi/uwsgi.ini
+# Object stores aren't thread-safe yet
+# https://github.com/eclipse-basyx/basyx-python-sdk/issues/205
+ENV UWSGI_CHEAPER=0
+ENV UWSGI_PROCESSES=1
+ENV NGINX_MAX_UPLOAD=1M
+ENV NGINX_WORKER_PROCESSES=1
+ENV LISTEN_PORT=80
+ENV CLIENT_BODY_BUFFER_SIZE=1M
+ENV API_BASE_PATH=/api/v3.1/
+
+# Default values for the storage envs
+ENV INPUT=/input
+ENV STORAGE=/storage
+ENV STORAGE_PERSISTENCY=False
+ENV STORAGE_OVERWRITE=False
+VOLUME ["/input", "/storage"]
+
+# Copy the entrypoint that will generate Nginx additional configs
+COPY server/docker/common/entrypoint.sh /entrypoint.sh
+RUN chmod +x /entrypoint.sh
+
+ENTRYPOINT ["/entrypoint.sh"]
+
+ENV SETUPTOOLS_SCM_PRETEND_VERSION=1.0.0
+
+WORKDIR /server/app
+RUN pip install ../../sdk
+RUN pip install ..
+
+CMD ["/usr/bin/supervisord", "-c", "/etc/supervisor/conf.d/supervisord.ini"]
diff --git a/server/docker/repository/uwsgi.ini b/server/docker/repository/uwsgi.ini
new file mode 100644
index 000000000..ac4294aca
--- /dev/null
+++ b/server/docker/repository/uwsgi.ini
@@ -0,0 +1,9 @@
+[uwsgi]
+wsgi-file = /server/app/services/run_repository.py
+socket = /tmp/uwsgi.sock
+chown-socket = nginx:nginx
+chmod-socket = 664
+hook-master-start = unix_signal:15 gracefully_kill_them_all
+need-app = true
+die-on-term = true
+show-config = false
diff --git a/server/example_configurations/discovery_standalone/README.md b/server/example_configurations/discovery_standalone/README.md
new file mode 100644
index 000000000..7bc2f1324
--- /dev/null
+++ b/server/example_configurations/discovery_standalone/README.md
@@ -0,0 +1,46 @@
+# Eclipse BaSyx Python SDK - Discovery Service
+
+This is a Python-based implementation of the **BaSyx Asset Administration Shell (AAS) Discovery Service**.
+It provides basic discovery functionality for AAS IDs and their corresponding assets, as specified in the official [Discovery Service Specification v3.1.1_SSP-001](https://app.swaggerhub.com/apis/Plattform_i40/DiscoveryServiceSpecification/V3.1.1_SSP-001).
+
+## Overview
+
+The Discovery Service stores and retrieves relations between AAS identifiers and asset identifiers. It acts as a lookup service for resolving asset-related queries to corresponding AAS.
+
+## Features
+
+| Function | Description | Example URL |
+|------------------------------------------|----------------------------------------------------------|-----------------------------------------------------------------------|
+| **get_description** | Return the supported Discovery Service profiles | `GET http://localhost:8084/api/v3.1/description` |
+| **get_all_aas_ids_by_asset_link** | Find AAS identifiers by asset link query parameter | `GET http://localhost:8084/api/v3.1/lookup/shells?assetIds={assetIds}` |
+| **search_all_aas_ids_by_asset_link** | Find AAS identifiers by providing asset link values | `POST http://localhost:8084/api/v3.1/lookup/shellsByAssetLink` |
+| **get_all_specific_asset_ids_by_aas_id** | Return specific asset ids associated with an AAS ID | `GET http://localhost:8084/api/v3.1/lookup/shells/{aasIdentifier}` |
+| **post_all_asset_links_by_id** | Register specific asset ids linked to an AAS | `POST http://localhost:8084/api/v3.1/lookup/shells/{aasIdentifier}` |
+| **delete_all_asset_links_by_id** | Delete all asset links associated with a specific AAS ID | `DELETE http://localhost:8084/api/v3.1/lookup/shells/{aasIdentifier}` |
+
+
+## Configuration
+This example Docker compose configuration starts a discovery server.
+
+The container image can also be built and run via:
+```
+$ docker compose up
+```
+
+## Persistence
+
+The discovery service can run in persistent or non-persistent mode.
+
+### Persistent Mode
+
+Persistent mode configuration is provided in the `compose.yaml`.
+
+Only the AAS-to-asset-ID mapping is persisted. The reverse lookup index is rebuilt in memory when the service starts.
+
+### Non-Persistent Mode
+
+If `storage_path` is not set, the discovery service runs in memory only.
+
+## Notes
+- Stop the service before manually editing `discovery_store.json`.
+
diff --git a/server/example_configurations/discovery_standalone/compose.yml b/server/example_configurations/discovery_standalone/compose.yml
new file mode 100644
index 000000000..0aea1510b
--- /dev/null
+++ b/server/example_configurations/discovery_standalone/compose.yml
@@ -0,0 +1,12 @@
+name: basyx-python-server
+services:
+ app:
+ build:
+ context: ../../..
+ dockerfile: server/docker/discovery/Dockerfile
+ ports:
+ - "8084:80"
+ environment:
+ storage_path: /storage/discovery_store.json
+ volumes:
+ - ./storage:/storage
diff --git a/server/example_configurations/registry_standalone/README.md b/server/example_configurations/registry_standalone/README.md
new file mode 100644
index 000000000..63d6dd8ee
--- /dev/null
+++ b/server/example_configurations/registry_standalone/README.md
@@ -0,0 +1,54 @@
+# Eclipse BaSyx Python SDK - Registry Service
+
+This is a Python-based implementation of the **Asset Administration Shell (AAS) Registry Service**.
+It provides all registry functionality for AAS and submodels descriptors, as specified in the official [Asset Administration Shell Registry Service Specification v3.1.1_SSP-001](https://app.swaggerhub.com/apis/Plattform_i40/AssetAdministrationShellRegistryServiceSpecification/V3.1.1_SSP-001) and [Submodel Registry Service Specification v3.1.1_SSP-001](https://app.swaggerhub.com/apis/Plattform_i40/SubmodelRegistryServiceSpecification/V3.1.1_SSP-001).
+
+## Overview
+
+The Registry Service provides the endpoint for a given AAS-ID or Submodel-ID. Such an endpoint for an AAS and the related Submodel-IDs make the AAS and the submodels with their submodelElements accessible.
+
+
+
+## Features
+# AAS Registry:
+| Function | Description | Example URL |
+|--------------------------------------------------|----------------------------------------------------------------------|-----------------------------------------------------------------------------------------------------------------------|
+| **GetAllAssetAdministrationShellDescriptors** | Return all AAS descriptor | `GET http://localhost:8083/api/v3.1.1/shell-descriptors` |
+| **GetAssetAdministrationShellDescriptorById** | Return a specific AAS descriptor | `GET http://localhost:8083/api/v3.1.1/shell-descriptors/{aasIdentifier}` |
+| **PostAssetAdministrationShellDescriptor** | Register/create a new AAS descriptor | `POST http://localhost:8083/api/v3.1.1/shell-descriptors` |
+| **PutAssetAdministrationShellDescriptorById** | Create or update an existing AAS descriptor | `PUT http://localhost:8083/api/v3.1.1/shell-descriptors/{aasIdentifier}` |
+| **DeleteAssetAdministrationShellDescriptorById** | Delete an AAS descriptor by ID | `DELETE http://localhost:8083/api/v3.1.1/shell-descriptors/{aasIdentifier}` |
+| **GetSubmodelDescriptorsThroughSuperPath** | Return all submodel descriptors under AAS descriptor | `GET http://localhost:8083/api/v3.1.1/shell-descriptors/{aasIdentifier}/submodel-descriptors` |
+| **PostSubmodelDescriptorThroughSuperPath** | Register/create a new submodel descriptor under AAS descriptor | `POST http://localhost:8083/api/v3.1.1/shell-descriptors/{aasIdentifier}/submodel-descriptors` |
+| **GetSubmodelDescriptorThroughSuperPath** | Return a specific submodel descriptor under AAS descriptor | `GET http://localhost:8083/api/v3.1.1/shell-descriptors/{aasIdentifier}/submodel-descriptors/{submodelIdentifier}` |
+| **PutSubmodelDescriptorThroughSuperPath** | Create or update a specific submodel descriptor under AAS descriptor | `PUT http://localhost:8083/api/v3.1.1/shell-descriptors/{aasIdentifier}/submodel-descriptors/{submodelIdentifier}` |
+| **DeleteSubmodelDescriptorThroughSuperPath** | Delete a specific submodel descriptor under AAS descriptor | `DELETE http://localhost:8083/api/v3.1.1/shell-descriptors/{aasIdentifier}/submodel-descriptors/{submodelIdentifier}` |
+| **GetDescription** | Return the self‑description of the AAS registry service | `GET http://localhost:8083/api/v3.1.1/description` |
+
+# Submodel Registry:
+| Function | Description | Example URL |
+|----------------------------------|--------------------------------------------------------------|-----------------------------------------------------------------------------------|
+| **GetAllSubmodelDescriptors** | Return all submodel descriptors | `GET http://localhost:8083/api/v3.0/submodel-descriptors` |
+| **PostSubmodelDescriptor** | Register/create a new submodel descriptor | `POST http://localhost:8083/api/v3.0/submodel-descriptors` |
+| **GetSubmodelDescriptorById** | Return a specific submodel descriptor | `GET http://localhost:8083/api/v3.0/submodel-descriptors/{submodelIdentifier}` |
+| **PutSubmodelDescriptorById** | Create or update a specific submodel descriptor | `PUT http://localhost:8083/api/v3.0/submodel-descriptors/{submodelIdentifier}` |
+| **DeleteSubmodelDescriptorById** | Delete a specific submodel descriptor | `DELETE http://localhost:8083/api/v3.0/submodel-descriptors/{submodelIdentifier}` |
+| **GetDescription** | Return the self‑description of the submodel registry service | `GET http://localhost:8083/api/v3.0/description` |
+
+
+
+## Configuration
+
+This example Docker compose configuration starts a registry server.
+
+The container image can also be built and run via:
+```
+$ docker compose up
+```
+
+Input files are read from `./input` and stored persistently under `./storage` on your host system.
+The server can be accessed at http://localhost:8083/api/v3.1.1/ from your host system.
+To get a different setup, the `compose.yaml` file can be adapted using the options described in the main server [README.md](../../README.md#options).
+
+Note that the `Dockerfile` has to be specified explicitly via `dockerfile: server/docker/repository/Dockerfile`, as the build context must be set to the repository root to allow access to the local `/sdk`.
+
diff --git a/server/example_configurations/registry_standalone/compose.yml b/server/example_configurations/registry_standalone/compose.yml
new file mode 100644
index 000000000..f7ac9223e
--- /dev/null
+++ b/server/example_configurations/registry_standalone/compose.yml
@@ -0,0 +1,12 @@
+services:
+ app:
+ build:
+ context: ../../..
+ dockerfile: server/docker/registry/Dockerfile
+ ports:
+ - "8083:80"
+ volumes:
+ - ./input:/input
+ - ./storage:/storage
+ environment:
+ STORAGE_PERSISTENCY: True
\ No newline at end of file
diff --git a/server/example_configurations/repository_standalone/README.md b/server/example_configurations/repository_standalone/README.md
new file mode 100644
index 000000000..ce84c5f67
--- /dev/null
+++ b/server/example_configurations/repository_standalone/README.md
@@ -0,0 +1,15 @@
+# Repository Standalone
+
+This example Docker compose configuration starts a repository server.
+
+The container image can also be built and run via:
+```
+$ docker compose up
+```
+
+Input files are read from `./input` and stored persistently under `./storage` on your host system.
+The server can be accessed at http://localhost:8080/api/v3.0/ from your host system.
+To get a different setup, the `compose.yaml` file can be adapted using the options described in the main server [README.md](../../README.md#options).
+
+Note that the `Dockerfile` has to be specified explicitly via `dockerfile: server/docker/repository/Dockerfile`, as the build context must be set to the repository root to allow access to the local `/sdk`.
+
diff --git a/server/compose.yml b/server/example_configurations/repository_standalone/compose.yaml
similarity index 71%
rename from server/compose.yml
rename to server/example_configurations/repository_standalone/compose.yaml
index f7e014c37..10e8b17f1 100644
--- a/server/compose.yml
+++ b/server/example_configurations/repository_standalone/compose.yaml
@@ -2,8 +2,8 @@ name: basyx-python-server
services:
app:
build:
- context: ..
- dockerfile: server/Dockerfile
+ context: ../../..
+ dockerfile: server/docker/repository/Dockerfile
ports:
- "8080:80"
volumes:
diff --git a/server/app/pyproject.toml b/server/pyproject.toml
similarity index 90%
rename from server/app/pyproject.toml
rename to server/pyproject.toml
index 030be7397..8c40deff9 100644
--- a/server/app/pyproject.toml
+++ b/server/pyproject.toml
@@ -16,8 +16,8 @@ build-backend = "setuptools.build_meta"
# from app.version import version
# print(f"Project version: {version}")
# ```
-root = "../.." # Defines the path to the root of the repository
-version_file = "version.py"
+root = ".." # Defines the path to the root of the repository
+version_file = "app/version.py"
[project]
name = "basyx-python-server"
@@ -55,7 +55,7 @@ dev = [
"Homepage" = "https://github.com/eclipse-basyx/basyx-python-sdk"
[tool.setuptools]
-packages = { find = { exclude = ["test*"] } }
+packages = { find = { include = ["app*"], exclude = ["test*"] } }
[tool.setuptools.package-data]
app = ["py.typed"]
diff --git a/compliance_tool/test/files/test_demo_full_example_xml_aasx/aasx/aasx-origin b/server/test/backend/__init__.py
similarity index 100%
rename from compliance_tool/test/files/test_demo_full_example_xml_aasx/aasx/aasx-origin
rename to server/test/backend/__init__.py
diff --git a/server/test/backend/test_local_file.py b/server/test/backend/test_local_file.py
new file mode 100644
index 000000000..aeafb85c5
--- /dev/null
+++ b/server/test/backend/test_local_file.py
@@ -0,0 +1,96 @@
+# Copyright (c) 2026 the Eclipse BaSyx Authors
+#
+# This program and the accompanying materials are made available under the terms of the MIT License, available in
+# the LICENSE file of this project.
+#
+# SPDX-License-Identifier: MIT
+import os.path
+import shutil
+from unittest import TestCase
+
+from app import model
+from app.backend import local_file
+from app.model import provider
+
+store_path: str = os.path.dirname(__file__) + "/local_file_test_folder"
+source_core: str = "file://localhost/{}/".format(store_path)
+
+
+class LocalFileBackendTest(TestCase):
+ def setUp(self) -> None:
+ self.descriptor_store = local_file.LocalFileDescriptorStore(store_path)
+ self.descriptor_store.check_directory(create=True)
+ self.mock_endpoint = model.Endpoint(
+ interface="AAS-3.0", protocol_information=model.ProtocolInformation(href="https://example.org/")
+ )
+ self.aasd1 = model.AssetAdministrationShellDescriptor(
+ id_="https://example.org/AASDescriptor/1", endpoints=[self.mock_endpoint]
+ )
+ self.aasd2 = model.AssetAdministrationShellDescriptor(
+ id_="https://example.org/AASDescriptor/2", endpoints=[self.mock_endpoint]
+ )
+ self.sd1 = model.SubmodelDescriptor(
+ id_="https://example.org/SubmodelDescriptor/1", endpoints=[self.mock_endpoint]
+ )
+ self.sd2 = model.SubmodelDescriptor(
+ id_="https://example.org/SubmodelDescriptor/2", endpoints=[self.mock_endpoint]
+ )
+
+ def tearDown(self) -> None:
+ try:
+ self.descriptor_store.clear()
+ finally:
+ shutil.rmtree(store_path)
+
+ def test_add(self) -> None:
+ self.descriptor_store.add(self.aasd1)
+ # Note that this test is only checking that there are no errors during adding.
+ # The actual logic is tested together with retrieval in `test_retrieval`.
+
+ def test_retrieval(self) -> None:
+ self.descriptor_store.add(self.sd1)
+
+ # When retrieving the object, we should get the *same* instance as we added
+ retrieved_descriptor = self.descriptor_store.get_item("https://example.org/SubmodelDescriptor/1")
+ self.assertIs(self.sd1, retrieved_descriptor)
+
+ def test_iterating(self) -> None:
+ self.descriptor_store.add(self.sd1)
+ self.descriptor_store.add(self.sd2)
+ self.descriptor_store.add(self.aasd1)
+ self.descriptor_store.add(self.aasd2)
+ self.assertEqual(4, len(self.descriptor_store))
+
+ # Iterate objects, add them to a DictDescriptorStore and check them
+ retrieved_descriptor_store = provider.DictDescriptorStore()
+ for item in self.descriptor_store:
+ retrieved_descriptor_store.add(item)
+ self.assertEqual(4, len(retrieved_descriptor_store))
+ self.assertIn(self.sd1, retrieved_descriptor_store)
+ self.assertIn(self.sd2, retrieved_descriptor_store)
+ self.assertIn(self.aasd1, retrieved_descriptor_store)
+ self.assertIn(self.aasd2, retrieved_descriptor_store)
+
+ def test_key_errors(self) -> None:
+ self.descriptor_store.add(self.aasd1)
+ with self.assertRaises(KeyError) as cm:
+ self.descriptor_store.add(self.aasd1)
+ self.assertEqual(
+ "'Descriptor with id https://example.org/AASDescriptor/1 already exists in " "local file database'",
+ str(cm.exception),
+ )
+
+ self.descriptor_store.discard(self.aasd1)
+ with self.assertRaises(KeyError) as cm:
+ self.descriptor_store.get_item("https://example.org/AASDescriptor/1")
+ self.assertIsNone(self.descriptor_store.get("https://example.org/AASDescriptor/1"))
+ self.assertEqual(
+ "'No Identifiable with id https://example.org/AASDescriptor/1 found in local " "file database'",
+ str(cm.exception),
+ )
+
+ def test_reload_discard(self) -> None:
+ self.descriptor_store.add(self.sd1)
+ self.descriptor_store = local_file.LocalFileDescriptorStore(store_path)
+ self.descriptor_store.discard(self.sd1)
+ self.assertNotIn(self.sd1, self.descriptor_store)
diff --git a/server/test/interfaces/test_repository.py b/server/test/interfaces/test_repository.py
index 5177dfacb..01f3bd61d 100644
--- a/server/test/interfaces/test_repository.py
+++ b/server/test/interfaces/test_repository.py
@@ -1,4 +1,4 @@
-# Copyright (c) 2025 the Eclipse BaSyx Authors
+# Copyright (c) 2026 the Eclipse BaSyx Authors
#
# This program and the accompanying materials are made available under the terms of the MIT License, available in
# the LICENSE file of this project.
@@ -25,19 +25,18 @@
# TODO: add id_short format to schemata
import os
-import random
import pathlib
+import random
import urllib.parse
+from typing import Set
-import schemathesis
import hypothesis.strategies
-
+import schemathesis
from basyx.aas import model
from basyx.aas.adapter.aasx import DictSupplementaryFileContainer
-from server.app.interfaces.repository import WSGIApp
from basyx.aas.examples.data.example_aas import create_full_example
-from typing import Set
+from app.interfaces.repository import WSGIApp
def _encode_and_quote(identifier: model.Identifier) -> str:
@@ -63,7 +62,7 @@ def _check_transformed(response, case):
# disable the filter_too_much health check, which triggers if a strategy filters too much data, raising an error
suppress_health_check=[hypothesis.HealthCheck.filter_too_much],
# disable data generation deadlines, which would result in an error if data generation takes too much time
- deadline=None
+ deadline=None,
)
BASE_URL = "/api/v1"
@@ -82,16 +81,20 @@ def _check_transformed(response, case):
IDENTIFIER_SUBMODEL.add(_encode_and_quote(obj.id))
# load aas and submodel api specs
-AAS_SCHEMA = schemathesis.from_path(pathlib.Path(__file__).parent / "http-api-oas-aas.yaml",
- app=WSGIApp(create_full_example(), DictSupplementaryFileContainer()))
+AAS_SCHEMA = schemathesis.from_path(
+ pathlib.Path(__file__).parent / "http-api-oas-aas.yaml",
+ app=WSGIApp(create_full_example(), DictSupplementaryFileContainer()),
+)
-SUBMODEL_SCHEMA = schemathesis.from_path(pathlib.Path(__file__).parent / "http-api-oas-submodel.yaml",
- app=WSGIApp(create_full_example(), DictSupplementaryFileContainer()))
+SUBMODEL_SCHEMA = schemathesis.from_path(
+ pathlib.Path(__file__).parent / "http-api-oas-submodel.yaml",
+ app=WSGIApp(create_full_example(), DictSupplementaryFileContainer()),
+)
class APIWorkflowAAS(AAS_SCHEMA.as_state_machine()): # type: ignore
def setup(self):
- self.schema.app.object_store = create_full_example()
+ self.schema.app.identifiable_store = create_full_example()
# select random identifier for each test scenario
self.schema.base_url = BASE_URL + "/aas/" + random.choice(tuple(IDENTIFIER_AAS))
@@ -108,7 +111,7 @@ def validate_response(self, response, case, additional_checks=()) -> None:
class APIWorkflowSubmodel(SUBMODEL_SCHEMA.as_state_machine()): # type: ignore
def setup(self):
- self.schema.app.object_store = create_full_example()
+ self.schema.app.identifiable_store = create_full_example()
self.schema.base_url = BASE_URL + "/submodels/" + random.choice(tuple(IDENTIFIER_SUBMODEL))
def transform(self, result, direction, case):
diff --git a/server/test/interfaces/test_shells_asset_ids.py b/server/test/interfaces/test_shells_asset_ids.py
new file mode 100644
index 000000000..da103807a
--- /dev/null
+++ b/server/test/interfaces/test_shells_asset_ids.py
@@ -0,0 +1,49 @@
+# Copyright (c) 2026 the Eclipse BaSyx Authors
+#
+# This program and the accompanying materials are made available under the terms of the MIT License, available in
+# the LICENSE file of this project.
+#
+# SPDX-License-Identifier: MIT
+
+import base64
+import json
+import unittest
+
+from basyx.aas import model
+from basyx.aas.adapter.aasx import DictSupplementaryFileContainer
+from basyx.aas.examples.data.example_aas import create_full_example
+from werkzeug.test import Client
+
+from app.interfaces.repository import WSGIApp
+
+BASE_PATH = "/api/v3.1"
+
+
+def _encode_asset_id(name: str, value: str) -> str:
+ payload = json.dumps({"name": name, "value": value})
+ return base64.urlsafe_b64encode(payload.encode()).decode()
+
+
+class ShellsAssetIdsTest(unittest.TestCase):
+ def setUp(self) -> None:
+ self.example_data = create_full_example()
+ app = WSGIApp(self.example_data, DictSupplementaryFileContainer())
+ self.client = Client(app)
+
+ def test_multiple_global_asset_ids_returns_matching_results(self) -> None:
+ aas_list = [obj for obj in self.example_data if isinstance(obj, model.AssetAdministrationShell)]
+ known_id = aas_list[0].asset_information.global_asset_id
+ assert known_id is not None
+ unknown_id = "http://example.org/nonexistent_asset"
+ id1 = _encode_asset_id("globalAssetId", known_id)
+ id2 = _encode_asset_id("globalAssetId", unknown_id)
+ response = self.client.get(f"{BASE_PATH}/shells?assetIds={id1}&assetIds={id2}")
+ self.assertEqual(200, response.status_code)
+ result = json.loads(response.data)
+ returned_ids = [r["id"] for r in result]
+ self.assertIn(aas_list[0].id, returned_ids)
+
+ def test_malformed_asset_id_missing_field_returns_400(self) -> None:
+ bad_payload = base64.urlsafe_b64encode(b'{"name": "globalAssetId"}').decode()
+ response = self.client.get(f"{BASE_PATH}/shells?assetIds={bad_payload}")
+ self.assertEqual(400, response.status_code)
diff --git a/compliance_tool/test/files/test_demo_full_example_xml_wrong_attribute_aasx/aasx/aasx-origin b/server/test/model/__init__.py
similarity index 100%
rename from compliance_tool/test/files/test_demo_full_example_xml_wrong_attribute_aasx/aasx/aasx-origin
rename to server/test/model/__init__.py
diff --git a/server/test/model/test_provider.py b/server/test/model/test_provider.py
new file mode 100644
index 000000000..ee3b810da
--- /dev/null
+++ b/server/test/model/test_provider.py
@@ -0,0 +1,59 @@
+import unittest
+
+from app import model
+from app.model.provider import DictDescriptorStore
+
+
+class DictDescriptorStoreTest(unittest.TestCase):
+ def setUp(self) -> None:
+ self.mock_endpoint = model.Endpoint(
+ interface="AAS-3.0", protocol_information=model.ProtocolInformation(href="https://example.org/")
+ )
+ self.aasd1 = model.AssetAdministrationShellDescriptor(
+ id_="https://example.org/AASDescriptor/1", endpoints=[self.mock_endpoint]
+ )
+ self.aasd2 = model.AssetAdministrationShellDescriptor(
+ id_="https://example.org/AASDescriptor/2", endpoints=[self.mock_endpoint]
+ )
+ self.sd1 = model.SubmodelDescriptor(
+ id_="https://example.org/SubmodelDescriptor/1", endpoints=[self.mock_endpoint]
+ )
+ self.sd2 = model.SubmodelDescriptor(
+ id_="https://example.org/SubmodelDescriptor/2", endpoints=[self.mock_endpoint]
+ )
+
+ def test_store_retrieve(self) -> None:
+ descriptor_store: DictDescriptorStore = DictDescriptorStore()
+ descriptor_store.add(self.aasd1)
+ descriptor_store.add(self.aasd2)
+ self.assertIn(self.aasd1, descriptor_store)
+ self.assertFalse(self.sd1 in descriptor_store)
+
+ aasd3 = model.AssetAdministrationShellDescriptor(
+ id_="https://example.org/AASDescriptor/1", endpoints=[self.mock_endpoint]
+ )
+ with self.assertRaises(KeyError) as cm:
+ descriptor_store.add(aasd3)
+ self.assertEqual(
+ "'Descriptor object with same id https://example.org/AASDescriptor/1 is already " "stored in this store'",
+ str(cm.exception),
+ )
+ self.assertEqual(2, len(descriptor_store))
+ self.assertIs(self.aasd1, descriptor_store.get("https://example.org/AASDescriptor/1"))
+
+ descriptor_store.discard(self.aasd1)
+ with self.assertRaises(KeyError) as cm:
+ descriptor_store.get_item("https://example.org/AASDescriptor/1")
+ self.assertIsNone(descriptor_store.get("https://example.org/AASDescriptor/1"))
+ self.assertEqual("'https://example.org/AASDescriptor/1'", str(cm.exception))
+ self.assertIs(self.aasd2, descriptor_store.pop())
+ self.assertEqual(0, len(descriptor_store))
+
+ def test_store_update(self) -> None:
+ descriptor_store1: DictDescriptorStore = DictDescriptorStore()
+ descriptor_store2: DictDescriptorStore = DictDescriptorStore()
+ descriptor_store1.add(self.sd1)
+ descriptor_store2.add(self.sd2)
+ descriptor_store1.update(descriptor_store2)
+ self.assertIsInstance(descriptor_store1, DictDescriptorStore)
+ self.assertIn(self.sd2, descriptor_store1)
diff --git a/server/test/test_api_base_path.py b/server/test/test_api_base_path.py
new file mode 100644
index 000000000..7617102d7
--- /dev/null
+++ b/server/test/test_api_base_path.py
@@ -0,0 +1,54 @@
+# Copyright (c) 2026 the Eclipse BaSyx Authors
+#
+# This program and the accompanying materials are made available under the terms of the MIT License, available in
+# the LICENSE file of this project.
+#
+# SPDX-License-Identifier: MIT
+import unittest
+import pathlib
+import re
+
+SERVER_ROOT = pathlib.Path(__file__).resolve().parent.parent
+
+API_PATH_REGEX = re.compile(r"/api/v[\d.]+")
+
+FILES_TO_CHECK = [
+ # Server routes
+ SERVER_ROOT / "app" / "interfaces" / "discovery.py",
+ SERVER_ROOT / "app" / "interfaces" / "registry.py",
+ SERVER_ROOT / "app" / "interfaces" / "repository.py",
+ # Dockerfiles
+ SERVER_ROOT / "docker" / "discovery" / "Dockerfile",
+ SERVER_ROOT / "docker" / "registry" / "Dockerfile",
+ SERVER_ROOT / "docker" / "repository" / "Dockerfile",
+ # Tests
+ SERVER_ROOT / "test" / "interfaces" / "test_shells_asset_ids.py",
+]
+
+
+def _extract(path: pathlib.Path) -> str:
+ match = API_PATH_REGEX.search(path.read_text())
+ if match is None:
+ raise AssertionError(str(path.relative_to(SERVER_ROOT)) + ": no base path found")
+ return match.group(0)
+
+
+def _list_divergences(values) -> str:
+ output = "API base path diverges across files:\n"
+ for p, v in values.items():
+ output += f"\n{p} {v}"
+ return output
+
+
+class APIBasePathConsistencyTest(unittest.TestCase):
+ def test_base_path_aligned_across_known_files(self) -> None:
+ values = {}
+ for path in FILES_TO_CHECK:
+ values[str(path.relative_to(SERVER_ROOT))] = _extract(path)
+
+ distinct = set(values.values())
+ self.assertEqual(
+ 1,
+ len(distinct),
+ _list_divergences(values)
+ )