odxtools 7.2.0__py3-none-any.whl → 7.3.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 +10 -7
- odxtools/compumethods/linearsegment.py +0 -2
- odxtools/database.py +17 -11
- odxtools/decodestate.py +8 -2
- odxtools/diaglayer.py +10 -9
- odxtools/diagnostictroublecode.py +2 -2
- odxtools/diagservice.py +33 -20
- odxtools/dtcdop.py +39 -14
- odxtools/encodestate.py +14 -2
- odxtools/environmentdatadescription.py +134 -14
- odxtools/exceptions.py +10 -1
- odxtools/multiplexer.py +92 -56
- odxtools/multiplexercase.py +6 -5
- odxtools/multiplexerdefaultcase.py +7 -6
- odxtools/odxlink.py +12 -26
- odxtools/odxtypes.py +1 -1
- odxtools/parameterinfo.py +2 -2
- odxtools/parameters/nrcconstparameter.py +28 -37
- odxtools/parameters/systemparameter.py +1 -1
- odxtools/version.py +2 -2
- {odxtools-7.2.0.dist-info → odxtools-7.3.0.dist-info}/METADATA +1 -1
- {odxtools-7.2.0.dist-info → odxtools-7.3.0.dist-info}/RECORD +26 -26
- {odxtools-7.2.0.dist-info → odxtools-7.3.0.dist-info}/WHEEL +1 -1
- {odxtools-7.2.0.dist-info → odxtools-7.3.0.dist-info}/LICENSE +0 -0
- {odxtools-7.2.0.dist-info → odxtools-7.3.0.dist-info}/entry_points.txt +0 -0
- {odxtools-7.2.0.dist-info → odxtools-7.3.0.dist-info}/top_level.txt +0 -0
odxtools/multiplexer.py
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
# SPDX-License-Identifier: MIT
|
2
2
|
from dataclasses import dataclass
|
3
|
-
from typing import Any, Dict, List, Optional, Tuple
|
3
|
+
from typing import Any, Dict, List, Optional, Tuple, Union, cast
|
4
4
|
from xml.etree import ElementTree
|
5
5
|
|
6
6
|
from typing_extensions import override
|
@@ -8,7 +8,7 @@ from typing_extensions import override
|
|
8
8
|
from .complexdop import ComplexDop
|
9
9
|
from .decodestate import DecodeState
|
10
10
|
from .encodestate import EncodeState
|
11
|
-
from .exceptions import DecodeError, EncodeError, odxraise, odxrequire
|
11
|
+
from .exceptions import DecodeError, EncodeError, odxassert, odxraise, odxrequire
|
12
12
|
from .multiplexercase import MultiplexerCase
|
13
13
|
from .multiplexerdefaultcase import MultiplexerDefaultCase
|
14
14
|
from .multiplexerswitchkey import MultiplexerSwitchKey
|
@@ -79,87 +79,123 @@ class Multiplexer(ComplexDop):
|
|
79
79
|
def encode_into_pdu(self, physical_value: ParameterValue, encode_state: EncodeState) -> None:
|
80
80
|
|
81
81
|
if encode_state.cursor_bit_position != 0:
|
82
|
-
raise EncodeError(f"Multiplexer must be aligned, i.e. bit_position=0, but "
|
82
|
+
raise EncodeError(f"Multiplexer parameters must be aligned, i.e. bit_position=0, but "
|
83
83
|
f"{self.short_name} was passed the bit position "
|
84
84
|
f"{encode_state.cursor_bit_position}")
|
85
85
|
|
86
|
-
if not isinstance(physical_value, dict) or len(physical_value) != 1:
|
87
|
-
raise EncodeError("""Multiplexer should be defined as a dict
|
88
|
-
with only one key equal to the desired case""")
|
89
|
-
|
90
86
|
orig_origin = encode_state.origin_byte_position
|
91
|
-
|
92
87
|
encode_state.origin_byte_position = encode_state.cursor_byte_position
|
93
88
|
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
89
|
+
if isinstance(physical_value, (list, tuple)) and len(physical_value) == 2:
|
90
|
+
case_spec, case_value = physical_value
|
91
|
+
elif isinstance(physical_value, dict) and len(physical_value) == 1:
|
92
|
+
case_spec, case_value = next(iter(physical_value.items()))
|
93
|
+
else:
|
94
|
+
raise EncodeError(
|
95
|
+
f"Values of multiplexer parameters must be defined as a "
|
96
|
+
f"(case_name, content_value) tuple instead of as '{physical_value!r}'")
|
97
|
+
|
98
|
+
mux_case: Union[MultiplexerCase, MultiplexerDefaultCase]
|
99
|
+
if isinstance(case_spec, str):
|
100
|
+
applicable_cases = [x for x in self.cases if x.short_name == case_spec]
|
101
|
+
if len(applicable_cases) == 0:
|
102
|
+
raise EncodeError(
|
103
|
+
f"Multiplexer {self.short_name} does not know any case called {case_spec}")
|
104
|
+
|
105
|
+
odxassert(len(applicable_cases) == 1)
|
106
|
+
mux_case = applicable_cases[0]
|
107
|
+
key_value, _ = self._get_case_limits(mux_case)
|
108
|
+
elif isinstance(case_spec, int):
|
109
|
+
applicable_cases = []
|
110
|
+
for x in self.cases:
|
111
|
+
lower, upper = cast(Tuple[int, int], self._get_case_limits(x))
|
112
|
+
if lower <= case_spec and case_spec <= upper:
|
113
|
+
applicable_cases.append(x)
|
114
|
+
|
115
|
+
if len(applicable_cases) == 0:
|
116
|
+
if self.default_case is None:
|
117
|
+
raise EncodeError(
|
118
|
+
f"Multiplexer {self.short_name} does not know any case called {case_spec}")
|
119
|
+
mux_case = self.default_case
|
120
|
+
key_value = case_spec
|
121
|
+
else:
|
122
|
+
mux_case = applicable_cases[0]
|
123
|
+
key_value = case_spec
|
124
|
+
elif isinstance(case_spec, MultiplexerCase):
|
125
|
+
mux_case = case_spec
|
126
|
+
key_value, _ = self._get_case_limits(mux_case)
|
127
|
+
elif case_spec is None:
|
128
|
+
if self.default_case is None:
|
129
|
+
raise EncodeError(f"Multiplexer {self.short_name} does not define a default case")
|
130
|
+
key_value = 0
|
131
|
+
else:
|
132
|
+
raise EncodeError(f"Illegal case specification '{case_spec}' for "
|
133
|
+
f"multiplexer {self.short_name}")
|
134
|
+
|
135
|
+
# the byte position of the switch key is relative to
|
136
|
+
# the multiplexer's position
|
137
|
+
encode_state.cursor_byte_position = encode_state.origin_byte_position + self.switch_key.byte_position
|
138
|
+
encode_state.cursor_bit_position = self.switch_key.bit_position or 0
|
139
|
+
self.switch_key.dop.encode_into_pdu(physical_value=key_value, encode_state=encode_state)
|
140
|
+
encode_state.cursor_bit_position = 0
|
141
|
+
|
142
|
+
if mux_case.structure is not None:
|
143
|
+
# the byte position of the content is specified by the
|
144
|
+
# BYTE-POSITION attribute of the multiplexer
|
145
|
+
encode_state.cursor_byte_position = encode_state.origin_byte_position + self.byte_position
|
146
|
+
mux_case.structure.encode_into_pdu(physical_value=case_value, encode_state=encode_state)
|
147
|
+
|
148
|
+
encode_state.origin_byte_position = orig_origin
|
123
149
|
|
124
150
|
@override
|
125
151
|
def decode_from_pdu(self, decode_state: DecodeState) -> ParameterValue:
|
126
|
-
|
127
|
-
# multiplexers are structures and thus the origin position
|
128
|
-
# must be moved to the start of the multiplexer
|
129
152
|
orig_origin = decode_state.origin_byte_position
|
130
|
-
if self.byte_position is not None:
|
131
|
-
decode_state.cursor_byte_position = decode_state.origin_byte_position + self.byte_position
|
132
153
|
decode_state.origin_byte_position = decode_state.cursor_byte_position
|
133
154
|
|
155
|
+
# Decode the switch key. Its BYTE-POSITION is relative to the
|
156
|
+
# that of the multiplexer.
|
157
|
+
if self.switch_key.byte_position is not None:
|
158
|
+
decode_state.cursor_byte_position = decode_state.origin_byte_position + self.switch_key.byte_position
|
159
|
+
decode_state.cursor_bit_position = self.switch_key.bit_position or 0
|
134
160
|
key_value = self.switch_key.dop.decode_from_pdu(decode_state)
|
161
|
+
decode_state.cursor_bit_position = 0
|
135
162
|
|
136
163
|
if not isinstance(key_value, int):
|
137
164
|
odxraise(f"Multiplexer keys must be integers (is '{type(key_value).__name__}'"
|
138
165
|
f" for multiplexer '{self.short_name}')")
|
139
166
|
|
140
|
-
|
141
|
-
|
142
|
-
|
167
|
+
# "If a matching CASE is found, the referenced STRUCTURE is
|
168
|
+
# analyzed at the BYTE-POSITION (child element of MUX)
|
169
|
+
# relatively to the byte position of the MUX."
|
170
|
+
decode_state.cursor_byte_position = decode_state.origin_byte_position + self.byte_position
|
171
|
+
|
172
|
+
applicable_case: Optional[Union[MultiplexerCase, MultiplexerDefaultCase]] = None
|
173
|
+
for mux_case in self.cases:
|
143
174
|
lower, upper = self._get_case_limits(mux_case)
|
144
175
|
if lower <= key_value and key_value <= upper: # type: ignore[operator]
|
145
|
-
|
146
|
-
case_value = mux_case._structure.decode_from_pdu(decode_state)
|
176
|
+
applicable_case = mux_case
|
147
177
|
break
|
148
178
|
|
149
|
-
if
|
150
|
-
|
151
|
-
|
179
|
+
if applicable_case is None:
|
180
|
+
applicable_case = self.default_case
|
181
|
+
|
182
|
+
if applicable_case is None:
|
183
|
+
odxraise(
|
184
|
+
f"Cannot find an applicable case for value {key_value} in "
|
185
|
+
f"multiplexer {self.short_name}", DecodeError)
|
186
|
+
decode_state.origin_byte_position = orig_origin
|
187
|
+
return (None, None)
|
152
188
|
|
153
|
-
if
|
154
|
-
|
155
|
-
|
189
|
+
if applicable_case.structure is not None:
|
190
|
+
case_value = applicable_case.structure.decode_from_pdu(decode_state)
|
191
|
+
else:
|
192
|
+
case_value = {}
|
156
193
|
|
157
|
-
|
194
|
+
result = (applicable_case.short_name, case_value)
|
158
195
|
|
159
|
-
# go back to the original origin
|
160
196
|
decode_state.origin_byte_position = orig_origin
|
161
197
|
|
162
|
-
return
|
198
|
+
return result
|
163
199
|
|
164
200
|
@override
|
165
201
|
def _build_odxlinks(self) -> Dict[OdxLinkId, Any]:
|
odxtools/multiplexercase.py
CHANGED
@@ -3,13 +3,13 @@ from dataclasses import dataclass
|
|
3
3
|
from typing import Any, Dict, List, Optional
|
4
4
|
from xml.etree import ElementTree
|
5
5
|
|
6
|
-
from .basicstructure import BasicStructure
|
7
6
|
from .compumethods.limit import Limit
|
8
7
|
from .element import NamedElement
|
9
8
|
from .exceptions import odxrequire
|
10
9
|
from .odxlink import OdxDocFragment, OdxLinkDatabase, OdxLinkId, OdxLinkRef, resolve_snref
|
11
10
|
from .odxtypes import AtomicOdxType, DataType
|
12
11
|
from .snrefcontext import SnRefContext
|
12
|
+
from .structure import Structure
|
13
13
|
from .utils import dataclass_fields_asdict
|
14
14
|
|
15
15
|
|
@@ -23,7 +23,7 @@ class MultiplexerCase(NamedElement):
|
|
23
23
|
upper_limit: Limit
|
24
24
|
|
25
25
|
def __post_init__(self) -> None:
|
26
|
-
self._structure:
|
26
|
+
self._structure: Optional[Structure]
|
27
27
|
|
28
28
|
@staticmethod
|
29
29
|
def from_et(et_element: ElementTree.Element,
|
@@ -62,8 +62,9 @@ class MultiplexerCase(NamedElement):
|
|
62
62
|
|
63
63
|
def _mux_case_resolve_odxlinks(self, odxlinks: OdxLinkDatabase, *,
|
64
64
|
key_physical_type: DataType) -> None:
|
65
|
+
self._structure = None
|
65
66
|
if self.structure_ref:
|
66
|
-
self._structure = odxlinks.resolve(self.structure_ref)
|
67
|
+
self._structure = odxlinks.resolve(self.structure_ref, Structure)
|
67
68
|
|
68
69
|
self.lower_limit.set_value_type(key_physical_type)
|
69
70
|
self.upper_limit.set_value_type(key_physical_type)
|
@@ -71,12 +72,12 @@ class MultiplexerCase(NamedElement):
|
|
71
72
|
def _resolve_snrefs(self, context: SnRefContext) -> None:
|
72
73
|
if self.structure_snref:
|
73
74
|
ddds = odxrequire(context.diag_layer).diag_data_dictionary_spec
|
74
|
-
self._structure = resolve_snref(self.structure_snref, ddds.structures,
|
75
|
+
self._structure = resolve_snref(self.structure_snref, ddds.structures, Structure)
|
75
76
|
|
76
77
|
def applies(self, value: AtomicOdxType) -> bool:
|
77
78
|
return self.lower_limit.complies_to_lower(value) \
|
78
79
|
and self.upper_limit.complies_to_upper(value)
|
79
80
|
|
80
81
|
@property
|
81
|
-
def structure(self) ->
|
82
|
+
def structure(self) -> Optional[Structure]:
|
82
83
|
return self._structure
|
@@ -3,11 +3,11 @@ from dataclasses import dataclass
|
|
3
3
|
from typing import Any, Dict, List, Optional
|
4
4
|
from xml.etree import ElementTree
|
5
5
|
|
6
|
-
from .basicstructure import BasicStructure
|
7
6
|
from .element import NamedElement
|
8
7
|
from .exceptions import odxrequire
|
9
8
|
from .odxlink import OdxDocFragment, OdxLinkDatabase, OdxLinkId, OdxLinkRef, resolve_snref
|
10
9
|
from .snrefcontext import SnRefContext
|
10
|
+
from .structure import Structure
|
11
11
|
from .utils import dataclass_fields_asdict
|
12
12
|
|
13
13
|
|
@@ -18,12 +18,12 @@ class MultiplexerDefaultCase(NamedElement):
|
|
18
18
|
structure_snref: Optional[str]
|
19
19
|
|
20
20
|
def __post_init__(self) -> None:
|
21
|
-
self._structure:
|
21
|
+
self._structure: Optional[Structure]
|
22
22
|
|
23
23
|
@staticmethod
|
24
24
|
def from_et(et_element: ElementTree.Element,
|
25
25
|
doc_frags: List[OdxDocFragment]) -> "MultiplexerDefaultCase":
|
26
|
-
"""Reads a
|
26
|
+
"""Reads a default case for a multiplexer."""
|
27
27
|
kwargs = dataclass_fields_asdict(NamedElement.from_et(et_element, doc_frags))
|
28
28
|
|
29
29
|
structure_ref = OdxLinkRef.from_et(et_element.find("STRUCTURE-REF"), doc_frags)
|
@@ -38,14 +38,15 @@ class MultiplexerDefaultCase(NamedElement):
|
|
38
38
|
return {}
|
39
39
|
|
40
40
|
def _resolve_odxlinks(self, odxlinks: OdxLinkDatabase) -> None:
|
41
|
+
self._structure = None
|
41
42
|
if self.structure_ref is not None:
|
42
|
-
self._structure = odxlinks.resolve(self.structure_ref)
|
43
|
+
self._structure = odxlinks.resolve(self.structure_ref, Structure)
|
43
44
|
|
44
45
|
def _resolve_snrefs(self, context: SnRefContext) -> None:
|
45
46
|
if self.structure_snref:
|
46
47
|
ddds = odxrequire(context.diag_layer).diag_data_dictionary_spec
|
47
|
-
self._structure = resolve_snref(self.structure_snref, ddds.structures,
|
48
|
+
self._structure = resolve_snref(self.structure_snref, ddds.structures, Structure)
|
48
49
|
|
49
50
|
@property
|
50
|
-
def structure(self) ->
|
51
|
+
def structure(self) -> Optional[Structure]:
|
51
52
|
return self._structure
|
odxtools/odxlink.py
CHANGED
@@ -26,7 +26,6 @@ class OdxDocFragment:
|
|
26
26
|
return self.doc_name == other.doc_name
|
27
27
|
|
28
28
|
def __hash__(self) -> int:
|
29
|
-
# only the document name is relevant for the hash value
|
30
29
|
return hash(self.doc_name) + hash(self.doc_type)
|
31
30
|
|
32
31
|
|
@@ -59,10 +58,12 @@ class OdxLinkId:
|
|
59
58
|
if not isinstance(other, OdxLinkId):
|
60
59
|
return False
|
61
60
|
|
62
|
-
#
|
63
|
-
|
64
|
-
|
65
|
-
|
61
|
+
# if the local ID is different, the whole id is different
|
62
|
+
if self.local_id != other.local_id:
|
63
|
+
return False
|
64
|
+
|
65
|
+
# the document fragments must be identical for the IDs to be identical
|
66
|
+
return self.doc_fragments == other.doc_fragments
|
66
67
|
|
67
68
|
def __str__(self) -> str:
|
68
69
|
return f"OdxLinkId('{self.local_id}')"
|
@@ -147,19 +148,6 @@ class OdxLinkRef:
|
|
147
148
|
def __str__(self) -> str:
|
148
149
|
return f"OdxLinkRef('{self.ref_id}')"
|
149
150
|
|
150
|
-
def __contains__(self, odx_id: OdxLinkId) -> bool:
|
151
|
-
"""
|
152
|
-
Returns true iff a given OdxLinkId object is referenced.
|
153
|
-
"""
|
154
|
-
|
155
|
-
# we must reference at to at least of the ID's document
|
156
|
-
# fragments
|
157
|
-
if not any(ref_doc in odx_id.doc_fragments for ref_doc in self.ref_docs):
|
158
|
-
return False
|
159
|
-
|
160
|
-
# the local ID of the reference and the object ID must match
|
161
|
-
return odx_id.local_id == self.ref_id
|
162
|
-
|
163
151
|
|
164
152
|
T = TypeVar("T")
|
165
153
|
|
@@ -172,7 +160,7 @@ class OdxLinkDatabase:
|
|
172
160
|
"""
|
173
161
|
|
174
162
|
def __init__(self) -> None:
|
175
|
-
self._db: Dict[OdxDocFragment, Dict[
|
163
|
+
self._db: Dict[OdxDocFragment, Dict[str, Any]] = {}
|
176
164
|
|
177
165
|
@overload
|
178
166
|
def resolve(self, ref: OdxLinkRef, expected_type: None = None) -> Any:
|
@@ -189,7 +177,6 @@ class OdxLinkDatabase:
|
|
189
177
|
If the database does not contain any object which is referred to, a
|
190
178
|
KeyError exception is raised.
|
191
179
|
"""
|
192
|
-
odx_id = OdxLinkId(ref.ref_id, ref.ref_docs)
|
193
180
|
for ref_frag in reversed(ref.ref_docs):
|
194
181
|
doc_frag_db = self._db.get(ref_frag)
|
195
182
|
if doc_frag_db is None:
|
@@ -204,8 +191,9 @@ class OdxLinkDatabase:
|
|
204
191
|
)
|
205
192
|
continue
|
206
193
|
|
207
|
-
|
208
|
-
|
194
|
+
# locate an object exhibiting with the referenced local ID
|
195
|
+
# in the ID database for the document fragment
|
196
|
+
if (obj := doc_frag_db.get(ref.ref_id)) is not None:
|
209
197
|
if expected_type is not None:
|
210
198
|
odxassert(isinstance(obj, expected_type))
|
211
199
|
|
@@ -234,7 +222,6 @@ class OdxLinkDatabase:
|
|
234
222
|
is returned.
|
235
223
|
"""
|
236
224
|
|
237
|
-
odx_id = OdxLinkId(ref.ref_id, ref.ref_docs)
|
238
225
|
for ref_frag in reversed(ref.ref_docs):
|
239
226
|
doc_frag_db = self._db.get(ref_frag)
|
240
227
|
if doc_frag_db is None:
|
@@ -249,8 +236,7 @@ class OdxLinkDatabase:
|
|
249
236
|
)
|
250
237
|
continue
|
251
238
|
|
252
|
-
obj
|
253
|
-
if obj is not None:
|
239
|
+
if (obj := doc_frag_db.get(ref.ref_id)) is not None:
|
254
240
|
if expected_type is not None:
|
255
241
|
odxassert(isinstance(obj, expected_type))
|
256
242
|
|
@@ -272,7 +258,7 @@ class OdxLinkDatabase:
|
|
272
258
|
if doc_frag not in self._db:
|
273
259
|
self._db[doc_frag] = {}
|
274
260
|
|
275
|
-
self._db[doc_frag][odx_id] = obj
|
261
|
+
self._db[doc_frag][odx_id.local_id] = obj
|
276
262
|
|
277
263
|
|
278
264
|
@overload
|
odxtools/odxtypes.py
CHANGED
@@ -235,7 +235,7 @@ class DataType(Enum):
|
|
235
235
|
expected_type = self.python_type
|
236
236
|
if isinstance(value, expected_type):
|
237
237
|
return True
|
238
|
-
elif expected_type
|
238
|
+
elif expected_type is float and isinstance(value, (int, float)):
|
239
239
|
return True
|
240
240
|
elif self == DataType.A_BYTEFIELD and isinstance(value, (bytearray, bytes)):
|
241
241
|
return True
|
odxtools/parameterinfo.py
CHANGED
@@ -108,8 +108,8 @@ def parameter_info(param_list: Iterable[Parameter], quoted_names: bool = False)
|
|
108
108
|
of.write(f"multiplexer; choices:\n")
|
109
109
|
for mux_case in dop.cases:
|
110
110
|
of.write(f" ({repr(mux_case.short_name)}, {{\n")
|
111
|
-
|
112
|
-
textwrap.indent(parameter_info(
|
111
|
+
if (struc := mux_case.structure) is not None:
|
112
|
+
of.write(textwrap.indent(parameter_info(struc.parameters, True), " "))
|
113
113
|
of.write(f" }})\n")
|
114
114
|
continue
|
115
115
|
|
@@ -1,7 +1,6 @@
|
|
1
1
|
# SPDX-License-Identifier: MIT
|
2
|
-
import warnings
|
3
2
|
from dataclasses import dataclass
|
4
|
-
from typing import Any, Dict, List, Optional
|
3
|
+
from typing import Any, Dict, List, Optional
|
5
4
|
from xml.etree import ElementTree
|
6
5
|
|
7
6
|
from typing_extensions import override
|
@@ -10,7 +9,7 @@ from ..createanydiagcodedtype import create_any_diag_coded_type_from_et
|
|
10
9
|
from ..decodestate import DecodeState
|
11
10
|
from ..diagcodedtype import DiagCodedType
|
12
11
|
from ..encodestate import EncodeState
|
13
|
-
from ..exceptions import
|
12
|
+
from ..exceptions import DecodeMismatch, EncodeError, odxraise, odxrequire
|
14
13
|
from ..odxlink import OdxDocFragment, OdxLinkId
|
15
14
|
from ..odxtypes import AtomicOdxType, DataType, ParameterValue
|
16
15
|
from ..utils import dataclass_fields_asdict
|
@@ -19,7 +18,8 @@ from .parameter import Parameter, ParameterType
|
|
19
18
|
|
20
19
|
@dataclass
|
21
20
|
class NrcConstParameter(Parameter):
|
22
|
-
"""A
|
21
|
+
"""A parameter of type NRC-CONST defines a set of values to be
|
22
|
+
matched for a negative response object to apply
|
23
23
|
|
24
24
|
The behaviour of NRC-CONST parameters is similar to CODED-CONST
|
25
25
|
parameters in that they allow to specify which coding objects
|
@@ -88,49 +88,40 @@ class NrcConstParameter(Parameter):
|
|
88
88
|
@override
|
89
89
|
def _encode_positioned_into_pdu(self, physical_value: Optional[ParameterValue],
|
90
90
|
encode_state: EncodeState) -> None:
|
91
|
-
|
91
|
+
# NRC-CONST parameters are not encoding any value on its
|
92
|
+
# own. instead, it is supposed to overlap with a value
|
93
|
+
# parameter.
|
92
94
|
if physical_value is not None:
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
odxraise("The diag coded type of NRC-CONST parameters must "
|
109
|
-
"exhibit a static size")
|
110
|
-
return
|
111
|
-
|
112
|
-
encode_state.cursor_byte_position += (bit_pos + bit_len + 7) // 8
|
113
|
-
encode_state.cursor_bit_position = 0
|
95
|
+
odxraise("The value of NRC-CONST parameters cannot be set directly!", EncodeError)
|
96
|
+
|
97
|
+
# TODO (?): extract the parameter and check if it is one of
|
98
|
+
# the values of self.coded_values. if not, throw an
|
99
|
+
# EncodeMismatch exception! This is probably a bad idea
|
100
|
+
# because the parameter which determines the value of the
|
101
|
+
# NRC-CONST might possibly be specified after the NRC-CONST.
|
102
|
+
|
103
|
+
# move the cursor forward by the size of the parameter
|
104
|
+
bit_pos = encode_state.cursor_bit_position
|
105
|
+
bit_len = self.diag_coded_type.get_static_bit_length()
|
106
|
+
|
107
|
+
if bit_len is None:
|
108
|
+
odxraise("The diag coded type of NRC-CONST parameters must "
|
109
|
+
"exhibit a static size")
|
114
110
|
return
|
115
111
|
|
116
|
-
|
112
|
+
encode_state.cursor_byte_position += (bit_pos + bit_len + 7) // 8
|
113
|
+
encode_state.cursor_bit_position = 0
|
114
|
+
|
115
|
+
encode_state.emplace_bytes(b'', self.short_name)
|
117
116
|
|
118
117
|
@override
|
119
118
|
def _decode_positioned_from_pdu(self, decode_state: DecodeState) -> AtomicOdxType:
|
120
|
-
# Extract coded
|
119
|
+
# Extract coded value
|
121
120
|
coded_value = self.diag_coded_type.decode_from_pdu(decode_state)
|
122
121
|
|
123
122
|
# Check if the coded value in the message is correct.
|
124
123
|
if coded_value not in self.coded_values:
|
125
|
-
|
126
|
-
f"Coded constant parameter does not match! "
|
127
|
-
f"The parameter {self.short_name} expected a coded "
|
128
|
-
f"value in {str(self.coded_values)} but got {str(coded_value)} "
|
129
|
-
f"at byte position {decode_state.cursor_byte_position} "
|
130
|
-
f"in coded message {decode_state.coded_message.hex()}.",
|
131
|
-
DecodeError,
|
132
|
-
stacklevel=1,
|
133
|
-
)
|
124
|
+
raise DecodeMismatch(f"NRC-CONST parameter {self.short_name} does not apply")
|
134
125
|
|
135
126
|
return coded_value
|
136
127
|
|
@@ -26,7 +26,7 @@ class SystemParameter(ParameterWithDOP):
|
|
26
26
|
|
27
27
|
kwargs = dataclass_fields_asdict(ParameterWithDOP.from_et(et_element, doc_frags))
|
28
28
|
|
29
|
-
sysparam = odxrequire(et_element.
|
29
|
+
sysparam = odxrequire(et_element.get("SYSPARAM"))
|
30
30
|
|
31
31
|
return SystemParameter(sysparam=sysparam, **kwargs)
|
32
32
|
|
odxtools/version.py
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: odxtools
|
3
|
-
Version: 7.
|
3
|
+
Version: 7.3.0
|
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>
|