diff --git a/examples/somersault.pdx b/examples/somersault.pdx
index 874e58c8..d6b0a1ec 100644
Binary files a/examples/somersault.pdx and b/examples/somersault.pdx differ
diff --git a/odxtools/parameters/codedconstparameter.py b/odxtools/parameters/codedconstparameter.py
index 2f7f7234..d0e4d878 100644
--- a/odxtools/parameters/codedconstparameter.py
+++ b/odxtools/parameters/codedconstparameter.py
@@ -1,16 +1,19 @@
# SPDX-License-Identifier: MIT
import warnings
from dataclasses import dataclass
-from typing import TYPE_CHECKING, Any, Dict, Optional
+from typing import TYPE_CHECKING, Any, Dict, List, Optional
+from xml.etree import ElementTree
from typing_extensions import override
+from ..createanydiagcodedtype import create_any_diag_coded_type_from_et
from ..decodestate import DecodeState
from ..diagcodedtype import DiagCodedType
from ..encodestate import EncodeState
-from ..exceptions import DecodeError
-from ..odxlink import OdxLinkDatabase, OdxLinkId
+from ..exceptions import DecodeError, odxrequire
+from ..odxlink import OdxDocFragment, OdxLinkDatabase, OdxLinkId
from ..odxtypes import AtomicOdxType, DataType
+from ..utils import dataclass_fields_asdict
from .parameter import Parameter, ParameterType
if TYPE_CHECKING:
@@ -23,10 +26,27 @@ class CodedConstParameter(Parameter):
diag_coded_type: DiagCodedType
coded_value: AtomicOdxType
+ @staticmethod
+ @override
+ def from_et(et_element: ElementTree.Element,
+ doc_frags: List[OdxDocFragment]) -> "CodedConstParameter":
+
+ kwargs = dataclass_fields_asdict(Parameter.from_et(et_element, doc_frags))
+
+ dct_elem = odxrequire(et_element.find("DIAG-CODED-TYPE"))
+ diag_coded_type = create_any_diag_coded_type_from_et(dct_elem, doc_frags)
+ coded_value = diag_coded_type.base_data_type.from_string(
+ odxrequire(et_element.findtext("CODED-VALUE")))
+
+ return CodedConstParameter(
+ diag_coded_type=diag_coded_type, coded_value=coded_value, **kwargs)
+
@property
+ @override
def parameter_type(self) -> ParameterType:
return "CODED-CONST"
+ @override
def _build_odxlinks(self) -> Dict[OdxLinkId, Any]:
result = super()._build_odxlinks()
@@ -34,12 +54,15 @@ def _build_odxlinks(self) -> Dict[OdxLinkId, Any]:
return result
+ @override
def _resolve_odxlinks(self, odxlinks: OdxLinkDatabase) -> None:
super()._resolve_odxlinks(odxlinks)
+ @override
def _resolve_snrefs(self, diag_layer: "DiagLayer") -> None:
super()._resolve_snrefs(diag_layer)
+ @override
def get_static_bit_length(self) -> Optional[int]:
return self.diag_coded_type.get_static_bit_length()
@@ -48,13 +71,16 @@ def internal_data_type(self) -> DataType:
return self.diag_coded_type.base_data_type
@property
+ @override
def is_required(self) -> bool:
return False
@property
+ @override
def is_settable(self) -> bool:
return False
+ @override
def get_coded_value_as_bytes(self, encode_state: EncodeState) -> bytes:
if (self.short_name in encode_state.parameter_values and
encode_state.parameter_values[self.short_name] != self.coded_value):
diff --git a/odxtools/parameters/createanyparameter.py b/odxtools/parameters/createanyparameter.py
index 0688ca2d..6f2c4548 100644
--- a/odxtools/parameters/createanyparameter.py
+++ b/odxtools/parameters/createanyparameter.py
@@ -2,13 +2,9 @@
from typing import List
from xml.etree import ElementTree
-from ..createanydiagcodedtype import create_any_diag_coded_type_from_et
-from ..createsdgs import create_sdgs_from_et
-from ..element import NamedElement
-from ..exceptions import odxrequire
+from ..exceptions import odxraise
from ..globals import xsi
-from ..odxlink import OdxDocFragment, OdxLinkId, OdxLinkRef
-from ..utils import dataclass_fields_asdict
+from ..odxlink import OdxDocFragment
from .codedconstparameter import CodedConstParameter
from .dynamicparameter import DynamicParameter
from .lengthkeyparameter import LengthKeyParameter
@@ -27,205 +23,33 @@
def create_any_parameter_from_et(et_element: ElementTree.Element,
doc_frags: List[OdxDocFragment]) \
-> Parameter:
- kwargs = dataclass_fields_asdict(NamedElement.from_et(et_element, doc_frags))
- semantic = et_element.get("SEMANTIC")
- byte_position_str = et_element.findtext("BYTE-POSITION")
- byte_position = int(byte_position_str) if byte_position_str is not None else None
- bit_position_str = et_element.findtext("BIT-POSITION")
- bit_position = None
- if bit_position_str is not None:
- bit_position = int(bit_position_str)
parameter_type = et_element.get(f"{xsi}type")
- sdgs = create_sdgs_from_et(et_element.find("SDGS"), doc_frags)
-
# Which attributes are set depends on the type of the parameter.
- dop_ref = None
- dop_snref = None
- if parameter_type in ["VALUE", "PHYS-CONST", "SYSTEM", "LENGTH-KEY"]:
- dop_ref = OdxLinkRef.from_et(et_element.find("DOP-REF"), doc_frags)
- dop_snref = None
- if (dop_snref_elem := et_element.find("DOP-SNREF")) is not None:
- dop_snref = odxrequire(dop_snref_elem.get("SHORT-NAME"))
-
- if dop_ref is None and dop_snref is None:
- raise ValueError(
- f"A parameter of type {parameter_type} must reference a DOP! {dop_ref}, {dop_snref}"
- )
-
if parameter_type == "VALUE":
- physical_default_value_raw = (
- et_element.findtext("PHYSICAL-DEFAULT-VALUE")
- if et_element.find("PHYSICAL-DEFAULT-VALUE") is not None else None)
-
- return ValueParameter(
- semantic=semantic,
- byte_position=byte_position,
- bit_position=bit_position,
- dop_ref=dop_ref,
- dop_snref=dop_snref,
- physical_default_value_raw=physical_default_value_raw,
- sdgs=sdgs,
- **kwargs)
-
- elif parameter_type == "PHYS-CONST":
- physical_constant_value = odxrequire(et_element.findtext("PHYS-CONSTANT-VALUE"))
-
- return PhysicalConstantParameter(
- semantic=semantic,
- byte_position=byte_position,
- bit_position=bit_position,
- dop_ref=dop_ref,
- dop_snref=dop_snref,
- physical_constant_value_raw=physical_constant_value,
- sdgs=sdgs,
- **kwargs)
-
+ return ValueParameter.from_et(et_element, doc_frags)
elif parameter_type == "CODED-CONST":
- dct_elem = odxrequire(et_element.find("DIAG-CODED-TYPE"))
- diag_coded_type = create_any_diag_coded_type_from_et(dct_elem, doc_frags)
- coded_value = diag_coded_type.base_data_type.from_string(
- odxrequire(et_element.findtext("CODED-VALUE")))
-
- return CodedConstParameter(
- semantic=semantic,
- diag_coded_type=diag_coded_type,
- coded_value=coded_value,
- byte_position=byte_position,
- bit_position=bit_position,
- sdgs=sdgs,
- **kwargs)
-
+ return CodedConstParameter.from_et(et_element, doc_frags)
+ elif parameter_type == "PHYS-CONST":
+ return PhysicalConstantParameter.from_et(et_element, doc_frags)
+ elif parameter_type == "SYSTEM":
+ return SystemParameter.from_et(et_element, doc_frags)
+ elif parameter_type == "LENGTH-KEY":
+ return LengthKeyParameter.from_et(et_element, doc_frags)
elif parameter_type == "NRC-CONST":
- diag_coded_type = create_any_diag_coded_type_from_et(
- odxrequire(et_element.find("DIAG-CODED-TYPE")), doc_frags)
- coded_values = [
- diag_coded_type.base_data_type.from_string(odxrequire(val.text))
- for val in et_element.iterfind("CODED-VALUES/CODED-VALUE")
- ]
-
- return NrcConstParameter(
- semantic=semantic,
- diag_coded_type=diag_coded_type,
- coded_values=coded_values,
- byte_position=byte_position,
- bit_position=bit_position,
- sdgs=sdgs,
- **kwargs)
-
+ return NrcConstParameter.from_et(et_element, doc_frags)
elif parameter_type == "RESERVED":
- bit_length = int(odxrequire(et_element.findtext("BIT-LENGTH")))
-
- return ReservedParameter(
- bit_length=bit_length,
- semantic=semantic,
- byte_position=byte_position,
- bit_position=bit_position,
- sdgs=sdgs,
- **kwargs)
-
+ return ReservedParameter.from_et(et_element, doc_frags)
elif parameter_type == "MATCHING-REQUEST-PARAM":
- byte_length = int(odxrequire(et_element.findtext("BYTE-LENGTH")))
- request_byte_pos = int(odxrequire(et_element.findtext("REQUEST-BYTE-POS")))
-
- return MatchingRequestParameter(
- semantic=semantic,
- byte_position=byte_position,
- bit_position=bit_position,
- request_byte_position=request_byte_pos,
- byte_length=byte_length,
- sdgs=sdgs,
- **kwargs)
-
- elif parameter_type == "SYSTEM":
- sysparam = odxrequire(et_element.get("SYSPARAM"))
-
- return SystemParameter(
- sysparam=sysparam,
- semantic=semantic,
- byte_position=byte_position,
- bit_position=bit_position,
- dop_ref=dop_ref,
- dop_snref=dop_snref,
- sdgs=sdgs,
- **kwargs)
-
- elif parameter_type == "LENGTH-KEY":
- odx_id = odxrequire(OdxLinkId.from_et(et_element, doc_frags))
-
- return LengthKeyParameter(
- odx_id=odx_id,
- semantic=semantic,
- byte_position=byte_position,
- bit_position=bit_position,
- dop_ref=dop_ref,
- dop_snref=dop_snref,
- sdgs=sdgs,
- **kwargs)
-
+ return MatchingRequestParameter.from_et(et_element, doc_frags)
elif parameter_type == "DYNAMIC":
-
- return DynamicParameter(
- semantic=semantic,
- byte_position=byte_position,
- bit_position=bit_position,
- sdgs=sdgs,
- **kwargs)
-
+ return DynamicParameter.from_et(et_element, doc_frags)
elif parameter_type == "TABLE-STRUCT":
- key_ref = OdxLinkRef.from_et(et_element.find("TABLE-KEY-REF"), doc_frags)
- if (key_snref_elem := et_element.find("TABLE-KEY-SNREF")) is not None:
- key_snref = odxrequire(key_snref_elem.get("SHORT-NAME"))
- else:
- key_snref = None
-
- return TableStructParameter(
- table_key_ref=key_ref,
- table_key_snref=key_snref,
- semantic=semantic,
- byte_position=byte_position,
- bit_position=bit_position,
- sdgs=sdgs,
- **kwargs)
-
+ return TableStructParameter.from_et(et_element, doc_frags)
elif parameter_type == "TABLE-KEY":
-
- parameter_id = odxrequire(OdxLinkId.from_et(et_element, doc_frags))
- table_ref = OdxLinkRef.from_et(et_element.find("TABLE-REF"), doc_frags)
- if (table_snref_elem := et_element.find("TABLE-SNREF")) is not None:
- table_snref = odxrequire(table_snref_elem.get("SHORT-NAME"))
- else:
- table_snref = None
-
- table_row_ref = OdxLinkRef.from_et(et_element.find("TABLE-ROW-REF"), doc_frags)
- if (table_row_snref_elem := et_element.find("TABLE-ROW-SNREF")) is not None:
- table_row_snref = odxrequire(table_row_snref_elem.get("SHORT-NAME"))
- else:
- table_row_snref = None
-
- return TableKeyParameter(
- table_ref=table_ref,
- table_snref=table_snref,
- table_row_snref=table_row_snref,
- table_row_ref=table_row_ref,
- odx_id=parameter_id,
- byte_position=byte_position,
- bit_position=bit_position,
- semantic=semantic,
- sdgs=sdgs,
- **kwargs)
-
+ return TableKeyParameter.from_et(et_element, doc_frags)
elif parameter_type == "TABLE-ENTRY":
- target = odxrequire(et_element.findtext("TARGET"))
- table_row_ref = odxrequire(OdxLinkRef.from_et(et_element.find("TABLE-ROW-REF"), doc_frags))
-
- return TableEntryParameter(
- target=target,
- table_row_ref=table_row_ref,
- byte_position=byte_position,
- bit_position=bit_position,
- semantic=semantic,
- sdgs=sdgs,
- **kwargs)
+ return TableEntryParameter.from_et(et_element, doc_frags)
- raise NotImplementedError(f"I don't know about parameters of type {parameter_type}")
+ odxraise(f"I don't know about parameters of type {parameter_type}", NotImplementedError)
+ return Parameter.from_et(et_element, doc_frags)
diff --git a/odxtools/parameters/dynamicparameter.py b/odxtools/parameters/dynamicparameter.py
index 103985a2..1323bc95 100644
--- a/odxtools/parameters/dynamicparameter.py
+++ b/odxtools/parameters/dynamicparameter.py
@@ -1,29 +1,46 @@
# SPDX-License-Identifier: MIT
from dataclasses import dataclass
+from typing import List
+from xml.etree import ElementTree
from typing_extensions import override
from ..decodestate import DecodeState
from ..encodestate import EncodeState
+from ..odxlink import OdxDocFragment
from ..odxtypes import ParameterValue
+from ..utils import dataclass_fields_asdict
from .parameter import Parameter, ParameterType
@dataclass
class DynamicParameter(Parameter):
+ @staticmethod
+ @override
+ def from_et(et_element: ElementTree.Element,
+ doc_frags: List[OdxDocFragment]) -> "DynamicParameter":
+
+ kwargs = dataclass_fields_asdict(Parameter.from_et(et_element, doc_frags))
+
+ return DynamicParameter(**kwargs)
+
@property
+ @override
def parameter_type(self) -> ParameterType:
return "DYNAMIC"
@property
+ @override
def is_required(self) -> bool:
raise NotImplementedError(".is_required for a DynamicParameter")
@property
+ @override
def is_settable(self) -> bool:
raise NotImplementedError(".is_settable for a DynamicParameter")
+ @override
def get_coded_value_as_bytes(self, encode_state: EncodeState) -> bytes:
raise NotImplementedError("Encoding a DynamicParameter is not implemented yet.")
diff --git a/odxtools/parameters/lengthkeyparameter.py b/odxtools/parameters/lengthkeyparameter.py
index 4bf9226e..3c458541 100644
--- a/odxtools/parameters/lengthkeyparameter.py
+++ b/odxtools/parameters/lengthkeyparameter.py
@@ -1,14 +1,16 @@
# SPDX-License-Identifier: MIT
from dataclasses import dataclass
-from typing import TYPE_CHECKING, Any, Dict
+from typing import TYPE_CHECKING, Any, Dict, List
+from xml.etree import ElementTree
from typing_extensions import override
from ..decodestate import DecodeState
from ..encodestate import EncodeState
from ..exceptions import odxraise, odxrequire
-from ..odxlink import OdxLinkDatabase, OdxLinkId
+from ..odxlink import OdxDocFragment, OdxLinkDatabase, OdxLinkId
from ..odxtypes import ParameterValue
+from ..utils import dataclass_fields_asdict
from .parameter import ParameterType
from .parameterwithdop import ParameterWithDOP
@@ -29,10 +31,23 @@ class LengthKeyParameter(ParameterWithDOP):
odx_id: OdxLinkId
+ @staticmethod
+ @override
+ def from_et(et_element: ElementTree.Element,
+ doc_frags: List[OdxDocFragment]) -> "LengthKeyParameter":
+
+ kwargs = dataclass_fields_asdict(ParameterWithDOP.from_et(et_element, doc_frags))
+
+ odx_id = odxrequire(OdxLinkId.from_et(et_element, doc_frags))
+
+ return LengthKeyParameter(odx_id=odx_id, **kwargs)
+
@property
+ @override
def parameter_type(self) -> ParameterType:
return "LENGTH-KEY"
+ @override
def _build_odxlinks(self) -> Dict[OdxLinkId, Any]:
result = super()._build_odxlinks()
@@ -40,23 +55,28 @@ def _build_odxlinks(self) -> Dict[OdxLinkId, Any]:
return result
+ @override
def _resolve_odxlinks(self, odxlinks: OdxLinkDatabase) -> None:
super()._resolve_odxlinks(odxlinks)
+ @override
def _resolve_snrefs(self, diag_layer: "DiagLayer") -> None:
super()._resolve_snrefs(diag_layer)
@property
+ @override
def is_required(self) -> bool:
return False
@property
+ @override
def is_settable(self) -> bool:
# length keys can be explicitly set, but they do not need to
# be because they can be implicitly determined by the length
# of the corresponding field
return True
+ @override
def get_coded_value_as_bytes(self, encode_state: EncodeState) -> bytes:
physical_value = encode_state.parameter_values.get(self.short_name, 0)
@@ -65,6 +85,7 @@ def get_coded_value_as_bytes(self, encode_state: EncodeState) -> bytes:
f"A DOP is required for length key parameter {self.short_name}")
return dop.convert_physical_to_bytes(physical_value, encode_state, bit_position=bit_pos)
+ @override
def encode_into_pdu(self, encode_state: EncodeState) -> bytes:
return super().encode_into_pdu(encode_state)
diff --git a/odxtools/parameters/matchingrequestparameter.py b/odxtools/parameters/matchingrequestparameter.py
index b2ef4da5..8711c237 100644
--- a/odxtools/parameters/matchingrequestparameter.py
+++ b/odxtools/parameters/matchingrequestparameter.py
@@ -1,13 +1,16 @@
# SPDX-License-Identifier: MIT
from dataclasses import dataclass
-from typing import Optional
+from typing import List, Optional
+from xml.etree import ElementTree
from typing_extensions import override
from ..decodestate import DecodeState
from ..encodestate import EncodeState
-from ..exceptions import EncodeError
+from ..exceptions import EncodeError, odxrequire
+from ..odxlink import OdxDocFragment
from ..odxtypes import DataType, ParameterValue
+from ..utils import dataclass_fields_asdict
from .parameter import Parameter, ParameterType
@@ -16,21 +19,39 @@ class MatchingRequestParameter(Parameter):
request_byte_position: int
byte_length: int
+ @staticmethod
+ @override
+ def from_et(et_element: ElementTree.Element,
+ doc_frags: List[OdxDocFragment]) -> "MatchingRequestParameter":
+
+ kwargs = dataclass_fields_asdict(Parameter.from_et(et_element, doc_frags))
+
+ request_byte_position = int(odxrequire(et_element.findtext("REQUEST-BYTE-POS")))
+ byte_length = int(odxrequire(et_element.findtext("BYTE-LENGTH")))
+
+ return MatchingRequestParameter(
+ request_byte_position=request_byte_position, byte_length=byte_length, **kwargs)
+
@property
+ @override
def parameter_type(self) -> ParameterType:
return "MATCHING-REQUEST-PARAM"
+ @override
def get_static_bit_length(self) -> Optional[int]:
return 8 * self.byte_length
@property
+ @override
def is_required(self) -> bool:
return False
@property
+ @override
def is_settable(self) -> bool:
return False
+ @override
def get_coded_value_as_bytes(self, encode_state: EncodeState) -> bytes:
if not encode_state.triggering_request:
raise EncodeError(f"Parameter '{self.short_name}' is of matching request type,"
diff --git a/odxtools/parameters/nrcconstparameter.py b/odxtools/parameters/nrcconstparameter.py
index e9e23ed9..8cc3ceef 100644
--- a/odxtools/parameters/nrcconstparameter.py
+++ b/odxtools/parameters/nrcconstparameter.py
@@ -2,15 +2,18 @@
import warnings
from dataclasses import dataclass
from typing import TYPE_CHECKING, Any, Dict, List, Optional
+from xml.etree import ElementTree
from typing_extensions import override
+from ..createanydiagcodedtype import create_any_diag_coded_type_from_et
from ..decodestate import DecodeState
from ..diagcodedtype import DiagCodedType
from ..encodestate import EncodeState
-from ..exceptions import DecodeError, EncodeError
-from ..odxlink import OdxLinkDatabase, OdxLinkId
+from ..exceptions import DecodeError, EncodeError, odxrequire
+from ..odxlink import OdxDocFragment, OdxLinkDatabase, OdxLinkId
from ..odxtypes import AtomicOdxType, DataType
+from ..utils import dataclass_fields_asdict
from .parameter import Parameter, ParameterType
if TYPE_CHECKING:
@@ -31,10 +34,29 @@ class NrcConstParameter(Parameter):
diag_coded_type: DiagCodedType
coded_values: List[AtomicOdxType]
+ @staticmethod
+ @override
+ def from_et(et_element: ElementTree.Element,
+ doc_frags: List[OdxDocFragment]) -> "NrcConstParameter":
+
+ kwargs = dataclass_fields_asdict(Parameter.from_et(et_element, doc_frags))
+
+ dct_elem = odxrequire(et_element.find("DIAG-CODED-TYPE"))
+ diag_coded_type = create_any_diag_coded_type_from_et(dct_elem, doc_frags)
+ coded_values = [
+ diag_coded_type.base_data_type.from_string(odxrequire(val.text))
+ for val in et_element.iterfind("CODED-VALUES/CODED-VALUE")
+ ]
+
+ return NrcConstParameter(
+ diag_coded_type=diag_coded_type, coded_values=coded_values, **kwargs)
+
@property
+ @override
def parameter_type(self) -> ParameterType:
return "NRC-CONST"
+ @override
def _build_odxlinks(self) -> Dict[OdxLinkId, Any]:
result = super()._build_odxlinks()
@@ -42,12 +64,15 @@ def _build_odxlinks(self) -> Dict[OdxLinkId, Any]:
return result
+ @override
def _resolve_odxlinks(self, odxlinks: OdxLinkDatabase) -> None:
super()._resolve_odxlinks(odxlinks)
+ @override
def _resolve_snrefs(self, diag_layer: "DiagLayer") -> None:
super()._resolve_snrefs(diag_layer)
+ @override
def get_static_bit_length(self) -> Optional[int]:
return self.diag_coded_type.get_static_bit_length()
@@ -56,13 +81,16 @@ def internal_data_type(self) -> DataType:
return self.diag_coded_type.base_data_type
@property
+ @override
def is_required(self) -> bool:
return False
@property
+ @override
def is_settable(self) -> bool:
return False
+ @override
def get_coded_value_as_bytes(self, encode_state: EncodeState) -> bytes:
if self.short_name in encode_state.parameter_values:
if encode_state.parameter_values[self.short_name] not in self.coded_values:
diff --git a/odxtools/parameters/parameter.py b/odxtools/parameters/parameter.py
index 0264a33b..5aa200a4 100644
--- a/odxtools/parameters/parameter.py
+++ b/odxtools/parameters/parameter.py
@@ -1,16 +1,18 @@
# SPDX-License-Identifier: MIT
-import abc
from dataclasses import dataclass
from typing import TYPE_CHECKING, Any, Dict, List, Literal, Optional
+from xml.etree import ElementTree
-from typing_extensions import final
+from typing_extensions import final, override
+from ..createsdgs import create_sdgs_from_et
from ..decodestate import DecodeState
from ..element import NamedElement
from ..encodestate import EncodeState
-from ..odxlink import OdxLinkDatabase, OdxLinkId
+from ..odxlink import OdxDocFragment, OdxLinkDatabase, OdxLinkId
from ..odxtypes import ParameterValue
from ..specialdatagroup import SpecialDataGroup
+from ..utils import dataclass_fields_asdict
if TYPE_CHECKING:
from ..diaglayer import DiagLayer
@@ -32,7 +34,7 @@
@dataclass
-class Parameter(NamedElement, abc.ABC):
+class Parameter(NamedElement):
"""This class corresponds to POSITIONABLE-PARAM in the ODX
specification.
@@ -46,6 +48,28 @@ class Parameter(NamedElement, abc.ABC):
semantic: Optional[str]
sdgs: List[SpecialDataGroup]
+ @staticmethod
+ @override
+ def from_et(et_element: ElementTree.Element, doc_frags: List[OdxDocFragment]) -> "Parameter":
+
+ kwargs = dataclass_fields_asdict(NamedElement.from_et(et_element, doc_frags))
+
+ semantic = et_element.get("SEMANTIC")
+ sdgs = create_sdgs_from_et(et_element.find("SDGS"), doc_frags)
+
+ byte_position_str = et_element.findtext("BYTE-POSITION")
+ bit_position_str = et_element.findtext("BIT-POSITION")
+
+ byte_position = int(byte_position_str) if byte_position_str is not None else None
+ bit_position = int(bit_position_str) if bit_position_str is not None else None
+
+ return Parameter(
+ byte_position=byte_position,
+ bit_position=bit_position,
+ semantic=semantic,
+ sdgs=sdgs,
+ **kwargs)
+
def _build_odxlinks(self) -> Dict[OdxLinkId, Any]:
result = {}
@@ -63,9 +87,9 @@ def _resolve_snrefs(self, diag_layer: "DiagLayer") -> None:
sdg._resolve_snrefs(diag_layer)
@property
- @abc.abstractmethod
def parameter_type(self) -> ParameterType:
- pass
+ raise NotImplementedError(
+ ".parameter_type is not implemented by the concrete parameter class")
def get_static_bit_length(self) -> Optional[int]:
return None
@@ -80,7 +104,7 @@ def is_required(self) -> bool:
specified.
"""
- raise NotImplementedError
+ raise NotImplementedError(".is_required is not implemented by the concrete parameter class")
@property
def is_settable(self) -> bool:
@@ -91,14 +115,14 @@ def is_settable(self) -> bool:
have a default value are settable but not required to be
specified.
"""
- raise NotImplementedError
+ raise NotImplementedError(".is_settable is not implemented by the concrete parameter class")
- @abc.abstractmethod
def get_coded_value_as_bytes(self, encode_state: EncodeState) -> bytes:
"""Get the coded value of the parameter given the encode state.
Note that this method is called by `encode_into_pdu`.
"""
- pass
+ raise NotImplementedError(
+ ".get_coded_value_as_bytes() is not implemented by the concrete parameter class")
@final
def decode_from_pdu(self, decode_state: DecodeState) -> ParameterValue:
diff --git a/odxtools/parameters/parameterwithdop.py b/odxtools/parameters/parameterwithdop.py
index 8474c62d..a222fc84 100644
--- a/odxtools/parameters/parameterwithdop.py
+++ b/odxtools/parameters/parameterwithdop.py
@@ -1,6 +1,7 @@
# SPDX-License-Identifier: MIT
from dataclasses import dataclass
-from typing import TYPE_CHECKING, Any, Dict, Optional
+from typing import TYPE_CHECKING, Any, Dict, List, Optional
+from xml.etree import ElementTree
from typing_extensions import override
@@ -10,9 +11,10 @@
from ..dtcdop import DtcDop
from ..encodestate import EncodeState
from ..exceptions import odxassert, odxrequire
-from ..odxlink import OdxLinkDatabase, OdxLinkId, OdxLinkRef
+from ..odxlink import OdxDocFragment, OdxLinkDatabase, OdxLinkId, OdxLinkRef
from ..odxtypes import ParameterValue
from ..physicaltype import PhysicalType
+from ..utils import dataclass_fields_asdict
from .parameter import Parameter
if TYPE_CHECKING:
@@ -24,14 +26,30 @@ class ParameterWithDOP(Parameter):
dop_ref: Optional[OdxLinkRef]
dop_snref: Optional[str]
+ @staticmethod
+ @override
+ def from_et(et_element: ElementTree.Element,
+ doc_frags: List[OdxDocFragment]) -> "ParameterWithDOP":
+
+ kwargs = dataclass_fields_asdict(Parameter.from_et(et_element, doc_frags))
+
+ dop_ref = OdxLinkRef.from_et(et_element.find("DOP-REF"), doc_frags)
+ dop_snref = None
+ if (dop_snref_elem := et_element.find("DOP-SNREF")) is not None:
+ dop_snref = odxrequire(dop_snref_elem.get("SHORT-NAME"))
+
+ return ParameterWithDOP(dop_ref=dop_ref, dop_snref=dop_snref, **kwargs)
+
def __post_init__(self) -> None:
odxassert(self.dop_snref is not None or self.dop_ref is not None,
f"Param {self.short_name} without a DOP-(SN)REF should not exist!")
self._dop: Optional[DopBase] = None
+ @override
def _build_odxlinks(self) -> Dict[OdxLinkId, Any]:
return super()._build_odxlinks()
+ @override
def _resolve_odxlinks(self, odxlinks: OdxLinkDatabase) -> None:
super()._resolve_odxlinks(odxlinks)
@@ -42,6 +60,7 @@ def _resolve_odxlinks(self, odxlinks: OdxLinkDatabase) -> None:
# (e.g., static and dynamic fields)
self._dop = odxlinks.resolve_lenient(self.dop_ref)
+ @override
def _resolve_snrefs(self, diag_layer: "DiagLayer") -> None:
super()._resolve_snrefs(diag_layer)
@@ -57,6 +76,7 @@ def dop(self) -> DopBase:
self._dop, "Specifying a data object property is mandatory but it "
"could not be resolved")
+ @override
def get_static_bit_length(self) -> Optional[int]:
if self._dop is not None:
return self._dop.get_static_bit_length()
@@ -70,6 +90,7 @@ def physical_type(self) -> Optional[PhysicalType]:
else:
return None
+ @override
def get_coded_value_as_bytes(self, encode_state: EncodeState) -> bytes:
dop = odxrequire(self.dop, "Reference to DOP is not resolved")
physical_value = encode_state.parameter_values[self.short_name]
diff --git a/odxtools/parameters/physicalconstantparameter.py b/odxtools/parameters/physicalconstantparameter.py
index b7ad29fc..c15191a5 100644
--- a/odxtools/parameters/physicalconstantparameter.py
+++ b/odxtools/parameters/physicalconstantparameter.py
@@ -1,6 +1,7 @@
# SPDX-License-Identifier: MIT
from dataclasses import dataclass
-from typing import TYPE_CHECKING, Any, Dict
+from typing import TYPE_CHECKING, Any, Dict, List
+from xml.etree import ElementTree
from typing_extensions import override
@@ -8,8 +9,9 @@
from ..decodestate import DecodeState
from ..encodestate import EncodeState
from ..exceptions import DecodeError, odxraise, odxrequire
-from ..odxlink import OdxLinkDatabase, OdxLinkId
+from ..odxlink import OdxDocFragment, OdxLinkDatabase, OdxLinkId
from ..odxtypes import ParameterValue
+from ..utils import dataclass_fields_asdict
from .parameter import ParameterType
from .parameterwithdop import ParameterWithDOP
@@ -22,16 +24,32 @@ class PhysicalConstantParameter(ParameterWithDOP):
physical_constant_value_raw: str
+ @staticmethod
+ @override
+ def from_et(et_element: ElementTree.Element,
+ doc_frags: List[OdxDocFragment]) -> "PhysicalConstantParameter":
+
+ kwargs = dataclass_fields_asdict(ParameterWithDOP.from_et(et_element, doc_frags))
+
+ physical_constant_value_raw = odxrequire(et_element.findtext("PHYS-CONSTANT-VALUE"))
+
+ return PhysicalConstantParameter(
+ physical_constant_value_raw=physical_constant_value_raw, **kwargs)
+
@property
+ @override
def parameter_type(self) -> ParameterType:
return "PHYS-CONST"
+ @override
def _build_odxlinks(self) -> Dict[OdxLinkId, Any]:
return super()._build_odxlinks()
+ @override
def _resolve_odxlinks(self, odxlinks: OdxLinkDatabase) -> None:
super()._resolve_odxlinks(odxlinks)
+ @override
def _resolve_snrefs(self, diag_layer: "DiagLayer") -> None:
super()._resolve_snrefs(diag_layer)
@@ -46,13 +64,16 @@ def physical_constant_value(self) -> ParameterValue:
return self._physical_constant_value
@property
+ @override
def is_required(self) -> bool:
return False
@property
+ @override
def is_settable(self) -> bool:
return False
+ @override
def get_coded_value_as_bytes(self, encode_state: EncodeState) -> bytes:
dop = odxrequire(self.dop, "Reference to DOP is not resolved")
if (self.short_name in encode_state.parameter_values and
diff --git a/odxtools/parameters/reservedparameter.py b/odxtools/parameters/reservedparameter.py
index d917217a..56af745b 100644
--- a/odxtools/parameters/reservedparameter.py
+++ b/odxtools/parameters/reservedparameter.py
@@ -1,12 +1,16 @@
# SPDX-License-Identifier: MIT
from dataclasses import dataclass
-from typing import Optional
+from typing import List, Optional
+from xml.etree import ElementTree
from typing_extensions import override
from ..decodestate import DecodeState
from ..encodestate import EncodeState
+from ..exceptions import odxrequire
+from ..odxlink import OdxDocFragment
from ..odxtypes import DataType, ParameterValue
+from ..utils import dataclass_fields_asdict
from .parameter import Parameter, ParameterType
@@ -14,21 +18,37 @@
class ReservedParameter(Parameter):
bit_length: int
+ @staticmethod
+ @override
+ def from_et(et_element: ElementTree.Element,
+ doc_frags: List[OdxDocFragment]) -> "ReservedParameter":
+
+ kwargs = dataclass_fields_asdict(Parameter.from_et(et_element, doc_frags))
+
+ bit_length = int(odxrequire(et_element.findtext("BIT-LENGTH")))
+
+ return ReservedParameter(bit_length=bit_length, **kwargs)
+
@property
+ @override
def parameter_type(self) -> ParameterType:
return "RESERVED"
@property
+ @override
def is_required(self) -> bool:
return False
@property
+ @override
def is_settable(self) -> bool:
return False
+ @override
def get_static_bit_length(self) -> Optional[int]:
return self.bit_length
+ @override
def get_coded_value_as_bytes(self, encode_state: EncodeState) -> bytes:
return (0).to_bytes(((self.bit_position or 0) + self.bit_length + 7) // 8, "big")
diff --git a/odxtools/parameters/systemparameter.py b/odxtools/parameters/systemparameter.py
index e8444dd1..34f0615f 100644
--- a/odxtools/parameters/systemparameter.py
+++ b/odxtools/parameters/systemparameter.py
@@ -1,11 +1,16 @@
# SPDX-License-Identifier: MIT
from dataclasses import dataclass
+from typing import List
+from xml.etree import ElementTree
from typing_extensions import override
from ..decodestate import DecodeState
from ..encodestate import EncodeState
+from ..exceptions import odxrequire
+from ..odxlink import OdxDocFragment
from ..odxtypes import ParameterValue
+from ..utils import dataclass_fields_asdict
from .parameter import ParameterType
from .parameterwithdop import ParameterWithDOP
@@ -14,18 +19,33 @@
class SystemParameter(ParameterWithDOP):
sysparam: str
+ @staticmethod
+ @override
+ def from_et(et_element: ElementTree.Element,
+ doc_frags: List[OdxDocFragment]) -> "SystemParameter":
+
+ kwargs = dataclass_fields_asdict(ParameterWithDOP.from_et(et_element, doc_frags))
+
+ sysparam = odxrequire(et_element.findtext("SYSPARAM"))
+
+ return SystemParameter(sysparam=sysparam, **kwargs)
+
@property
+ @override
def parameter_type(self) -> ParameterType:
return "SYSTEM"
@property
+ @override
def is_required(self) -> bool:
raise NotImplementedError("SystemParameter.is_required is not implemented yet.")
@property
+ @override
def is_settable(self) -> bool:
raise NotImplementedError("SystemParameter.is_settable is not implemented yet.")
+ @override
def get_coded_value_as_bytes(self, encode_state: EncodeState) -> bytes:
raise NotImplementedError("Encoding a SystemParameter is not implemented yet.")
diff --git a/odxtools/parameters/tableentryparameter.py b/odxtools/parameters/tableentryparameter.py
index e3447a56..358118ef 100644
--- a/odxtools/parameters/tableentryparameter.py
+++ b/odxtools/parameters/tableentryparameter.py
@@ -1,35 +1,71 @@
# SPDX-License-Identifier: MIT
from dataclasses import dataclass
+from typing import TYPE_CHECKING, List
+from xml.etree import ElementTree
from typing_extensions import override
from ..decodestate import DecodeState
from ..encodestate import EncodeState
-from ..odxlink import OdxLinkRef
+from ..exceptions import odxrequire
+from ..odxlink import OdxDocFragment, OdxLinkDatabase, OdxLinkRef
from ..odxtypes import ParameterValue
+from ..utils import dataclass_fields_asdict
from .parameter import Parameter, ParameterType
+if TYPE_CHECKING:
+ from ..tablerow import TableRow
+
@dataclass
class TableEntryParameter(Parameter):
target: str
table_row_ref: OdxLinkRef
+ @staticmethod
+ @override
+ def from_et(et_element: ElementTree.Element,
+ doc_frags: List[OdxDocFragment]) -> "TableEntryParameter":
+
+ kwargs = dataclass_fields_asdict(Parameter.from_et(et_element, doc_frags))
+
+ target = odxrequire(et_element.findtext("TARGET"))
+ table_row_ref = odxrequire(OdxLinkRef.from_et(et_element.find("TABLE-ROW-REF"), doc_frags))
+
+ return TableEntryParameter(target=target, table_row_ref=table_row_ref, **kwargs)
+
+ @override
+ def _resolve_odxlinks(self, odxlinks: OdxLinkDatabase) -> None:
+ super()._resolve_odxlinks(odxlinks)
+
+ if TYPE_CHECKING:
+ self._table_row = odxlinks.resolve(self.table_row_ref, TableRow)
+ else:
+ self._table_row = odxlinks.resolve(self.table_row_ref)
+
@property
+ @override
def parameter_type(self) -> ParameterType:
return "TABLE-ENTRY"
@property
+ @override
def is_required(self) -> bool:
raise NotImplementedError("TableEntryParameter.is_required is not implemented yet.")
@property
+ @override
def is_settable(self) -> bool:
raise NotImplementedError("TableEntryParameter.is_settable is not implemented yet.")
+ @override
def get_coded_value_as_bytes(self, encode_state: EncodeState) -> bytes:
raise NotImplementedError("Encoding a TableEntryParameter is not implemented yet.")
+ @property
+ def table_row(self) -> "TableRow":
+ return self._table_row
+
@override
def _decode_positioned_from_pdu(self, decode_state: DecodeState) -> ParameterValue:
raise NotImplementedError("Decoding a TableEntryParameter is not implemented yet.")
diff --git a/odxtools/parameters/tablekeyparameter.py b/odxtools/parameters/tablekeyparameter.py
index 85aa442c..e0f2e148 100644
--- a/odxtools/parameters/tablekeyparameter.py
+++ b/odxtools/parameters/tablekeyparameter.py
@@ -1,14 +1,16 @@
# SPDX-License-Identifier: MIT
from dataclasses import dataclass
-from typing import TYPE_CHECKING, Any, Dict, Optional
+from typing import TYPE_CHECKING, Any, Dict, List, Optional
+from xml.etree import ElementTree
from typing_extensions import override
from ..decodestate import DecodeState
from ..encodestate import EncodeState
from ..exceptions import DecodeError, EncodeError, odxraise, odxrequire
-from ..odxlink import OdxLinkDatabase, OdxLinkId, OdxLinkRef
+from ..odxlink import OdxDocFragment, OdxLinkDatabase, OdxLinkId, OdxLinkRef
from ..odxtypes import ParameterValue
+from ..utils import dataclass_fields_asdict
from .parameter import Parameter, ParameterType
if TYPE_CHECKING:
@@ -26,6 +28,33 @@ class TableKeyParameter(Parameter):
table_row_snref: Optional[str]
table_row_ref: Optional[OdxLinkRef]
+ @staticmethod
+ @override
+ def from_et(et_element: ElementTree.Element,
+ doc_frags: List[OdxDocFragment]) -> "TableKeyParameter":
+
+ kwargs = dataclass_fields_asdict(Parameter.from_et(et_element, doc_frags))
+
+ odx_id = odxrequire(OdxLinkId.from_et(et_element, doc_frags))
+
+ table_ref = OdxLinkRef.from_et(et_element.find("TABLE-REF"), doc_frags)
+ table_snref = None
+ if (table_snref_elem := et_element.find("TABLE-SNREF")) is not None:
+ table_snref = odxrequire(table_snref_elem.get("SHORT-NAME"))
+
+ table_row_ref = OdxLinkRef.from_et(et_element.find("TABLE-ROW-REF"), doc_frags)
+ table_row_snref = None
+ if (table_row_snref_elem := et_element.find("TABLE-ROW-SNREF")) is not None:
+ table_row_snref = odxrequire(table_row_snref_elem.get("SHORT-NAME"))
+
+ return TableKeyParameter(
+ odx_id=odx_id,
+ table_ref=table_ref,
+ table_snref=table_snref,
+ table_row_ref=table_row_ref,
+ table_row_snref=table_row_snref,
+ **kwargs)
+
def __post_init__(self) -> None:
self._table: "Table"
self._table_row: Optional["TableRow"] = None
@@ -34,9 +63,11 @@ def __post_init__(self) -> None:
odxraise("Either a table or a table row must be defined.")
@property
+ @override
def parameter_type(self) -> ParameterType:
return "TABLE-KEY"
+ @override
def _build_odxlinks(self) -> Dict[OdxLinkId, Any]:
result = super()._build_odxlinks()
@@ -44,6 +75,7 @@ def _build_odxlinks(self) -> Dict[OdxLinkId, Any]:
return result
+ @override
def _resolve_odxlinks(self, odxlinks: OdxLinkDatabase) -> None:
super()._resolve_odxlinks(odxlinks)
@@ -61,6 +93,7 @@ def _resolve_odxlinks(self, odxlinks: OdxLinkDatabase) -> None:
self._table_row = odxlinks.resolve(self.table_row_ref)
self._table = self._table_row.table
+ @override
def _resolve_snrefs(self, diag_layer: "DiagLayer") -> None:
super()._resolve_snrefs(diag_layer)
@@ -88,15 +121,18 @@ def table_row(self) -> Optional["TableRow"]:
return self._table_row
@property
+ @override
def is_required(self) -> bool:
# TABLE-KEY parameters can be implicitly determined from the
# corresponding TABLE-STRUCT
return False
@property
+ @override
def is_settable(self) -> bool:
return True
+ @override
def get_coded_value_as_bytes(self, encode_state: EncodeState) -> bytes:
tr_short_name = encode_state.parameter_values.get(self.short_name)
@@ -134,6 +170,7 @@ def get_coded_value_as_bytes(self, encode_state: EncodeState) -> bytes:
bit_position = 0 if self.bit_position is None else self.bit_position
return key_dop.convert_physical_to_bytes(tr.key, encode_state, bit_position=bit_position)
+ @override
def encode_into_pdu(self, encode_state: EncodeState) -> bytes:
return super().encode_into_pdu(encode_state)
diff --git a/odxtools/parameters/tablestructparameter.py b/odxtools/parameters/tablestructparameter.py
index 4a5f8481..ecb0cdb8 100644
--- a/odxtools/parameters/tablestructparameter.py
+++ b/odxtools/parameters/tablestructparameter.py
@@ -1,15 +1,17 @@
# SPDX-License-Identifier: MIT
import warnings
from dataclasses import dataclass
-from typing import TYPE_CHECKING, Any, Dict, Optional, cast
+from typing import TYPE_CHECKING, Any, Dict, List, Optional, cast
+from xml.etree import ElementTree
from typing_extensions import override
from ..decodestate import DecodeState
from ..encodestate import EncodeState
-from ..exceptions import DecodeError, EncodeError, OdxWarning, odxraise
-from ..odxlink import OdxLinkDatabase, OdxLinkId, OdxLinkRef
+from ..exceptions import DecodeError, EncodeError, OdxWarning, odxraise, odxrequire
+from ..odxlink import OdxDocFragment, OdxLinkDatabase, OdxLinkId, OdxLinkRef
from ..odxtypes import ParameterValue
+from ..utils import dataclass_fields_asdict
from .parameter import Parameter, ParameterType
from .tablekeyparameter import TableKeyParameter
@@ -23,23 +25,42 @@ class TableStructParameter(Parameter):
table_key_ref: Optional[OdxLinkRef]
table_key_snref: Optional[str]
+ @staticmethod
+ @override
+ def from_et(et_element: ElementTree.Element,
+ doc_frags: List[OdxDocFragment]) -> "TableStructParameter":
+
+ kwargs = dataclass_fields_asdict(Parameter.from_et(et_element, doc_frags))
+
+ table_key_ref = OdxLinkRef.from_et(et_element.find("TABLE-KEY-REF"), doc_frags)
+ table_key_snref = None
+ if (table_key_snref_elem := et_element.find("TABLE-KEY-SNREF")) is not None:
+ table_key_snref = odxrequire(table_key_snref_elem.get("SHORT-NAME"))
+
+ return TableStructParameter(
+ table_key_ref=table_key_ref, table_key_snref=table_key_snref, **kwargs)
+
def __post_init__(self) -> None:
if self.table_key_ref is None and self.table_key_snref is None:
odxraise("Either table_key_ref or table_key_snref must be defined.")
@property
+ @override
def parameter_type(self) -> ParameterType:
return "TABLE-STRUCT"
+ @override
def _build_odxlinks(self) -> Dict[OdxLinkId, Any]:
return super()._build_odxlinks()
+ @override
def _resolve_odxlinks(self, odxlinks: OdxLinkDatabase) -> None:
super()._resolve_odxlinks(odxlinks)
if self.table_key_ref is not None:
self._table_key = odxlinks.resolve(self.table_key_ref, TableKeyParameter)
+ @override
def _resolve_snrefs(self, diag_layer: "DiagLayer") -> None:
super()._resolve_snrefs(diag_layer)
@@ -55,13 +76,16 @@ def table_key(self) -> TableKeyParameter:
return self._table_key
@property
+ @override
def is_required(self) -> bool:
return True
@property
+ @override
def is_settable(self) -> bool:
return True
+ @override
def get_coded_value_as_bytes(self, encode_state: EncodeState) -> bytes:
physical_value = encode_state.parameter_values.get(self.short_name)
@@ -124,6 +148,7 @@ def get_coded_value_as_bytes(self, encode_state: EncodeState) -> bytes:
return tr.dop.convert_physical_to_bytes(
tr_value, encode_state=encode_state, bit_position=bit_position)
+ @override
def encode_into_pdu(self, encode_state: EncodeState) -> bytes:
return super().encode_into_pdu(encode_state)
diff --git a/odxtools/parameters/valueparameter.py b/odxtools/parameters/valueparameter.py
index ecfb7196..3f439e0f 100644
--- a/odxtools/parameters/valueparameter.py
+++ b/odxtools/parameters/valueparameter.py
@@ -1,12 +1,16 @@
# SPDX-License-Identifier: MIT
from dataclasses import dataclass
-from typing import TYPE_CHECKING, Any, Dict, Optional
+from typing import TYPE_CHECKING, Any, Dict, List, Optional
+from xml.etree import ElementTree
+
+from typing_extensions import override
from ..dataobjectproperty import DataObjectProperty
from ..encodestate import EncodeState
from ..exceptions import odxraise, odxrequire
-from ..odxlink import OdxLinkDatabase, OdxLinkId
+from ..odxlink import OdxDocFragment, OdxLinkDatabase, OdxLinkId
from ..odxtypes import AtomicOdxType
+from ..utils import dataclass_fields_asdict
from .parameter import ParameterType
from .parameterwithdop import ParameterWithDOP
@@ -21,16 +25,31 @@ class ValueParameter(ParameterWithDOP):
def __post_init__(self) -> None:
self._physical_default_value: Optional[AtomicOdxType] = None
+ @staticmethod
+ @override
+ def from_et(et_element: ElementTree.Element,
+ doc_frags: List[OdxDocFragment]) -> "ValueParameter":
+
+ kwargs = dataclass_fields_asdict(ParameterWithDOP.from_et(et_element, doc_frags))
+
+ physical_default_value_raw = et_element.findtext("PHYSICAL-DEFAULT-VALUE")
+
+ return ValueParameter(physical_default_value_raw=physical_default_value_raw, **kwargs)
+
@property
+ @override
def parameter_type(self) -> ParameterType:
return "VALUE"
+ @override
def _build_odxlinks(self) -> Dict[OdxLinkId, Any]:
return super()._build_odxlinks()
+ @override
def _resolve_odxlinks(self, odxlinks: OdxLinkDatabase) -> None:
super()._resolve_odxlinks(odxlinks)
+ @override
def _resolve_snrefs(self, diag_layer: "DiagLayer") -> None:
super()._resolve_snrefs(diag_layer)
@@ -48,13 +67,16 @@ def physical_default_value(self) -> Optional[AtomicOdxType]:
return self._physical_default_value
@property
+ @override
def is_required(self) -> bool:
return self._physical_default_value is None
@property
+ @override
def is_settable(self) -> bool:
return True
+ @override
def get_coded_value_as_bytes(self, encode_state: EncodeState) -> bytes:
physical_value = encode_state.parameter_values.get(self.short_name,
self.physical_default_value)
diff --git a/odxtools/templates/macros/printParam.xml.jinja2 b/odxtools/templates/macros/printParam.xml.jinja2
index f8bf0e83..ed68b4a6 100644
--- a/odxtools/templates/macros/printParam.xml.jinja2
+++ b/odxtools/templates/macros/printParam.xml.jinja2
@@ -8,17 +8,17 @@
{%- import('macros/printSpecialData.xml.jinja2') as psd %}
{%- macro printParam(param) -%}
-{%- if param.semantic is not none %}
-{%- set semattrib = " SEMANTIC=\""+param.semantic+"\"" -%}
-{%- else %}
-{%- set semattrib = "" -%}
-{%- endif -%}
-{%- if param.parameter_type == "TABLE-KEY" and param.odx_id is not none %}
-
+{%- set semattrib = make_xml_attrib("SEMANTIC", param.semantic) -%}
+{%- if param.parameter_type == "TABLE-KEY" %}
+
{%- elif param.parameter_type == "SYSTEM" %}
-
+
{%- else %}
-
+
{%- endif%}
{{ peid.printElementIdSubtags(param)|indent(1) }}
{{- psd.printSpecialDataGroups(param.sdgs)|indent(1, first=True) }}