Skip to content

Commit 543dec5

Browse files
committed
Refactor AbstractObjectProvider
Previously, the `AbstractObjectProvider` only worked with `Identifiables`, which made it incompatible with the new version of the AAS metamodel. This renames and restructures both the `AbstractObjectProvider` and the `AbstractObjectStore`. In all non-abstract subclasses where `Object` appears in the class or method name, it has been replaced with `Identifiable`. Old classes are still available with a deprecation warning. Moreover, `AbstractObjectProvider` and `AbstractObjectStore` are now generic to be able to handle more classes than just `Identifiables`. In order to handle the new `AASDescriptor`, a new class `HasIdentifier` has been added. It is intended to be an abstract superclass for all classes that have an identifier, but are not `Identifiables`. As of now, this PR is intended to serve as a basis for discussion. Therefore, the documentation has not yet been adapted. Fixes #428
1 parent a081c0c commit 543dec5

40 files changed

Lines changed: 454 additions & 368 deletions

compliance_tool/aas_compliance_tool/compliance_check_aasx.py

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@
3131

3232
def check_deserialization(file_path: str, state_manager: ComplianceToolStateManager,
3333
file_info: Optional[str] = None) \
34-
-> Tuple[model.DictObjectStore, aasx.DictSupplementaryFileContainer, pyecma376_2.OPCCoreProperties]:
34+
-> Tuple[model.DictIdentifiableStore, aasx.DictSupplementaryFileContainer, pyecma376_2.OPCCoreProperties]:
3535
"""
3636
Read a AASX file and reports any issues using the given
3737
:class:`~basyx.aas.compliance_tool.state_manager.ComplianceToolStateManager`
@@ -68,24 +68,24 @@ def check_deserialization(file_path: str, state_manager: ComplianceToolStateMana
6868
state_manager.set_step_status_from_log()
6969
state_manager.add_step('Read file')
7070
state_manager.set_step_status(Status.NOT_EXECUTED)
71-
return model.DictObjectStore(), aasx.DictSupplementaryFileContainer(), pyecma376_2.OPCCoreProperties()
71+
return model.DictIdentifiableStore(), aasx.DictSupplementaryFileContainer(), pyecma376_2.OPCCoreProperties()
7272

7373
try:
7474
# read given file
7575
state_manager.add_step('Read file')
76-
obj_store: model.DictObjectStore[model.Identifiable] = model.DictObjectStore()
76+
id_store: model.DictIdentifiableStore[model.Identifiable] = model.DictIdentifiableStore()
7777
files = aasx.DictSupplementaryFileContainer()
78-
reader.read_into(obj_store, files)
78+
reader.read_into(id_store, files)
7979
new_cp = reader.get_core_properties()
8080
state_manager.set_step_status(Status.SUCCESS)
8181
except (ValueError, KeyError) as error:
8282
logger.error(error)
8383
state_manager.set_step_status(Status.FAILED)
84-
return model.DictObjectStore(), aasx.DictSupplementaryFileContainer(), pyecma376_2.OPCCoreProperties()
84+
return model.DictIdentifiableStore(), aasx.DictSupplementaryFileContainer(), pyecma376_2.OPCCoreProperties()
8585
finally:
8686
reader.close()
8787

88-
return obj_store, files, new_cp
88+
return id_store, files, new_cp
8989

9090

9191
def check_schema(file_path: str, state_manager: ComplianceToolStateManager) -> None:
@@ -187,7 +187,7 @@ def check_aas_example(file_path: str, state_manager: ComplianceToolStateManager,
187187

188188
state_manager.add_step('Check if data is equal to example data')
189189
example_data = create_example_aas_binding()
190-
checker.check_object_store(obj_store, example_data)
190+
checker.check_identifiable_store(obj_store, example_data)
191191
state_manager.add_log_records_from_data_checker(checker)
192192

193193
if state_manager.status in (Status.FAILED, Status.NOT_EXECUTED):
@@ -238,21 +238,21 @@ def check_aas_example(file_path: str, state_manager: ComplianceToolStateManager,
238238

239239
# Check if file in file object is the same
240240
list_of_id_shorts = ["ExampleSubmodelCollection", "ExampleFile"]
241-
obj = example_data.get_identifiable("https://acplt.org/Test_Submodel")
241+
identifiable = example_data.get_item("https://acplt.org/Test_Submodel")
242242
for id_short in list_of_id_shorts:
243-
obj = obj.get_referable(id_short)
244-
obj2 = obj_store.get_identifiable("https://acplt.org/Test_Submodel")
243+
identifiable = identifiable.get_referable(id_short)
244+
obj2 = obj_store.get_item("https://acplt.org/Test_Submodel")
245245
for id_short in list_of_id_shorts:
246246
obj2 = obj2.get_referable(id_short)
247247
try:
248-
sha_file = files.get_sha256(obj.value)
248+
sha_file = files.get_sha256(identifiable.value)
249249
except KeyError as error:
250250
state_manager.add_log_records_from_data_checker(checker2)
251251
logger.error(error)
252252
state_manager.set_step_status(Status.FAILED)
253253
return
254254

255-
checker2.check(sha_file == files.get_sha256(obj2.value), "File of {} must be {}.".format(obj.value, obj2.value),
255+
checker2.check(sha_file == files.get_sha256(obj2.value), "File of {} must be {}.".format(identifiable.value, obj2.value),
256256
value=obj2.value)
257257
state_manager.add_log_records_from_data_checker(checker2)
258258
if state_manager.status in (Status.FAILED, Status.NOT_EXECUTED):
@@ -294,7 +294,7 @@ def check_aasx_files_equivalence(file_path_1: str, file_path_2: str, state_manag
294294
checker = AASDataChecker(raise_immediately=False, **kwargs)
295295
try:
296296
state_manager.add_step('Check if data in files are equal')
297-
checker.check_object_store(obj_store_1, obj_store_2)
297+
checker.check_identifiable_store(obj_store_1, obj_store_2)
298298
except (KeyError, AssertionError) as error:
299299
state_manager.set_step_status(Status.FAILED)
300300
logger.error(error)

compliance_tool/aas_compliance_tool/compliance_check_json.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,7 @@ def _check_schema(file_to_be_checked: IO[str], state_manager: ComplianceToolStat
102102

103103

104104
def check_deserialization(file_path: str, state_manager: ComplianceToolStateManager,
105-
file_info: Optional[str] = None) -> model.DictObjectStore:
105+
file_info: Optional[str] = None) -> model.DictIdentifiableStore:
106106
"""
107107
Deserializes a JSON AAS file and reports any issues using the given
108108
:class:`~basyx.aas.compliance_tool.state_manager.ComplianceToolStateManager`
@@ -140,7 +140,7 @@ def check_deserialization(file_path: str, state_manager: ComplianceToolStateMana
140140
else:
141141
state_manager.add_step('Read file and check if it is deserializable')
142142
state_manager.set_step_status(Status.NOT_EXECUTED)
143-
return model.DictObjectStore()
143+
return model.DictIdentifiableStore()
144144

145145
with file_to_be_checked:
146146
state_manager.set_step_status(Status.SUCCESS)
@@ -184,7 +184,7 @@ def check_aas_example(file_path: str, state_manager: ComplianceToolStateManager,
184184
checker = AASDataChecker(raise_immediately=False, **kwargs)
185185

186186
state_manager.add_step('Check if data is equal to example data')
187-
checker.check_object_store(obj_store, create_example())
187+
checker.check_identifiable_store(obj_store, create_example())
188188

189189
state_manager.add_log_records_from_data_checker(checker)
190190

@@ -220,7 +220,7 @@ def check_json_files_equivalence(file_path_1: str, file_path_2: str, state_manag
220220
checker = AASDataChecker(raise_immediately=False, **kwargs)
221221
try:
222222
state_manager.add_step('Check if data in files are equal')
223-
checker.check_object_store(obj_store_1, obj_store_2)
223+
checker.check_identifiable_store(obj_store_1, obj_store_2)
224224
except (KeyError, AssertionError) as error:
225225
state_manager.set_step_status(Status.FAILED)
226226
logger.error(error)

compliance_tool/aas_compliance_tool/compliance_check_xml.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,7 @@ def _check_schema(file_to_be_checked, state_manager):
101101

102102

103103
def check_deserialization(file_path: str, state_manager: ComplianceToolStateManager,
104-
file_info: Optional[str] = None) -> model.DictObjectStore:
104+
file_info: Optional[str] = None) -> model.DictIdentifiableStore:
105105
"""
106106
Deserializes a XML AAS file and reports any issues using the given
107107
:class:`~basyx.aas.compliance_tool.state_manager.ComplianceToolStateManager`
@@ -139,7 +139,7 @@ def check_deserialization(file_path: str, state_manager: ComplianceToolStateMana
139139
else:
140140
state_manager.add_step('Read file and check if it is deserializable')
141141
state_manager.set_step_status(Status.NOT_EXECUTED)
142-
return model.DictObjectStore()
142+
return model.DictIdentifiableStore()
143143

144144
with file_to_be_checked:
145145
state_manager.set_step_status(Status.SUCCESS)
@@ -183,7 +183,7 @@ def check_aas_example(file_path: str, state_manager: ComplianceToolStateManager,
183183
checker = AASDataChecker(raise_immediately=False, **kwargs)
184184

185185
state_manager.add_step('Check if data is equal to example data')
186-
checker.check_object_store(obj_store, create_example())
186+
checker.check_identifiable_store(obj_store, create_example())
187187

188188
state_manager.add_log_records_from_data_checker(checker)
189189

@@ -219,7 +219,7 @@ def check_xml_files_equivalence(file_path_1: str, file_path_2: str, state_manage
219219
checker = AASDataChecker(raise_immediately=False, **kwargs)
220220
try:
221221
state_manager.add_step('Check if data in files are equal')
222-
checker.check_object_store(obj_store_1, obj_store_2)
222+
checker.check_identifiable_store(obj_store_1, obj_store_2)
223223
except (KeyError, AssertionError) as error:
224224
state_manager.set_step_status(Status.FAILED)
225225
logger.error(error)

compliance_tool/test/test_aas_compliance_tool.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -141,7 +141,7 @@ def test_json_create_example(self) -> None:
141141
json_object_store = read_aas_json_file(f, failsafe=False)
142142
data = create_example()
143143
checker = AASDataChecker(raise_immediately=True)
144-
checker.check_object_store(json_object_store, data)
144+
checker.check_identifiable_store(json_object_store, data)
145145
os.unlink(filename)
146146

147147
def test_json_deserialization(self) -> None:
@@ -187,7 +187,7 @@ def test_xml_create_example(self) -> None:
187187
xml_object_store = read_aas_xml_file(f, failsafe=False)
188188
data = create_example()
189189
checker = AASDataChecker(raise_immediately=True)
190-
checker.check_object_store(xml_object_store, data)
190+
checker.check_identifiable_store(xml_object_store, data)
191191
os.unlink(filename)
192192

193193
def test_xml_deseralization(self) -> None:
@@ -229,7 +229,7 @@ def test_aasx_create_example(self) -> None:
229229
self.assertIn('SUCCESS: Write data to file', str(output.stdout))
230230

231231
# Read AASX file
232-
new_data: model.DictObjectStore[model.Identifiable] = model.DictObjectStore()
232+
new_data: model.DictIdentifiableStore[model.Identifiable] = model.DictIdentifiableStore()
233233
new_files = aasx.DictSupplementaryFileContainer()
234234
with aasx.AASXReader(filename) as reader:
235235
reader.read_into(new_data, new_files)

sdk/README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -113,7 +113,7 @@ Serialize the `Submodel` to XML:
113113
```python
114114
from basyx.aas.adapter.xml import write_aas_xml_file
115115

116-
data: model.DictObjectStore[model.Identifiable] = model.DictObjectStore()
116+
data: model.DictIdentifiableStore[model.Identifiable] = model.DictIdentifiableStore()
117117
data.add(submodel)
118118
write_aas_xml_file(file='Simple_Submodel.xml', data=data)
119119
```

sdk/basyx/aas/adapter/__init__.py

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -11,12 +11,12 @@
1111
from basyx.aas.adapter.aasx import AASXReader, DictSupplementaryFileContainer
1212
from basyx.aas.adapter.json import read_aas_json_file_into
1313
from basyx.aas.adapter.xml import read_aas_xml_file_into
14-
from basyx.aas.model.provider import DictObjectStore
14+
from basyx.aas.model.provider import DictIdentifiableStore
1515
from pathlib import Path
1616
from typing import Union
1717

1818

19-
def load_directory(directory: Union[Path, str]) -> tuple[DictObjectStore, DictSupplementaryFileContainer]:
19+
def load_directory(directory: Union[Path, str]) -> tuple[DictIdentifiableStore, DictSupplementaryFileContainer]:
2020
"""
2121
Create a new :class:`~basyx.aas.model.provider.DictObjectStore` and use it to load Asset Administration Shell and
2222
Submodel files in ``AASX``, ``JSON`` and ``XML`` format from a given directory into memory. Additionally, load all
@@ -28,7 +28,7 @@ def load_directory(directory: Union[Path, str]) -> tuple[DictObjectStore, DictSu
2828
:class:`~basyx.aas.adapter.aasx.DictSupplementaryFileContainer` containing all loaded data
2929
"""
3030

31-
dict_object_store: DictObjectStore = DictObjectStore()
31+
dict_identifiable_store: DictIdentifiableStore = DictIdentifiableStore()
3232
file_container: DictSupplementaryFileContainer = DictSupplementaryFileContainer()
3333

3434
directory = Path(directory)
@@ -40,12 +40,12 @@ def load_directory(directory: Union[Path, str]) -> tuple[DictObjectStore, DictSu
4040
suffix = file.suffix.lower()
4141
if suffix == ".json":
4242
with open(file) as f:
43-
read_aas_json_file_into(dict_object_store, f)
43+
read_aas_json_file_into(dict_identifiable_store, f)
4444
elif suffix == ".xml":
4545
with open(file) as f:
46-
read_aas_xml_file_into(dict_object_store, f)
46+
read_aas_xml_file_into(dict_identifiable_store, f)
4747
elif suffix == ".aasx":
4848
with AASXReader(file) as reader:
49-
reader.read_into(object_store=dict_object_store, file_store=file_container)
49+
reader.read_into(object_store=dict_identifiable_store, file_store=file_container)
5050

51-
return dict_object_store, file_container
51+
return dict_identifiable_store, file_container

sdk/basyx/aas/adapter/aasx.py

Lines changed: 21 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -234,7 +234,7 @@ def _read_aas_part_into(self, part_name: str,
234234
elif isinstance(obj, model.AssetAdministrationShell):
235235
self._collect_supplementary_files(part_name, obj, file_store)
236236

237-
def _parse_aas_part(self, part_name: str, **kwargs) -> model.DictObjectStore:
237+
def _parse_aas_part(self, part_name: str, **kwargs) -> model.DictIdentifiableStore:
238238
"""
239239
Helper function to parse the AAS objects from a single JSON or XML part of the AASX package.
240240
@@ -261,7 +261,7 @@ def _parse_aas_part(self, part_name: str, **kwargs) -> model.DictObjectStore:
261261
logger.error(error_message)
262262
else:
263263
raise ValueError(error_message)
264-
return model.DictObjectStore()
264+
return model.DictIdentifiableStore()
265265

266266
def _collect_supplementary_files(self, part_name: str,
267267
root_element: Union[model.AssetAdministrationShell, model.Submodel],
@@ -380,7 +380,7 @@ def __init__(self, file: Union[os.PathLike, str, IO], failsafe: bool = True):
380380

381381
def write_aas(self,
382382
aas_ids: Union[model.Identifier, Iterable[model.Identifier]],
383-
object_store: model.AbstractObjectStore,
383+
object_store: model.AbstractObjectStore[model.Identifier, model.Identifiable],
384384
file_store: "AbstractSupplementaryFileContainer",
385385
write_json: bool = False) -> None:
386386
"""
@@ -430,10 +430,10 @@ def write_aas(self,
430430
if isinstance(aas_ids, model.Identifier):
431431
aas_ids = (aas_ids,)
432432

433-
objects_to_be_written: model.DictObjectStore[model.Identifiable] = model.DictObjectStore()
433+
objects_to_be_written: model.DictIdentifiableStore[model.Identifiable] = model.DictIdentifiableStore()
434434
for aas_id in aas_ids:
435435
try:
436-
aas = object_store.get_identifiable(aas_id)
436+
aas = object_store.get_item(aas_id)
437437
if not isinstance(aas, model.AssetAdministrationShell):
438438
raise TypeError(f"Identifier {aas_id} does not belong to an AssetAdministrationShell object but to "
439439
f"{aas!r}")
@@ -507,8 +507,8 @@ def write_aas_objects(self,
507507
A thin wrapper around :meth:`write_all_aas_objects` to ensure downwards compatibility
508508
509509
This method takes the AAS's :class:`~basyx.aas.model.base.Identifier` (as ``aas_id``) to retrieve it
510-
from the given object_store. If the list of written objects includes :class:`~basyx.aas.model.submodel.Submodel`
511-
objects, Supplementary files which are referenced by :class:`~basyx.aas.model.submodel.File` objects within
510+
from the given object_store. If the list of written identifiables includes :class:`~basyx.aas.model.submodel.Submodel`
511+
identifiables, Supplementary files which are referenced by :class:`~basyx.aas.model.submodel.File` identifiables within
512512
those Submodels, are also added to the AASX package.
513513
514514
.. attention::
@@ -519,44 +519,44 @@ def write_aas_objects(self,
519519
:param part_name: Name of the Part within the AASX package to write the files to. Must be a valid ECMA376-2
520520
part name and unique within the package. The extension of the part should match the data format (i.e.
521521
'.json' if ``write_json`` else '.xml').
522-
:param object_ids: A list of :class:`Identifiers <basyx.aas.model.base.Identifier>` of the objects to be written
523-
to the AASX package. Only these :class:`~basyx.aas.model.base.Identifiable` objects (and included
524-
:class:`~basyx.aas.model.base.Referable` objects) are written to the package.
525-
:param object_store: The objects store to retrieve the :class:`~basyx.aas.model.base.Identifiable` objects from
522+
:param object_ids: A list of :class:`Identifiers <basyx.aas.model.base.Identifier>` of the identifiables to be written
523+
to the AASX package. Only these :class:`~basyx.aas.model.base.Identifiable` identifiables (and included
524+
:class:`~basyx.aas.model.base.Referable` identifiables) are written to the package.
525+
:param object_store: The identifiables store to retrieve the :class:`~basyx.aas.model.base.Identifiable` identifiables from
526526
:param file_store: The
527527
:class:`SupplementaryFileContainer <basyx.aas.adapter.aasx.AbstractSupplementaryFileContainer>`
528528
to retrieve supplementary files from (if there are any :class:`~basyx.aas.model.submodel.File`
529-
objects within the written objects.
529+
identifiables within the written identifiables.
530530
:param write_json: If ``True``, the part is written as a JSON file instead of an XML file. Defaults to
531531
``False``.
532532
:param split_part: If ``True``, no aas-spec relationship is added from the aasx-origin to this part. You must
533533
make sure to reference it via an aas-spec-split relationship from another aas-spec part
534534
:param additional_relationships: Optional OPC/ECMA376 relationships which should originate at the AAS object
535535
part to be written, in addition to the aas-suppl relationships which are created automatically.
536536
"""
537-
logger.debug(f"Writing AASX part {part_name} with AAS objects ...")
537+
logger.debug(f"Writing AASX part {part_name} with AAS identifiables ...")
538538

539-
objects: model.DictObjectStore[model.Identifiable] = model.DictObjectStore()
539+
identifiables: model.DictIdentifiableStore[model.Identifiable] = model.DictIdentifiableStore()
540540

541-
# Retrieve objects and scan for referenced supplementary files
541+
# Retrieve identifiables and scan for referenced supplementary files
542542
for identifier in object_ids:
543543
try:
544-
the_object = object_store.get_identifiable(identifier)
544+
the_identifiable = object_store.get_item(identifier)
545545
except KeyError:
546546
if self.failsafe:
547-
logger.error(f"Could not find object {identifier} in ObjectStore")
547+
logger.error(f"Could not find identifiable {identifier} in IdentifiableStore")
548548
continue
549549
else:
550-
raise KeyError(f"Could not find object {identifier!r} in ObjectStore")
551-
objects.add(the_object)
550+
raise KeyError(f"Could not find identifiable {identifier!r} in IdentifiableStore")
551+
identifiables.add(the_identifiable)
552552

553-
self.write_all_aas_objects(part_name, objects, file_store, write_json, split_part, additional_relationships)
553+
self.write_all_aas_objects(part_name, identifiables, file_store, write_json, split_part, additional_relationships)
554554

555555
# TODO remove `split_part` parameter in future version.
556556
# Not required anymore since changes from DotAAS version 2.0.1 to 3.0RC01
557557
def write_all_aas_objects(self,
558558
part_name: str,
559-
objects: model.AbstractObjectStore[model.Identifiable],
559+
objects: model.AbstractObjectStore[model.Identifier, model.Identifiable],
560560
file_store: "AbstractSupplementaryFileContainer",
561561
write_json: bool = False,
562562
split_part: bool = False,

0 commit comments

Comments
 (0)