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,