Commit 3052c559 authored by Michael Thies's avatar Michael Thies
Browse files

Merge branch feature/aasx_tutorial with refs/heads/master into refs/merge-requests/73/train

parents 0f3a478e edc9c8cf
Pipeline #407618 passed with stage
in 1 minute and 55 seconds
#!/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 exporting Asset Administration Shells with related objects and auxiliary files to AASX package files, using
the aas.adapter.aasx module from PyI40AAS.
.. warning:
This tutorial is only valid for the 0.2.x branch of PyI40AAS. With version 3.0 of *Details of the Asset
Administration Shell* (i.e. version 0.3.x of PyI40AAS) some specifications of AASX files will change, resulting in
changes of the `AASXWriter` interface.
"""
import datetime
from pathlib import Path # Used for easier handling of auxiliary file's local path
import pyecma376_2 # The base library for Open Packaging Specifications. We will use the OPCCoreProperties class.
from aas import model
from aas.adapter import aasx
# step 1: Setting up an SupplementaryFileContainer and AAS & submodel with File objects
# step 2: Writing AAS objects and auxiliary files to an AASX package
# step 3: Reading AAS objects and auxiliary files from an AASX package
#########################################################################################
# Step 1: Setting up an SupplementaryFileContainer and AAS & submodel with File objects #
#########################################################################################
# Let's first create a basic Asset Adminstration Shell with a simple submodel.
# See `tutorial_create_simple_aas.py` for more details.
submodel = model.Submodel(
identification=model.Identifier('https://acplt.org/Simple_Submodel', model.IdentifierType.IRI)
)
asset = model.Asset(
kind=model.AssetKind.INSTANCE, # define that the Asset is of kind instance
identification=model.Identifier(id_='https://acplt.org/Simple_Asset', id_type=model.IdentifierType.IRI)
)
aas = model.AssetAdministrationShell(
identification=model.Identifier('https://acplt.org/Simple_AAS', model.IdentifierType.IRI),
asset=model.AASReference.from_referable(asset),
submodel={model.AASReference.from_referable(submodel)}
)
# Another submodel, which is not related to the AAS:
unrelated_submodel = model.Submodel(
identification=model.Identifier('https://acplt.org/Unrelated_Submodel', model.IdentifierType.IRI)
)
# We add these objects to an ObjectStore for easy retrieval by identification.
# See `tutorial_storage.py` for more details. We could also use a database-backed ObjectStore here
# (see `tutorial_backend_couchdb.py`).
object_store = model.DictObjectStore([submodel, asset, aas, unrelated_submodel])
# For holding auxiliary files, which will eventually be added to an AASX package, we need an SupplementaryFileContainer.
# The `DictSupplementaryFileContainer` is a simple SupplementaryFileContainer, that stores the files' contents in simple
# bytes objects in memory.
file_store = aasx.DictSupplementaryFileContainer()
# Now, we add an example file from our local filesystem to the SupplementaryFileContainer.
#
# For this purpose, we need to specify the file's name in the SupplementaryFileContainer. This name is used to reference
# the file in the container and will later be used as the filename in the AASX package file. Thus, this file must begin
# with a slash and should begin with `/aasx/`. Here, we use `/aasx/suppl/MyExampleFile.pdf`. The
# SupplementaryFileContainer's add_file() method will ensure uniqueness of the name by adding a suffix if a equally
# named file with different contents exists. The final name is returned.
#
# In addition, we need to specify the MIME type of the file, which is later used in the metadata of the AASX package.
# (This is actually a requirement of the underlying Open Packaging Conventions (ECMA376-2) format, which imposes the
# specification of the MIME type ("content type") of every single file within the package.)
with open(Path(__file__).parent / 'data' / 'TestFile.pdf', 'rb') as f:
actual_file_name = file_store.add_file("/aasx/suppl/MyExampleFile.pdf", f, "application/pdf")
# With the actual_file_name in the SupplementaryFileContainer, we can create a reference to that file in our AAS
# Submodel, in the form of a `File` object:
submodel.submodel_element.add(
model.File(id_short="documentationFile",
mime_type="application/pdf",
value=actual_file_name))
######################################################################
# Step 2: Writing AAS objects and auxiliary files to an AASX package #
######################################################################
# After setting everything up in Step 1, writing the AAS, including the Submdel and Asset objects and the auxiliary
# file to an AASX package is simple.
# Open an AASXWriter with the destination file name and use it as a context handler, to make sure it is properly closed
# after doing the modifications:
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 identification, the referenced
# Asset object and all referenced Submodel and ConceptDescription objects from the ObjectStore. It will also
# scan all sbmodels for `File` objects and fetch the referenced auxiliary files from the SupplementaryFileContainer.
writer.write_aas(aas_id=model.Identifier('https://acplt.org/Simple_AAS', model.IdentifierType.IRI),
object_store=object_store,
file_store=file_store,
submodel_split_parts=False) # for compatibility with AASX Package Explorer
# For adding a second AAS to the package, we can simply call `write_aas()` again.
# Warning: This will create a second XML/JSON part in the AASX Package, which is compliant with the "Details of the
# Asset Administration Shell" standard up to version 2.0.1, but not supported by AASX Package Explorer.
# We can also use a more low-level interface to add a JSON/XML part with any Identifiable objects (not only an AAS
# and referenced objects) in the AASX package manually. `write_aas_objects()` will also take care of adding
# referenced auxiliary files by scanning all submodel objects for contained `File` objects.
# Warning: AASX Package Explorer will only read the first XML part in an AASX package. Thus, in this example, it
# will only find the objects, written by `write_aas()` above.
writer.write_aas_objects(part_name="/aasx/my_aas_part.xml",
object_ids=[
model.Identifier('https://acplt.org/Unrelated_Submodel', model.IdentifierType.IRI)
],
object_store=object_store,
file_store=file_store)
# We can also add a thumbnail image to the package (using `writer.write_thumbnail()`) or add metadata:
meta_data = pyecma376_2.OPCCoreProperties()
meta_data.creator = "Chair of Process Control Engineering"
meta_data.created = datetime.datetime.now()
writer.write_core_properties(meta_data)
# Closing the AASXWriter will write some required parts with relationships and MIME types to the AASX package file and
# close the package file afterwards. Make sure, to always call `AASXWriter.close()` or use the AASXWriter in a `with`
# statement (as a context manager) as shown above.
########################################################################
# Step 3: Reading AAS objects and auxiliary files from an AASX package #
########################################################################
# 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()
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
# In contrast to the AASX Package Explorer, we are not limited to a single XML part in the package, but instead we
# will read the contents of all included JSON and XML parts into the ObjectStore
reader.read_into(object_store=new_object_store,
file_store=new_file_store)
# We can also read the meta data
new_meta_data = reader.get_core_properties()
# We could also read the thumbnail image, using `reader.get_thumbnail()`
# Some quick checks to make sure, reading worked as expected
assert model.Identifier('https://acplt.org/Simple_Submodel', model.IdentifierType.IRI) in new_object_store
assert model.Identifier('https://acplt.org/Unrelated_Submodel', model.IdentifierType.IRI) in new_object_store
assert actual_file_name in new_file_store
assert new_meta_data.creator == "Chair of Process Control Engineering"
......@@ -45,6 +45,12 @@ class TutorialTest(unittest.TestCase):
pass
# The tutorial already includes assert statements for the relevant points. So no further checks are required.
def test_tutorial_aasx(self):
with temporary_workingdirectory():
from aas.examples import tutorial_aasx
pass
# The tutorial already includes assert statements for the relevant points. So no further checks are required.
@contextmanager
def temporary_workingdirectory():
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment