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/basicstructure.py
CHANGED
@@ -10,7 +10,7 @@ from .complexdop import ComplexDop
|
|
10
10
|
from .dataobjectproperty import DataObjectProperty
|
11
11
|
from .decodestate import DecodeState
|
12
12
|
from .encodestate import EncodeState
|
13
|
-
from .exceptions import EncodeError, OdxWarning, odxassert, odxraise
|
13
|
+
from .exceptions import EncodeError, OdxWarning, odxassert, odxraise
|
14
14
|
from .nameditemlist import NamedItemList
|
15
15
|
from .odxlink import OdxDocFragment, OdxLinkDatabase, OdxLinkId
|
16
16
|
from .odxtypes import ParameterDict, ParameterValue, ParameterValueDict
|
@@ -18,7 +18,6 @@ from .parameters.codedconstparameter import CodedConstParameter
|
|
18
18
|
from .parameters.createanyparameter import create_any_parameter_from_et
|
19
19
|
from .parameters.lengthkeyparameter import LengthKeyParameter
|
20
20
|
from .parameters.matchingrequestparameter import MatchingRequestParameter
|
21
|
-
from .parameters.nrcconstparameter import NrcConstParameter
|
22
21
|
from .parameters.parameter import Parameter
|
23
22
|
from .parameters.parameterwithdop import ParameterWithDOP
|
24
23
|
from .parameters.physicalconstantparameter import PhysicalConstantParameter
|
@@ -75,10 +74,11 @@ class BasicStructure(ComplexDop):
|
|
75
74
|
|
76
75
|
for param in self.parameters:
|
77
76
|
if (isinstance(param, MatchingRequestParameter) and param.request_byte_position < len(request_prefix)) or \
|
78
|
-
isinstance(param, (CodedConstParameter,
|
77
|
+
isinstance(param, (CodedConstParameter, PhysicalConstantParameter)):
|
79
78
|
param.encode_into_pdu(physical_value=None, encode_state=encode_state)
|
80
79
|
else:
|
81
80
|
break
|
81
|
+
|
82
82
|
return encode_state.coded_message
|
83
83
|
|
84
84
|
@property
|
@@ -160,8 +160,8 @@ class BasicStructure(ComplexDop):
|
|
160
160
|
orig_is_end_of_pdu = encode_state.is_end_of_pdu
|
161
161
|
encode_state.is_end_of_pdu = False
|
162
162
|
|
163
|
-
#
|
164
|
-
if
|
163
|
+
# ensure that no values for unknown parameters are specified.
|
164
|
+
if not encode_state.allow_unknown_parameters:
|
165
165
|
param_names = {param.short_name for param in self.parameters}
|
166
166
|
for param_value_name in physical_value:
|
167
167
|
if param_value_name not in param_names:
|
@@ -193,8 +193,10 @@ class BasicStructure(ComplexDop):
|
|
193
193
|
odxraise(f"No value for required parameter {param.short_name} specified",
|
194
194
|
EncodeError)
|
195
195
|
|
196
|
-
param.
|
197
|
-
|
196
|
+
param_phys_value = physical_value.get(param.short_name)
|
197
|
+
param.encode_into_pdu(physical_value=param_phys_value, encode_state=encode_state)
|
198
|
+
|
199
|
+
encode_state.journal.append((param, param_phys_value))
|
198
200
|
|
199
201
|
encode_state.is_end_of_pdu = False
|
200
202
|
if self.byte_size is not None:
|
@@ -236,6 +238,7 @@ class BasicStructure(ComplexDop):
|
|
236
238
|
for param in self.parameters:
|
237
239
|
value = param.decode_from_pdu(decode_state)
|
238
240
|
|
241
|
+
decode_state.journal.append((param, value))
|
239
242
|
result[param.short_name] = value
|
240
243
|
|
241
244
|
# decoding of the structure finished. go back the original origin.
|
@@ -46,8 +46,6 @@ class LinearSegment:
|
|
46
46
|
|
47
47
|
inverse_value: Union[int, float] = 0
|
48
48
|
if scale.compu_inverse_value is not None:
|
49
|
-
if abs(factor) < 1e-10:
|
50
|
-
odxraise(f"COMPU-INVERSE-VALUE for non-zero slope ({factor}) defined")
|
51
49
|
x = odxrequire(scale.compu_inverse_value).value
|
52
50
|
if not isinstance(x, (int, float)):
|
53
51
|
odxraise(f"Non-numeric COMPU-INVERSE-VALUE specified ({x!r})")
|
odxtools/database.py
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
# SPDX-License-Identifier: MIT
|
2
2
|
from itertools import chain
|
3
3
|
from pathlib import Path
|
4
|
-
from typing import IO, List, Optional, OrderedDict
|
4
|
+
from typing import IO, Any, Dict, List, Optional, OrderedDict
|
5
5
|
from xml.etree import ElementTree
|
6
6
|
from zipfile import ZipFile
|
7
7
|
|
@@ -13,7 +13,7 @@ from .diaglayer import DiagLayer
|
|
13
13
|
from .diaglayercontainer import DiagLayerContainer
|
14
14
|
from .exceptions import odxraise
|
15
15
|
from .nameditemlist import NamedItemList
|
16
|
-
from .odxlink import OdxLinkDatabase
|
16
|
+
from .odxlink import OdxLinkDatabase, OdxLinkId
|
17
17
|
|
18
18
|
|
19
19
|
class Database:
|
@@ -108,15 +108,7 @@ class Database:
|
|
108
108
|
|
109
109
|
# Build odxlinks
|
110
110
|
self._odxlinks = OdxLinkDatabase()
|
111
|
-
|
112
|
-
for subset in self.comparam_subsets:
|
113
|
-
self._odxlinks.update(subset._build_odxlinks())
|
114
|
-
|
115
|
-
for spec in self.comparam_specs:
|
116
|
-
self._odxlinks.update(spec._build_odxlinks())
|
117
|
-
|
118
|
-
for dlc in self.diag_layer_containers:
|
119
|
-
self._odxlinks.update(dlc._build_odxlinks())
|
111
|
+
self._odxlinks.update(self._build_odxlinks())
|
120
112
|
|
121
113
|
# Resolve ODXLINK references
|
122
114
|
for subset in self.comparam_subsets:
|
@@ -133,6 +125,20 @@ class Database:
|
|
133
125
|
for dlc in self.diag_layer_containers:
|
134
126
|
dlc._finalize_init(self, self._odxlinks)
|
135
127
|
|
128
|
+
def _build_odxlinks(self) -> Dict[OdxLinkId, Any]:
|
129
|
+
result: Dict[OdxLinkId, Any] = {}
|
130
|
+
|
131
|
+
for subset in self.comparam_subsets:
|
132
|
+
result.update(subset._build_odxlinks())
|
133
|
+
|
134
|
+
for spec in self.comparam_specs:
|
135
|
+
result.update(spec._build_odxlinks())
|
136
|
+
|
137
|
+
for dlc in self.diag_layer_containers:
|
138
|
+
result.update(dlc._build_odxlinks())
|
139
|
+
|
140
|
+
return result
|
141
|
+
|
136
142
|
@property
|
137
143
|
def odxlinks(self) -> OdxLinkDatabase:
|
138
144
|
"""A map from odx_id to object"""
|
odxtools/decodestate.py
CHANGED
@@ -1,11 +1,11 @@
|
|
1
1
|
# SPDX-License-Identifier: MIT
|
2
2
|
from dataclasses import dataclass, field
|
3
|
-
from typing import TYPE_CHECKING, Dict, cast
|
3
|
+
from typing import TYPE_CHECKING, Dict, List, Optional, Tuple, cast
|
4
4
|
|
5
5
|
import odxtools.exceptions as exceptions
|
6
6
|
|
7
7
|
from .exceptions import DecodeError
|
8
|
-
from .odxtypes import AtomicOdxType, DataType
|
8
|
+
from .odxtypes import AtomicOdxType, DataType, ParameterValue
|
9
9
|
|
10
10
|
try:
|
11
11
|
import bitstruct.c as bitstruct
|
@@ -13,6 +13,7 @@ except ImportError:
|
|
13
13
|
import bitstruct
|
14
14
|
|
15
15
|
if TYPE_CHECKING:
|
16
|
+
from .parameters.parameter import Parameter
|
16
17
|
from .tablerow import TableRow
|
17
18
|
|
18
19
|
|
@@ -46,6 +47,11 @@ class DecodeState:
|
|
46
47
|
#: values of the table key parameters decoded so far
|
47
48
|
table_keys: Dict[str, "TableRow"] = field(default_factory=dict)
|
48
49
|
|
50
|
+
#: List of parameters that have been decoded so far. The journal
|
51
|
+
#: is used by some types of parameters which depend on the values of
|
52
|
+
#: other parameters; i.e., environment data description parameters
|
53
|
+
journal: List[Tuple["Parameter", Optional[ParameterValue]]] = field(default_factory=list)
|
54
|
+
|
49
55
|
def extract_atomic_value(
|
50
56
|
self,
|
51
57
|
bit_length: int,
|
odxtools/diaglayer.py
CHANGED
@@ -161,11 +161,6 @@ class DiagLayer:
|
|
161
161
|
excessive memory consumption for large databases...
|
162
162
|
"""
|
163
163
|
|
164
|
-
# this attribute may be removed later. it is currently
|
165
|
-
# required to properly deal with auxiliary files within the
|
166
|
-
# diagnostic layer.
|
167
|
-
self._database = database
|
168
|
-
|
169
164
|
#####
|
170
165
|
# fill in all applicable objects that use value inheritance
|
171
166
|
#####
|
@@ -1200,16 +1195,19 @@ class DiagLayer:
|
|
1200
1195
|
for service in candidate_services:
|
1201
1196
|
try:
|
1202
1197
|
decoded_messages.append(service.decode_message(message))
|
1203
|
-
except DecodeError:
|
1198
|
+
except DecodeError as e:
|
1204
1199
|
# check if the message can be decoded as a global
|
1205
1200
|
# negative response for the service
|
1201
|
+
gnr_found = False
|
1206
1202
|
for gnr in self.global_negative_responses:
|
1207
1203
|
try:
|
1208
1204
|
decoded_gnr = gnr.decode(message)
|
1205
|
+
gnr_found = True
|
1209
1206
|
if not isinstance(decoded_gnr, dict):
|
1210
|
-
|
1211
|
-
|
1212
|
-
|
1207
|
+
odxraise(
|
1208
|
+
f"Expected the decoded value of a global "
|
1209
|
+
f"negative response to be a dictionary, "
|
1210
|
+
f"got {type(decoded_gnr)} for {self.short_name}", DecodeError)
|
1213
1211
|
|
1214
1212
|
decoded_messages.append(
|
1215
1213
|
Message(
|
@@ -1220,6 +1218,9 @@ class DiagLayer:
|
|
1220
1218
|
except DecodeError:
|
1221
1219
|
pass
|
1222
1220
|
|
1221
|
+
if not gnr_found:
|
1222
|
+
raise e
|
1223
|
+
|
1223
1224
|
if len(decoded_messages) == 0:
|
1224
1225
|
raise DecodeError(
|
1225
1226
|
f"None of the services {[x.short_name for x in candidate_services]} could parse {message.hex()}."
|
@@ -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, Optional
|
4
4
|
from xml.etree import ElementTree
|
5
5
|
|
6
6
|
from .element import IdentifiableElement
|
@@ -17,7 +17,7 @@ class DiagnosticTroubleCode(IdentifiableElement):
|
|
17
17
|
trouble_code: int
|
18
18
|
text: Optional[str]
|
19
19
|
display_trouble_code: Optional[str]
|
20
|
-
level:
|
20
|
+
level: Optional[int]
|
21
21
|
is_temporary_raw: Optional[bool]
|
22
22
|
sdgs: List[SpecialDataGroup]
|
23
23
|
|
odxtools/diagservice.py
CHANGED
@@ -6,7 +6,7 @@ from xml.etree import ElementTree
|
|
6
6
|
|
7
7
|
from .comparaminstance import ComparamInstance
|
8
8
|
from .diagcomm import DiagComm
|
9
|
-
from .exceptions import DecodeError, odxassert, odxraise, odxrequire
|
9
|
+
from .exceptions import DecodeError, DecodeMismatch, odxassert, odxraise, odxrequire
|
10
10
|
from .message import Message
|
11
11
|
from .nameditemlist import NamedItemList
|
12
12
|
from .odxlink import OdxDocFragment, OdxLinkDatabase, OdxLinkId, OdxLinkRef
|
@@ -42,7 +42,7 @@ class DiagService(DiagComm):
|
|
42
42
|
pos_response_refs: List[OdxLinkRef]
|
43
43
|
neg_response_refs: List[OdxLinkRef]
|
44
44
|
|
45
|
-
# TODO: pos_response_suppressable: Optional[PosResponseSuppressable]
|
45
|
+
# TODO: pos_response_suppressable: Optional[PosResponseSuppressable] # (sic!)
|
46
46
|
|
47
47
|
is_cyclic_raw: Optional[bool]
|
48
48
|
is_multiple_raw: Optional[bool]
|
@@ -181,8 +181,9 @@ class DiagService(DiagComm):
|
|
181
181
|
for cpr in self.comparam_refs:
|
182
182
|
cpr._resolve_snrefs(context)
|
183
183
|
|
184
|
-
#
|
185
|
-
#
|
184
|
+
# The named item list of communication parameters is created
|
185
|
+
# here because ComparamInstance.short_name is only valid after
|
186
|
+
# reference resolution
|
186
187
|
self._comparams = NamedItemList(self.comparam_refs)
|
187
188
|
|
188
189
|
context.diag_service = None
|
@@ -202,24 +203,36 @@ class DiagService(DiagComm):
|
|
202
203
|
if len(raw_message) >= len(prefix) and prefix == raw_message[:len(prefix)]:
|
203
204
|
coding_objects.append(candidate_coding_object)
|
204
205
|
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
|
206
|
+
result_list: List[Message] = []
|
207
|
+
for coding_object in coding_objects:
|
208
|
+
try:
|
209
|
+
result_list.append(
|
210
|
+
Message(
|
211
|
+
coded_message=raw_message,
|
212
|
+
service=self,
|
213
|
+
coding_object=coding_object,
|
214
|
+
param_dict=coding_object.decode(raw_message)))
|
215
|
+
except DecodeMismatch:
|
216
|
+
# An NRC-CONST or environment data parameter
|
217
|
+
# encountered a non-matching value -> coding object
|
218
|
+
# does not apply
|
219
|
+
pass
|
220
|
+
|
221
|
+
if len(result_list) < 1:
|
222
|
+
odxraise(f"The service {self.short_name} cannot decode the message {raw_message.hex()}",
|
223
|
+
DecodeError)
|
224
|
+
return Message(
|
225
|
+
coded_message=raw_message, service=self, coding_object=None, param_dict={})
|
226
|
+
elif len(result_list) > 1:
|
227
|
+
odxraise(
|
228
|
+
f"The service {self.short_name} cannot uniquely decode the message {raw_message.hex()}",
|
229
|
+
DecodeError)
|
230
|
+
|
231
|
+
return result_list[0]
|
219
232
|
|
220
233
|
def encode_request(self, **kwargs: ParameterValue) -> bytes:
|
221
|
-
"""
|
222
|
-
|
234
|
+
"""Prepare an array of bytes ready to be send over the wire
|
235
|
+
for the request of this service.
|
223
236
|
"""
|
224
237
|
# make sure that all parameters which are required for
|
225
238
|
# encoding are specified (parameters which have a default are
|
odxtools/dtcdop.py
CHANGED
@@ -130,24 +130,49 @@ class DtcDop(DopBase):
|
|
130
130
|
sdgs=[],
|
131
131
|
)
|
132
132
|
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
trouble_code = physical_value.trouble_code
|
138
|
-
elif isinstance(physical_value, int):
|
133
|
+
def convert_to_numerical_trouble_code(self, dtc_value: ParameterValue) -> int:
|
134
|
+
if isinstance(dtc_value, DiagnosticTroubleCode):
|
135
|
+
return dtc_value.trouble_code
|
136
|
+
elif isinstance(dtc_value, int):
|
139
137
|
# assume that physical value is the trouble_code
|
140
|
-
|
141
|
-
elif isinstance(
|
138
|
+
return dtc_value
|
139
|
+
elif isinstance(dtc_value, str):
|
142
140
|
# assume that physical value is the short_name
|
143
|
-
dtcs = [dtc for dtc in self.dtcs if dtc.short_name ==
|
144
|
-
|
145
|
-
|
141
|
+
dtcs = [dtc for dtc in self.dtcs if dtc.short_name == dtc_value]
|
142
|
+
if len(dtcs) != 1:
|
143
|
+
odxraise(f"No DTC named {dtc_value} found for DTC-DOP "
|
144
|
+
f"{self.short_name}.", EncodeError)
|
145
|
+
return cast(int, None)
|
146
|
+
|
147
|
+
return dtcs[0].trouble_code
|
146
148
|
else:
|
147
|
-
|
148
|
-
|
149
|
+
odxraise(
|
150
|
+
f"The DTC-DOP {self.short_name} expected a"
|
151
|
+
f" diagnostic trouble code but got {type(dtc_value).__name__}", EncodeError)
|
152
|
+
return cast(int, None)
|
153
|
+
|
154
|
+
@override
|
155
|
+
def encode_into_pdu(self, physical_value: Optional[ParameterValue],
|
156
|
+
encode_state: EncodeState) -> None:
|
157
|
+
if physical_value is None:
|
158
|
+
odxraise(f"No DTC specified", EncodeError)
|
159
|
+
return
|
160
|
+
|
161
|
+
trouble_code = self.convert_to_numerical_trouble_code(physical_value)
|
162
|
+
|
163
|
+
internal_trouble_code = int(self.compu_method.convert_physical_to_internal(trouble_code))
|
164
|
+
|
165
|
+
found = False
|
166
|
+
for dtc in self.dtcs:
|
167
|
+
if internal_trouble_code == dtc.trouble_code:
|
168
|
+
found = True
|
169
|
+
break
|
170
|
+
|
171
|
+
if not found:
|
172
|
+
odxraise(
|
173
|
+
f"Unknown diagnostic trouble code {physical_value!r} "
|
174
|
+
f"(0x{internal_trouble_code: 06x}) specified", EncodeError)
|
149
175
|
|
150
|
-
internal_trouble_code = self.compu_method.convert_physical_to_internal(trouble_code)
|
151
176
|
self.diag_coded_type.encode_into_pdu(internal_trouble_code, encode_state)
|
152
177
|
|
153
178
|
def _build_odxlinks(self) -> Dict[OdxLinkId, Any]:
|
odxtools/encodestate.py
CHANGED
@@ -1,16 +1,19 @@
|
|
1
1
|
# SPDX-License-Identifier: MIT
|
2
2
|
import warnings
|
3
3
|
from dataclasses import dataclass, field
|
4
|
-
from typing import Dict, Optional, SupportsBytes
|
4
|
+
from typing import TYPE_CHECKING, Dict, List, Optional, SupportsBytes, Tuple
|
5
5
|
|
6
6
|
from .exceptions import EncodeError, OdxWarning, odxassert, odxraise
|
7
|
-
from .odxtypes import AtomicOdxType, DataType
|
7
|
+
from .odxtypes import AtomicOdxType, DataType, ParameterValue
|
8
8
|
|
9
9
|
try:
|
10
10
|
import bitstruct.c as bitstruct
|
11
11
|
except ImportError:
|
12
12
|
import bitstruct
|
13
13
|
|
14
|
+
if TYPE_CHECKING:
|
15
|
+
from .parameters.parameter import Parameter
|
16
|
+
|
14
17
|
|
15
18
|
@dataclass
|
16
19
|
class EncodeState:
|
@@ -56,6 +59,15 @@ class EncodeState:
|
|
56
59
|
#: (needed for MinMaxLengthType, EndOfPduField, etc.)
|
57
60
|
is_end_of_pdu: bool = True
|
58
61
|
|
62
|
+
#: list of parameters that have been encoded so far. The journal
|
63
|
+
#: is used by some types of parameters which depend on the values of
|
64
|
+
#: other parameters; e.g., environment data description parameters
|
65
|
+
journal: List[Tuple["Parameter", Optional[ParameterValue]]] = field(default_factory=list)
|
66
|
+
|
67
|
+
#: If this is True, specifying unknown parameters for encoding
|
68
|
+
#: will raise an OdxError exception in strict mode.
|
69
|
+
allow_unknown_parameters = False
|
70
|
+
|
59
71
|
def __post_init__(self) -> None:
|
60
72
|
# if a coded message has been specified, but no used_mask, we
|
61
73
|
# assume that all of the bits of the coded message are
|
@@ -1,17 +1,19 @@
|
|
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, Optional, cast
|
4
4
|
from xml.etree import ElementTree
|
5
5
|
|
6
6
|
from typing_extensions import override
|
7
7
|
|
8
8
|
from .complexdop import ComplexDop
|
9
9
|
from .decodestate import DecodeState
|
10
|
+
from .dtcdop import DtcDop
|
10
11
|
from .encodestate import EncodeState
|
11
12
|
from .environmentdata import EnvironmentData
|
12
13
|
from .exceptions import odxraise, odxrequire
|
13
14
|
from .odxlink import OdxDocFragment, OdxLinkDatabase, OdxLinkId, OdxLinkRef
|
14
|
-
from .odxtypes import ParameterValue
|
15
|
+
from .odxtypes import ParameterValue, ParameterValueDict
|
16
|
+
from .parameters.parameter import Parameter
|
15
17
|
from .snrefcontext import SnRefContext
|
16
18
|
from .utils import dataclass_fields_asdict
|
17
19
|
|
@@ -27,16 +29,26 @@ class EnvironmentDataDescription(ComplexDop):
|
|
27
29
|
|
28
30
|
"""
|
29
31
|
|
32
|
+
param_snref: Optional[str]
|
33
|
+
param_snpathref: Optional[str]
|
34
|
+
|
30
35
|
# in ODX 2.0.0, ENV-DATAS seems to be a mandatory
|
31
36
|
# sub-element of ENV-DATA-DESC, in ODX 2.2 it is not
|
32
37
|
# present
|
33
38
|
env_datas: List[EnvironmentData]
|
34
39
|
env_data_refs: List[OdxLinkRef]
|
35
|
-
param_snref: Optional[str]
|
36
|
-
param_snpathref: Optional[str]
|
37
40
|
|
38
|
-
|
39
|
-
|
41
|
+
@property
|
42
|
+
def param(self) -> Parameter:
|
43
|
+
# the parameter referenced via SNREF cannot be resolved here
|
44
|
+
# because the relevant list of parameters depends on the
|
45
|
+
# concrete codec object processed, whilst an environment data
|
46
|
+
# description object can be featured in an arbitrary number of
|
47
|
+
# responses. Instead, lookup of the appropriate parameter is
|
48
|
+
# done within the encode and decode methods.
|
49
|
+
odxraise("The parameter of ENV-DATA-DESC objects cannot be resolved "
|
50
|
+
"because it depends on the context")
|
51
|
+
return cast(None, Parameter)
|
40
52
|
|
41
53
|
@staticmethod
|
42
54
|
def from_et(et_element: ElementTree.Element,
|
@@ -96,17 +108,125 @@ class EnvironmentDataDescription(ComplexDop):
|
|
96
108
|
def encode_into_pdu(self, physical_value: Optional[ParameterValue],
|
97
109
|
encode_state: EncodeState) -> None:
|
98
110
|
"""Convert a physical value into bytes and emplace them into a PDU.
|
99
|
-
|
100
|
-
Since environmental data is supposed to never appear on the
|
101
|
-
wire, this method just raises an EncodeError exception.
|
102
111
|
"""
|
103
|
-
|
112
|
+
|
113
|
+
# retrieve the relevant DTC parameter which must be located in
|
114
|
+
# front of the environment data description.
|
115
|
+
if self.param_snref is None:
|
116
|
+
odxraise("Specifying the DTC parameter for environment data "
|
117
|
+
"descriptions via SNPATHREF is not supported yet")
|
118
|
+
return None
|
119
|
+
|
120
|
+
dtc_param: Optional[Parameter] = None
|
121
|
+
dtc_dop: Optional[DtcDop] = None
|
122
|
+
dtc_param_value: Optional[ParameterValue] = None
|
123
|
+
for prev_param, prev_param_value in reversed(encode_state.journal):
|
124
|
+
if prev_param.short_name == self.param_snref:
|
125
|
+
dtc_param = prev_param
|
126
|
+
prev_dop = getattr(prev_param, "dop", None)
|
127
|
+
if not isinstance(prev_dop, DtcDop):
|
128
|
+
odxraise(f"The DOP of the parameter referenced by environment data "
|
129
|
+
f"descriptions must be a DTC-DOP (is '{type(prev_dop).__name__}')")
|
130
|
+
return
|
131
|
+
dtc_dop = prev_dop
|
132
|
+
dtc_param_value = prev_param_value
|
133
|
+
break
|
134
|
+
|
135
|
+
if dtc_param is None:
|
136
|
+
odxraise("Environment data description parameters are only allowed following "
|
137
|
+
"the referenced value parameter.")
|
138
|
+
return
|
139
|
+
|
140
|
+
if dtc_param_value is None or dtc_dop is None:
|
141
|
+
# this should never happen
|
142
|
+
odxraise()
|
143
|
+
return
|
144
|
+
|
145
|
+
numerical_dtc = dtc_dop.convert_to_numerical_trouble_code(dtc_param_value)
|
146
|
+
|
147
|
+
# deal with the "all value" environment data. This holds
|
148
|
+
# parameters that are common to all DTCs. Be aware that the
|
149
|
+
# specification mandates that there is at most one such
|
150
|
+
# environment data object
|
151
|
+
for env_data in self.env_datas:
|
152
|
+
if env_data.all_value:
|
153
|
+
tmp = encode_state.allow_unknown_parameters
|
154
|
+
encode_state.allow_unknown_parameters = True
|
155
|
+
env_data.encode_into_pdu(physical_value, encode_state)
|
156
|
+
encode_state.allow_unknown_parameters = tmp
|
157
|
+
break
|
158
|
+
|
159
|
+
# find the environment data corresponding to the given trouble
|
160
|
+
# code
|
161
|
+
for env_data in self.env_datas:
|
162
|
+
if numerical_dtc in env_data.dtc_values:
|
163
|
+
tmp = encode_state.allow_unknown_parameters
|
164
|
+
encode_state.allow_unknown_parameters = True
|
165
|
+
env_data.encode_into_pdu(physical_value, encode_state)
|
166
|
+
encode_state.allow_unknown_parameters = tmp
|
167
|
+
break
|
104
168
|
|
105
169
|
@override
|
106
170
|
def decode_from_pdu(self, decode_state: DecodeState) -> ParameterValue:
|
107
171
|
"""Extract the bytes from a PDU and convert them to a physical value.
|
108
|
-
|
109
|
-
Since environmental data is supposed to never appear on the
|
110
|
-
wire, this method just raises an DecodeError exception.
|
111
172
|
"""
|
112
|
-
|
173
|
+
|
174
|
+
# retrieve the relevant DTC parameter which must be located in
|
175
|
+
# front of the environment data description.
|
176
|
+
if self.param_snref is None:
|
177
|
+
odxraise("Specifying the DTC parameter for environment data "
|
178
|
+
"descriptions via SNPATHREF is not supported yet")
|
179
|
+
return None
|
180
|
+
|
181
|
+
dtc_param: Optional[Parameter] = None
|
182
|
+
dtc_dop: Optional[DtcDop] = None
|
183
|
+
dtc_param_value: Optional[ParameterValue] = None
|
184
|
+
for prev_param, prev_param_value in reversed(decode_state.journal):
|
185
|
+
if prev_param.short_name == self.param_snref:
|
186
|
+
dtc_param = prev_param
|
187
|
+
prev_dop = getattr(prev_param, "dop", None)
|
188
|
+
if not isinstance(prev_dop, DtcDop):
|
189
|
+
odxraise(f"The DOP of the parameter referenced by environment data "
|
190
|
+
f"descriptions must be a DTC-DOP (is '{type(prev_dop).__name__}')")
|
191
|
+
return
|
192
|
+
dtc_dop = prev_dop
|
193
|
+
dtc_param_value = prev_param_value
|
194
|
+
break
|
195
|
+
|
196
|
+
if dtc_param is None:
|
197
|
+
odxraise("Environment data description parameters are only allowed following "
|
198
|
+
"the referenced value parameter.")
|
199
|
+
return
|
200
|
+
|
201
|
+
if dtc_param_value is None or dtc_dop is None:
|
202
|
+
# this should never happen
|
203
|
+
odxraise()
|
204
|
+
return
|
205
|
+
|
206
|
+
numerical_dtc = dtc_dop.convert_to_numerical_trouble_code(dtc_param_value)
|
207
|
+
|
208
|
+
result: ParameterValueDict = {}
|
209
|
+
|
210
|
+
# deal with the "all value" environment data. This holds
|
211
|
+
# parameters that are common to all DTCs. Be aware that the
|
212
|
+
# specification mandates that there is at most one such
|
213
|
+
# environment data object
|
214
|
+
for env_data in self.env_datas:
|
215
|
+
if env_data.all_value:
|
216
|
+
tmp = env_data.decode_from_pdu(decode_state)
|
217
|
+
if not isinstance(tmp, dict):
|
218
|
+
odxraise()
|
219
|
+
result.update(tmp)
|
220
|
+
break
|
221
|
+
|
222
|
+
# find the environment data corresponding to the given trouble
|
223
|
+
# code
|
224
|
+
for env_data in self.env_datas:
|
225
|
+
if numerical_dtc in env_data.dtc_values:
|
226
|
+
tmp = env_data.decode_from_pdu(decode_state)
|
227
|
+
if not isinstance(tmp, dict):
|
228
|
+
odxraise()
|
229
|
+
result.update(tmp)
|
230
|
+
break
|
231
|
+
|
232
|
+
return result
|
odxtools/exceptions.py
CHANGED
@@ -9,13 +9,22 @@ class OdxError(Exception):
|
|
9
9
|
|
10
10
|
|
11
11
|
class EncodeError(Warning, OdxError):
|
12
|
-
"""Encoding of a message to raw data failed
|
12
|
+
"""Encoding of a message to raw data failed"""
|
13
13
|
|
14
14
|
|
15
15
|
class DecodeError(Warning, OdxError):
|
16
16
|
"""Decoding raw data failed."""
|
17
17
|
|
18
18
|
|
19
|
+
class DecodeMismatch(DecodeError):
|
20
|
+
"""Decoding failed because some parameters exhibit an incorrect value
|
21
|
+
|
22
|
+
This is can happen if NRC-CONST or environment data descriptions
|
23
|
+
are present.
|
24
|
+
|
25
|
+
"""
|
26
|
+
|
27
|
+
|
19
28
|
class OdxWarning(Warning):
|
20
29
|
"""Any warning that happens during interacting with diagnostic objects."""
|
21
30
|
|