odxtools 9.4.1__py3-none-any.whl → 9.6.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (113) hide show
  1. odxtools/additionalaudience.py +2 -2
  2. odxtools/admindata.py +3 -0
  3. odxtools/audience.py +9 -13
  4. odxtools/basecomparam.py +1 -2
  5. odxtools/basevariantpattern.py +38 -0
  6. odxtools/basicstructure.py +34 -35
  7. odxtools/commrelation.py +2 -1
  8. odxtools/companydata.py +1 -2
  9. odxtools/companyspecificinfo.py +3 -0
  10. odxtools/comparam.py +16 -8
  11. odxtools/comparaminstance.py +12 -12
  12. odxtools/comparamspec.py +4 -3
  13. odxtools/comparamsubset.py +26 -24
  14. odxtools/compumethods/compuconst.py +4 -4
  15. odxtools/compumethods/limit.py +9 -9
  16. odxtools/compumethods/linearsegment.py +8 -8
  17. odxtools/dataobjectproperty.py +16 -18
  18. odxtools/description.py +4 -2
  19. odxtools/determinenumberofitems.py +4 -4
  20. odxtools/diagcodedtype.py +20 -20
  21. odxtools/diagcomm.py +61 -41
  22. odxtools/diagdatadictionaryspec.py +51 -55
  23. odxtools/diaglayercontainer.py +25 -25
  24. odxtools/diaglayers/basevariant.py +5 -0
  25. odxtools/diaglayers/basevariantraw.py +7 -1
  26. odxtools/diaglayers/diaglayerraw.py +26 -27
  27. odxtools/diaglayers/ecuvariant.py +16 -0
  28. odxtools/diagnostictroublecode.py +13 -10
  29. odxtools/diagservice.py +48 -50
  30. odxtools/diagvariable.py +10 -8
  31. odxtools/docrevision.py +5 -5
  32. odxtools/dtcdop.py +17 -17
  33. odxtools/dynamicendmarkerfield.py +8 -8
  34. odxtools/dynamiclengthfield.py +2 -0
  35. odxtools/dyndefinedspec.py +21 -8
  36. odxtools/ecuvariantpattern.py +20 -9
  37. odxtools/encodestate.py +3 -3
  38. odxtools/endofpdufield.py +7 -9
  39. odxtools/environmentdatadescription.py +9 -20
  40. odxtools/field.py +21 -21
  41. odxtools/inputparam.py +15 -14
  42. odxtools/leadinglengthinfotype.py +4 -4
  43. odxtools/matchingbasevariantparameter.py +38 -0
  44. odxtools/matchingparameter.py +108 -28
  45. odxtools/minmaxlengthtype.py +6 -6
  46. odxtools/multiplexer.py +38 -39
  47. odxtools/multiplexercase.py +3 -6
  48. odxtools/multiplexerdefaultcase.py +3 -6
  49. odxtools/multiplexerswitchkey.py +4 -4
  50. odxtools/negoutputparam.py +6 -9
  51. odxtools/odxlink.py +21 -5
  52. odxtools/odxtypes.py +7 -6
  53. odxtools/outputparam.py +9 -8
  54. odxtools/parameterinfo.py +1 -1
  55. odxtools/parameters/codedconstparameter.py +28 -27
  56. odxtools/parameters/dynamicparameter.py +9 -9
  57. odxtools/parameters/lengthkeyparameter.py +18 -18
  58. odxtools/parameters/matchingrequestparameter.py +15 -15
  59. odxtools/parameters/nrcconstparameter.py +32 -24
  60. odxtools/parameters/parameter.py +35 -37
  61. odxtools/parameters/parameterwithdop.py +6 -6
  62. odxtools/parameters/physicalconstantparameter.py +19 -20
  63. odxtools/parameters/reservedparameter.py +10 -11
  64. odxtools/parameters/systemparameter.py +10 -11
  65. odxtools/parameters/tableentryparameter.py +19 -20
  66. odxtools/parameters/tablekeyparameter.py +0 -2
  67. odxtools/parameters/tablestructparameter.py +27 -21
  68. odxtools/parameters/valueparameter.py +20 -20
  69. odxtools/parentref.py +6 -7
  70. odxtools/physicaldimension.py +11 -11
  71. odxtools/physicaltype.py +9 -14
  72. odxtools/preconditionstateref.py +85 -0
  73. odxtools/progcode.py +1 -2
  74. odxtools/protstack.py +4 -4
  75. odxtools/relateddoc.py +3 -4
  76. odxtools/scaleconstr.py +0 -1
  77. odxtools/singleecujob.py +8 -4
  78. odxtools/specialdata.py +10 -9
  79. odxtools/specialdatagroup.py +1 -0
  80. odxtools/standardlengthtype.py +18 -18
  81. odxtools/statechart.py +10 -6
  82. odxtools/statemachine.py +186 -0
  83. odxtools/statetransitionref.py +231 -0
  84. odxtools/structure.py +4 -4
  85. odxtools/subcomponent.py +78 -11
  86. odxtools/table.py +23 -13
  87. odxtools/tablerow.py +86 -69
  88. odxtools/teammember.py +4 -4
  89. odxtools/templates/macros/printBaseVariant.xml.jinja2 +4 -9
  90. odxtools/templates/macros/printBaseVariantPattern.xml.jinja2 +32 -0
  91. odxtools/templates/macros/printCompanyData.xml.jinja2 +2 -2
  92. odxtools/templates/macros/printComparam.xml.jinja2 +3 -5
  93. odxtools/templates/macros/printDOP.xml.jinja2 +4 -1
  94. odxtools/templates/macros/printDiagComm.xml.jinja2 +6 -5
  95. odxtools/templates/macros/printEcuVariantPattern.xml.jinja2 +7 -6
  96. odxtools/templates/macros/printParam.xml.jinja2 +5 -5
  97. odxtools/templates/macros/printPreConditionStateRef.xml.jinja2 +18 -0
  98. odxtools/templates/macros/printStateTransitionRef.xml.jinja2 +18 -0
  99. odxtools/templates/macros/printTable.xml.jinja2 +13 -9
  100. odxtools/text.py +35 -0
  101. odxtools/unit.py +1 -3
  102. odxtools/unitgroup.py +6 -8
  103. odxtools/variantmatcher.py +209 -0
  104. odxtools/variantpattern.py +38 -0
  105. odxtools/version.py +2 -2
  106. {odxtools-9.4.1.dist-info → odxtools-9.6.0.dist-info}/METADATA +3 -2
  107. {odxtools-9.4.1.dist-info → odxtools-9.6.0.dist-info}/RECORD +111 -102
  108. {odxtools-9.4.1.dist-info → odxtools-9.6.0.dist-info}/WHEEL +1 -1
  109. odxtools/createecuvariantpatterns.py +0 -18
  110. odxtools/ecuvariantmatcher.py +0 -171
  111. {odxtools-9.4.1.dist-info → odxtools-9.6.0.dist-info}/entry_points.txt +0 -0
  112. {odxtools-9.4.1.dist-info → odxtools-9.6.0.dist-info/licenses}/LICENSE +0 -0
  113. {odxtools-9.4.1.dist-info → odxtools-9.6.0.dist-info}/top_level.txt +0 -0
@@ -12,7 +12,7 @@ from .encodestate import EncodeState
12
12
  from .encoding import get_string_encoding
13
13
  from .exceptions import DecodeError, EncodeError, odxassert, odxraise, odxrequire
14
14
  from .odxlink import OdxDocFragment
15
- from .odxtypes import AtomicOdxType, DataType
15
+ from .odxtypes import AtomicOdxType, BytesTypes, DataType
16
16
  from .utils import dataclass_fields_asdict
17
17
 
18
18
 
@@ -24,8 +24,8 @@ class Termination(Enum):
24
24
 
25
25
  @dataclass
26
26
  class MinMaxLengthType(DiagCodedType):
27
- min_length: int
28
27
  max_length: Optional[int]
28
+ min_length: int
29
29
  termination: Termination
30
30
 
31
31
  @property
@@ -38,12 +38,12 @@ class MinMaxLengthType(DiagCodedType):
38
38
  doc_frags: List[OdxDocFragment]) -> "MinMaxLengthType":
39
39
  kwargs = dataclass_fields_asdict(DiagCodedType.from_et(et_element, doc_frags))
40
40
 
41
- min_length = int(odxrequire(et_element.findtext("MIN-LENGTH")))
42
41
  max_length = None
43
42
  if et_element.find("MAX-LENGTH") is not None:
44
43
  max_length = int(odxrequire(et_element.findtext("MAX-LENGTH")))
44
+ min_length = int(odxrequire(et_element.findtext("MIN-LENGTH")))
45
45
 
46
- termination_str = odxrequire(et_element.get("TERMINATION"))
46
+ termination_str = odxrequire(et_element.attrib.get("TERMINATION"))
47
47
  try:
48
48
  termination = Termination(termination_str)
49
49
  except ValueError:
@@ -51,7 +51,7 @@ class MinMaxLengthType(DiagCodedType):
51
51
  odxraise(f"Encountered unknown termination type '{termination_str}'")
52
52
 
53
53
  return MinMaxLengthType(
54
- min_length=min_length, max_length=max_length, termination=termination, **kwargs)
54
+ max_length=max_length, min_length=min_length, termination=termination, **kwargs)
55
55
 
56
56
  def __post_init__(self) -> None:
57
57
  odxassert(self.max_length is None or self.min_length <= self.max_length)
@@ -83,7 +83,7 @@ class MinMaxLengthType(DiagCodedType):
83
83
  @override
84
84
  def encode_into_pdu(self, internal_value: AtomicOdxType, encode_state: EncodeState) -> None:
85
85
 
86
- if not isinstance(internal_value, (bytes, str, bytearray)):
86
+ if not isinstance(internal_value, (str, BytesTypes)):
87
87
  odxraise("MinMaxLengthType is currently only implemented for strings and byte arrays",
88
88
  EncodeError)
89
89
 
odxtools/multiplexer.py CHANGED
@@ -34,12 +34,15 @@ class Multiplexer(ComplexDop):
34
34
  cases: NamedItemList[MultiplexerCase]
35
35
  is_visible_raw: Optional[bool]
36
36
 
37
+ @property
38
+ def is_visible(self) -> bool:
39
+ return self.is_visible_raw is True
40
+
37
41
  @staticmethod
38
42
  @override
39
43
  def from_et(et_element: ElementTree.Element, doc_frags: List[OdxDocFragment]) -> "Multiplexer":
40
44
  """Reads a Multiplexer from Diag Layer."""
41
- base_obj = ComplexDop.from_et(et_element, doc_frags)
42
- kwargs = dataclass_fields_asdict(base_obj)
45
+ kwargs = dataclass_fields_asdict(ComplexDop.from_et(et_element, doc_frags))
43
46
 
44
47
  byte_position = int(et_element.findtext("BYTE-POSITION", "0"))
