odxtools 7.0.0__py3-none-any.whl → 7.1.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.
- odxtools/basicstructure.py +1 -5
- odxtools/diagcomm.py +11 -3
- odxtools/diagdatadictionaryspec.py +14 -14
- odxtools/diaglayer.py +7 -1
- odxtools/diaglayerraw.py +13 -7
- odxtools/dynamicendmarkerfield.py +119 -0
- odxtools/dynamiclengthfield.py +9 -3
- odxtools/dynenddopref.py +38 -0
- odxtools/encodestate.py +3 -3
- odxtools/endofpdufield.py +20 -3
- odxtools/environmentdata.py +8 -1
- odxtools/environmentdatadescription.py +8 -2
- odxtools/field.py +4 -3
- odxtools/matchingparameter.py +2 -2
- odxtools/multiplexercase.py +2 -2
- odxtools/multiplexerdefaultcase.py +7 -3
- odxtools/odxlink.py +38 -4
- odxtools/odxtypes.py +3 -2
- odxtools/parameters/codedconstparameter.py +3 -2
- odxtools/parameters/lengthkeyparameter.py +4 -3
- odxtools/parameters/nrcconstparameter.py +3 -2
- odxtools/parameters/parameter.py +6 -0
- odxtools/parameters/parameterwithdop.py +10 -14
- odxtools/parameters/physicalconstantparameter.py +4 -3
- odxtools/parameters/tablekeyparameter.py +9 -9
- odxtools/parameters/tablestructparameter.py +6 -25
- odxtools/parameters/valueparameter.py +4 -3
- odxtools/statechart.py +5 -9
- odxtools/statetransition.py +3 -8
- odxtools/staticfield.py +17 -3
- odxtools/tablerow.py +5 -3
- odxtools/templates/macros/printDynamicEndmarkerField.xml.jinja2 +16 -0
- odxtools/templates/macros/printVariant.xml.jinja2 +8 -0
- odxtools/version.py +2 -2
- {odxtools-7.0.0.dist-info → odxtools-7.1.0.dist-info}/METADATA +1 -1
- {odxtools-7.0.0.dist-info → odxtools-7.1.0.dist-info}/RECORD +40 -37
- {odxtools-7.0.0.dist-info → odxtools-7.1.0.dist-info}/LICENSE +0 -0
- {odxtools-7.0.0.dist-info → odxtools-7.1.0.dist-info}/WHEEL +0 -0
- {odxtools-7.0.0.dist-info → odxtools-7.1.0.dist-info}/entry_points.txt +0 -0
- {odxtools-7.0.0.dist-info → odxtools-7.1.0.dist-info}/top_level.txt +0 -0
odxtools/basicstructure.py
CHANGED
@@ -23,7 +23,6 @@ from .parameters.parameter import Parameter
|
|
23
23
|
from .parameters.parameterwithdop import ParameterWithDOP
|
24
24
|
from .parameters.physicalconstantparameter import PhysicalConstantParameter
|
25
25
|
from .parameters.tablekeyparameter import TableKeyParameter
|
26
|
-
from .parameters.tablestructparameter import TableStructParameter
|
27
26
|
from .utils import dataclass_fields_asdict
|
28
27
|
|
29
28
|
if TYPE_CHECKING:
|
@@ -307,7 +306,4 @@ class BasicStructure(ComplexDop):
|
|
307
306
|
super()._resolve_snrefs(diag_layer)
|
308
307
|
|
309
308
|
for param in self.parameters:
|
310
|
-
|
311
|
-
param._table_struct_resolve_snrefs(diag_layer, param_list=self.parameters)
|
312
|
-
else:
|
313
|
-
param._resolve_snrefs(diag_layer)
|
309
|
+
param._parameter_resolve_snrefs(diag_layer, param_list=self.parameters)
|
odxtools/diagcomm.py
CHANGED
@@ -11,7 +11,7 @@ from .element import IdentifiableElement
|
|
11
11
|
from .exceptions import odxraise, odxrequire
|
12
12
|
from .functionalclass import FunctionalClass
|
13
13
|
from .nameditemlist import NamedItemList
|
14
|
-
from .odxlink import OdxDocFragment, OdxLinkDatabase, OdxLinkId, OdxLinkRef
|
14
|
+
from .odxlink import OdxDocFragment, OdxLinkDatabase, OdxLinkId, OdxLinkRef, resolve_snref
|
15
15
|
from .odxtypes import odxstr_to_bool
|
16
16
|
from .specialdatagroup import SpecialDataGroup
|
17
17
|
from .state import State
|
@@ -211,5 +211,13 @@ class DiagComm(IdentifiableElement):
|
|
211
211
|
for sdg in self.sdgs:
|
212
212
|
sdg._resolve_snrefs(diag_layer)
|
213
213
|
|
214
|
-
|
215
|
-
|
214
|
+
if TYPE_CHECKING:
|
215
|
+
self._protocols = NamedItemList([
|
216
|
+
resolve_snref(prot_snref, diag_layer.protocols, DiagLayer)
|
217
|
+
for prot_snref in self.protocol_snrefs
|
218
|
+
])
|
219
|
+
else:
|
220
|
+
self._protocols = NamedItemList([
|
221
|
+
resolve_snref(prot_snref, diag_layer.protocols)
|
222
|
+
for prot_snref in self.protocol_snrefs
|
223
|
+
])
|
@@ -10,6 +10,7 @@ from .createsdgs import create_sdgs_from_et
|
|
10
10
|
from .dataobjectproperty import DataObjectProperty
|
11
11
|
from .dopbase import DopBase
|
12
12
|
from .dtcdop import DtcDop
|
13
|
+
from .dynamicendmarkerfield import DynamicEndmarkerField
|
13
14
|
from .dynamiclengthfield import DynamicLengthField
|
14
15
|
from .endofpdufield import EndOfPduField
|
15
16
|
from .environmentdata import EnvironmentData
|
@@ -37,7 +38,7 @@ class DiagDataDictionarySpec:
|
|
37
38
|
structures: NamedItemList[BasicStructure]
|
38
39
|
static_fields: NamedItemList[StaticField]
|
39
40
|
dynamic_length_fields: NamedItemList[DynamicLengthField]
|
40
|
-
|
41
|
+
dynamic_endmarker_fields: NamedItemList[DynamicEndmarkerField]
|
41
42
|
end_of_pdu_fields: NamedItemList[EndOfPduField]
|
42
43
|
muxs: NamedItemList[Multiplexer]
|
43
44
|
env_datas: NamedItemList[EnvironmentData]
|
@@ -54,7 +55,7 @@ class DiagDataDictionarySpec:
|
|
54
55
|
self.structures,
|
55
56
|
self.static_fields,
|
56
57
|
self.dynamic_length_fields,
|
57
|
-
|
58
|
+
self.dynamic_endmarker_fields,
|
58
59
|
self.end_of_pdu_fields,
|
59
60
|
self.muxs,
|
60
61
|
self.env_datas,
|
@@ -99,11 +100,10 @@ class DiagDataDictionarySpec:
|
|
99
100
|
for dl_element in et_element.iterfind("DYNAMIC-LENGTH-FIELDS/DYNAMIC-LENGTH-FIELD")
|
100
101
|
]
|
101
102
|
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
#]
|
103
|
+
dynamic_endmarker_fields = [
|
104
|
+
DynamicEndmarkerField.from_et(dl_element, doc_frags) for dl_element in
|
105
|
+
et_element.iterfind("DYNAMIC-ENDMARKER-FIELDS/DYNAMIC-ENDMARKER-FIELD")
|
106
|
+
]
|
107
107
|
|
108
108
|
end_of_pdu_fields = [
|
109
109
|
EndOfPduField.from_et(eofp_element, doc_frags)
|
@@ -145,7 +145,7 @@ class DiagDataDictionarySpec:
|
|
145
145
|
structures=NamedItemList(structures),
|
146
146
|
static_fields=NamedItemList(static_fields),
|
147
147
|
dynamic_length_fields=NamedItemList(dynamic_length_fields),
|
148
|
-
|
148
|
+
dynamic_endmarker_fields=NamedItemList(dynamic_endmarker_fields),
|
149
149
|
end_of_pdu_fields=NamedItemList(end_of_pdu_fields),
|
150
150
|
muxs=NamedItemList(muxs),
|
151
151
|
env_datas=NamedItemList(env_datas),
|
@@ -172,8 +172,8 @@ class DiagDataDictionarySpec:
|
|
172
172
|
odxlinks.update(static_field._build_odxlinks())
|
173
173
|
for dynamic_length_field in self.dynamic_length_fields:
|
174
174
|
odxlinks.update(dynamic_length_field._build_odxlinks())
|
175
|
-
|
176
|
-
|
175
|
+
for dynamic_endmarker_field in self.dynamic_endmarker_fields:
|
176
|
+
odxlinks.update(dynamic_endmarker_field._build_odxlinks())
|
177
177
|
for end_of_pdu_field in self.end_of_pdu_fields:
|
178
178
|
odxlinks.update(end_of_pdu_field._build_odxlinks())
|
179
179
|
for mux in self.muxs:
|
@@ -204,8 +204,8 @@ class DiagDataDictionarySpec:
|
|
204
204
|
static_field._resolve_odxlinks(odxlinks)
|
205
205
|
for dynamic_length_field in self.dynamic_length_fields:
|
206
206
|
dynamic_length_field._resolve_odxlinks(odxlinks)
|
207
|
-
|
208
|
-
|
207
|
+
for dynamic_endmarker_field in self.dynamic_endmarker_fields:
|
208
|
+
dynamic_endmarker_field._resolve_odxlinks(odxlinks)
|
209
209
|
for end_of_pdu_field in self.end_of_pdu_fields:
|
210
210
|
end_of_pdu_field._resolve_odxlinks(odxlinks)
|
211
211
|
for mux in self.muxs:
|
@@ -234,8 +234,8 @@ class DiagDataDictionarySpec:
|
|
234
234
|
static_field._resolve_snrefs(diag_layer)
|
235
235
|
for dynamic_length_field in self.dynamic_length_fields:
|
236
236
|
dynamic_length_field._resolve_snrefs(diag_layer)
|
237
|
-
|
238
|
-
|
237
|
+
for dynamic_endmarker_field in self.dynamic_endmarker_fields:
|
238
|
+
dynamic_endmarker_field._resolve_snrefs(diag_layer)
|
239
239
|
for end_of_pdu_field in self.end_of_pdu_fields:
|
240
240
|
end_of_pdu_field._resolve_snrefs(diag_layer)
|
241
241
|
for mux in self.muxs:
|
odxtools/diaglayer.py
CHANGED
@@ -15,6 +15,7 @@ from .admindata import AdminData
|
|
15
15
|
from .companydata import CompanyData
|
16
16
|
from .comparaminstance import ComparamInstance
|
17
17
|
from .comparamspec import ComparamSpec
|
18
|
+
from .comparamsubset import ComparamSubset
|
18
19
|
from .diagcomm import DiagComm
|
19
20
|
from .diagdatadictionaryspec import DiagDataDictionarySpec
|
20
21
|
from .diaglayerraw import DiagLayerRaw
|
@@ -216,6 +217,10 @@ class DiagLayer:
|
|
216
217
|
lambda ddd_spec: ddd_spec.end_of_pdu_fields,
|
217
218
|
lambda parent_ref: parent_ref.not_inherited_dops,
|
218
219
|
)
|
220
|
+
dynamic_endmarker_fields = self._compute_available_ddd_spec_items(
|
221
|
+
lambda ddd_spec: ddd_spec.dynamic_endmarker_fields,
|
222
|
+
lambda parent_ref: parent_ref.not_inherited_dops,
|
223
|
+
)
|
219
224
|
dynamic_length_fields = self._compute_available_ddd_spec_items(
|
220
225
|
lambda ddd_spec: ddd_spec.dynamic_length_fields,
|
221
226
|
lambda parent_ref: parent_ref.not_inherited_dops,
|
@@ -246,6 +251,7 @@ class DiagLayer:
|
|
246
251
|
structures=structures,
|
247
252
|
static_fields=NamedItemList(),
|
248
253
|
end_of_pdu_fields=end_of_pdu_fields,
|
254
|
+
dynamic_endmarker_fields=dynamic_endmarker_fields,
|
249
255
|
dynamic_length_fields=dynamic_length_fields,
|
250
256
|
tables=tables,
|
251
257
|
env_data_descs=env_data_descs,
|
@@ -351,7 +357,7 @@ class DiagLayer:
|
|
351
357
|
return self.diag_layer_raw.prot_stack_snref
|
352
358
|
|
353
359
|
@property
|
354
|
-
def comparam_spec(self) -> Optional[ComparamSpec]:
|
360
|
+
def comparam_spec(self) -> Optional[Union[ComparamSpec, ComparamSubset]]:
|
355
361
|
return self.diag_layer_raw.comparam_spec
|
356
362
|
|
357
363
|
@property
|
odxtools/diaglayerraw.py
CHANGED
@@ -9,6 +9,7 @@ from .admindata import AdminData
|
|
9
9
|
from .companydata import CompanyData
|
10
10
|
from .comparaminstance import ComparamInstance
|
11
11
|
from .comparamspec import ComparamSpec
|
12
|
+
from .comparamsubset import ComparamSubset
|
12
13
|
from .createsdgs import create_sdgs_from_et
|
13
14
|
from .diagcomm import DiagComm
|
14
15
|
from .diagdatadictionaryspec import DiagDataDictionarySpec
|
@@ -19,7 +20,7 @@ from .element import IdentifiableElement
|
|
19
20
|
from .exceptions import odxassert, odxraise, odxrequire
|
20
21
|
from .functionalclass import FunctionalClass
|
21
22
|
from .nameditemlist import NamedItemList
|
22
|
-
from .odxlink import OdxDocFragment, OdxLinkDatabase, OdxLinkId, OdxLinkRef
|
23
|
+
from .odxlink import OdxDocFragment, OdxLinkDatabase, OdxLinkId, OdxLinkRef, resolve_snref
|
23
24
|
from .parentref import ParentRef
|
24
25
|
from .protstack import ProtStack
|
25
26
|
from .request import Request
|
@@ -243,7 +244,10 @@ class DiagLayerRaw(IdentifiableElement):
|
|
243
244
|
"""Recursively resolve all references."""
|
244
245
|
|
245
246
|
if self.comparam_spec_ref is not None:
|
246
|
-
|
247
|
+
spec = odxlinks.resolve(self.comparam_spec_ref)
|
248
|
+
if not isinstance(spec, (ComparamSubset, ComparamSpec)):
|
249
|
+
odxraise(f"Type {type(spec).__name__} is not allowed for comparam specs")
|
250
|
+
self._comparam_spec = spec
|
247
251
|
|
248
252
|
# do ODXLINK reference resolution
|
249
253
|
if self.admin_data is not None:
|
@@ -281,8 +285,10 @@ class DiagLayerRaw(IdentifiableElement):
|
|
281
285
|
def _resolve_snrefs(self, diag_layer: "DiagLayer") -> None:
|
282
286
|
self._prot_stack: Optional[ProtStack] = None
|
283
287
|
if self.prot_stack_snref is not None:
|
284
|
-
|
285
|
-
|
288
|
+
cp_spec = self.comparam_spec
|
289
|
+
if isinstance(cp_spec, ComparamSpec):
|
290
|
+
self._prot_stack = resolve_snref(self.prot_stack_snref, cp_spec.prot_stacks,
|
291
|
+
ProtStack)
|
286
292
|
|
287
293
|
# do short-name reference resolution
|
288
294
|
if self.admin_data is not None:
|
@@ -292,8 +298,8 @@ class DiagLayerRaw(IdentifiableElement):
|
|
292
298
|
|
293
299
|
for company_data in self.company_datas:
|
294
300
|
company_data._resolve_snrefs(diag_layer)
|
295
|
-
for
|
296
|
-
|
301
|
+
for functional_class in self.functional_classes:
|
302
|
+
functional_class._resolve_snrefs(diag_layer)
|
297
303
|
for diag_comm in self.diag_comms:
|
298
304
|
if isinstance(diag_comm, OdxLinkRef):
|
299
305
|
continue
|
@@ -318,7 +324,7 @@ class DiagLayerRaw(IdentifiableElement):
|
|
318
324
|
comparam._resolve_snrefs(diag_layer)
|
319
325
|
|
320
326
|
@property
|
321
|
-
def comparam_spec(self) -> Optional[ComparamSpec]:
|
327
|
+
def comparam_spec(self) -> Optional[Union[ComparamSpec, ComparamSubset]]:
|
322
328
|
return self._comparam_spec
|
323
329
|
|
324
330
|
@property
|
@@ -0,0 +1,119 @@
|
|
1
|
+
# SPDX-License-Identifier: MIT
|
2
|
+
from dataclasses import dataclass
|
3
|
+
from typing import TYPE_CHECKING, Any, Dict, List, Sequence
|
4
|
+
from xml.etree import ElementTree
|
5
|
+
|
6
|
+
from typing_extensions import override
|
7
|
+
|
8
|
+
from .dataobjectproperty import DataObjectProperty
|
9
|
+
from .decodestate import DecodeState
|
10
|
+
from .dynenddopref import DynEndDopRef
|
11
|
+
from .encodestate import EncodeState
|
12
|
+
from .exceptions import DecodeError, EncodeError, odxassert, odxraise, odxrequire
|
13
|
+
from .field import Field
|
14
|
+
from .odxlink import OdxDocFragment, OdxLinkDatabase, OdxLinkId
|
15
|
+
from .odxtypes import AtomicOdxType, ParameterValue
|
16
|
+
from .utils import dataclass_fields_asdict
|
17
|
+
|
18
|
+
if TYPE_CHECKING:
|
19
|
+
from .diaglayer import DiagLayer
|
20
|
+
|
21
|
+
|
22
|
+
@dataclass
|
23
|
+
class DynamicEndmarkerField(Field):
|
24
|
+
"""Array of a structure with variable length determined by a termination sequence"""
|
25
|
+
|
26
|
+
dyn_end_dop_ref: DynEndDopRef
|
27
|
+
|
28
|
+
@staticmethod
|
29
|
+
def from_et(et_element: ElementTree.Element,
|
30
|
+
doc_frags: List[OdxDocFragment]) -> "DynamicEndmarkerField":
|
31
|
+
kwargs = dataclass_fields_asdict(Field.from_et(et_element, doc_frags))
|
32
|
+
|
33
|
+
dyn_end_dop_ref = DynEndDopRef.from_et(
|
34
|
+
odxrequire(et_element.find("DYN-END-DOP-REF")), doc_frags)
|
35
|
+
|
36
|
+
return DynamicEndmarkerField(dyn_end_dop_ref=dyn_end_dop_ref, **kwargs)
|
37
|
+
|
38
|
+
def _build_odxlinks(self) -> Dict[OdxLinkId, Any]:
|
39
|
+
odxlinks = super()._build_odxlinks()
|
40
|
+
return odxlinks
|
41
|
+
|
42
|
+
def _resolve_odxlinks(self, odxlinks: OdxLinkDatabase) -> None:
|
43
|
+
super()._resolve_odxlinks(odxlinks)
|
44
|
+
|
45
|
+
self._dyn_end_dop = odxlinks.resolve(self.dyn_end_dop_ref, DataObjectProperty)
|
46
|
+
|
47
|
+
tv_string = self.dyn_end_dop_ref.termination_value_raw
|
48
|
+
tv_physical = self._dyn_end_dop.diag_coded_type.base_data_type.from_string(tv_string)
|
49
|
+
|
50
|
+
self._termination_value = tv_physical
|
51
|
+
|
52
|
+
def _resolve_snrefs(self, diag_layer: "DiagLayer") -> None:
|
53
|
+
super()._resolve_snrefs(diag_layer)
|
54
|
+
|
55
|
+
@property
|
56
|
+
def dyn_end_dop(self) -> DataObjectProperty:
|
57
|
+
return self._dyn_end_dop
|
58
|
+
|
59
|
+
@property
|
60
|
+
def termination_value(self) -> AtomicOdxType:
|
61
|
+
return self._termination_value
|
62
|
+
|
63
|
+
@override
|
64
|
+
def encode_into_pdu(self, physical_value: ParameterValue, encode_state: EncodeState) -> None:
|
65
|
+
|
66
|
+
odxassert(encode_state.cursor_bit_position == 0,
|
67
|
+
"No bit position can be specified for dynamic endmarker fields!")
|
68
|
+
if not isinstance(physical_value, Sequence):
|
69
|
+
odxraise(
|
70
|
+
f"Expected a sequence of values for dynamic endmarker field {self.short_name}, "
|
71
|
+
f"got {type(physical_value).__name__}", EncodeError)
|
72
|
+
return
|
73
|
+
|
74
|
+
orig_is_end_of_pdu = encode_state.is_end_of_pdu
|
75
|
+
encode_state.is_end_of_pdu = False
|
76
|
+
for i, item in enumerate(physical_value):
|
77
|
+
if i == len(physical_value) - 1:
|
78
|
+
encode_state.is_end_of_pdu = orig_is_end_of_pdu
|
79
|
+
|
80
|
+
self.structure.encode_into_pdu(item, encode_state)
|
81
|
+
encode_state.is_end_of_pdu = orig_is_end_of_pdu
|
82
|
+
|
83
|
+
if not encode_state.is_end_of_pdu:
|
84
|
+
# only add an endmarker if we are not at the end of the PDU
|
85
|
+
self.dyn_end_dop.encode_into_pdu(self.termination_value, encode_state)
|
86
|
+
|
87
|
+
@override
|
88
|
+
def decode_from_pdu(self, decode_state: DecodeState) -> ParameterValue:
|
89
|
+
|
90
|
+
odxassert(decode_state.cursor_bit_position == 0,
|
91
|
+
"No bit position can be specified for dynamic endmarker fields!")
|
92
|
+
|
93
|
+
orig_origin = decode_state.origin_byte_position
|
94
|
+
orig_cursor = decode_state.cursor_byte_position
|
95
|
+
decode_state.origin_byte_position = decode_state.cursor_byte_position
|
96
|
+
|
97
|
+
result: List[ParameterValue] = []
|
98
|
+
while True:
|
99
|
+
# check if we're at the end of the PDU
|
100
|
+
if decode_state.cursor_byte_position == len(decode_state.coded_message):
|
101
|
+
break
|
102
|
+
|
103
|
+
# check if the cursor currently points to a termination
|
104
|
+
# value
|
105
|
+
tmp_cursor = decode_state.cursor_byte_position
|
106
|
+
try:
|
107
|
+
tv_candidate = self.dyn_end_dop.decode_from_pdu(decode_state)
|
108
|
+
if tv_candidate == self.termination_value:
|
109
|
+
break
|
110
|
+
except DecodeError:
|
111
|
+
pass
|
112
|
+
decode_state.cursor_byte_position = tmp_cursor
|
113
|
+
|
114
|
+
result.append(self.structure.decode_from_pdu(decode_state))
|
115
|
+
|
116
|
+
decode_state.origin_byte_position = orig_origin
|
117
|
+
decode_state.cursor_byte_position = max(orig_cursor, decode_state.cursor_byte_position)
|
118
|
+
|
119
|
+
return result
|
odxtools/dynamiclengthfield.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
|
@@ -55,7 +55,7 @@ class DynamicLengthField(Field):
|
|
55
55
|
odxassert(encode_state.cursor_bit_position == 0,
|
56
56
|
"No bit position can be specified for dynamic length fields!")
|
57
57
|
|
58
|
-
if not isinstance(physical_value,
|
58
|
+
if not isinstance(physical_value, Sequence):
|
59
59
|
odxraise(
|
60
60
|
f"Expected a list of values for dynamic length field {self.short_name}, "
|
61
61
|
f"got {type(physical_value)}", EncodeError)
|
@@ -77,8 +77,14 @@ class DynamicLengthField(Field):
|
|
77
77
|
encode_state.cursor_byte_position = encode_state.origin_byte_position + self.offset
|
78
78
|
encode_state.cursor_bit_position = 0
|
79
79
|
|
80
|
-
|
80
|
+
orig_is_end_of_pdu = encode_state.is_end_of_pdu
|
81
|
+
encode_state.is_end_of_pdu = False
|
82
|
+
for i, value in enumerate(physical_value):
|
83
|
+
if i == len(physical_value) - 1:
|
84
|
+
encode_state.is_end_of_pdu = orig_is_end_of_pdu
|
85
|
+
|
81
86
|
self.structure.encode_into_pdu(value, encode_state)
|
87
|
+
encode_state.is_end_of_pdu = orig_is_end_of_pdu
|
82
88
|
|
83
89
|
# ensure the correct message size if the field is empty
|
84
90
|
if len(physical_value) == 0:
|
odxtools/dynenddopref.py
ADDED
@@ -0,0 +1,38 @@
|
|
1
|
+
# SPDX-License-Identifier: MIT
|
2
|
+
from dataclasses import dataclass
|
3
|
+
from typing import List, Optional, overload
|
4
|
+
from xml.etree import ElementTree
|
5
|
+
|
6
|
+
from .exceptions import odxraise, odxrequire
|
7
|
+
from .odxlink import OdxDocFragment, OdxLinkRef
|
8
|
+
from .utils import dataclass_fields_asdict
|
9
|
+
|
10
|
+
|
11
|
+
@dataclass
|
12
|
+
class DynEndDopRef(OdxLinkRef):
|
13
|
+
termination_value_raw: str
|
14
|
+
|
15
|
+
@staticmethod
|
16
|
+
@overload
|
17
|
+
def from_et(et_element: None, source_doc_frags: List[OdxDocFragment]) -> None:
|
18
|
+
...
|
19
|
+
|
20
|
+
@staticmethod
|
21
|
+
@overload
|
22
|
+
def from_et(et_element: ElementTree.Element,
|
23
|
+
source_doc_frags: List[OdxDocFragment]) -> "DynEndDopRef":
|
24
|
+
...
|
25
|
+
|
26
|
+
@staticmethod
|
27
|
+
def from_et(et_element: Optional[ElementTree.Element],
|
28
|
+
source_doc_frags: List[OdxDocFragment]) -> Optional["DynEndDopRef"]:
|
29
|
+
|
30
|
+
if et_element is None:
|
31
|
+
odxraise("Mandatory DYN-END-DOP-REF tag is missing")
|
32
|
+
return None
|
33
|
+
|
34
|
+
kwargs = dataclass_fields_asdict(OdxLinkRef.from_et(et_element, source_doc_frags))
|
35
|
+
|
36
|
+
termination_value_raw = odxrequire(et_element.findtext("TERMINATION-VALUE"))
|
37
|
+
|
38
|
+
return DynEndDopRef(termination_value_raw=termination_value_raw, **kwargs)
|
odxtools/encodestate.py
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
# SPDX-License-Identifier: MIT
|
2
2
|
import warnings
|
3
3
|
from dataclasses import dataclass, field
|
4
|
-
from typing import Dict, Optional
|
4
|
+
from typing import Dict, Optional, SupportsBytes
|
5
5
|
|
6
6
|
from .exceptions import EncodeError, OdxWarning, odxassert, odxraise
|
7
7
|
from .odxtypes import AtomicOdxType, DataType
|
@@ -83,13 +83,13 @@ class EncodeState:
|
|
83
83
|
|
84
84
|
# Check that bytes and strings actually fit into the bit length
|
85
85
|
if base_data_type == DataType.A_BYTEFIELD:
|
86
|
-
if not isinstance(internal_value, bytes):
|
86
|
+
if not isinstance(internal_value, (bytes, bytearray, SupportsBytes)):
|
87
87
|
odxraise()
|
88
88
|
if 8 * len(internal_value) > bit_length:
|
89
89
|
raise EncodeError(f"The bytefield {internal_value.hex()} is too large "
|
90
90
|
f"({len(internal_value)} bytes)."
|
91
91
|
f" The maximum length is {bit_length//8}.")
|
92
|
-
raw_value = internal_value
|
92
|
+
raw_value = bytes(internal_value)
|
93
93
|
elif base_data_type == DataType.A_ASCIISTRING:
|
94
94
|
if not isinstance(internal_value, str):
|
95
95
|
odxraise()
|
odxtools/endofpdufield.py
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
# SPDX-License-Identifier: MIT
|
2
2
|
from dataclasses import dataclass
|
3
|
-
from typing import List, Optional
|
3
|
+
from typing import List, Optional, Sequence
|
4
4
|
from xml.etree import ElementTree
|
5
5
|
|
6
6
|
from typing_extensions import override
|
@@ -46,21 +46,35 @@ class EndOfPduField(Field):
|
|
46
46
|
encode_state: EncodeState) -> None:
|
47
47
|
odxassert(not encode_state.cursor_bit_position,
|
48
48
|
"No bit position can be specified for end-of-pdu fields!")
|
49
|
+
odxassert(encode_state.is_end_of_pdu,
|
50
|
+
"End-of-pdu fields can only be located at the end of PDUs!")
|
49
51
|
|
50
|
-
if not isinstance(physical_value,
|
52
|
+
if not isinstance(physical_value, Sequence):
|
51
53
|
odxraise(
|
52
54
|
f"Invalid type {type(physical_value).__name__} of physical "
|
53
55
|
f"value for end-of-pdu field, expected a list", EncodeError)
|
54
56
|
return
|
55
57
|
|
56
|
-
|
58
|
+
orig_is_end_of_pdu = encode_state.is_end_of_pdu
|
59
|
+
encode_state.is_end_of_pdu = False
|
60
|
+
|
61
|
+
for i, value in enumerate(physical_value):
|
62
|
+
if i == len(physical_value) - 1:
|
63
|
+
encode_state.is_end_of_pdu = orig_is_end_of_pdu
|
64
|
+
|
57
65
|
self.structure.encode_into_pdu(value, encode_state)
|
58
66
|
|
67
|
+
encode_state.is_end_of_pdu = orig_is_end_of_pdu
|
68
|
+
|
59
69
|
@override
|
60
70
|
def decode_from_pdu(self, decode_state: DecodeState) -> ParameterValue:
|
61
71
|
odxassert(not decode_state.cursor_bit_position,
|
62
72
|
"No bit position can be specified for end-of-pdu fields!")
|
63
73
|
|
74
|
+
orig_origin = decode_state.origin_byte_position
|
75
|
+
orig_cursor = decode_state.cursor_byte_position
|
76
|
+
decode_state.origin_byte_position = decode_state.cursor_byte_position
|
77
|
+
|
64
78
|
result: List[ParameterValue] = []
|
65
79
|
while decode_state.cursor_byte_position < len(decode_state.coded_message):
|
66
80
|
# ATTENTION: the ODX specification is very misleading
|
@@ -69,4 +83,7 @@ class EndOfPduField(Field):
|
|
69
83
|
# repeated are identical, not their values
|
70
84
|
result.append(self.structure.decode_from_pdu(decode_state))
|
71
85
|
|
86
|
+
decode_state.origin_byte_position = orig_origin
|
87
|
+
decode_state.cursor_byte_position = max(orig_cursor, decode_state.cursor_byte_position)
|
88
|
+
|
72
89
|
return result
|
odxtools/environmentdata.py
CHANGED
@@ -11,7 +11,14 @@ from .utils import dataclass_fields_asdict
|
|
11
11
|
|
12
12
|
@dataclass
|
13
13
|
class EnvironmentData(BasicStructure):
|
14
|
-
"""This class represents Environment Data that describes the
|
14
|
+
"""This class represents Environment Data that describes the
|
15
|
+
circumstances in which the error occurred.
|
16
|
+
|
17
|
+
This is one of the many multiplexer mechanisms specified by the
|
18
|
+
ODX standard, because an environment data parameter must only be
|
19
|
+
used if a DTC parameter has a certain set of values. (In this
|
20
|
+
sense, it is quite similar to NRC-CONST parameters.)
|
21
|
+
"""
|
15
22
|
|
16
23
|
all_value: Optional[bool]
|
17
24
|
dtc_values: List[int]
|
@@ -20,8 +20,14 @@ if TYPE_CHECKING:
|
|
20
20
|
|
21
21
|
@dataclass
|
22
22
|
class EnvironmentDataDescription(ComplexDop):
|
23
|
-
"""This class represents
|
24
|
-
|
23
|
+
"""This class represents environment data descriptions
|
24
|
+
|
25
|
+
An environment data description provides a list of all environment
|
26
|
+
data objects that are potentially applicable to decode a given
|
27
|
+
response. (If a given environment data object is applicable
|
28
|
+
depends on the value of the DtcDOP that is associated with it.)
|
29
|
+
|
30
|
+
"""
|
25
31
|
|
26
32
|
# in ODX 2.0.0, ENV-DATAS seems to be a mandatory
|
27
33
|
# sub-element of ENV-DATA-DESC, in ODX 2.2 it is not
|
odxtools/field.py
CHANGED
@@ -6,7 +6,7 @@ from .basicstructure import BasicStructure
|
|
6
6
|
from .complexdop import ComplexDop
|
7
7
|
from .environmentdatadescription import EnvironmentDataDescription
|
8
8
|
from .exceptions import odxassert, odxrequire
|
9
|
-
from .odxlink import OdxDocFragment, OdxLinkDatabase, OdxLinkRef
|
9
|
+
from .odxlink import OdxDocFragment, OdxLinkDatabase, OdxLinkRef, resolve_snref
|
10
10
|
from .odxtypes import odxstr_to_bool
|
11
11
|
from .utils import dataclass_fields_asdict
|
12
12
|
|
@@ -84,8 +84,9 @@ class Field(ComplexDop):
|
|
84
84
|
"""Recursively resolve any short-name references"""
|
85
85
|
if self.structure_snref is not None:
|
86
86
|
structures = diag_layer.diag_data_dictionary_spec.structures
|
87
|
-
self._structure =
|
87
|
+
self._structure = resolve_snref(self.structure_snref, structures, BasicStructure)
|
88
88
|
|
89
89
|
if self.env_data_desc_snref is not None:
|
90
90
|
env_data_descs = diag_layer.diag_data_dictionary_spec.env_data_descs
|
91
|
-
self._env_data_desc =
|
91
|
+
self._env_data_desc = resolve_snref(self.env_data_desc_snref, env_data_descs,
|
92
|
+
EnvironmentDataDescription)
|
odxtools/matchingparameter.py
CHANGED
@@ -32,8 +32,8 @@ class MatchingParameter:
|
|
32
32
|
doc_frags: List[OdxDocFragment]) -> "MatchingParameter":
|
33
33
|
|
34
34
|
expected_value = odxrequire(et_element.findtext("EXPECTED-VALUE"))
|
35
|
-
|
36
|
-
diag_comm_snref = odxrequire(
|
35
|
+
diag_comm_snref_el = odxrequire(et_element.find("DIAG-COMM-SNREF"))
|
36
|
+
diag_comm_snref = odxrequire(diag_comm_snref_el.get("SHORT-NAME"))
|
37
37
|
out_param_snref_el = et_element.find("OUT-PARAM-IF-SNREF")
|
38
38
|
out_param_snpathref_el = et_element.find("OUT-PARAM-IF-SNPATHREF")
|
39
39
|
out_param_if = None
|
odxtools/multiplexercase.py
CHANGED
@@ -7,7 +7,7 @@ from .basicstructure import BasicStructure
|
|
7
7
|
from .compumethods.limit import Limit
|
8
8
|
from .element import NamedElement
|
9
9
|
from .exceptions import odxrequire
|
10
|
-
from .odxlink import OdxDocFragment, OdxLinkDatabase, OdxLinkId, OdxLinkRef
|
10
|
+
from .odxlink import OdxDocFragment, OdxLinkDatabase, OdxLinkId, OdxLinkRef, resolve_snref
|
11
11
|
from .odxtypes import AtomicOdxType, DataType
|
12
12
|
from .utils import dataclass_fields_asdict
|
13
13
|
|
@@ -73,7 +73,7 @@ class MultiplexerCase(NamedElement):
|
|
73
73
|
def _resolve_snrefs(self, diag_layer: "DiagLayer") -> None:
|
74
74
|
if self.structure_snref:
|
75
75
|
ddds = diag_layer.diag_data_dictionary_spec
|
76
|
-
self._structure =
|
76
|
+
self._structure = resolve_snref(self.structure_snref, ddds.structures, BasicStructure)
|
77
77
|
|
78
78
|
def applies(self, value: AtomicOdxType) -> bool:
|
79
79
|
return self.lower_limit.complies_to_lower(value) \
|
@@ -6,7 +6,7 @@ from xml.etree import ElementTree
|
|
6
6
|
from .basicstructure import BasicStructure
|
7
7
|
from .element import NamedElement
|
8
8
|
from .exceptions import odxrequire
|
9
|
-
from .odxlink import OdxDocFragment, OdxLinkDatabase, OdxLinkId, OdxLinkRef
|
9
|
+
from .odxlink import OdxDocFragment, OdxLinkDatabase, OdxLinkId, OdxLinkRef, resolve_snref
|
10
10
|
from .utils import dataclass_fields_asdict
|
11
11
|
|
12
12
|
if TYPE_CHECKING:
|
@@ -20,7 +20,7 @@ class MultiplexerDefaultCase(NamedElement):
|
|
20
20
|
structure_snref: Optional[str]
|
21
21
|
|
22
22
|
def __post_init__(self) -> None:
|
23
|
-
self._structure:
|
23
|
+
self._structure: BasicStructure
|
24
24
|
|
25
25
|
@staticmethod
|
26
26
|
def from_et(et_element: ElementTree.Element,
|
@@ -46,4 +46,8 @@ class MultiplexerDefaultCase(NamedElement):
|
|
46
46
|
def _resolve_snrefs(self, diag_layer: "DiagLayer") -> None:
|
47
47
|
if self.structure_snref:
|
48
48
|
ddds = diag_layer.diag_data_dictionary_spec
|
49
|
-
self._structure =
|
49
|
+
self._structure = resolve_snref(self.structure_snref, ddds.structures, BasicStructure)
|
50
|
+
|
51
|
+
@property
|
52
|
+
def structure(self) -> BasicStructure:
|
53
|
+
return self._structure
|