Skip to content

Commit

Permalink
Merge pull request eclipse-basyx#174 from eclipse-basyx/improve/V30
Browse files Browse the repository at this point in the history
Implement v3.0 of the metamodel specification

For the detailed documentation of the changes, please refer to the [prior PRs](https://github.com/eclipse-basyx/basyx-python-sdk/pulls?page=4&q=is%3Apr+base%3Aimprove%2FV30)
  • Loading branch information
s-heppner committed Nov 25, 2023
2 parents b181e1c + 9d8312c commit f17bcff
Show file tree
Hide file tree
Showing 56 changed files with 17,990 additions and 5,815 deletions.
22 changes: 15 additions & 7 deletions compliance-tool/basyx/aas/compliance_tool/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,7 @@ def parse_cli_arguments() -> argparse.ArgumentParser:
group.add_argument('--xml', help="Use AAS xml format when checking or creating files", action='store_true')
parser.add_argument('-l', '--logfile', help="Log file to be created in addition to output to stdout", default=None)
parser.add_argument('--aasx', help="Create or read AASX files", action='store_true')
parser.add_argument('--dont-check-extensions', help="Don't compare Extensions", action='store_false')
return parser


Expand All @@ -96,6 +97,10 @@ def main():
logger.propagate = False
logger.addHandler(manager)

data_checker_kwargs = {
'check_extensions': args.dont_check_extensions
}

if args.action == 'create' or args.action == 'c':
manager.add_step('Create example data')
if args.aasx:
Expand Down Expand Up @@ -126,7 +131,7 @@ def main():
cp.title = "Test Title"

writer.write_aas_objects("/aasx/data.json" if args.json else "/aasx/data.xml",
[obj.identification for obj in data], data, files,
[obj.id for obj in data], data, files,
write_json=args.json)
writer.write_core_properties(cp)
manager.set_step_status(Status.SUCCESS)
Expand Down Expand Up @@ -154,19 +159,22 @@ def main():
compliance_tool_xml.check_deserialization(args.file_1, manager)
elif args.action == 'example' or args.action == 'e':
if args.aasx:
compliance_tool_aasx.check_aas_example(args.file_1, manager)
compliance_tool_aasx.check_aas_example(args.file_1, manager, **data_checker_kwargs)
elif args.json:
compliance_tool_json.check_aas_example(args.file_1, manager)
compliance_tool_json.check_aas_example(args.file_1, manager, **data_checker_kwargs)
elif args.xml:
compliance_tool_xml.check_aas_example(args.file_1, manager)
compliance_tool_xml.check_aas_example(args.file_1, manager, **data_checker_kwargs)
elif args.action == 'files' or args.action == 'f':
if args.file_2:
if args.aasx:
compliance_tool_aasx.check_aasx_files_equivalence(args.file_1, args.file_2, manager)
compliance_tool_aasx.check_aasx_files_equivalence(args.file_1, args.file_2, manager,
**data_checker_kwargs)
elif args.json:
compliance_tool_json.check_json_files_equivalence(args.file_1, args.file_2, manager)
compliance_tool_json.check_json_files_equivalence(args.file_1, args.file_2, manager,
**data_checker_kwargs)
elif args.xml:
compliance_tool_xml.check_xml_files_equivalence(args.file_1, args.file_2, manager)
compliance_tool_xml.check_xml_files_equivalence(args.file_1, args.file_2, manager,
**data_checker_kwargs)
else:
parser.error("f or files requires two file path.")
exit()
Expand Down
17 changes: 10 additions & 7 deletions compliance-tool/basyx/aas/compliance_tool/compliance_check_aasx.py
Original file line number Diff line number Diff line change
Expand Up @@ -158,7 +158,7 @@ def check_schema(file_path: str, state_manager: ComplianceToolStateManager) -> N
reader.close()


def check_aas_example(file_path: str, state_manager: ComplianceToolStateManager) -> None:
def check_aas_example(file_path: str, state_manager: ComplianceToolStateManager, **kwargs) -> None:
"""
Checks if a file contains all elements of the aas example and reports any issues using the given
:class:`~aas.compliance_tool.state_manager.ComplianceToolStateManager`
Expand All @@ -168,6 +168,7 @@ def check_aas_example(file_path: str, state_manager: ComplianceToolStateManager)
:param file_path: Given file which should be checked
:param state_manager: :class:`~aas.compliance_tool.state_manager.ComplianceToolStateManager` to log the steps
:param kwargs: Additional arguments to pass to :class:`~aas.examples.data._helper.AASDataChecker`
"""
logger = logging.getLogger('compliance_check')
logger.addHandler(state_manager)
Expand All @@ -189,7 +190,7 @@ def check_aas_example(file_path: str, state_manager: ComplianceToolStateManager)
state_manager.set_step_status(Status.NOT_EXECUTED)
return

checker = AASDataChecker(raise_immediately=False)
checker = AASDataChecker(raise_immediately=False, **kwargs)

state_manager.add_step('Check if data is equal to example data')
example_data = create_example_aas_binding()
Expand Down Expand Up @@ -243,11 +244,11 @@ def check_aas_example(file_path: str, state_manager: ComplianceToolStateManager)
checker2.check(cp_new.title == cp.title, "title must be {}".format(cp.title), title=cp_new.title)

# Check if file in file object is the same
list_of_id_shorts = ["ExampleSubmodelCollectionUnordered", "ExampleFile"]
obj = example_data.get_identifiable(model.Identifier("https://acplt.org/Test_Submodel", model.IdentifierType.IRI))
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(model.Identifier("https://acplt.org/Test_Submodel", model.IdentifierType.IRI))
obj2 = obj_store.get_identifiable("https://acplt.org/Test_Submodel")
for id_short in list_of_id_shorts:
obj2 = obj2.get_referable(id_short)
try:
Expand All @@ -267,7 +268,8 @@ def check_aas_example(file_path: str, state_manager: ComplianceToolStateManager)
state_manager.set_step_status(Status.SUCCESS)


def check_aasx_files_equivalence(file_path_1: str, file_path_2: str, state_manager: ComplianceToolStateManager) -> None:
def check_aasx_files_equivalence(file_path_1: str, file_path_2: str, state_manager: ComplianceToolStateManager,
**kwargs) -> None:
"""
Checks if two aasx files contain the same elements in any order and reports any issues using the given
:class:`~aas.compliance_tool.state_manager.ComplianceToolStateManager`
Expand All @@ -278,6 +280,7 @@ def check_aasx_files_equivalence(file_path_1: str, file_path_2: str, state_manag
:param file_path_1: Given first file which should be checked
:param file_path_2: Given second file which should be checked
:param state_manager: :class:`~aas.compliance_tool.state_manager.ComplianceToolStateManager` to log the steps
:param kwargs: Additional arguments to pass to :class:`~aas.examples.data._helper.AASDataChecker`
"""
logger = logging.getLogger('compliance_check')
logger.addHandler(state_manager)
Expand All @@ -295,7 +298,7 @@ def check_aasx_files_equivalence(file_path_1: str, file_path_2: str, state_manag
state_manager.set_step_status(Status.NOT_EXECUTED)
return

checker = AASDataChecker(raise_immediately=False)
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)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -162,7 +162,7 @@ def check_deserialization(file_path: str, state_manager: ComplianceToolStateMana
return obj_store


def check_aas_example(file_path: str, state_manager: ComplianceToolStateManager) -> None:
def check_aas_example(file_path: str, state_manager: ComplianceToolStateManager, **kwargs) -> None:
"""
Checks if a file contains all elements of the aas example and reports any issues using the given
:class:`~aas.compliance_tool.state_manager.ComplianceToolStateManager`
Expand All @@ -172,6 +172,7 @@ def check_aas_example(file_path: str, state_manager: ComplianceToolStateManager)
:param file_path: Given file which should be checked
:param state_manager: :class:`~aas.compliance_tool.state_manager.ComplianceToolStateManager` to log the steps
:param kwargs: Additional arguments to pass to :class:`~aas.examples.data._helper.AASDataChecker`
"""
# create handler to get logger info
logger_example = logging.getLogger(example_aas.__name__)
Expand All @@ -186,15 +187,16 @@ def check_aas_example(file_path: str, state_manager: ComplianceToolStateManager)
state_manager.set_step_status(Status.NOT_EXECUTED)
return

checker = AASDataChecker(raise_immediately=False)
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())

state_manager.add_log_records_from_data_checker(checker)


def check_json_files_equivalence(file_path_1: str, file_path_2: str, state_manager: ComplianceToolStateManager) -> None:
def check_json_files_equivalence(file_path_1: str, file_path_2: str, state_manager: ComplianceToolStateManager,
**kwargs) -> None:
"""
Checks if two json files contain the same elements in any order and reports any issues using the given
:class:`~aas.compliance_tool.state_manager.ComplianceToolStateManager`
Expand All @@ -205,6 +207,7 @@ def check_json_files_equivalence(file_path_1: str, file_path_2: str, state_manag
:param file_path_1: Given first file which should be checked
:param file_path_2: Given second file which should be checked
:param state_manager: :class:`~aas.compliance_tool.state_manager.ComplianceToolStateManager` to log the steps
:param kwargs: Additional arguments to pass to :class:`~aas.examples.data._helper.AASDataChecker`
"""
logger = logging.getLogger('compliance_check')
logger.addHandler(state_manager)
Expand All @@ -220,7 +223,7 @@ def check_json_files_equivalence(file_path_1: str, file_path_2: str, state_manag
state_manager.set_step_status(Status.NOT_EXECUTED)
return

checker = AASDataChecker(raise_immediately=False)
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)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -162,7 +162,7 @@ def check_deserialization(file_path: str, state_manager: ComplianceToolStateMana
return obj_store


def check_aas_example(file_path: str, state_manager: ComplianceToolStateManager) -> None:
def check_aas_example(file_path: str, state_manager: ComplianceToolStateManager, **kwargs) -> None:
"""
Checks if a file contains all elements of the aas example and reports any issues using the given
:class:`~aas.compliance_tool.state_manager.ComplianceToolStateManager`
Expand All @@ -172,6 +172,7 @@ def check_aas_example(file_path: str, state_manager: ComplianceToolStateManager)
:param file_path: Given file which should be checked
:param state_manager: :class:`~aas.compliance_tool.state_manager.ComplianceToolStateManager` to log the steps
:param kwargs: Additional arguments to pass to :class:`~aas.examples.data._helper.AASDataChecker`
"""
# create handler to get logger info
logger_example = logging.getLogger(example_aas.__name__)
Expand All @@ -186,15 +187,16 @@ def check_aas_example(file_path: str, state_manager: ComplianceToolStateManager)
state_manager.set_step_status(Status.NOT_EXECUTED)
return

checker = AASDataChecker(raise_immediately=False)
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())

state_manager.add_log_records_from_data_checker(checker)


def check_xml_files_equivalence(file_path_1: str, file_path_2: str, state_manager: ComplianceToolStateManager) -> None:
def check_xml_files_equivalence(file_path_1: str, file_path_2: str, state_manager: ComplianceToolStateManager,
**kwargs) -> None:
"""
Checks if two xml files contain the same elements in any order and reports any issues using the given
:class:`~aas.compliance_tool.state_manager.ComplianceToolStateManager`
Expand All @@ -205,6 +207,7 @@ def check_xml_files_equivalence(file_path_1: str, file_path_2: str, state_manage
:param file_path_1: Given first file which should be checked
:param file_path_2: Given second file which should be checked
:param state_manager: :class:`~aas.compliance_tool.state_manager.ComplianceToolStateManager` to log the steps
:param kwargs: Additional arguments to pass to :class:`~aas.examples.data._helper.AASDataChecker`
"""
logger = logging.getLogger('compliance_check')
logger.addHandler(state_manager)
Expand All @@ -220,7 +223,7 @@ def check_xml_files_equivalence(file_path_1: str, file_path_2: str, state_manage
state_manager.set_step_status(Status.NOT_EXECUTED)
return

checker = AASDataChecker(raise_immediately=False)
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)
Expand Down
14 changes: 11 additions & 3 deletions compliance-tool/basyx/aas/compliance_tool/state_manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,14 @@

@enum.unique
class Status(enum.IntEnum):
"""
Possible Status States:
:cvar SUCCESS:
:cvar SUCCESS_WITH_WARNINGS:
:cvar FAILED:
:cvar NOT_EXECUTED:
"""
SUCCESS = 0
SUCCESS_WITH_WARNINGS = 1
FAILED = 2
Expand All @@ -25,11 +33,11 @@ class Status(enum.IntEnum):

class Step:
"""
A step represents a single test stage in a test protocol of a ComplianceToolStateManager
A step represents a single test stage in a test protocol of a :class:`~.ComplianceToolStateManager`
:ivar name: Name of the step
:ivar ~.status: status of the step from type Status
:ivar log_list: list of LogRecords which belong to this step
:ivar ~.status: Status of the step from type Status
:ivar log_list: List of `logging.LogRecords` which belong to this step
"""
def __init__(self, name: str, status: Status, log_list: List[logging.LogRecord]):
self.name = name
Expand Down
28 changes: 28 additions & 0 deletions compliance-tool/test/compliance_tool/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
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()
Binary file not shown.
Loading

0 comments on commit f17bcff

Please sign in to comment.