45
48
  switch_key = MultiplexerSwitchKey.from_et(
@@ -55,16 +58,45 @@ class Multiplexer(ComplexDop):
55
58
  is_visible_raw = odxstr_to_bool(et_element.get("IS-VISIBLE"))
56
59
 
57
60
  return Multiplexer(
58
- is_visible_raw=is_visible_raw,
59
61
  byte_position=byte_position,
60
62
  switch_key=switch_key,
61
63
  default_case=default_case,
62
64
  cases=cases,
65
+ is_visible_raw=is_visible_raw,
63
66
  **kwargs)
64
67
 
65
- @property
66
- def is_visible(self) -> bool:
67
- return self.is_visible_raw is True
68
+ @override
69
+ def _build_odxlinks(self) -> Dict[OdxLinkId, Any]:
70
+ odxlinks = super()._build_odxlinks()
71
+
72
+ odxlinks.update(self.switch_key._build_odxlinks())
73
+ if self.default_case is not None:
74
+ odxlinks.update(self.default_case._build_odxlinks())
75
+
76
+ return odxlinks
77
+
78
+ @override
79
+ def _resolve_odxlinks(self, odxlinks: OdxLinkDatabase) -> None:
80
+ super()._resolve_odxlinks(odxlinks)
81
+
82
+ self.switch_key._resolve_odxlinks(odxlinks)
83
+ if self.default_case is not None:
84
+ self.default_case._resolve_odxlinks(odxlinks)
85
+
86
+ for mux_case in self.cases:
87
+ mux_case._mux_case_resolve_odxlinks(
88
+ odxlinks, key_physical_type=self.switch_key.dop.physical_type.base_data_type)
89
+
90
+ @override
91
+ def _resolve_snrefs(self, context: SnRefContext) -> None:
92
+ super()._resolve_snrefs(context)
93
+
94
+ self.switch_key._resolve_snrefs(context)
95
+ if self.default_case is not None:
96
+ self.default_case._resolve_snrefs(context)
97
+
98
+ for mux_case in self.cases:
99
+ mux_case._resolve_snrefs(context)
68
100
 
69
101
  def _get_case_limits(self, case: MultiplexerCase) -> Tuple[AtomicOdxType, AtomicOdxType]:
70
102
  key_type = self.switch_key.dop.physical_type.base_data_type
@@ -203,36 +235,3 @@ class Multiplexer(ComplexDop):
203
235
  decode_state.origin_byte_position = orig_origin
204
236
 
205
237
  return result
206
-
207
- @override
208
- def _build_odxlinks(self) -> Dict[OdxLinkId, Any]:
209
- odxlinks = super()._build_odxlinks()
210
-
211
- odxlinks.update(self.switch_key._build_odxlinks())
212
- if self.default_case is not None:
213
- odxlinks.update(self.default_case._build_odxlinks())
214
-
215
- return odxlinks
216
-
217
- @override
218
- def _resolve_odxlinks(self, odxlinks: OdxLinkDatabase) -> None:
219
- super()._resolve_odxlinks(odxlinks)
220
-
221
- self.switch_key._resolve_odxlinks(odxlinks)
222
- if self.default_case is not None:
223
- self.default_case._resolve_odxlinks(odxlinks)
224
-
225
- for mux_case in self.cases:
226
- mux_case._mux_case_resolve_odxlinks(
227
- odxlinks, key_physical_type=self.switch_key.dop.physical_type.base_data_type)
228
-
229
- @override
230
- def _resolve_snrefs(self, context: SnRefContext) -> None:
231
- super()._resolve_snrefs(context)
232
-
233
- self.switch_key._resolve_snrefs(context)
234
- if self.default_case is not None:
235
- self.default_case._resolve_snrefs(context)
236
-
237
- for mux_case in self.cases:
238
- mux_case._resolve_snrefs(context)
@@ -22,8 +22,9 @@ class MultiplexerCase(NamedElement):
22
22
  lower_limit: Limit
23
23
  upper_limit: Limit
24
24
 
25
- def __post_init__(self) -> None:
26
- self._structure: Optional[Structure]
25
+ @property
26
+ def structure(self) -> Optional[Structure]:
27
+ return self._structure
27
28
 
28
29
  @staticmethod
29
30
  def from_et(et_element: ElementTree.Element,
@@ -77,7 +78,3 @@ class MultiplexerCase(NamedElement):
77
78
  def applies(self, value: AtomicOdxType) -> bool:
78
79
  return self.lower_limit.complies_to_lower(value) \
79
80
  and self.upper_limit.complies_to_upper(value)
80
-
81
- @property
82
- def structure(self) -> Optional[Structure]:
83
- return self._structure
@@ -17,8 +17,9 @@ class MultiplexerDefaultCase(NamedElement):
17
17
  structure_ref: Optional[OdxLinkRef]
18
18
  structure_snref: Optional[str]
19
19
 
20
- def __post_init__(self) -> None:
21
- self._structure: Optional[Structure]
20
+ @property
21
+ def structure(self) -> Optional[Structure]:
22
+ return self._structure
22
23
 
23
24
  @staticmethod
24
25
  def from_et(et_element: ElementTree.Element,
@@ -46,7 +47,3 @@ class MultiplexerDefaultCase(NamedElement):
46
47
  if self.structure_snref:
47
48
  ddds = odxrequire(context.diag_layer).diag_data_dictionary_spec
48
49
  self._structure = resolve_snref(self.structure_snref, ddds.structures, Structure)
49
-
50
- @property
51
- def structure(self) -> Optional[Structure]:
52
- return self._structure
@@ -18,6 +18,10 @@ class MultiplexerSwitchKey:
18
18
  bit_position: Optional[int]
19
19
  dop_ref: OdxLinkRef
20
20
 
21
+ @property
22
+ def dop(self) -> DataObjectProperty:
23
+ return self._dop
24
+
21
25
  @staticmethod
22
26
  def from_et(et_element: ElementTree.Element,
23
27
  doc_frags: List[OdxDocFragment]) -> "MultiplexerSwitchKey":
@@ -40,7 +44,3 @@ class MultiplexerSwitchKey:
40
44
 
41
45
  def _resolve_snrefs(self, context: SnRefContext) -> None:
42
46
  pass
43
-
44
- @property
45
- def dop(self) -> DataObjectProperty:
46
- return self._dop
@@ -1,6 +1,6 @@
1
1
  # SPDX-License-Identifier: MIT
2
2
  from dataclasses import dataclass
3
- from typing import Any, Dict, List, Optional
3
+ from typing import Any, Dict, List
4
4
  from xml.etree import ElementTree
5
5
 
6
6
  from .dopbase import DopBase
@@ -15,8 +15,10 @@ from .utils import dataclass_fields_asdict
15
15
  class NegOutputParam(NamedElement):
16
16
  dop_base_ref: OdxLinkRef
17
17
 
18
- def __post_init__(self) -> None:
19
- self._dop: Optional[DopBase] = None
18
+ @property
19
+ def dop(self) -> DopBase:
20
+ """The data object property describing this parameter."""
21
+ return self._dop
20
22
 
21
23
  @staticmethod
22
24
  def from_et(et_element: ElementTree.Element,
@@ -31,12 +33,7 @@ class NegOutputParam(NamedElement):
31
33
  return {}
32
34
 
33
35
  def _resolve_odxlinks(self, odxlinks: OdxLinkDatabase) -> None:
34
- self._dop = odxlinks.resolve(self.dop_base_ref)
36
+ self._dop = odxlinks.resolve(self.dop_base_ref, DopBase)
35
37
 
36
38
  def _resolve_snrefs(self, context: SnRefContext) -> None:
37
39
  pass
38
-
39
- @property
40
- def dop(self) -> Optional[DopBase]:
41
- """The data object property describing this parameter."""
42
- return self._dop
odxtools/odxlink.py CHANGED
@@ -277,24 +277,40 @@ class OdxLinkDatabase:
277
277
  @overload
278
278
  def resolve_snref(target_short_name: str,
279
279
  items: Iterable[OdxNamed],
280
- expected_type: None = None) -> Any:
280
+ expected_type: None = None,
281
+ *,
282
+ lenient: None = None) -> Any:
281
283
  """Resolve a short name reference given a sequence of candidate objects"""
282
284
  ...
283
285
 
284
286
 
285
287
  @overload
286
- def resolve_snref(target_short_name: str, items: Iterable[OdxNamed],
287
- expected_type: Type[TNamed]) -> TNamed:
288
+ def resolve_snref(target_short_name: str,
289
+ items: Iterable[OdxNamed],
290
+ expected_type: Type[TNamed],
291
+ *,
292
+ lenient: None = None) -> TNamed:
293
+ ...
294
+
295
+
296
+ @overload
297
+ def resolve_snref(target_short_name: str,
298
+ items: Iterable[OdxNamed],
299
+ expected_type: Type[TNamed],
300
+ *,
301
+ lenient: bool = True) -> Optional[TNamed]:
288
302
  ...
289
303
 
290
304
 
291
305
  def resolve_snref(target_short_name: str,
292
306
  items: Iterable[OdxNamed],
293
- expected_type: Any = None) -> Any:
307
+ expected_type: Any = None,
308
+ lenient: Optional[bool] = None) -> Any:
294
309
  candidates = [x for x in items if x.short_name == target_short_name]
295
310
 
296
311
  if not candidates:
297
- odxraise(f"Cannot resolve short name reference to '{target_short_name}'")
312
+ if not lenient:
313
+ odxraise(f"Cannot resolve short name reference to '{target_short_name}'")
298
314
  return None
299
315
  elif len(candidates) > 1:
300
316
  odxraise(f"Cannot uniquely resolve short name reference to '{target_short_name}'")
odxtools/odxtypes.py CHANGED
@@ -1,7 +1,7 @@
1
1
  # SPDX-License-Identifier: MIT
2
2
  from enum import Enum
3
- from typing import (TYPE_CHECKING, Any, Callable, Dict, Iterable, Optional, Tuple, Type, Union,
4
- overload)
3
+ from typing import (TYPE_CHECKING, Any, Callable, Dict, Iterable, Optional, SupportsBytes, Tuple,
4
+ Type, Union, overload)
5
5
  from xml.etree import ElementTree
6
6
 
7
7
  from .exceptions import odxassert, odxraise, odxrequire
@@ -16,7 +16,8 @@ def bytefield_to_bytearray(bytefield: str) -> bytearray:
16
16
  return bytearray([int(x, 16) for x in bytes_string])
17
17
 
18
18
 
19
- AtomicOdxType = Union[str, int, float, bytes]
19
+ BytesTypes = (bytearray, bytes, SupportsBytes)
20
+ AtomicOdxType = Union[str, int, float, bytearray, bytes]
20
21
 
21
22
  # dictionary mapping short names to a Parameter that needs to be
22
23
  # specified. Complex parameters (structures) may contain
@@ -135,8 +136,8 @@ def compare_odx_values(a: AtomicOdxType, b: AtomicOdxType) -> int:
135
136
 
136
137
  # bytefields are treated like long integers: to pad the shorter
137
138
  # object with zeros and treat the results like strings.
138
- if isinstance(a, (bytes, bytearray)):
139
- if not isinstance(b, (bytes, bytearray)):
139
+ if isinstance(a, BytesTypes):
140
+ if not isinstance(b, BytesTypes):
140
141
  odxraise()
141
142
 
142
143
  obj_len = max(len(a), len(b))
@@ -237,7 +238,7 @@ class DataType(Enum):
237
238
  return True
238
239
  elif expected_type is float and isinstance(value, (int, float)):
239
240
  return True
240
- elif self == DataType.A_BYTEFIELD and isinstance(value, (bytearray, bytes)):
241
+ elif self == DataType.A_BYTEFIELD and isinstance(value, BytesTypes):
241
242
  return True
242
243
  else:
243
244
  return False
odxtools/outputparam.py CHANGED
@@ -18,10 +18,19 @@ class OutputParam(IdentifiableElement):
18
18
  dop_base_ref: OdxLinkRef
19
19
  semantic: Optional[str]
20
20
 
21
+ @property
22
+ def dop(self) -> DopBase:
23
+ return self._dop
24
+
25
+ @deprecated(details="use .dop") # type: ignore[misc]
26
+ def dop_base(self) -> DopBase:
27
+ return self._dop
28
+
21
29
  @staticmethod
22
30
  def from_et(et_element: ElementTree.Element, doc_frags: List[OdxDocFragment]) -> "OutputParam":
23
31
 
24
32
  kwargs = dataclass_fields_asdict(IdentifiableElement.from_et(et_element, doc_frags))
33
+
25
34
  dop_base_ref = odxrequire(OdxLinkRef.from_et(et_element.find("DOP-BASE-REF"), doc_frags))
26
35
  semantic = et_element.get("SEMANTIC")
27
36
 
@@ -35,11 +44,3 @@ class OutputParam(IdentifiableElement):
35
44
 
36
45
  def _resolve_snrefs(self, context: SnRefContext) -> None:
37
46
  pass
38
-
39
- @property
40
- def dop(self) -> DopBase:
41
- return self._dop
42
-
43
- @deprecated(details="use .dop") # type: ignore[misc]
44
- def dop_base(self) -> DopBase:
45
- return self._dop
odxtools/parameterinfo.py CHANGED
@@ -73,7 +73,7 @@ def parameter_info(param_list: Iterable[Parameter], quoted_names: bool = False)
73
73
  of = StringIO()
74
74
  for param in param_list:
75
75
  if isinstance(param, CodedConstParameter):
76
- of.write(f"{q}{param.short_name}{q}: const = {param._coded_value_str}\n")
76
+ of.write(f"{q}{param.short_name}{q}: const = {param.coded_value_raw}\n")
77
77
  continue
78
78
  elif isinstance(param, MatchingRequestParameter):
79
79
  of.write(f"{q}{param.short_name}{q}: <matches request>\n")
@@ -1,7 +1,7 @@
1
1
  # SPDX-License-Identifier: MIT
2
2
  import warnings
3
3
  from dataclasses import dataclass
4
- from typing import Any, Dict, List, Optional
4
+ from typing import Any, Dict, List, Optional, cast
5
5
  from xml.etree import ElementTree
6
6
 
7
7
  from typing_extensions import override
@@ -20,8 +20,27 @@ from .parameter import Parameter, ParameterType
20
20
  @dataclass
21
21
  class CodedConstParameter(Parameter):
22
22
 
23
+ coded_value_raw: str
23
24
  diag_coded_type: DiagCodedType
24
- coded_value: AtomicOdxType
25
+
26
+ @property
27
+ @override
28
+ def parameter_type(self) -> ParameterType:
29
+ return "CODED-CONST"
30
+
31
+ @property
32
+ @override
33
+ def is_required(self) -> bool:
34
+ return False
35
+
36
+ @property
37
+ @override
38
+ def is_settable(self) -> bool:
39
+ return False
40
+
41
+ @property
42
+ def coded_value(self) -> AtomicOdxType:
43
+ return self._coded_value
25
44
 
26
45
  @staticmethod
27
46
  @override
@@ -30,18 +49,16 @@ class CodedConstParameter(Parameter):
30
49
 
31
50
  kwargs = dataclass_fields_asdict(Parameter.from_et(et_element, doc_frags))
32
51
 
52
+ coded_value_raw = odxrequire(et_element.findtext("CODED-VALUE"))
33
53
  dct_elem = odxrequire(et_element.find("DIAG-CODED-TYPE"))
34
54
  diag_coded_type = create_any_diag_coded_type_from_et(dct_elem, doc_frags)
35
- coded_value = diag_coded_type.base_data_type.from_string(
36
- odxrequire(et_element.findtext("CODED-VALUE")))
37
55
 
38
56
  return CodedConstParameter(
39
- diag_coded_type=diag_coded_type, coded_value=coded_value, **kwargs)
57
+ coded_value_raw=coded_value_raw, diag_coded_type=diag_coded_type, **kwargs)
40
58
 
41
- @property
42
- @override
43
- def parameter_type(self) -> ParameterType:
44
- return "CODED-CONST"
59
+ def __post_init__(self) -> None:
60
+ self._coded_value = cast(
61
+ AtomicOdxType, self.diag_coded_type.base_data_type.from_string(self.coded_value_raw))
45
62
 
46
63
  @override
47
64
  def _build_odxlinks(self) -> Dict[OdxLinkId, Any]:
@@ -59,16 +76,6 @@ class CodedConstParameter(Parameter):
59
76
  def internal_data_type(self) -> DataType:
60
77
  return self.diag_coded_type.base_data_type
61
78
 
62
- @property
63
- @override
64
- def is_required(self) -> bool:
65
- return False
66
-
67
- @property
68
- @override
69
- def is_settable(self) -> bool:
70
- return False
71
-
72
79
  @override
73
80
  def _encode_positioned_into_pdu(self, physical_value: Optional[ParameterValue],
74
81
  encode_state: EncodeState) -> None:
@@ -91,7 +98,7 @@ class CodedConstParameter(Parameter):
91
98
  warnings.warn(
92
99
  f"Coded constant parameter does not match! "
93
100
  f"The parameter {self.short_name} expected coded "
94
- f"value {str(self._coded_value_str)} but got {str(coded_val)} "
101
+ f"value {str(self.coded_value)} but got {str(coded_val)} "
95
102
  f"at byte position {decode_state.cursor_byte_position} "
96
103
  f"in coded message {decode_state.coded_message.hex()}.",
97
104
  DecodeError,
@@ -100,12 +107,6 @@ class CodedConstParameter(Parameter):
100
107
 
101
108
  return coded_val
102
109
 
103
- @property
104
- def _coded_value_str(self) -> str:
105
- if isinstance(self.coded_value, bytes):
106
- return self.coded_value.hex()
107
- return str(self.coded_value)
108
-
109
110
  def get_description_of_valid_values(self) -> str:
110
111
  """return a human-understandable description of valid physical values"""
111
- return f"Constant internal value: {self._coded_value_str}"
112
+ return f"Constant internal value: {str(self.coded_value)}"
@@ -16,15 +16,6 @@ from .parameter import Parameter, ParameterType
16
16
  @dataclass
17
17
  class DynamicParameter(Parameter):
18
18
 
19
- @staticmethod
20
- @override
21
- def from_et(et_element: ElementTree.Element,
22
- doc_frags: List[OdxDocFragment]) -> "DynamicParameter":
23
-
24
- kwargs = dataclass_fields_asdict(Parameter.from_et(et_element, doc_frags))
25
-
26
- return DynamicParameter(**kwargs)
27
-
28
19
  @property
29
20
  @override
30
21
  def parameter_type(self) -> ParameterType:
@@ -40,6 +31,15 @@ class DynamicParameter(Parameter):
40
31
  def is_settable(self) -> bool:
41
32
  raise NotImplementedError(".is_settable for a DynamicParameter")
42
33
 
34
+ @staticmethod
35
+ @override
36
+ def from_et(et_element: ElementTree.Element,
37
+ doc_frags: List[OdxDocFragment]) -> "DynamicParameter":
38
+
39
+ kwargs = dataclass_fields_asdict(Parameter.from_et(et_element, doc_frags))
40
+
41
+ return DynamicParameter(**kwargs)
42
+
43
43
  @override
44
44
  def _encode_positioned_into_pdu(self, physical_value: Optional[ParameterValue],
45
45
  encode_state: EncodeState) -> None:
@@ -28,6 +28,24 @@ class LengthKeyParameter(ParameterWithDOP):
28
28
 
29
29
  odx_id: OdxLinkId
30
30
 
31
+ @property
32
+ @override
33
+ def parameter_type(self) -> ParameterType:
34
+ return "LENGTH-KEY"
35
+
36
+ @property
37
+ @override
38
+ def is_required(self) -> bool:
39
+ return False
40
+
41
+ @property
42
+ @override
43
+ def is_settable(self) -> bool:
44
+ # length keys can be explicitly set, but they do not need to
45
+ # be because they can be implicitly determined by the length
46
+ # of the corresponding field
47
+ return True
48
+
31
49
  @staticmethod
32
50
  @override
33
51
  def from_et(et_element: ElementTree.Element,
@@ -39,11 +57,6 @@ class LengthKeyParameter(ParameterWithDOP):
39
57
 
40
58
  return LengthKeyParameter(odx_id=odx_id, **kwargs)
41
59
 
42
- @property
43
- @override
44
- def parameter_type(self) -> ParameterType:
45
- return "LENGTH-KEY"
46
-
47
60
  @override
48
61
  def _build_odxlinks(self) -> Dict[OdxLinkId, Any]:
49
62
  result = super()._build_odxlinks()
@@ -52,19 +65,6 @@ class LengthKeyParameter(ParameterWithDOP):
52
65
 
53
66
  return result
54
67
 
55
- @property
56
- @override
57
- def is_required(self) -> bool:
58
- return False
59
-
60
- @property
61
- @override
62
- def is_settable(self) -> bool:
63
- # length keys can be explicitly set, but they do not need to
64
- # be because they can be implicitly determined by the length
65
- # of the corresponding field
66
- return True
67
-
68
68
  @override
69
69
  @final
70
70
  def _encode_positioned_into_pdu(self, physical_value: Optional[ParameterValue],
@@ -19,6 +19,21 @@ class MatchingRequestParameter(Parameter):
19
19
  request_byte_position: int
20
20
  byte_length: int
21
21
 
22
+ @property
23
+ @override
24
+ def parameter_type(self) -> ParameterType:
25
+ return "MATCHING-REQUEST-PARAM"
26
+
27
+ @property
28
+ @override
29
+ def is_required(self) -> bool:
30
+ return False
31
+
32
+ @property
33
+ @override
34
+ def is_settable(self) -> bool:
35
+ return False
36
+
22
37
  @staticmethod
23
38
  @override
24
39
  def from_et(et_element: ElementTree.Element,
@@ -32,25 +47,10 @@ class MatchingRequestParameter(Parameter):
32
47
  return MatchingRequestParameter(
33
48
  request_byte_position=request_byte_position, byte_length=byte_length, **kwargs)
34
49
 
35
- @property
36
- @override
37
- def parameter_type(self) -> ParameterType:
38
- return "MATCHING-REQUEST-PARAM"
39
-
40
50
  @override
41
51
  def get_static_bit_length(self) -> Optional[int]:
42
52
  return 8 * self.byte_length
43
53
 
44
- @property
45
- @override
46
- def is_required(self) -> bool:
47
- return False
48
-
49
- @property
50
- @override
51
- def is_settable(self) -> bool:
52
- return False
53
-
54
54
  @override
55
55
  def _encode_positioned_into_pdu(self, physical_value: Optional[ParameterValue],
56
56
  encode_state: EncodeState) -> None: