From 8ffe1992e4e8f54ef1c61a85825748f6d2a87ae8 Mon Sep 17 00:00:00 2001 From: cogu Date: Wed, 8 Nov 2023 21:44:20 +0100 Subject: [PATCH] Implement ConstantSpecification, ConstantReference --- CHANGELOG.md | 10 +- README.md | 8 +- src/autosar/xml/element.py | 74 ++++++++++- src/autosar/xml/enumeration.py | 29 +++-- src/autosar/xml/reader.py | 230 ++++++++++++++++++++++----------- src/autosar/xml/writer.py | 61 ++++++++- tests/xml/test_constant.py | 81 ++++++++++++ 7 files changed, 398 insertions(+), 95 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 068e8f0..95a3abf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,20 +17,26 @@ The name in the parenthesis after each element is the name used in the XML schem #### XML - Calibration elements -* SwAValueCont | SW-VALUE-CONT +* SwValueCont | SW-VALUE-CONT * SwAxisCont | SW-AXIS-CONT * SwValues | SW-VALUES * ValueGroup | VALUE-GROUP -#### XML - Value specification elements +#### XML - Constant and Value specification elements +* ConstantSpecification | CONSTANT-SPECIFICATION | `collectable` * ApplicationValueSpecification | APPLICATION-VALUE-SPECIFICATION * ArrayValueSpecification | ARRAY-VALUE-SPECIFICATION +* ConstantReference | CONSTANT-REFERENCE * NotAvailableValueSpecification | NOT-AVAILABLE-VALUE-SPECIFICATION * NumericalValueSpecification | NUMERICAL-VALUE-SPECIFICATION * RecordValueSpecification | RECORD-VALUE-SPECIFICATION * TextValueSpecification | TEXT-VALUE-SPECIFICATION +#### XML - Reference elements + +* ConstantRef (different class from ConstantReference) + ## [v0.5.0] - 2023-10-27 ### Added diff --git a/README.md b/README.md index ce6e330..9b34dd9 100644 --- a/README.md +++ b/README.md @@ -9,9 +9,11 @@ It also has some support for parsing AUTOSAR XML files. **Important notes:** -1. Python AUTOSAR v0.5+ uses a new API and is incompatible with previous versions. -2. Currently, only AUTOSAR data types are supported. If you want a full API, wait for v0.6.0. -3. For Python AUTOSAR v0.4, see the [v0.4 maintenance branch](https://github.com/cogu/autosar/tree/maintenance/0.4). +1. Python AUTOSAR v0.5+ uses a new API and is incompatible with earlier versions. +2. For Python AUTOSAR v0.4, see the [v0.4 maintenance branch](https://github.com/cogu/autosar/tree/maintenance/0.4). +3. Currently, only the categories mentioned below are supported. If you want a full API, wait for v0.6.0: + * Data Types + * Constants ## Major design changes diff --git a/src/autosar/xml/element.py b/src/autosar/xml/element.py index 874af64..0c9b1f1 100644 --- a/src/autosar/xml/element.py +++ b/src/autosar/xml/element.py @@ -21,7 +21,11 @@ ValueSpeficationElement = Union["TextValueSpecification", "NumericalValueSpecification", - "NotAvailableValueSpecification"] + "NotAvailableValueSpecification", + "ArrayValueSpecification", + "RecordValueSpecification", + "ApplicationValueSpecification", + "ConstantReference"] # Helper classes @@ -533,6 +537,18 @@ def _accepted_subtypes(self) -> set[ar_enum.IdentifiableSubTypes]: ar_enum.IdentifiableSubTypes.IMPLEMENTATION_DATA_TYPE} +class ConstantRef(BaseRef): + """ + Reference to ConstantSpecification + """ + + def __init__(self, value: str) -> None: + super().__init__(value, ar_enum.IdentifiableSubTypes.CONSTANT_SPECIFICATION) + + def _accepted_subtypes(self) -> set[ar_enum.IdentifiableSubTypes]: + """Acceptable values for dest""" + return {ar_enum.IdentifiableSubTypes.CONSTANT_SPECIFICATION} + # Documentation Elements @@ -2703,6 +2719,62 @@ def __init__(self, raise TypeError(error_msg + f" Got {str(type(sw_axis_conts))}") +class ConstantSpecification(ARElement): + """ + Complex-type AR:CONSTANT-SPECIFICATION + Type: Concrete + Tag Variants: 'CONSTANT-SPECIFICATION' + """ + + def __init__(self, name: str, value: ValueSpeficationElement | None = None, **kwargs) -> None: + super().__init__(name, **kwargs) + self.value: ValueSpeficationElement = None # .VALUE-SPEC + if value is not None: + if isinstance(value, ValueSpecification): + self.value = value + else: + error_msg = "Invalid type for parameter 'value'. Expected a subclass of ValueSpecification," + raise TypeError(error_msg + f" got {str(type(value))}") + + def ref(self) -> ConstantRef: + """ + Reference + """ + assert self.parent is not None + ref_parts: list[str] = [self.name] + self.parent.update_ref_parts(ref_parts) + value = '/'.join(reversed(ref_parts)) + return ConstantRef(value) + + @classmethod + def make_constant(cls, + name: str, + value: tuple[str, Any] | Any, + **kwargs) -> "ConstantSpecification": + """ + Creates a new constant object and populates it from Python data. + """ + value = ValueSpecification.make_value(value) + return cls(name, value, **kwargs) + + +class ConstantReference(ValueSpecification): + """ + Complex type AR:CONSTANT-REFERENCE + Type: Concrete + Tag variants 'CONSTANT-REFERENCE' + + It's easy to confuse this with the ConstantRef class. + This class is just a wrapper around an instance of ConstantRef. + """ + + def __init__(self, + label: str | None = None, + constant_ref: ConstantRef | None = None) -> None: + self.constant_ref: ConstantRef = None + super().__init__(label) + self._assign_optional_strict("constant_ref", constant_ref, ConstantRef) + # !!UNFINISHED!! Port Interfaces diff --git a/src/autosar/xml/enumeration.py b/src/autosar/xml/enumeration.py index 9bc5c7f..a5df724 100644 --- a/src/autosar/xml/enumeration.py +++ b/src/autosar/xml/enumeration.py @@ -146,12 +146,13 @@ class IdentifiableSubTypes(Enum): AUTOSAR_DATA_TYPE = 8 BSW_MODULE_ENTRY = 9 COMPU_METHOD = 10 - DATA_CONSTR = 11 - IMPLEMENTATION_DATA_TYPE = 12 - PHYSICAL_DIMENSION = 13 - SW_ADDR_METHOD = 14 - SW_BASE_TYPE = 15 - UNIT = 16 + CONSTANT_SPECIFICATION = 11 + DATA_CONSTR = 12 + IMPLEMENTATION_DATA_TYPE = 13 + PHYSICAL_DIMENSION = 14 + SW_ADDR_METHOD = 15 + SW_BASE_TYPE = 16 + UNIT = 17 class IntervalType(Enum): @@ -538,12 +539,13 @@ class VersionedTextValue: "AUTOSAR-DATA-TYPE": IdentifiableSubTypes.AUTOSAR_DATA_TYPE, "BSW-MODULE-ENTRY": IdentifiableSubTypes.BSW_MODULE_ENTRY, "COMPU-METHOD": IdentifiableSubTypes.COMPU_METHOD, + "CONSTANT-SPECIFICATION": IdentifiableSubTypes.CONSTANT_SPECIFICATION, "DATA-CONSTR": IdentifiableSubTypes.DATA_CONSTR, "IMPLEMENTATION-DATA-TYPE": IdentifiableSubTypes.IMPLEMENTATION_DATA_TYPE, - "UNIT": IdentifiableSubTypes.UNIT, "PHYSICAL-DIMENSION": IdentifiableSubTypes.PHYSICAL_DIMENSION, "SW-ADDR-METHOD": IdentifiableSubTypes.SW_ADDR_METHOD, "SW-BASE-TYPE": IdentifiableSubTypes.SW_BASE_TYPE, + "UNIT": IdentifiableSubTypes.UNIT, }, "IntervalType": { "CLOSED": IntervalType.CLOSED, @@ -805,12 +807,13 @@ def xml_to_enum(enum_type_name: str, xml_text: str, schema_version: int = ar_bas "AUTOSAR-DATA-TYPE", # 8 "BSW-MODULE-ENTRY", # 9 "COMPU-METHOD", # 10 - "DATA-CONSTR", # 11 - "IMPLEMENTATION-DATA-TYPE", # 12 - "PHYSICAL-DIMENSION", # 13 - "SW-ADDR-METHOD", # 14 - "SW-BASE-TYPE", # 15 - "UNIT", # 16 + "CONSTANT-SPECIFICATION", # 11 + "DATA-CONSTR", # 12 + "IMPLEMENTATION-DATA-TYPE", # 13 + "PHYSICAL-DIMENSION", # 14 + "SW-ADDR-METHOD", # 15 + "SW-BASE-TYPE", # 16 + "UNIT", # 17 ], "IntervalType": [ "CLOSED", # 0 diff --git a/src/autosar/xml/reader.py b/src/autosar/xml/reader.py index 45f4442..788fb8a 100644 --- a/src/autosar/xml/reader.py +++ b/src/autosar/xml/reader.py @@ -5,7 +5,7 @@ import re import sys -from typing import Iterable, Any +from typing import Iterable, Any, Union import lxml.etree as ElementTree import autosar.base as ar_base import autosar.xml.document as ar_document @@ -17,6 +17,14 @@ MultiLanguageOverviewParagraph = ar_element.MultiLanguageOverviewParagraph +ValueSpeficationElement = Union[ar_element.TextValueSpecification, + ar_element.NumericalValueSpecification, + ar_element.NotAvailableValueSpecification, + ar_element.ArrayValueSpecification, + ar_element.RecordValueSpecification, + ar_element.ApplicationValueSpecification, + ar_element.ConstantReference] + # Helper classes @@ -118,6 +126,9 @@ def __init__(self, # Unit elements 'UNIT': self._read_unit, + + # Constant elements + 'CONSTANT-SPECIFICATION': self._read_constant_specification, } # Value specification elements self.switcher_value_specification = { @@ -127,6 +138,7 @@ def __init__(self, 'ARRAY-VALUE-SPECIFICATION': self._read_array_value_specification, 'RECORD-VALUE-SPECIFICATION': self._read_record_value_specification, 'APPLICATION-VALUE-SPECIFICATION': self._read_application_value_specification, + 'CONSTANT-REFERENCE': self._read_constant_reference, } self.switcher_non_collectable = { # Non-collectable, used only for unit testing # Documentation elements @@ -174,6 +186,7 @@ def __init__(self, # Reference elements 'PHYSICAL-DIMENSION-REF': self._read_physical_dimension_ref, 'APPLICATION-DATA-TYPE-REF': self._read_application_data_type_ref, + 'CONSTANT-REF': self._read_constant_ref, } self.switcher_all = {} self.switcher_all.update(self.switcher_collectable) @@ -1813,6 +1826,69 @@ def _read_application_record_data_type_group(self, child_elements: ChildElementM elements.append(self._read_application_record_element(xml_record_element)) data["elements"] = elements + def _read_data_type_map(self, xml_element: ElementTree.Element) -> ar_element.DataTypeMap: + """ + Reads AR:DATA-TYPE-MAP + Type: Concrete + Tag variants: 'DATA-TYPE-MAP' + """ + data = {} + child_elements = ChildElementMap(xml_element) + xml_child = child_elements.get("APPLICATION-DATA-TYPE-REF") + if xml_child is not None: + data["appl_data_type_ref"] = self._read_application_data_type_ref(xml_child) + xml_child = child_elements.get("IMPLEMENTATION-DATA-TYPE-REF") + if xml_child is not None: + data["impl_data_type_ref"] = self._read_impl_data_type_ref(xml_child) + return ar_element.DataTypeMap(**data) + + def _read_data_type_mapping_set(self, xml_element: ElementTree.Element) -> ar_element.DataTypeMappingSet: + """ + Reads AR:DATA-TYPE-MAPPING-SET + Type: Concrete + Tag variants: 'DATA-TYPE-MAPPING-SET' + """ + data = {} + child_elements = ChildElementMap(xml_element) + self._read_referrable(child_elements, data) + self._read_multi_language_referrable(child_elements, data) + self._read_identifiable(child_elements, xml_element.attrib, data) + self._read_read_data_type_mapping_set_group(child_elements, data) + self._report_unprocessed_elements(child_elements) + element = ar_element.DataTypeMappingSet(**data) + return element + + def _read_read_data_type_mapping_set_group(self, child_elements: ChildElementMap, data: dict) -> None: + """ + Reads group AR:DATA-TYPE-MAPPING-SET + Type: Abstract + """ + xml_child = child_elements.get("DATA-TYPE-MAPS") + if xml_child is not None: + data_type_maps = [] + for xml_data_type_map_element in xml_child.findall("./DATA-TYPE-MAP"): + data_type_maps.append(self._read_data_type_map(xml_data_type_map_element)) + data["data_type_maps"] = data_type_maps + + def _read_value_list(self, xml_element: ElementTree.Element) -> ar_element.ValueList: + """ + Reads complex-type AR:VALUE-LIST + Type: Concrete + Tag variants: 'SW-ARRAYSIZE' + """ + values = [] + data = {"values": values} + for xml_value in xml_element.findall("./V"): + number = ar_element.NumericalValue(xml_value.text) + if number.value_format in (ar_enum.ValueFormat.HEXADECIMAL, + ar_enum.ValueFormat.BINARY, + ar_enum.ValueFormat.SCIENTIFIC): + values.append(number) + else: + values.append(number.value) + element = ar_element.ValueList(**data) + return element + # Reference elements def _read_compu_method_ref(self, xml_elem: ElementTree.Element) -> ar_element.CompuMethodRef: @@ -1960,73 +2036,26 @@ def _read_autosar_data_type_ref( dest_enum = ar_enum.xml_to_enum('IdentifiableSubTypes', dest_text, self.schema_version) return ar_element.AutosarDataTypeRef(xml_elem.text, dest_enum) - def _read_base_ref_attributes(self, attr: dict, data: dict) -> None: - data['dest'] = attr.get('DEST', None) - if data['dest'] is None: - raise ar_exception.ParseError("Missing required attribute 'DEST'") - - def _read_data_type_map(self, xml_element: ElementTree.Element) -> ar_element.DataTypeMap: - """ - Reads AR:DATA-TYPE-MAP - Type: Concrete - Tag variants: 'DATA-TYPE-MAP' - """ - data = {} - child_elements = ChildElementMap(xml_element) - xml_child = child_elements.get("APPLICATION-DATA-TYPE-REF") - if xml_child is not None: - data["appl_data_type_ref"] = self._read_application_data_type_ref(xml_child) - xml_child = child_elements.get("IMPLEMENTATION-DATA-TYPE-REF") - if xml_child is not None: - data["impl_data_type_ref"] = self._read_impl_data_type_ref(xml_child) - return ar_element.DataTypeMap(**data) - - def _read_data_type_mapping_set(self, xml_element: ElementTree.Element) -> ar_element.DataTypeMappingSet: + def _read_constant_ref(self, + xml_elem: ElementTree.Element + ) -> ar_element.ConstantRef: """ - Reads AR:DATA-TYPE-MAPPING-SET + Reads reference to ConstantSpecification Type: Concrete - Tag variants: 'DATA-TYPE-MAPPING-SET' - """ - data = {} - child_elements = ChildElementMap(xml_element) - self._read_referrable(child_elements, data) - self._read_multi_language_referrable(child_elements, data) - self._read_identifiable(child_elements, xml_element.attrib, data) - self._read_read_data_type_mapping_set_group(child_elements, data) - self._report_unprocessed_elements(child_elements) - element = ar_element.DataTypeMappingSet(**data) - return element - - def _read_read_data_type_mapping_set_group(self, child_elements: ChildElementMap, data: dict) -> None: + Tag variants: 'CONSTANT-REF' """ - Reads group AR:DATA-TYPE-MAPPING-SET - Type: Abstract - """ - xml_child = child_elements.get("DATA-TYPE-MAPS") - if xml_child is not None: - data_type_maps = [] - for xml_data_type_map_element in xml_child.findall("./DATA-TYPE-MAP"): - data_type_maps.append(self._read_data_type_map(xml_data_type_map_element)) - data["data_type_maps"] = data_type_maps + dest_text = xml_elem.attrib['DEST'] + dest_enum = ar_enum.xml_to_enum('IdentifiableSubTypes', dest_text, self.schema_version) + if dest_enum != ar_enum.IdentifiableSubTypes.CONSTANT_SPECIFICATION: + self._raise_parse_error(xml_elem, + f"Invalid DEST attribute '{dest_text}'." + f"Expected 'CONSTANT-SPECIFICATION'") + return ar_element.ConstantRef(xml_elem.text) - def _read_value_list(self, xml_element: ElementTree.Element) -> ar_element.ValueList: - """ - Reads complex-type AR:VALUE-LIST - Type: Concrete - Tag variants: 'SW-ARRAYSIZE' - """ - values = [] - data = {"values": values} - for xml_value in xml_element.findall("./V"): - number = ar_element.NumericalValue(xml_value.text) - if number.value_format in (ar_enum.ValueFormat.HEXADECIMAL, - ar_enum.ValueFormat.BINARY, - ar_enum.ValueFormat.SCIENTIFIC): - values.append(number) - else: - values.append(number.value) - element = ar_element.ValueList(**data) - return element + def _read_base_ref_attributes(self, attr: dict, data: dict) -> None: + data['dest'] = attr.get('DEST', None) + if data['dest'] is None: + raise ar_exception.ParseError("Missing required attribute 'DEST'") # Constant and value specifications @@ -2129,11 +2158,8 @@ def _read_array_value_specification_group(self, child_elements: ChildElementMap, if xml_elements is not None: elements = [] for xml_child_elem in xml_elements.findall('./*'): - read_method = self.switcher_value_specification.get(xml_child_elem.tag, None) - if read_method is not None: - elements.append(read_method(xml_child_elem)) - else: - raise NotImplementedError(f"Found no reader for '{xml_child_elem.tag}'") + element = self._read_value_specification_element(xml_child_elem) + elements.append(element) data["elements"] = elements def _read_record_value_specification(self, @@ -2153,17 +2179,14 @@ def _read_record_value_specification(self, def _read_record_value_specification_group(self, child_elements: ChildElementMap, data: dict) -> None: """ - Reads group AR:NOT-AVAILABLE-VALUE-SPECIFICATION + Reads group AR:RECORD-VALUE-SPECIFICATION """ xml_elements = child_elements.get("FIELDS") if xml_elements is not None: fields = [] for xml_child_elem in xml_elements.findall('./*'): - read_method = self.switcher_value_specification.get(xml_child_elem.tag, None) - if read_method is not None: - fields.append(read_method(xml_child_elem)) - else: - raise NotImplementedError(f"Found no reader for '{xml_child_elem.tag}'") + field = self._read_value_specification_element(xml_child_elem) + fields.append(field) data["fields"] = fields def _read_value_specification_group(self, child_elements: ChildElementMap, data: dict) -> None: @@ -2207,6 +2230,63 @@ def _read_application_value_specification_group(self, if xml_child is not None: data["sw_value_cont"] = self._read_sw_value_cont(xml_child) + def _read_value_specification_element(self, + xml_element: ElementTree.Element) -> ValueSpeficationElement: + """ + Reads any ValueSpecificationElement + """ + read_method = self.switcher_value_specification.get(xml_element.tag, None) + if read_method is not None: + return read_method(xml_element) + else: + print(f"Found no reader for '{xml_element.tag}'", file=sys.stderr) + + def _read_constant_specification(self, + xml_element: ElementTree.Element) -> ar_element.ConstantSpecification: + """ + Reads complex type AR:CONSTANT-SPECIFICATION + Type: Concrete + """ + data = {} + child_elements = ChildElementMap(xml_element) + self._read_referrable(child_elements, data) + self._read_multi_language_referrable(child_elements, data) + self._read_identifiable(child_elements, xml_element.attrib, data) + self._read_constant_specification_group(child_elements, data) + self._report_unprocessed_elements(child_elements) + return ar_element.ConstantSpecification(**data) + + def _read_constant_specification_group(self, child_elements: ChildElementMap, data: dict) -> None: + """ + Reads group AR:CONSTANT-SPECIFICATION + """ + xml_child = child_elements.get("VALUE-SPEC") + if xml_child is not None: + xml_grand_child = xml_child.find("./*") + data["value"] = self._read_value_specification_element(xml_grand_child) + + def _read_constant_reference(self, + xml_element: ElementTree.Element + ) -> ar_element.ConstantReference: + """ + Reads complex-type AR:CONSTANT-REFERENCE + """ + data = {} + child_elements = ChildElementMap(xml_element) + self._read_value_specification_group(child_elements, data) + self._read_constant_reference_group(child_elements, data) + self._report_unprocessed_elements(child_elements) + element = ar_element.ConstantReference(**data) + return element + + def _read_constant_reference_group(self, child_elements: ChildElementMap, data: dict) -> None: + """ + Reads group AR:CONSTANT-REFERENCE + """ + xml_child = child_elements.get("CONSTANT-REF") + if xml_child is not None: + data["constant_ref"] = self._read_constant_ref(xml_child) + # CalibrationData elements def _read_sw_values(self, diff --git a/src/autosar/xml/writer.py b/src/autosar/xml/writer.py index 208b40a..a801b7b 100644 --- a/src/autosar/xml/writer.py +++ b/src/autosar/xml/writer.py @@ -17,7 +17,11 @@ TupleList = list[tuple[str, str]] ValueSpeficationElement = Union[ar_element.TextValueSpecification, ar_element.NumericalValueSpecification, - ar_element.NotAvailableValueSpecification] + ar_element.NotAvailableValueSpecification, + ar_element.ArrayValueSpecification, + ar_element.RecordValueSpecification, + ar_element.ApplicationValueSpecification, + ar_element.ConstantReference] class _XMLWriter: @@ -195,6 +199,8 @@ def __init__(self) -> None: 'DataConstraint': self._write_data_constraint, # Unit elements 'Unit': self._write_unit, + # Constant elements + 'ConstantSpecification': self._write_constant_specification, } # Value specification elements self.switcher_value_specification = { @@ -204,6 +210,7 @@ def __init__(self) -> None: 'ArrayValueSpecification': self._write_array_value_specification, 'RecordValueSpecification': self._write_record_value_specification, 'ApplicationValueSpecification': self._write_application_value_specification, + 'ConstantReference': self._write_constant_reference, } # Elements used only for unit test purposes self.switcher_non_collectable = { @@ -253,6 +260,7 @@ def __init__(self) -> None: # Reference elements 'PhysicalDimensionRef': self._write_physical_dimension_ref, 'ApplicationDataTypeRef': self._write_application_data_type_ref, + 'ConstantRef': self._write_constant_ref, } self.switcher_all = {} # All concrete elements (used for unit testing) self.switcher_all.update(self.switcher_collectable) @@ -1736,6 +1744,19 @@ def _write_application_data_type_ref(self, elem: ar_element.ApplicationDataTypeR self._collect_base_ref_attr(elem, attr) self._add_content(tag, elem.value, attr) + def _write_constant_ref(self, elem: ar_element.ApplicationDataTypeRef, tag: str) -> None: + """ + Writes reference to ConstantSpecification + Type: Concrete + Tag variants: 'CONSTANT-REF' + + Don't confuse this with the ConstantReference class. + """ + assert isinstance(elem, ar_element.ConstantRef) + attr: TupleList = [] + self._collect_base_ref_attr(elem, attr) + self._add_content(tag, elem.value, attr) + # Constant and value specifications def _write_text_value_specification(self, elem: ar_element.TextValueSpecification) -> None: @@ -1890,6 +1911,44 @@ def _write_value_specification_element(self, elem: ValueSpeficationElement) -> N else: raise NotImplementedError(f"Found no writer for class {class_name}") + def _write_constant_specification(self, elem: ar_element.ConstantSpecification) -> None: + """ + Writes complex type AR:CONSTANT-SPECIFICATION + """ + assert isinstance(elem, ar_element.ConstantSpecification) + attr: TupleList = [] + self._collect_identifiable_attributes(elem, attr) + self._add_child('CONSTANT-SPECIFICATION', attr) + self._write_referrable(elem) + self._write_multilanguage_referrable(elem) + self._write_identifiable(elem) + self._write_constant_specification_group(elem) + self._leave_child() + + def _write_constant_specification_group(self, elem: ar_element.ConstantSpecification) -> None: + """ + Writes group AR:CONSTANT-SPECIFICATION + """ + if elem.value is not None: + self._add_child("VALUE-SPEC") + self._write_value_specification_element(elem.value) + self._leave_child() + + def _write_constant_reference(self, elem: ar_element.ConstantReference) -> None: + """ + Writes complex type AR:CONSTANT-REFERENCE + """ + assert isinstance(elem, ar_element.ConstantReference) + tag = "CONSTANT-REFERENCE" + if elem.is_empty: + self._add_content(tag) + else: + self._add_child(tag) + self._write_value_specification_group(elem) + if elem.constant_ref is not None: + self._write_constant_ref(elem.constant_ref, "CONSTANT-REF") + self._leave_child() + # CalibrationData elements def _write_sw_values(self, elem: ar_element.SwValues) -> None: diff --git a/tests/xml/test_constant.py b/tests/xml/test_constant.py index 076cbc3..0ac05ca 100644 --- a/tests/xml/test_constant.py +++ b/tests/xml/test_constant.py @@ -434,5 +434,86 @@ def test_read_write_sw_value_cont(self): self.assertEqual(elem.sw_value_cont.sw_values_phys.values, [1, 2, 3, 4]) +class TestConstantSpecification(unittest.TestCase): + + def test_read_write_name_only(self): + element = ar_element.ConstantSpecification("MyName") + writer = autosar.xml.Writer() + xml = ''' + MyName +''' + self.assertEqual(writer.write_str_elem(element), xml) + reader = autosar.xml.Reader() + elem: ar_element.ConstantSpecification = reader.read_str_elem(xml) + self.assertIsInstance(elem, ar_element.ConstantSpecification) + self.assertEqual(elem.name, "MyName") + + def test_read_write_value_spec_from_object(self): + value = ar_element.NumericalValueSpecification(label="MyLabel", value=2.5) + element = ar_element.ConstantSpecification("MyName", value=value) + writer = autosar.xml.Writer() + xml = ''' + MyName + + + MyLabel + 2.5 + + +''' + self.assertEqual(writer.write_str_elem(element), xml) + reader = autosar.xml.Reader() + elem: ar_element.ConstantSpecification = reader.read_str_elem(xml) + self.assertIsInstance(elem, ar_element.ConstantSpecification) + self.assertEqual(elem.name, "MyName") + self.assertEqual(elem.value.label, "MyLabel") + self.assertEqual(elem.value.value, 2.5) + + def test_read_write_value_spec_made_from_value_builder(self): + element = ar_element.ConstantSpecification.make_constant("MyName", + ("MyLabel", 2.5)) + writer = autosar.xml.Writer() + xml = ''' + MyName + + + MyLabel + 2.5 + + +''' + self.assertEqual(writer.write_str_elem(element), xml) + reader = autosar.xml.Reader() + elem: ar_element.ConstantSpecification = reader.read_str_elem(xml) + self.assertIsInstance(elem, ar_element.ConstantSpecification) + self.assertEqual(elem.name, "MyName") + self.assertEqual(elem.value.label, "MyLabel") + self.assertEqual(elem.value.value, 2.5) + + +class TestConstantReference(unittest.TestCase): + + def test_read_write_empty(self): + element = ar_element.ConstantReference() + writer = autosar.xml.Writer() + xml = writer.write_str_elem(element) + self.assertEqual(xml, '') + reader = autosar.xml.Reader() + elem: ar_element.ConstantReference = reader.read_str_elem(xml) + self.assertIsInstance(elem, ar_element.ConstantReference) + + def test_read_write_constant_ref(self): + element = ar_element.ConstantReference(constant_ref=ar_element.ConstantRef("/Constants/MyConstant")) + writer = autosar.xml.Writer() + xml = ''' + /Constants/MyConstant +''' + self.assertEqual(writer.write_str_elem(element), xml) + reader = autosar.xml.Reader() + elem: ar_element.ConstantReference = reader.read_str_elem(xml) + self.assertIsInstance(elem, ar_element.ConstantReference) + self.assertEqual(str(elem.constant_ref), "/Constants/MyConstant") + + if __name__ == '__main__': unittest.main()