odxtools 7.0.0__py3-none-any.whl → 7.1.1__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 (41) hide show
  1. odxtools/basicstructure.py +1 -5
  2. odxtools/diagcomm.py +11 -3
  3. odxtools/diagdatadictionaryspec.py +14 -14
  4. odxtools/diaglayer.py +7 -1
  5. odxtools/diaglayerraw.py +13 -7
  6. odxtools/dynamicendmarkerfield.py +119 -0
  7. odxtools/dynamiclengthfield.py +9 -3
  8. odxtools/dynenddopref.py +38 -0
  9. odxtools/encodestate.py +3 -3
  10. odxtools/endofpdufield.py +20 -3
  11. odxtools/environmentdata.py +8 -1
  12. odxtools/environmentdatadescription.py +8 -2
  13. odxtools/field.py +4 -3
  14. odxtools/matchingparameter.py +2 -2
  15. odxtools/multiplexercase.py +2 -2
  16. odxtools/multiplexerdefaultcase.py +7 -3
  17. odxtools/odxlink.py +38 -4
  18. odxtools/odxtypes.py +3 -2
  19. odxtools/parameters/codedconstparameter.py +3 -2
  20. odxtools/parameters/lengthkeyparameter.py +4 -3
  21. odxtools/parameters/nrcconstparameter.py +3 -2
  22. odxtools/parameters/parameter.py +6 -0
  23. odxtools/parameters/parameterwithdop.py +10 -14
  24. odxtools/parameters/physicalconstantparameter.py +4 -3
  25. odxtools/parameters/tablekeyparameter.py +9 -9
  26. odxtools/parameters/tablestructparameter.py +6 -25
  27. odxtools/parameters/valueparameter.py +4 -3
  28. odxtools/py.typed +0 -0
  29. odxtools/statechart.py +5 -9
  30. odxtools/statetransition.py +3 -8
  31. odxtools/staticfield.py +17 -3
  32. odxtools/tablerow.py +5 -3
  33. odxtools/templates/macros/printDynamicEndmarkerField.xml.jinja2 +16 -0
  34. odxtools/templates/macros/printVariant.xml.jinja2 +8 -0
  35. odxtools/version.py +2 -2
  36. {odxtools-7.0.0.dist-info → odxtools-7.1.1.dist-info}/METADATA +1 -1
  37. {odxtools-7.0.0.dist-info → odxtools-7.1.1.dist-info}/RECORD +41 -37
  38. {odxtools-7.0.0.dist-info → odxtools-7.1.1.dist-info}/LICENSE +0 -0
  39. {odxtools-7.0.0.dist-info → odxtools-7.1.1.dist-info}/WHEEL +0 -0
  40. {odxtools-7.0.0.dist-info → odxtools-7.1.1.dist-info}/entry_points.txt +0 -0
  41. {odxtools-7.0.0.dist-info → odxtools-7.1.1.dist-info}/top_level.txt +0 -0
odxtools/odxlink.py CHANGED
@@ -1,10 +1,11 @@
1
1
  # SPDX-License-Identifier: MIT
2
2
  import warnings
3
3
  from dataclasses import dataclass
4
- from typing import Any, Dict, List, Optional, Type, TypeVar, overload
4
+ from typing import Any, Dict, Iterable, List, Optional, Type, TypeVar, overload
5
5
  from xml.etree import ElementTree
6
6
 
7
- from .exceptions import OdxWarning, odxassert
7
+ from .exceptions import OdxWarning, odxassert, odxraise
8
+ from .nameditemlist import OdxNamed, TNamed
8
9
 
9
10
 
10
11
  @dataclass(frozen=True)
@@ -210,8 +211,10 @@ class OdxLinkDatabase:
210
211
 
211
212
  return obj
212
213
 
213
- raise KeyError(f"ODXLINK reference {ref} could not be resolved for any "
214
- f"of the document fragments {ref.ref_docs}")
214
+ odxraise(
215
+ f"ODXLINK reference {ref} could not be resolved for any "
216
+ f"of the document fragments {ref.ref_docs}", KeyError)
217
+ return None
215
218
 
