3.3. adapter.aasx - Read and write AASX-files

Functionality for reading and writing AASX files according to “Details of the Asset Administration Shell Part 1 V2.0”, section 7.

The AASX file format is built upon the Open Packaging Conventions (OPC; ECMA 376-2). We use the pyecma376_2 library for low level OPC reading and writing. It currently supports all required features except for embedded digital signatures.

Writing and reading of AASX packages is performed through the AASXReader and AASXWriter classes. Each instance of these classes wraps an existing AASX file resp. a file to be created and allows to read/write the included AAS objects into/form ObjectStores. For handling of embedded supplementary files, this module provides the AbstractSupplementaryFileContainer class interface and the DictSupplementaryFileContainer implementation.

class basyx.aas.adapter.aasx.AASXReader(file: Union[os.PathLike, str, IO])

An AASXReader wraps an existing AASX package file to allow reading its contents and metadata.

Basic usage:

objects = DictObjectStore()
files = DictSupplementaryFileContainer()
with AASXReader("filename.aasx") as reader:
    meta_data = reader.get_core_properties()
    reader.read_into(objects, files)
close() None

Close the AASXReader and the underlying OPC / ZIP file readers. Must be called after reading the file.

get_core_properties() pyecma376_2.core_properties.OPCCoreProperties

Retrieve the OPC Core Properties (meta data) of the AASX package file.

If no meta data is provided in the package file, an emtpy OPCCoreProperties object is returned.

Returns

The AASX package’s meta data

get_thumbnail() Optional[bytes]

Retrieve the packages thumbnail image

The thumbnail image file is read into memory and returned as bytes object. You may use some python image library for further processing or conversion, e.g. pillow:

import io
from PIL import Image
thumbnail = Image.open(io.BytesIO(reader.get_thumbnail()))
Returns

The AASX package thumbnail’s file contents or None if no thumbnail is provided

read_into(object_store: basyx.aas.model.provider.AbstractObjectStore, file_store: basyx.aas.adapter.aasx.AbstractSupplementaryFileContainer, override_existing: bool = False, **kwargs) Set[str]

Read the contents of the AASX package and add them into a given ObjectStore

This function does the main job of reading the AASX file’s contents. It traverses the relationships within the package to find AAS JSON or XML parts, parses them and adds the contained AAS objects into the provided object_store. While doing so, it searches all parsed Submodels for File objects to extract the supplementary files. The referenced supplementary files are added to the given file_store and the File objects’ values are updated with the absolute name of the supplementary file to allow for robust resolution the file within the file_store later.

Parameters
  • object_store – An ObjectStore to add the AAS objects from the AASX file to

  • file_store – A SupplementaryFileContainer to add the embedded supplementary files to

  • override_existing – If True, existing objects in the object store are overridden with objects from the AASX that have the same Identifier. Default behavior is to skip those objects from the AASX.

Returns

A set of the Identifiers of all Identifiable objects parsed from the AASX file

class basyx.aas.adapter.aasx.AASXWriter(file: Union[os.PathLike, str, IO])

An AASXWriter wraps a new AASX package file to write its contents to it piece by piece.

Basic usage:

# object_store and file_store are expected to be given (e.g. some storage backend or previously created data)
cp = OPCCoreProperties()
cp.creator = "ACPLT"
cp.created = datetime.datetime.now()

with AASXWriter("filename.aasx") as writer:
    writer.write_aas("https://acplt.org/AssetAdministrationShell",
                     object_store,
                     file_store)
    writer.write_aas("https://acplt.org/AssetAdministrationShell2",
                     object_store,
                     file_store)
    writer.write_core_properties(cp)

Attention: The AASXWriter must always be closed using the close() method or its context manager functionality (as shown above). Otherwise the resulting AASX file will lack important data structures and will not be readable.

close()

Write relationships for all data files to package and close underlying OPC package and ZIP file.

write_aas(aas_ids: Union[str, Iterable[str]], object_store: basyx.aas.model.provider.AbstractObjectStore, file_store: basyx.aas.adapter.aasx.AbstractSupplementaryFileContainer, write_json: bool = False) None

Convenience method to write one or more AssetAdministrationShells with all included and referenced objects to the AASX package according to the part name conventions from DotAAS.

This method takes the AASs’ Identifiers (as aas_ids) to retrieve the AASs from the given object_store. References to Submodels and ConceptDescriptions (via semanticId attributes) are also resolved using the object_store. All of these objects are written to an aas-spec part /aasx/data.xml or /aasx/data.json in the AASX package, compliant to the convention presented in “Details of the Asset Administration Shell”. Supplementary files which are referenced by a File object in any of the Submodels are also added to the AASX package.

This method uses write_all_aas_objects() to write the AASX part.

Attention

This method must only be used once on a single AASX package. Otherwise, the /aasx/data.json (or …xml) part would be written twice to the package, hiding the first part and possibly causing problems when reading the package.

To write multiple Asset Administration Shells to a single AASX package file, call this method once, passing a list of AAS Identifiers to the aas_ids parameter.

Parameters
  • aas_idsIdentifier or Iterable of Identifiers of the AAS(s) to be written to the AASX file

  • object_storeObjectStore to retrieve the Identifiable AAS objects (AssetAdministrationShell, ConceptDescription and Submodel) from

  • file_storeSupplementaryFileContainer to retrieve supplementary files from, which are referenced by File objects

  • write_json – If True, JSON parts are created for the AAS and each Submodel in the AASX package file instead of XML parts. Defaults to False.

Raises
  • KeyError – If one of the AAS could not be retrieved from the object store (unresolvable Submodels and ConceptDescriptions are skipped, logging a warning/info message)

  • TypeError – If one of the given AAS ids does not resolve to an AAS (but another Identifiable object)

write_aas_objects(part_name: str, object_ids: Iterable[str], object_store: basyx.aas.model.provider.AbstractObjectStore, file_store: basyx.aas.adapter.aasx.AbstractSupplementaryFileContainer, write_json: bool = False, split_part: bool = False, additional_relationships: Iterable[pyecma376_2.package_model.OPCRelationship] = ()) None

A thin wrapper around write_all_aas_objects() to ensure downwards compatibility

This method takes the AAS’s Identifier (as aas_id) to retrieve it from the given object_store. If the list of written objects includes aas.model.submodel.Submodel objects, Supplementary files which are referenced by File objects within those submodels, are also added to the AASX package.

Attention

You must make sure to call this method or write_all_aas_objects only once per unique part_name on a single package instance.

Parameters
  • 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’).

  • object_ids – A list of Identifiers of the objects to be written to the AASX package. Only these Identifiable objects (and included Referable objects) are written to the package.

  • object_store – The objects store to retrieve the Identifiable objects from

  • file_store – The SupplementaryFileContainer to retrieve supplementary files from (if there are any File objects within the written objects.

  • write_json – If True, the part is written as a JSON file instead of an XML file. Defaults to False.

  • split_part – If True, no aas-spec relationship is added from the aasx-origin to this part. You must make sure to reference it via a aas-spec-split relationship from another aas-spec part

  • 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.

write_all_aas_objects(part_name: str, objects: basyx.aas.model.provider.AbstractObjectStore[basyx.aas.model.base.Identifiable], file_store: basyx.aas.adapter.aasx.AbstractSupplementaryFileContainer, write_json: bool = False, split_part: bool = False, additional_relationships: Iterable[pyecma376_2.package_model.OPCRelationship] = ()) None

Write all AAS objects in a given ObjectStore to an XML or JSON part in the AASX package and add the referenced supplementary files to the package.

This method takes an ObjectStore and writes all contained objects into an “aas_env” part in the AASX package. If the ObjectStore includes Submodel objects, supplementary files which are referenced by File objects within those Submodels, are fetched from the file_store and added to the AASX package.

Attention

You must make sure to call this method only once per unique part_name on a single package instance.

Parameters
  • 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’).

  • objects – The objects to be written to the AASX package. Only these Identifiable objects (and included Referable objects) are written to the package.

  • file_store – The SupplementaryFileContainer to retrieve supplementary files from (if there are any File objects within the written objects.

  • write_json – If True, the part is written as a JSON file instead of an XML file. Defaults to False.

  • split_part – If True, no aas-spec relationship is added from the aasx-origin to this part. You must make sure to reference it via a aas-spec-split relationship from another aas-spec part

  • 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.

write_core_properties(core_properties: pyecma376_2.core_properties.OPCCoreProperties)

Write OPC Core Properties (meta data) to the AASX package file.

Attention

This method may only be called once for each AASXWriter!

Parameters

core_properties – The OPCCoreProperties object with the meta data to be written to the package file

write_thumbnail(name: str, data: bytearray, content_type: str)

Write an image file as thumbnail image to the AASX package.

Attention

This method may only be called once for each AASXWriter!

Parameters
  • name – The OPC part name of the thumbnail part. Should not contain ‘/’ or URI-encoded ‘/’ or ‘’.

  • data – The image file’s binary contents to be written

  • content_type – OPC content type (MIME type) of the image file

class basyx.aas.adapter.aasx.AbstractSupplementaryFileContainer

Abstract interface for containers of supplementary files for AASs.

Supplementary files may be PDF files or other binary or textual files, referenced in a File object of an AAS by their name. They are used to provide associated documents without embedding their contents (as Blob object) in the AAS.

A SupplementaryFileContainer keeps track of the name and content_type (MIME type) for each file. Additionally it allows to resolve name conflicts by comparing the files’ contents and providing an alternative name for a dissimilar new file. It also provides each files sha256 hash sum to allow name conflict checking in other classes (e.g. when writing AASX files).

abstract add_file(name: str, file: IO[bytes], content_type: str) str

Add a new file to the SupplementaryFileContainer and resolve name conflicts.

The file contents must be provided as a binary file-like object to be read by the SupplementaryFileContainer. If the container already contains an equally named file, the content_type and file contents are compared (using a hash sum). In case of dissimilar files, a new unique name for the new file is computed and returned. It should be used to update in the File object of the AAS.

Parameters
  • name – The file’s proposed name. Should start with a ‘/’. Should not contain URI-encoded ‘/’ or ‘’

  • file – A binary file-like opened for reading the file contents

  • content_type – The file’s content_type

Returns

The file name as stored in the SupplementaryFileContainer. Typically name or a modified version of name to resolve conflicts.

abstract get_content_type(name: str) str

Get a stored file’s content_type.

Parameters

name – file name of questioned file

Returns

The file’s content_type

Raises

KeyError – If no file with this name is stored

abstract get_sha256(name: str) bytes

Get a stored file content’s sha256 hash sum.

This may be used by other classes (e.g. the AASXWriter) to check for name conflicts.

Parameters

name – file name of questioned file

Returns

The file content’s sha256 hash sum

Raises

KeyError – If no file with this name is stored

abstract write_file(name: str, file: IO[bytes]) None

Retrieve a stored file’s contents by writing them into a binary writable file-like object.

Parameters
  • name – file name of questioned file

  • file – A binary file-like object with write() method to write the file contents into

Raises

KeyError – If no file with this name is stored

class basyx.aas.adapter.aasx.DictSupplementaryFileContainer

SupplementaryFileContainer implementation using a dict to store the file contents in-memory.

add_file(name: str, file: IO[bytes], content_type: str) str

Add a new file to the SupplementaryFileContainer and resolve name conflicts.

The file contents must be provided as a binary file-like object to be read by the SupplementaryFileContainer. If the container already contains an equally named file, the content_type and file contents are compared (using a hash sum). In case of dissimilar files, a new unique name for the new file is computed and returned. It should be used to update in the File object of the AAS.

Parameters
  • name – The file’s proposed name. Should start with a ‘/’. Should not contain URI-encoded ‘/’ or ‘’

  • file – A binary file-like opened for reading the file contents

  • content_type – The file’s content_type

Returns

The file name as stored in the SupplementaryFileContainer. Typically name or a modified version of name to resolve conflicts.

get_content_type(name: str) str

Get a stored file’s content_type.

Parameters

name – file name of questioned file

Returns

The file’s content_type

Raises

KeyError – If no file with this name is stored

get_sha256(name: str) bytes

Get a stored file content’s sha256 hash sum.

This may be used by other classes (e.g. the AASXWriter) to check for name conflicts.

Parameters

name – file name of questioned file

Returns

The file content’s sha256 hash sum

Raises

KeyError – If no file with this name is stored

write_file(name: str, file: IO[bytes]) None

Retrieve a stored file’s contents by writing them into a binary writable file-like object.

Parameters
  • name – file name of questioned file

  • file – A binary file-like object with write() method to write the file contents into

Raises

KeyError – If no file with this name is stored

class basyx.aas.adapter.aasx.NameFriendlyfier

A simple helper class to create unique “AAS friendly names” according to DotAAS, section 7.6.

Objects of this class store the already created friendly names to avoid name collisions within one set of names.

get_friendly_name(identifier: str)

Generate a friendly name from an AAS identifier.

TODO: This information is outdated. The whole class is no longer needed.

According to section 7.6 of “Details of the Asset Administration Shell”, all non-alphanumerical characters are replaced with underscores. We also replace all non-ASCII characters to generate valid URIs as the result. If this replacement results in a collision with a previously generated friendly name of this NameFriendlifier, a number is appended with underscore to the friendly name.

Example:

friendlyfier = NameFriendlyfier()
friendlyfier.get_friendly_name("http://example.com/AAS-a")
 > "http___example_com_AAS_a"

friendlyfier.get_friendly_name("http://example.com/AAS+a")
 >  "http___example_com_AAS_a_1"