diff --git a/examples/somersaultecu.py b/examples/somersaultecu.py index fbb70e93..9d9aaebb 100755 --- a/examples/somersaultecu.py +++ b/examples/somersaultecu.py @@ -461,6 +461,7 @@ class SomersaultSID(IntEnum): compu_method=somersault_compumethods["uint_passthrough"], unit_ref=None, sdgs=[], + internal_constr=None, ), "soberness_check": DataObjectProperty( @@ -474,6 +475,7 @@ class SomersaultSID(IntEnum): compu_method=somersault_compumethods["uint_passthrough"], unit_ref=None, sdgs=[], + internal_constr=None, ), "dizzyness_level": DataObjectProperty( @@ -487,6 +489,7 @@ class SomersaultSID(IntEnum): compu_method=somersault_compumethods["uint_passthrough"], unit_ref=None, sdgs=[], + internal_constr=None, ), "happiness_level": DataObjectProperty( @@ -500,6 +503,7 @@ class SomersaultSID(IntEnum): compu_method=somersault_compumethods["uint_passthrough"], unit_ref=None, sdgs=[], + internal_constr=None, ), "duration": DataObjectProperty( @@ -513,6 +517,7 @@ class SomersaultSID(IntEnum): compu_method=somersault_compumethods["uint_passthrough"], unit_ref=OdxLinkRef.from_id(somersault_units["second"].odx_id), sdgs=[], + internal_constr=None, ), "temperature": DataObjectProperty( @@ -526,6 +531,7 @@ class SomersaultSID(IntEnum): compu_method=somersault_compumethods["uint_passthrough"], unit_ref=OdxLinkRef.from_id(somersault_units["celsius"].odx_id), sdgs=[], + internal_constr=None, ), "error_code": DataObjectProperty( @@ -539,6 +545,7 @@ class SomersaultSID(IntEnum): compu_method=somersault_compumethods["uint_passthrough"], unit_ref=None, sdgs=[], + internal_constr=None, ), "boolean": DataObjectProperty( @@ -553,6 +560,7 @@ class SomersaultSID(IntEnum): compu_method=somersault_compumethods["boolean"], unit_ref=None, sdgs=[], + internal_constr=None, ), "uint8": DataObjectProperty( @@ -566,6 +574,7 @@ class SomersaultSID(IntEnum): compu_method=somersault_compumethods["uint_passthrough"], unit_ref=None, sdgs=[], + internal_constr=None, ), "float": DataObjectProperty( @@ -579,6 +588,7 @@ class SomersaultSID(IntEnum): compu_method=somersault_compumethods["float_passthrough"], unit_ref=None, sdgs=[], + internal_constr=None, ), } diff --git a/odxtools/dataobjectproperty.py b/odxtools/dataobjectproperty.py index aba3d6cf..e1a5daa3 100644 --- a/odxtools/dataobjectproperty.py +++ b/odxtools/dataobjectproperty.py @@ -11,6 +11,7 @@ from .dopbase import DopBase from .encodestate import EncodeState from .exceptions import DecodeError, EncodeError, odxassert, odxrequire +from .internalconstr import InternalConstr from .odxlink import OdxDocFragment, OdxLinkDatabase, OdxLinkId, OdxLinkRef from .odxtypes import AtomicOdxType, ParameterValue from .physicaltype import PhysicalType @@ -42,7 +43,7 @@ class DataObjectProperty(DopBase): unit_ref: Optional[OdxLinkRef] # TODO: physical_const: Optional[InternalConstr] - # TODO: internal_const: Optional[InternalConstr] + internal_constr: Optional[InternalConstr] @staticmethod def from_et(et_element: ElementTree.Element, @@ -63,11 +64,17 @@ def from_et(et_element: ElementTree.Element, ) unit_ref = OdxLinkRef.from_et(et_element.find("UNIT-REF"), doc_frags) + internal_constr = None + if (internal_constr_elem := et_element.find("INTERNAL-CONSTR")) is not None: + internal_constr = InternalConstr.from_et( + internal_constr_elem, internal_type=diag_coded_type.base_data_type) + return DataObjectProperty( diag_coded_type=diag_coded_type, physical_type=physical_type, compu_method=compu_method, unit_ref=unit_ref, + internal_constr=internal_constr, **kwargs) def _build_odxlinks(self) -> Dict[OdxLinkId, Any]: diff --git a/odxtools/internalconstr.py b/odxtools/internalconstr.py new file mode 100644 index 00000000..e09eb454 --- /dev/null +++ b/odxtools/internalconstr.py @@ -0,0 +1,79 @@ +# SPDX-License-Identifier: MIT +from dataclasses import dataclass +from enum import Enum +from typing import List, Optional +from xml.etree import ElementTree + +from .compumethods.limit import Limit +from .exceptions import odxraise, odxrequire +from .odxtypes import DataType +from .utils import create_description_from_et + + +class Validity(Enum): + VALID = "VALID" + NOT_VALID = "NOT-VALID" + NOT_DEFINED = "NOT-DEFINED" + NOT_AVAILABLE = "NOT-AVAILABLE" + + +@dataclass +class ScaleConstr: + """This class represents a SCALE-CONSTR. + """ + + short_label: Optional[str] + description: Optional[str] + lower_limit: Optional[Limit] + upper_limit: Optional[Limit] + validity: Validity + + @staticmethod + def from_et(et_element: ElementTree.Element, internal_type: DataType) -> "ScaleConstr": + short_label = et_element.findtext("SHORT-LABEL") + description = create_description_from_et(et_element.find("DESC")) + + lower_limit = Limit.from_et(et_element.find("LOWER-LIMIT"), internal_type=internal_type) + upper_limit = Limit.from_et(et_element.find("UPPER-LIMIT"), internal_type=internal_type) + + validity_str = odxrequire(et_element.get("VALIDITY")) + try: + validity = Validity(validity_str) + except ValueError: + odxraise(f"Encountered unknown Validity '{validity_str}'") + + return ScaleConstr( + short_label=short_label, + description=description, + lower_limit=lower_limit, + upper_limit=upper_limit, + validity=validity) + + +@dataclass +class InternalConstr: + """This class represents a INTERNAL-CONSTR. + """ + + lower_limit: Optional[Limit] + upper_limit: Optional[Limit] + scale_constrs: List[ScaleConstr] + + @staticmethod + def from_et(et_element: ElementTree.Element, internal_type: DataType) -> "InternalConstr": + + lower_limit = None + if lower_limit_elem := et_element.find("LOWER-LIMIT"): + lower_limit = Limit.from_et(lower_limit_elem, internal_type=internal_type) + + upper_limit = None + if upper_limit_elem := et_element.find("UPPER-LIMIT"): + upper_limit = Limit.from_et(upper_limit_elem, internal_type=internal_type) + + scale_constrs = [ + ScaleConstr.from_et(sc_el, internal_type) + for sc_el in et_element.iterfind("SCALE-CONSTRS/SCALE-CONSTR") + ] + + return InternalConstr( + lower_limit=lower_limit, upper_limit=upper_limit, scale_constrs=scale_constrs) diff --git a/odxtools/templates/macros/printDOP.xml.jinja2 b/odxtools/templates/macros/printDOP.xml.jinja2 index 136e4f91..80a86c50 100644 --- a/odxtools/templates/macros/printDOP.xml.jinja2 +++ b/odxtools/templates/macros/printDOP.xml.jinja2 @@ -34,7 +34,7 @@ {%- macro printPhysicalType(physical_type) %} {%- if physical_type.display_radix is not none %} -{%- elif physical_type.precision is not none %} +{%- elif physical_type.precision is not none %} {{physical_type.precision}} @@ -44,12 +44,43 @@ {%- endmacro -%} {%- macro printLimitValue(lv) -%} -{%- if hasattr(lv, 'hex') %} +{%- if hasattr(lv, 'hex') -%} {#- bytes or bytarray limit #} {{lv.hex()}} -{%- else %} +{%- else -%} {{lv}} -{%- endif %} +{%- endif -%} +{%- endmacro -%} + +{%- macro printScaleConstr(sc) %} + + {%- if sc.short_label and sc.short_label.strip() %} + {{sc.short_label|e}} + {%- endif %} + {%- if sc.description and sc.description.strip() %} + + {{sc.description}} + + {%- endif %} + {{printLimitValue(sc.lower_limit.value)}} + {{printLimitValue(sc.upper_limit.value)}} + +{%- endmacro -%} + +{%- macro printInternalConstr(ic) %} + + {%- if ic.lower_limit %} + {{printLimitValue(ic.lower_limit.value)}} + {%- endif %} + {%- if ic.upper_limit %} + {{printLimitValue(ic.upper_limit.value)}} + {%- endif %} + {%- for sc in ic.scale_constrs %} + + {{- printScaleConstr(sc)|indent(4)}} + + {%- endfor %} + {%- endmacro -%} {%- macro printCompuMethod(cm) -%} @@ -69,10 +100,10 @@ {%- endif %} {%- if cs.lower_limit is not none %} - {{printLimitValue(cs.lower_limit.value)}} + {{printLimitValue(cs.lower_limit.value)}} {%- endif %} {%- if cs.upper_limit is not none %} - {{printLimitValue(cs.upper_limit.value)}} + {{printLimitValue(cs.upper_limit.value)}} {%- endif %} {%- if cs.compu_inverse_value is not none %} @@ -194,6 +225,9 @@ {%- if dop.unit_ref %} {%- endif %} + {%- if dop.internal_constr %} + {{- printInternalConstr(dop.internal_constr)|indent(1) }} + {%- endif %} {%- endmacro %} diff --git a/tests/test_decoding.py b/tests/test_decoding.py index 7ed5c160..6f6f5e9b 100644 --- a/tests/test_decoding.py +++ b/tests/test_decoding.py @@ -541,6 +541,7 @@ def test_decode_request_structure(self) -> None: compu_method=compu_method, unit_ref=None, sdgs=[], + internal_constr=None, ) req_param1 = CodedConstParameter( @@ -731,6 +732,7 @@ def test_decode_request_end_of_pdu_field(self) -> None: compu_method=compu_method, unit_ref=None, sdgs=[], + internal_constr=None, ) req_param1 = CodedConstParameter( @@ -940,6 +942,7 @@ def test_decode_request_linear_compu_method(self) -> None: compu_method=compu_method, unit_ref=None, sdgs=[], + internal_constr=None, ) req_param1 = CodedConstParameter( short_name="SID", @@ -1367,6 +1370,7 @@ def setUp(self) -> None: internal_type=DataType.A_BYTEFIELD, physical_type=DataType.A_BYTEFIELD), unit_ref=None, sdgs=[], + internal_constr=None, ) dop = self.dop_bytes_termination_end_of_pdu odxlinks.update(dop._build_odxlinks()) @@ -1521,6 +1525,7 @@ def test_physical_constant_parameter(self) -> None: ), unit_ref=None, sdgs=[], + internal_constr=None, ) odxlinks.update(dop._build_odxlinks()) req_param1 = CodedConstParameter( diff --git a/tests/test_diag_coded_types.py b/tests/test_diag_coded_types.py index 130c3e03..7beaa7d8 100644 --- a/tests/test_diag_coded_types.py +++ b/tests/test_diag_coded_types.py @@ -192,6 +192,7 @@ def test_end_to_end(self) -> None: compu_method=compumethods["bytes_passthrough"], unit_ref=None, sdgs=[], + internal_constr=None, ), } @@ -459,6 +460,7 @@ def test_end_to_end(self) -> None: compu_method=compumethods["multiply_with_8"], unit_ref=None, sdgs=[], + internal_constr=None, ), "certificateClient": DataObjectProperty( @@ -473,6 +475,7 @@ def test_end_to_end(self) -> None: compu_method=compumethods["uint_passthrough"], unit_ref=None, sdgs=[], + internal_constr=None, ), } @@ -780,6 +783,7 @@ def test_end_to_end(self) -> None: compu_method=compumethods["bytes_passthrough"], unit_ref=None, sdgs=[], + internal_constr=None, ), } diff --git a/tests/test_diag_data_dictionary_spec.py b/tests/test_diag_data_dictionary_spec.py index 7360a022..47848e29 100644 --- a/tests/test_diag_data_dictionary_spec.py +++ b/tests/test_diag_data_dictionary_spec.py @@ -81,6 +81,7 @@ def test_initialization(self) -> None: compu_method=ident_compu_method, unit_ref=None, sdgs=[], + internal_constr=None, ) dop_2 = DataObjectProperty( @@ -94,6 +95,7 @@ def test_initialization(self) -> None: compu_method=ident_compu_method, unit_ref=None, sdgs=[], + internal_constr=None, ) flip_quality_id = OdxLinkId("somersault.table.flip_quality", doc_frags) diff --git a/tests/test_encoding.py b/tests/test_encoding.py index 6935bc51..6b5dc38c 100644 --- a/tests/test_encoding.py +++ b/tests/test_encoding.py @@ -142,6 +142,7 @@ def test_encode_linear(self) -> None: compu_method=compu_method, unit_ref=None, sdgs=[], + internal_constr=None, ) odxlinks.update({dop.odx_id: dop}) param1 = ValueParameter( @@ -358,6 +359,7 @@ def test_bit_mask(self) -> None: "sdgs": [], "short_name": 'dop', "unit_ref": None, + "internal_constr": None, } param_kwargs: Dict[str, Any] = { "long_name": None, diff --git a/tests/test_singleecujob.py b/tests/test_singleecujob.py index 5ae5fd76..e31f9381 100644 --- a/tests/test_singleecujob.py +++ b/tests/test_singleecujob.py @@ -109,6 +109,7 @@ class Context(NamedTuple): ), unit_ref=None, sdgs=[], + internal_constr=None, ), outputDOP=DataObjectProperty( odx_id=OdxLinkId("ID.outputDOP", doc_frags), @@ -137,6 +138,7 @@ class Context(NamedTuple): ), unit_ref=None, sdgs=[], + internal_constr=None, ), negOutputDOP=DataObjectProperty( odx_id=OdxLinkId("ID.negOutputDOP", doc_frags), @@ -165,6 +167,7 @@ class Context(NamedTuple): ), unit_ref=None, sdgs=[], + internal_constr=None, ), ) diff --git a/tests/test_unit_spec.py b/tests/test_unit_spec.py index bbd59d8e..9d96ccc7 100644 --- a/tests/test_unit_spec.py +++ b/tests/test_unit_spec.py @@ -119,6 +119,7 @@ def test_resolve_odxlinks(self) -> None: internal_type=DataType.A_UINT32, physical_type=DataType.A_UINT32), unit_ref=OdxLinkRef.from_id(unit.odx_id), sdgs=[], + internal_constr=None, ) dl_raw = DiagLayerRaw( variant_type=DiagLayerType.BASE_VARIANT,