216
219
  @overload
217
220
  def resolve_lenient(self, ref: OdxLinkRef, expected_type: None = None) -> Any:
@@ -270,3 +273,34 @@ class OdxLinkDatabase:
270
273
  self._db[doc_frag] = {}
271
274
 
272
275
  self._db[doc_frag][odx_id] = obj
276
+
277
+
278
+ @overload
279
+ def resolve_snref(target_short_name: str,
280
+ items: Iterable[OdxNamed],
281
+ expected_type: None = None) -> Any:
282
+ """Resolve a short name reference given a sequence of candidate objects"""
283
+ ...
284
+
285
+
286
+ @overload
287
+ def resolve_snref(target_short_name: str, items: Iterable[OdxNamed],
288
+ expected_type: Type[TNamed]) -> TNamed:
289
+ ...
290
+
291
+
292
+ def resolve_snref(target_short_name: str,
293
+ items: Iterable[OdxNamed],
294
+ expected_type: Any = None) -> Any:
295
+ candidates = [x for x in items if x.short_name == target_short_name]
296
+
297
+ if not candidates:
298
+ odxraise(f"Cannot resolve short name reference to '{target_short_name}'")
299
+ return None
300
+ elif len(candidates) > 1:
301
+ odxraise(f"Cannot uniquely resolve short name reference to '{target_short_name}'")
302
+ elif expected_type is not None and not isinstance(candidates[0], expected_type):
303
+ odxraise(f"Reference '{target_short_name}' points to a {type(candidates[0]).__name__}"
304
+ f"object while expecting {expected_type.__name__}")
305
+
306
+ return candidates[0]
odxtools/odxtypes.py CHANGED
@@ -1,6 +1,7 @@
1
1
  # SPDX-License-Identifier: MIT
2
2
  from enum import Enum
3
- from typing import TYPE_CHECKING, Any, Callable, Dict, List, Optional, Tuple, Type, Union, overload
3
+ from typing import (TYPE_CHECKING, Any, Callable, Dict, Iterable, Optional, Tuple, Type, Union,
4
+ overload)
4
5
  from xml.etree import ElementTree
5
6
 
6
7
  from .exceptions import odxassert, odxraise, odxrequire
@@ -28,7 +29,7 @@ ParameterDict = Dict[str, Union["Parameter", "ParameterDict"]]
28
29
  # multiple items, so this can be a list of objects.
29
30
  TableStructParameterValue = Tuple[str, "ParameterValue"]
30
31
  ParameterValue = Union[AtomicOdxType, "ParameterValueDict", TableStructParameterValue,
31
- List["ParameterValue"], "DiagnosticTroubleCode"]
32
+ Iterable["ParameterValue"], "DiagnosticTroubleCode"]
32
33
  ParameterValueDict = Dict[str, ParameterValue]
33
34
 
34
35
 
@@ -59,8 +59,9 @@ class CodedConstParameter(Parameter):
59
59
  super()._resolve_odxlinks(odxlinks)
60
60
 
61
61
  @override
62
- def _resolve_snrefs(self, diag_layer: "DiagLayer") -> None:
63
- super()._resolve_snrefs(diag_layer)
62
+ def _parameter_resolve_snrefs(self, diag_layer: "DiagLayer", *,
63
+ param_list: List[Parameter]) -> None:
64
+ super()._parameter_resolve_snrefs(diag_layer, param_list=param_list)
64
65
 
65
66
  @override
66
67
  def get_static_bit_length(self) -> Optional[int]:
@@ -11,7 +11,7 @@ from ..exceptions import EncodeError, odxraise, odxrequire
11
11
  from ..odxlink import OdxDocFragment, OdxLinkDatabase, OdxLinkId
12
12
  from ..odxtypes import ParameterValue
13
13
  from ..utils import dataclass_fields_asdict
14
- from .parameter import ParameterType
14
+ from .parameter import Parameter, ParameterType
15
15
  from .parameterwithdop import ParameterWithDOP
16
16
 
17
17
  if TYPE_CHECKING:
@@ -60,8 +60,9 @@ class LengthKeyParameter(ParameterWithDOP):
60
60
  super()._resolve_odxlinks(odxlinks)
61
61
 
62
62
  @override
63
- def _resolve_snrefs(self, diag_layer: "DiagLayer") -> None:
64
- super()._resolve_snrefs(diag_layer)
63
+ def _parameter_resolve_snrefs(self, diag_layer: "DiagLayer", *,
64
+ param_list: List[Parameter]) -> None:
65
+ super()._parameter_resolve_snrefs(diag_layer, param_list=param_list)
65
66
 
66
67
  @property
67
68
  @override
@@ -75,8 +75,9 @@ class NrcConstParameter(Parameter):
75
75
  super()._resolve_odxlinks(odxlinks)
76
76
 
77
77
  @override
78
- def _resolve_snrefs(self, diag_layer: "DiagLayer") -> None:
79
- super()._resolve_snrefs(diag_layer)
78
+ def _parameter_resolve_snrefs(self, diag_layer: "DiagLayer", *,
79
+ param_list: List[Parameter]) -> None:
80
+ super()._parameter_resolve_snrefs(diag_layer, param_list=param_list)
80
81
 
81
82
  @override
82
83
  def get_static_bit_length(self) -> Optional[int]:
@@ -82,7 +82,13 @@ class Parameter(NamedElement):
82
82
  for sdg in self.sdgs:
83
83
  sdg._resolve_odxlinks(odxlinks)
84
84
 
85
+ @final
85
86
  def _resolve_snrefs(self, diag_layer: "DiagLayer") -> None:
87
+ raise RuntimeError("Calling _resolve_snrefs() is not allowed for parameters. "
88
+ "Use _parameter_resolve_snrefs() instead.")
89
+
90
+ def _parameter_resolve_snrefs(self, diag_layer: "DiagLayer", *,
91
+ param_list: List["Parameter"]) -> None:
86
92
  for sdg in self.sdgs:
87
93
  sdg._resolve_snrefs(diag_layer)
88
94
 
@@ -11,7 +11,7 @@ from ..dopbase import DopBase
11
11
  from ..dtcdop import DtcDop
12
12
  from ..encodestate import EncodeState
13
13
  from ..exceptions import odxassert, odxrequire
14
- from ..odxlink import OdxDocFragment, OdxLinkDatabase, OdxLinkId, OdxLinkRef
14
+ from ..odxlink import OdxDocFragment, OdxLinkDatabase, OdxLinkId, OdxLinkRef, resolve_snref
15
15
  from ..odxtypes import AtomicOdxType, ParameterValue
16
16
  from ..physicaltype import PhysicalType
17
17
  from ..utils import dataclass_fields_asdict
@@ -43,7 +43,7 @@ class ParameterWithDOP(Parameter):
43
43
  def __post_init__(self) -> None:
44
44
  odxassert(self.dop_snref is not None or self.dop_ref is not None,
45
45
  f"Param {self.short_name} without a DOP-(SN)REF should not exist!")
46
- self._dop: Optional[DopBase] = None
46
+ self._dop: DopBase
47
47
 
48
48
  @override
49
49
  def _build_odxlinks(self) -> Dict[OdxLinkId, Any]:
@@ -55,26 +55,22 @@ class ParameterWithDOP(Parameter):
55
55
 
56
56
  if self.dop_ref is not None:
57
57
  odxassert(self.dop_snref is None)
58
- # TODO: do not do lenient resolves here. The problem is
59
- # that currently not all kinds of DOPs are internalized
60
- # (e.g., static and dynamic fields)
61
- self._dop = odxlinks.resolve_lenient(self.dop_ref)
58
+ self._dop = odxlinks.resolve(self.dop_ref)
62
59
 
63
60
  @override
