Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

make Operation a Namespace #151

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 7 additions & 5 deletions basyx/aas/adapter/json/json_deserialization.py
Original file line number Diff line number Diff line change
Expand Up @@ -364,12 +364,14 @@ def _construct_administrative_information(
return ret

@classmethod
def _construct_operation_variable(
cls, dct: Dict[str, object], object_class=model.OperationVariable) -> model.OperationVariable:
def _construct_operation_variable(cls, dct: Dict[str, object]) -> model.SubmodelElement:
"""
Since we don't implement `OperationVariable`, this constructor discards the wrapping `OperationVariable` object
and just returns the contained :class:`~aas.model.submodel.SubmodelElement`.
"""
# TODO: remove the following type: ignore comments when mypy supports abstract types for Type[T]
# see https://github.com/python/mypy/issues/5374
ret = object_class(value=_get_ts(dct, 'value', model.SubmodelElement)) # type: ignore
return ret
return _get_ts(dct, 'value', model.SubmodelElement) # type: ignore

@classmethod
def _construct_lang_string_set(cls, lst: List[Dict[str, object]], object_class: Type[LSS]) -> LSS:
Expand Down Expand Up @@ -590,7 +592,7 @@ def _construct_operation(cls, dct: Dict[str, object], object_class=model.Operati
if json_name in dct:
for variable_data in _get_ts(dct, json_name, list):
try:
target.append(cls._construct_operation_variable(variable_data))
target.add(cls._construct_operation_variable(variable_data))
except (KeyError, TypeError) as e:
error_message = "Error while trying to convert JSON object into {} of {}: {}".format(
json_name, ret, pprint.pformat(variable_data, depth=2, width=2 ** 14, compact=True))
Expand Down
27 changes: 13 additions & 14 deletions basyx/aas/adapter/json/json_serialization.py
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,6 @@ def default(self, obj: object) -> object:
model.LangStringSet: self._lang_string_set_to_json,
model.MultiLanguageProperty: self._multi_language_property_to_json,
model.Operation: self._operation_to_json,
model.OperationVariable: self._operation_variable_to_json,
model.Property: self._property_to_json,
model.Qualifier: self._qualifier_to_json,
model.Range: self._range_to_json,
Expand Down Expand Up @@ -576,16 +575,17 @@ def _annotated_relationship_element_to_json(cls, obj: model.AnnotatedRelationshi
return data

@classmethod
def _operation_variable_to_json(cls, obj: model.OperationVariable) -> Dict[str, object]:
def _operation_variable_to_json(cls, obj: model.SubmodelElement) -> Dict[str, object]:
"""
serialization of an object from class OperationVariable to json
serialization of an object from class SubmodelElement to a json OperationVariable representation
Since we don't implement the `OperationVariable` class, which is just a wrapper for a single
:class:`~aas.model.submodel.SubmodelElement`, elements are serialized as the `value` attribute of an
`operationVariable` object.

:param obj: object of class OperationVariable
:return: dict with the serialized attributes of this object
:param obj: object of class `SubmodelElement`
:return: `OperationVariable` wrapper containing the serialized `SubmodelElement`
"""
data = cls._abstract_classes_to_json(obj)
data['value'] = obj.value
return data
return {'value': obj}

@classmethod
def _operation_to_json(cls, obj: model.Operation) -> Dict[str, object]:
Expand All @@ -596,12 +596,11 @@ def _operation_to_json(cls, obj: model.Operation) -> Dict[str, object]:
:return: dict with the serialized attributes of this object
"""
data = cls._abstract_classes_to_json(obj)
if obj.input_variable:
data['inputVariables'] = list(obj.input_variable)
if obj.output_variable:
data['outputVariables'] = list(obj.output_variable)
if obj.in_output_variable:
data['inoutputVariables'] = list(obj.in_output_variable)
for tag, nss in (('inputVariables', obj.input_variable),
('outputVariables', obj.output_variable),
('inoutputVariables', obj.in_output_variable)):
if nss:
data[tag] = [cls._operation_variable_to_json(obj) for obj in nss]
return data

@classmethod
Expand Down
53 changes: 22 additions & 31 deletions basyx/aas/adapter/xml/xml_deserialization.py
Original file line number Diff line number Diff line change
Expand Up @@ -533,6 +533,20 @@ def _construct_referable_reference(cls, element: etree.Element, **kwargs: Any) \
# see https://github.com/python/mypy/issues/5374
return cls.construct_model_reference_expect_type(element, model.Referable, **kwargs) # type: ignore

@classmethod
def _construct_operation_variable(cls, element: etree.Element, **kwargs: Any) -> model.SubmodelElement:
"""
Since we don't implement `OperationVariable`, this constructor discards the wrapping `aas:operationVariable`
and `aas:value` and just returns the contained :class:`~aas.model.submodel.SubmodelElement`.
"""
value = _get_child_mandatory(element, NS_AAS + "value")
if len(value) == 0:
raise KeyError(f"{_element_pretty_identifier(value)} has no submodel element!")
if len(value) > 1:
logger.warning(f"{_element_pretty_identifier(value)} has more than one submodel element, "
"using the first one...")
return cls.construct_submodel_element(value[0], **kwargs)

@classmethod
def construct_key(cls, element: etree.Element, object_class=model.Key, **_kwargs: Any) \
-> model.Key:
Expand Down Expand Up @@ -722,19 +736,6 @@ def construct_data_element(cls, element: etree.Element, abstract_class_name: str
raise KeyError(_element_pretty_identifier(element) + f" is not a valid {abstract_class_name}!")
return data_elements[element.tag](element, **kwargs)

@classmethod
def construct_operation_variable(cls, element: etree.Element, object_class=model.OperationVariable,
**_kwargs: Any) -> model.OperationVariable:
value = _get_child_mandatory(element, NS_AAS + "value")
if len(value) == 0:
raise KeyError(f"{_element_pretty_identifier(value)} has no submodel element!")
if len(value) > 1:
logger.warning(f"{_element_pretty_identifier(value)} has more than one submodel element, "
"using the first one...")
return object_class(
_failsafe_construct_mandatory(value[0], cls.construct_submodel_element)
)

@classmethod
def construct_annotated_relationship_element(cls, element: etree.Element,
object_class=model.AnnotatedRelationshipElement, **_kwargs: Any) \
Expand Down Expand Up @@ -856,21 +857,14 @@ def construct_multi_language_property(cls, element: etree.Element, object_class=
def construct_operation(cls, element: etree.Element, object_class=model.Operation, **_kwargs: Any) \
-> model.Operation:
operation = object_class(None)
input_variables = element.find(NS_AAS + "inputVariables")
if input_variables is not None:
for input_variable in _child_construct_multiple(input_variables, NS_AAS + "operationVariable",
cls.construct_operation_variable, cls.failsafe):
operation.input_variable.append(input_variable)
output_variables = element.find(NS_AAS + "outputVariables")
if output_variables is not None:
for output_variable in _child_construct_multiple(output_variables, NS_AAS + "operationVariable",
cls.construct_operation_variable, cls.failsafe):
operation.output_variable.append(output_variable)
in_output_variables = element.find(NS_AAS + "inoutputVariables")
if in_output_variables is not None:
for in_output_variable in _child_construct_multiple(in_output_variables, NS_AAS + "operationVariable",
cls.construct_operation_variable, cls.failsafe):
operation.in_output_variable.append(in_output_variable)
for tag, target in ((NS_AAS + "inputVariables", operation.input_variable),
(NS_AAS + "outputVariables", operation.output_variable),
(NS_AAS + "inoutputVariables", operation.in_output_variable)):
variables = element.find(tag)
if variables is not None:
for var in _child_construct_multiple(variables, NS_AAS + "operationVariable",
cls._construct_operation_variable, cls.failsafe):
target.add(var)
cls._amend_abstract_attributes(operation, element)
return operation

Expand Down Expand Up @@ -1236,7 +1230,6 @@ class XMLConstructables(enum.Enum):
ADMINISTRATIVE_INFORMATION = enum.auto()
QUALIFIER = enum.auto()
SECURITY = enum.auto()
OPERATION_VARIABLE = enum.auto()
ANNOTATED_RELATIONSHIP_ELEMENT = enum.auto()
BASIC_EVENT_ELEMENT = enum.auto()
BLOB = enum.auto()
Expand Down Expand Up @@ -1306,8 +1299,6 @@ def read_aas_xml_element(file: IO, construct: XMLConstructables, failsafe: bool
constructor = decoder_.construct_administrative_information
elif construct == XMLConstructables.QUALIFIER:
constructor = decoder_.construct_qualifier
elif construct == XMLConstructables.OPERATION_VARIABLE:
constructor = decoder_.construct_operation_variable
elif construct == XMLConstructables.ANNOTATED_RELATIONSHIP_ELEMENT:
constructor = decoder_.construct_annotated_relationship_element
elif construct == XMLConstructables.BASIC_EVENT_ELEMENT:
Expand Down
35 changes: 15 additions & 20 deletions basyx/aas/adapter/xml/xml_serialization.py
Original file line number Diff line number Diff line change
Expand Up @@ -726,18 +726,20 @@ def annotated_relationship_element_to_xml(obj: model.AnnotatedRelationshipElemen
return et_annotated_relationship_element


def operation_variable_to_xml(obj: model.OperationVariable,
tag: str = NS_AAS+"operationVariable") -> etree.Element:
def operation_variable_to_xml(obj: model.SubmodelElement, tag: str = NS_AAS+"operationVariable") -> etree.Element:
"""
Serialization of objects of class :class:`~aas.model.submodel.OperationVariable` to XML
Serialization of :class:`~aas.model.submodel.SubmodelElement` to the XML OperationVariable representation
Since we don't implement the `OperationVariable` class, which is just a wrapper for a single
:class:`~aas.model.submodel.SubmodelElement`, elements are serialized as the `aas:value` child of an
`aas:operationVariable` element.

:param obj: Object of class :class:`~aas.model.submodel.OperationVariable`
:param obj: Object of class :class:`~aas.model.submodel.SubmodelElement`
:param tag: Namespace+Tag of the serialized element (optional). Default is "aas:operationVariable"
:return: Serialized ElementTree object
"""
et_operation_variable = _generate_element(tag)
et_value = _generate_element(NS_AAS+"value")
et_value.append(submodel_element_to_xml(obj.value))
et_value.append(submodel_element_to_xml(obj))
et_operation_variable.append(et_value)
return et_operation_variable

Expand All @@ -752,21 +754,14 @@ def operation_to_xml(obj: model.Operation,
:return: Serialized ElementTree object
"""
et_operation = abstract_classes_to_xml(tag, obj)
if obj.input_variable:
et_input_variables = _generate_element(NS_AAS+"inputVariables")
for input_ov in obj.input_variable:
et_input_variables.append(operation_variable_to_xml(input_ov, NS_AAS+"operationVariable"))
et_operation.append(et_input_variables)
if obj.output_variable:
et_output_variables = _generate_element(NS_AAS+"outputVariables")
for output_ov in obj.output_variable:
et_output_variables.append(operation_variable_to_xml(output_ov, NS_AAS+"operationVariable"))
et_operation.append(et_output_variables)
if obj.in_output_variable:
et_inoutput_variables = _generate_element(NS_AAS+"inoutputVariables")
for in_out_ov in obj.in_output_variable:
et_inoutput_variables.append(operation_variable_to_xml(in_out_ov, NS_AAS+"operationVariable"))
et_operation.append(et_inoutput_variables)
for tag, nss in ((NS_AAS+"inputVariables", obj.input_variable),
(NS_AAS+"outputVariables", obj.output_variable),
(NS_AAS+"inoutputVariables", obj.in_output_variable)):
if nss:
et_variables = _generate_element(tag)
for submodel_element in nss:
et_variables.append(operation_variable_to_xml(submodel_element))
et_operation.append(et_variables)
return et_operation


Expand Down
30 changes: 7 additions & 23 deletions basyx/aas/examples/data/_helper.py
Original file line number Diff line number Diff line change
Expand Up @@ -550,17 +550,6 @@ def _find_extra_elements_by_id_short(self, object_list: model.NamespaceSet, sear
found_elements.add(object_list_element)
return found_elements

def _check_operation_variable_equal(self, object_: model.OperationVariable,
expected_value: model.OperationVariable):
"""
Checks if the given OperationVariable objects are equal

:param object_: Given OperationVariable object to check
:param expected_value: expected OperationVariable object
:return:
"""
self._check_submodel_element(object_.value, expected_value.value)

def check_operation_equal(self, object_: model.Operation, expected_value: model.Operation):
"""
Checks if the given Operation objects are equal
Expand All @@ -570,18 +559,13 @@ def check_operation_equal(self, object_: model.Operation, expected_value: model.
:return:
"""
self._check_abstract_attributes_submodel_element_equal(object_, expected_value)
self.check_contained_element_length(object_, 'input_variable', model.OperationVariable,
len(expected_value.input_variable))
self.check_contained_element_length(object_, 'output_variable', model.OperationVariable,
len(expected_value.output_variable))
self.check_contained_element_length(object_, 'in_output_variable', model.OperationVariable,
len(expected_value.in_output_variable))
for iv1, iv2 in zip(object_.input_variable, expected_value.input_variable):
self._check_operation_variable_equal(iv1, iv2)
for ov1, ov2 in zip(object_.output_variable, expected_value.output_variable):
self._check_operation_variable_equal(ov1, ov2)
for iov1, iov2 in zip(object_.in_output_variable, expected_value.in_output_variable):
self._check_operation_variable_equal(iov1, iov2)
for input_nss, expected_nss, attr_name in (
(object_.input_variable, expected_value.input_variable, 'input_variable'),
(object_.output_variable, expected_value.output_variable, 'output_variable'),
(object_.in_output_variable, expected_value.in_output_variable, 'in_output_variable')):
self.check_contained_element_length(object_, attr_name, model.SubmodelElement, len(expected_nss))
for var1, var2 in zip(input_nss, expected_nss):
self._check_submodel_element(var1, var2)

def check_capability_equal(self, object_: model.Capability, expected_value: model.Capability):
"""
Expand Down
57 changes: 44 additions & 13 deletions basyx/aas/examples/data/example_aas.py
Original file line number Diff line number Diff line change
Expand Up @@ -557,8 +557,8 @@ def create_example_submodel() -> model.Submodel:
embedded_data_specifications=()
)

operation_variable_property = model.Property(
id_short='ExampleProperty',
input_variable_property = model.Property(
id_short='ExamplePropertyInput',
value_type=model.datatypes.String,
value='exampleValue',
value_id=model.ExternalReference((model.Key(type_=model.KeyTypes.GLOBAL_REFERENCE,
Expand All @@ -570,27 +570,58 @@ 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://acplt.org/Properties/ExamplePropertyInput'),)),
qualifier=(),
extension=(),
supplemental_semantic_id=(),
embedded_data_specifications=()
)

submodel_element_operation_variable_input = model.OperationVariable(
value=operation_variable_property)

submodel_element_operation_variable_output = model.OperationVariable(
value=operation_variable_property)
output_variable_property = model.Property(
id_short='ExamplePropertyOutput',
value_type=model.datatypes.String,
value='exampleValue',
value_id=model.ExternalReference((model.Key(type_=model.KeyTypes.GLOBAL_REFERENCE,
value='http://acplt.org/ValueId/ExampleValueId'),)),
display_name=model.MultiLanguageNameType({'en-US': 'ExampleProperty',
'de': 'BeispielProperty'}),
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/ExamplePropertyOutput'),)),
qualifier=(),
extension=(),
supplemental_semantic_id=(),
embedded_data_specifications=()
)

submodel_element_operation_variable_in_output = model.OperationVariable(
value=operation_variable_property)
in_output_variable_property = model.Property(
id_short='ExamplePropertyInOutput',
value_type=model.datatypes.String,
value='exampleValue',
value_id=model.ExternalReference((model.Key(type_=model.KeyTypes.GLOBAL_REFERENCE,
value='http://acplt.org/ValueId/ExampleValueId'),)),
display_name=model.MultiLanguageNameType({'en-US': 'ExampleProperty',
'de': 'BeispielProperty'}),
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/ExamplePropertyInOutput'),)),
qualifier=(),
extension=(),
supplemental_semantic_id=(),
embedded_data_specifications=()
)

submodel_element_operation = model.Operation(
id_short='ExampleOperation',
input_variable=[submodel_element_operation_variable_input],
output_variable=[submodel_element_operation_variable_output],
in_output_variable=[submodel_element_operation_variable_in_output],
input_variable=[input_variable_property],
output_variable=[output_variable_property],
in_output_variable=[in_output_variable_property],
category='PARAMETER',
description=model.MultiLanguageTextType({'en-US': 'Example Operation object',
'de': 'Beispiel Operation Element'}),
Expand Down
Loading
Loading