64
- def _resolve_snrefs(self, diag_layer: "DiagLayer") -> None:
65
- super()._resolve_snrefs(diag_layer)
61
+ def _parameter_resolve_snrefs(self, diag_layer: "DiagLayer", *,
62
+ param_list: List[Parameter]) -> None:
63
+ super()._parameter_resolve_snrefs(diag_layer, param_list=param_list)
66
64
 
67
65
  if self.dop_snref:
68
- ddds = diag_layer.diag_data_dictionary_spec
69
- self._dop = odxrequire(ddds.all_data_object_properties.get(self.dop_snref))
66
+ all_dops = diag_layer.diag_data_dictionary_spec.all_data_object_properties
67
+ self._dop = resolve_snref(self.dop_snref, all_dops, DopBase)
70
68
 
71
69
  @property
72
70
  def dop(self) -> DopBase:
73
- """may be a DataObjectProperty, a Structure or None"""
71
+ """This is usually a DataObjectProperty or a Structure object"""
74
72
 
75
- return odxrequire(
76
- self._dop, "Specifying a data object property is mandatory but it "
77
- "could not be resolved")
73
+ return self._dop
78
74
 
79
75
  @override
80
76
  def get_static_bit_length(self) -> Optional[int]:
@@ -12,7 +12,7 @@ from ..exceptions import DecodeError, EncodeError, odxraise, odxrequire
12
12
  from ..odxlink import OdxDocFragment, OdxLinkDatabase, OdxLinkId
13
13
  from ..odxtypes import ParameterValue
14
14
  from ..utils import dataclass_fields_asdict
15
- from .parameter import ParameterType
15
+ from .parameter import Parameter, ParameterType
16
16
  from .parameterwithdop import ParameterWithDOP
17
17
 
18
18
  if TYPE_CHECKING:
@@ -50,8 +50,9 @@ class PhysicalConstantParameter(ParameterWithDOP):
50
50
  super()._resolve_odxlinks(odxlinks)
51
51
 
52
52
  @override
53
- def _resolve_snrefs(self, diag_layer: "DiagLayer") -> None:
54
- super()._resolve_snrefs(diag_layer)
53
+ def _parameter_resolve_snrefs(self, diag_layer: "DiagLayer", *,
54
+ param_list: List[Parameter]) -> None:
55
+ super()._parameter_resolve_snrefs(diag_layer, param_list=param_list)
55
56
 
56
57
  dop = odxrequire(self.dop)
57
58
  if not isinstance(dop, DataObjectProperty):
@@ -8,7 +8,7 @@ from typing_extensions import final, override
8
8
  from ..decodestate import DecodeState
9
9
  from ..encodestate import EncodeState
10
10
  from ..exceptions import DecodeError, EncodeError, odxraise, odxrequire
11
- from ..odxlink import OdxDocFragment, OdxLinkDatabase, OdxLinkId, OdxLinkRef
11
+ from ..odxlink import OdxDocFragment, OdxLinkDatabase, OdxLinkId, OdxLinkRef, resolve_snref
12
12
  from ..odxtypes import ParameterValue
13
13
  from ..utils import dataclass_fields_asdict
14
14
  from .parameter import Parameter, ParameterType
@@ -94,19 +94,19 @@ class TableKeyParameter(Parameter):
94
94
  self._table = self._table_row.table
95
95
 
96
96
  @override
97
- def _resolve_snrefs(self, diag_layer: "DiagLayer") -> None:
98
- super()._resolve_snrefs(diag_layer)
97
+ def _parameter_resolve_snrefs(self, diag_layer: "DiagLayer", *,
98
+ param_list: List[Parameter]) -> None:
99
+ super()._parameter_resolve_snrefs(diag_layer, param_list=param_list)
99
100
 
100
101
  if self.table_snref is not None:
101
- ddd_spec = diag_layer.diag_data_dictionary_spec
102
- self._table = ddd_spec.tables[self.table_snref]
102
+ tables = diag_layer.diag_data_dictionary_spec.tables
103
+ self._table = resolve_snref(self.table_snref, tables, Table)
103
104
  if self.table_row_snref is not None:
104
105
  # make sure that we know the table to which the table row
105
106
  # SNREF is relative to.
106
- table = odxrequire(
107
- self._table, "If a table-row short name reference is defined, a "
108
- "table must also be specified.")
109
- self._table_row = table.table_rows[self.table_row_snref]
107
+ table = odxrequire(self._table,
108
+ "If a table-row is referenced, a table must also be referenced.")
109
+ self._table_row = resolve_snref(self.table_row_snref, table.table_rows, TableRow)
110
110
 
111
111
  @property
112
112
  def table(self) -> "Table":
@@ -3,12 +3,12 @@ from dataclasses import dataclass
3
3
  from typing import TYPE_CHECKING, Any, Dict, List, Optional, cast
4
4
  from xml.etree import ElementTree
5
5
 
6
- from typing_extensions import final, override
6
+ from typing_extensions import override
7
7
 
8
8
  from ..decodestate import DecodeState
9
9
  from ..encodestate import EncodeState
10
10
  from ..exceptions import DecodeError, EncodeError, odxraise, odxrequire
11
- from ..odxlink import OdxDocFragment, OdxLinkDatabase, OdxLinkId, OdxLinkRef
11
+ from ..odxlink import OdxDocFragment, OdxLinkDatabase, OdxLinkId, OdxLinkRef, resolve_snref
12
12
  from ..odxtypes import ParameterValue
13
13
  from ..utils import dataclass_fields_asdict
14
14
  from .parameter import Parameter, ParameterType
@@ -60,31 +60,12 @@ class TableStructParameter(Parameter):
60
60
  self._table_key = odxlinks.resolve(self.table_key_ref, TableKeyParameter)
61
61
 
62
62
  @override
63
- @final
64
- def _resolve_snrefs(self, diag_layer: "DiagLayer") -> None:
65
- raise RuntimeError(f"Calling TableStructParameter._resolve_snref() is not allowed. "
66
- f"Use ._table_struct_resolve_snrefs() instead.")
67
-
68
- def _table_struct_resolve_snrefs(self, diag_layer: "DiagLayer", *,
69
- param_list: List[Parameter]) -> None:
70
- super()._resolve_snrefs(diag_layer)
63
+ def _parameter_resolve_snrefs(self, diag_layer: "DiagLayer", *,
64
+ param_list: List[Parameter]) -> None:
65
+ super()._parameter_resolve_snrefs(diag_layer, param_list=param_list)
71
66
 
72
67
  if self.table_key_snref is not None:
73
- tk_candidates = [p for p in param_list if p.short_name == self.table_key_snref]
74
- if len(tk_candidates) > 1:
75
- odxraise(f"Short name reference '{self.table_key_snref}' could "
76
- f"not be uniquely resolved.")
77
- elif len(tk_candidates) == 0:
78
- odxraise(f"Short name reference '{self.table_key_snref}' could "
79
- f"not be resolved.")
80
- return
81
-
82
- tk = tk_candidates[0]
83
- if not isinstance(tk, TableKeyParameter):
84
- odxraise(f"Table struct '{self.short_name}' references non-TableKey parameter "
85
- f"`{self.table_key_snref}' as its table key.")
86
-
87
- self._table_key = tk
68
+ self._table_key = resolve_snref(self.table_key_snref, param_list, TableKeyParameter)
88
69
 
89
70
  @property
90
71
  def table_key(self) -> TableKeyParameter:
@@ -11,7 +11,7 @@ from ..exceptions import EncodeError, odxraise, odxrequire
11
11
  from ..odxlink import OdxDocFragment, OdxLinkDatabase, OdxLinkId
12
12
  from ..odxtypes import AtomicOdxType, ParameterValue
13
13
  from ..utils import dataclass_fields_asdict
14
- from .parameter import ParameterType
14
+ from .parameter import Parameter, ParameterType
15
15
  from .parameterwithdop import ParameterWithDOP
16
16
 
17
17
  if TYPE_CHECKING:
@@ -50,8 +50,9 @@ class ValueParameter(ParameterWithDOP):
50
50
  super()._resolve_odxlinks(odxlinks)
51
51
 
52
52
  @override
53
- def _resolve_snrefs(self, diag_layer: "DiagLayer") -> None:
54
- super()._resolve_snrefs(diag_layer)
53
+ def _parameter_resolve_snrefs(self, diag_layer: "DiagLayer", *,
54
+ param_list: List[Parameter]) -> None:
55
+ super()._parameter_resolve_snrefs(diag_layer, param_list=param_list)
55
56
 
56
57
  if self.physical_default_value_raw is not None:
57
58
  dop = odxrequire(self.dop)
odxtools/py.typed ADDED
File without changes
odxtools/statechart.py CHANGED
@@ -6,7 +6,7 @@ from xml.etree import ElementTree
6
6
  from .element import IdentifiableElement
7
7
  from .exceptions import odxrequire
8
8
  from .nameditemlist import NamedItemList
9
- from .odxlink import OdxDocFragment, OdxLinkDatabase, OdxLinkId
9
+ from .odxlink import OdxDocFragment, OdxLinkDatabase, OdxLinkId, resolve_snref
10
10
  from .state import State
11
11
  from .statetransition import StateTransition
12
12
  from .utils import dataclass_fields_asdict
@@ -68,19 +68,15 @@ class StateChart(IdentifiableElement):
68
68
  for st in self.states:
69
69
  st._resolve_odxlinks(odxlinks)
70
70
 
71
- # For now, we assume that the start state short name ref
72
- # points to a state local to the state chart. TODO: The XML
71
+ def _resolve_snrefs(self, diag_layer: "DiagLayer") -> None:
72
+ # For now, we assume that the start state short name reference
73
+ # points to a local state of the state chart. TODO: The XSD
73
74
  # allows to define state charts without any states, yet the
74
75
  # start state SNREF is mandatory. Is this a gap in the spec or
75
76
  # does it allow "foreign" start states? If the latter, what
76
77
  # does that mean?
77
- self._start_state: State
78
- for st in self.states:
79
- if st.short_name == self.start_state_snref:
80
- self._start_state = st
81
- break
78
+ self._start_state = resolve_snref(self.start_state_snref, self.states, State)
82
79
 
83
- def _resolve_snrefs(self, diag_layer: "DiagLayer") -> None:
84
80
  for st in self.states:
85
81
  st._resolve_snrefs(diag_layer)
86
82
 
@@ -5,7 +5,7 @@ from xml.etree import ElementTree
5
5
 
6
6
  from .element import IdentifiableElement
7
7
  from .exceptions import odxrequire
8
- from .odxlink import OdxDocFragment, OdxLinkDatabase, OdxLinkId
8
+ from .odxlink import OdxDocFragment, OdxLinkDatabase, OdxLinkId, resolve_snref
9
9
  from .state import State
10
10
  from .utils import dataclass_fields_asdict
11
11
 
@@ -55,10 +55,5 @@ class StateTransition(IdentifiableElement):
55
55
  # chart. To mitigate this a bit, the non-standard parameters are
56
56
  # keyword-only...
57
57
  def _resolve_snrefs(self, diag_layer: "DiagLayer", *, states: Iterable[State]) -> None:
58
- self._source_state: State
59
- self._target_state: State
60
- for st in states:
61
- if st.short_name == self.source_snref:
62
- self._source_state = st
63
- if st.short_name == self.target_snref:
64
- self._target_state = st
58
+ self._source_state = resolve_snref(self.source_snref, states, State)
59
+ self._target_state = resolve_snref(self.target_snref, states, State)
odxtools/staticfield.py CHANGED
@@ -1,6 +1,6 @@
1
1
  # SPDX-License-Identifier: MIT
2
2
  from dataclasses import dataclass
3
- from typing import TYPE_CHECKING, Any, Dict, List
3
+ from typing import TYPE_CHECKING, Any, Dict, List, Sequence
4
4
  from xml.etree import ElementTree
5
5
 
6
6
  from typing_extensions import override
@@ -51,15 +51,20 @@ class StaticField(Field):
51
51
  def encode_into_pdu(self, physical_value: ParameterValue, encode_state: EncodeState) -> None:
52
52
 
53
53
  if not isinstance(physical_value,
54
- (tuple, list)) or len(physical_value) != self.fixed_number_of_items:
54
+ Sequence) or len(physical_value) != self.fixed_number_of_items:
55
55
  odxraise(f"Value for static field '{self.short_name}' "
56
56
  f"must be a list of size {self.fixed_number_of_items}")
57
57
 
58
- for val in physical_value:
58
+ orig_is_end_of_pdu = encode_state.is_end_of_pdu
59
+ encode_state.is_end_of_pdu = False
60
+ for i, val in enumerate(physical_value):
59
61
  if not isinstance(val, dict):
60
62
  odxraise(f"The individual parameter values for static field '{self.short_name}' "
61
63
  f"must be dictionaries for structure '{self.structure.short_name}'")
62
64
 
65
+ if i == len(physical_value) - 1:
66
+ encode_state.is_end_of_pdu = orig_is_end_of_pdu
67
+
63
68
  pos_before = encode_state.cursor_byte_position
64
69
  self.structure.encode_into_pdu(val, encode_state)
65
70
  pos_after = encode_state.cursor_byte_position
@@ -75,12 +80,18 @@ class StaticField(Field):
75
80
  encode_state.emplace_bytes(b'\x00' * (self.item_byte_size -
76
81
  (pos_after - pos_before)))
77
82
 
83
+ encode_state.is_end_of_pdu = orig_is_end_of_pdu
84
+
78
85
  @override
79
86
  def decode_from_pdu(self, decode_state: DecodeState) -> ParameterValue:
80
87
 
81
88
  odxassert(decode_state.cursor_bit_position == 0,
82
89
  "No bit position can be specified for static length fields!")
83
90
 
91
+ orig_origin = decode_state.origin_byte_position
92
+ orig_cursor = decode_state.cursor_byte_position
93
+ decode_state.origin_byte_position = decode_state.cursor_byte_position
94
+
84
95
  result: List[ParameterValue] = []
85
96
  for _ in range(self.fixed_number_of_items):
86
97
  orig_cursor = decode_state.cursor_byte_position
@@ -94,4 +105,7 @@ class StaticField(Field):
94
105
 
95
106
  decode_state.cursor_byte_position = orig_cursor + self.item_byte_size
96
107
 
108
+ decode_state.origin_byte_position = orig_origin
109
+ decode_state.cursor_byte_position = max(orig_cursor, decode_state.cursor_byte_position)
110
+
97
111
  return result
odxtools/tablerow.py CHANGED
@@ -9,7 +9,7 @@ from .dataobjectproperty import DataObjectProperty
9
9
  from .dtcdop import DtcDop
10
10
  from .element import IdentifiableElement
11
11
  from .exceptions import odxassert, odxraise, odxrequire
12
- from .odxlink import OdxDocFragment, OdxLinkDatabase, OdxLinkId, OdxLinkRef
12
+ from .odxlink import OdxDocFragment, OdxLinkDatabase, OdxLinkId, OdxLinkRef, resolve_snref
13
13
  from .odxtypes import AtomicOdxType
14
14
  from .specialdatagroup import SpecialDataGroup
15
15
  from .utils import dataclass_fields_asdict
@@ -128,9 +128,11 @@ class TableRow(IdentifiableElement):
128
128
  ddd_spec = diag_layer.diag_data_dictionary_spec
129
129
 
130
130
  if self.structure_snref is not None:
131
- self._structure = odxrequire(ddd_spec.structures.get(self.structure_snref))
131
+ self._structure = resolve_snref(self.structure_snref, ddd_spec.structures,
132
+ BasicStructure)
132
133
  if self.dop_snref is not None:
133
- self._dop = odxrequire(ddd_spec.data_object_props.get(self.dop_snref))
134
+ self._dop = resolve_snref(self.dop_snref, ddd_spec.data_object_props,
135
+ DataObjectProperty)
134
136
 
135
137
  for sdg in self.sdgs:
136
138
  sdg._resolve_snrefs(diag_layer)
@@ -0,0 +1,16 @@
1
+ {#- -*- mode: sgml; tab-width: 1; indent-tabs-mode: nil -*-
2
+ #
3
+ # SPDX-License-Identifier: MIT
4
+ -#}
5
+
6
+ {%- import('macros/printElementId.xml.jinja2') as peid %}
7
+
8
+ {%- macro printStaticField(demf) -%}
9
+ <DYNAMIC-ENDMARKER-FIELD ID="{{demf.odx_id.local_id}}">
10
+ {{ peid.printElementIdSubtags(demf)|indent(1) }}
11
+ <BASIC-STRUCTURE-REF ID-REF="{{demf.structure_ref.ref_id}}" />
12
+ <DYN-END-DOP-REF ID-REF="{{demf.dyn_end_dop_ref.ref_id}}">
13
+ <TERMINATION-VALUE>{{demf.dyn_end_dop_ref.termination_value_raw}}</TERMINATION-VALUE>
14
+ </DYN-END-DOP-REF>
15
+ </DYNAMIC-ENDMARKER-FIELD>
16
+ {%- endmacro -%}
@@ -11,6 +11,7 @@
11
11
  {%- import('macros/printEndOfPdu.xml.jinja2') as peopdu %}
12
12
  {%- import('macros/printStaticField.xml.jinja2') as psf %}
13
13
  {%- import('macros/printDynamicLengthField.xml.jinja2') as pdlf %}
14
+ {%- import('macros/printDynamicEndmarkerField.xml.jinja2') as pdemf %}
14
15
  {%- import('macros/printMux.xml.jinja2') as pm %}
15
16
  {%- import('macros/printEnvData.xml.jinja2') as ped %}
16
17
  {%- import('macros/printEnvDataDesc.xml.jinja2') as pedd %}
@@ -82,6 +83,13 @@
82
83
  {%- endfor %}
83
84
  </DYNAMIC-LENGTH-FIELDS>
84
85
  {%- endif %}
86
+ {%- if ddds.dynamic_endmarker_fields %}
87
+ <DYNAMIC-ENDMARKER-FIELDS>
88
+ {%- for demf in ddds.dynamic_endmarker_fields %}
89
+ {{ pdemf.printDynamicEndmarkerField(demf)|indent(3) }}
90
+ {%- endfor %}
91
+ </DYNAMIC-ENDMARKER-FIELDS>
92
+ {%- endif %}
85
93
  {%- if ddds.end_of_pdu_fields %}
86
94
  <END-OF-PDU-FIELDS>
87
95
  {%- for eopdu in ddds.end_of_pdu_fields %}
odxtools/version.py CHANGED
@@ -12,5 +12,5 @@ __version__: str
12
12
  __version_tuple__: VERSION_TUPLE
13
13
  version_tuple: VERSION_TUPLE
14
14
 
15
- __version__ = version = '7.0.0'
16
- __version_tuple__ = version_tuple = (7, 0, 0)
15
+ __version__ = version = '7.1.1'
16
+ __version_tuple__ = version_tuple = (7, 1, 1)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: odxtools
3
- Version: 7.0.0
3
+ Version: 7.1.1
4
4
  Summary: Utilities to work with the ODX standard for automotive diagnostics
5
5
  Author-email: Katrin Bauer <katrin.bauer@mbition.io>, Andreas Lauser <andreas.lauser@mbition.io>, Ayoub Kaanich <kayoub5@live.com>
6
6
  Maintainer-email: Andreas Lauser <andreas.lauser@mbition.io>, Ayoub Kaanich <kayoub5@live.com>