odxtools 5.3.1__py3-none-any.whl → 6.0.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.
- odxtools/__init__.py +1 -1
- odxtools/basicstructure.py +76 -91
- odxtools/cli/_parser_utils.py +12 -9
- odxtools/cli/_print_utils.py +7 -7
- odxtools/cli/browse.py +94 -73
- odxtools/cli/find.py +42 -59
- odxtools/cli/list.py +21 -17
- odxtools/cli/snoop.py +19 -18
- odxtools/communicationparameterref.py +6 -3
- odxtools/companydocinfo.py +2 -2
- odxtools/companyrevisioninfo.py +1 -1
- odxtools/comparamsubset.py +6 -6
- odxtools/complexcomparam.py +1 -1
- odxtools/compumethods/compumethod.py +6 -9
- odxtools/compumethods/createanycompumethod.py +11 -9
- odxtools/compumethods/identicalcompumethod.py +5 -4
- odxtools/compumethods/limit.py +9 -9
- odxtools/compumethods/linearcompumethod.py +25 -17
- odxtools/compumethods/scalelinearcompumethod.py +6 -5
- odxtools/compumethods/tabintpcompumethod.py +30 -9
- odxtools/compumethods/texttablecompumethod.py +22 -24
- odxtools/database.py +5 -5
- odxtools/dataobjectproperty.py +10 -23
- odxtools/decodestate.py +1 -1
- odxtools/determinenumberofitems.py +37 -8
- odxtools/diagcodedtype.py +14 -9
- odxtools/diagdatadictionaryspec.py +60 -37
- odxtools/diaglayer.py +30 -21
- odxtools/diaglayercontainer.py +40 -40
- odxtools/diaglayerraw.py +92 -63
- odxtools/diagnostictroublecode.py +2 -2
- odxtools/diagservice.py +53 -35
- odxtools/docrevision.py +1 -1
- odxtools/dopbase.py +14 -3
- odxtools/dtcdop.py +15 -9
- odxtools/dynamiclengthfield.py +6 -4
- odxtools/endofpdufield.py +22 -23
- odxtools/environmentdata.py +2 -5
- odxtools/environmentdatadescription.py +6 -4
- odxtools/field.py +3 -8
- odxtools/isotp_state_machine.py +52 -38
- odxtools/leadinglengthinfotype.py +9 -7
- odxtools/load_file.py +2 -1
- odxtools/load_odx_d_file.py +2 -5
- odxtools/load_pdx_file.py +2 -6
- odxtools/message.py +11 -3
- odxtools/minmaxlengthtype.py +107 -78
- odxtools/modification.py +2 -2
- odxtools/multiplexer.py +23 -21
- odxtools/multiplexerswitchkey.py +37 -8
- odxtools/nameditemlist.py +59 -58
- odxtools/odxlink.py +4 -2
- odxtools/odxtypes.py +4 -3
- odxtools/parameterinfo.py +6 -6
- odxtools/parameters/codedconstparameter.py +15 -25
- odxtools/parameters/createanyparameter.py +1 -1
- odxtools/parameters/dynamicparameter.py +6 -5
- odxtools/parameters/lengthkeyparameter.py +2 -1
- odxtools/parameters/matchingrequestparameter.py +8 -11
- odxtools/parameters/nrcconstparameter.py +11 -21
- odxtools/parameters/parameter.py +4 -18
- odxtools/parameters/parameterwithdop.py +14 -29
- odxtools/parameters/physicalconstantparameter.py +7 -9
- odxtools/parameters/reservedparameter.py +17 -38
- odxtools/parameters/systemparameter.py +6 -5
- odxtools/parameters/tableentryparameter.py +6 -5
- odxtools/parameters/tablekeyparameter.py +8 -15
- odxtools/parameters/tablestructparameter.py +11 -12
- odxtools/parameters/valueparameter.py +9 -24
- odxtools/paramlengthinfotype.py +11 -9
- odxtools/physicaldimension.py +1 -1
- odxtools/physicaltype.py +2 -2
- odxtools/response.py +7 -3
- odxtools/singleecujob.py +48 -22
- odxtools/standardlengthtype.py +11 -6
- odxtools/uds.py +1 -1
- odxtools/unit.py +5 -5
- odxtools/unitgroup.py +1 -1
- odxtools/unitspec.py +2 -2
- odxtools/version.py +13 -3
- odxtools/write_pdx_file.py +7 -4
- {odxtools-5.3.1.dist-info → odxtools-6.0.1.dist-info}/METADATA +7 -5
- {odxtools-5.3.1.dist-info → odxtools-6.0.1.dist-info}/RECORD +87 -88
- odxtools/positioneddataobjectproperty.py +0 -74
- {odxtools-5.3.1.dist-info → odxtools-6.0.1.dist-info}/LICENSE +0 -0
- {odxtools-5.3.1.dist-info → odxtools-6.0.1.dist-info}/WHEEL +0 -0
- {odxtools-5.3.1.dist-info → odxtools-6.0.1.dist-info}/entry_points.txt +0 -0
- {odxtools-5.3.1.dist-info → odxtools-6.0.1.dist-info}/top_level.txt +0 -0
odxtools/diaglayerraw.py
CHANGED
@@ -1,7 +1,6 @@
|
|
1
1
|
# SPDX-License-Identifier: MIT
|
2
2
|
from copy import copy
|
3
3
|
from dataclasses import dataclass
|
4
|
-
from itertools import chain
|
5
4
|
from typing import TYPE_CHECKING, Any, Dict, List, Optional, Union
|
6
5
|
from xml.etree import ElementTree
|
7
6
|
|
@@ -202,76 +201,106 @@ class DiagLayerRaw(IdentifiableElement):
|
|
202
201
|
"""Construct a mapping from IDs to all objects that are contained in this diagnostic layer."""
|
203
202
|
odxlinks = {self.odx_id: self}
|
204
203
|
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
self.additional_audiences,
|
217
|
-
self.sdgs,
|
218
|
-
self.parent_refs,
|
219
|
-
self.communication_parameters,
|
220
|
-
):
|
221
|
-
# the diag_comms may contain references.
|
222
|
-
if obj is None or isinstance(obj, OdxLinkRef):
|
204
|
+
if self.admin_data is not None:
|
205
|
+
odxlinks.update(self.admin_data._build_odxlinks())
|
206
|
+
if self.diag_data_dictionary_spec is not None:
|
207
|
+
odxlinks.update(self.diag_data_dictionary_spec._build_odxlinks())
|
208
|
+
|
209
|
+
for company_data in self.company_datas:
|
210
|
+
odxlinks.update(company_data._build_odxlinks())
|
211
|
+
for functional_class in self.functional_classes:
|
212
|
+
odxlinks.update(functional_class._build_odxlinks())
|
213
|
+
for diag_comm in self.diag_comms:
|
214
|
+
if isinstance(diag_comm, OdxLinkRef):
|
223
215
|
continue
|
224
|
-
|
225
|
-
|
216
|
+
odxlinks.update(diag_comm._build_odxlinks())
|
217
|
+
for request in self.requests:
|
218
|
+
odxlinks.update(request._build_odxlinks())
|
219
|
+
for positive_response in self.positive_responses:
|
220
|
+
odxlinks.update(positive_response._build_odxlinks())
|
221
|
+
for negative_response in self.negative_responses:
|
222
|
+
odxlinks.update(negative_response._build_odxlinks())
|
223
|
+
for global_negative_response in self.global_negative_responses:
|
224
|
+
odxlinks.update(global_negative_response._build_odxlinks())
|
225
|
+
for state_chart in self.state_charts:
|
226
|
+
odxlinks.update(state_chart._build_odxlinks())
|
227
|
+
for additional_audience in self.additional_audiences:
|
228
|
+
odxlinks.update(additional_audience._build_odxlinks())
|
229
|
+
for sdg in self.sdgs:
|
230
|
+
odxlinks.update(sdg._build_odxlinks())
|
231
|
+
for parent_ref in self.parent_refs:
|
232
|
+
odxlinks.update(parent_ref._build_odxlinks())
|
233
|
+
for communication_parameter in self.communication_parameters:
|
234
|
+
odxlinks.update(communication_parameter._build_odxlinks())
|
226
235
|
|
227
236
|
return odxlinks
|
228
237
|
|
229
238
|
def _resolve_odxlinks(self, odxlinks: OdxLinkDatabase) -> None:
|
230
239
|
"""Recursively resolve all references."""
|
231
240
|
|
232
|
-
# do reference resolution
|
233
|
-
|
234
|
-
|
235
|
-
|
236
|
-
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
|
241
|
-
|
242
|
-
|
243
|
-
|
244
|
-
self.state_charts,
|
245
|
-
self.additional_audiences,
|
246
|
-
self.sdgs,
|
247
|
-
self.parent_refs,
|
248
|
-
self.communication_parameters,
|
249
|
-
):
|
250
|
-
if obj is None or isinstance(obj, OdxLinkRef):
|
241
|
+
# do ODXLINK reference resolution
|
242
|
+
if self.admin_data is not None:
|
243
|
+
self.admin_data._resolve_odxlinks(odxlinks)
|
244
|
+
if self.diag_data_dictionary_spec is not None:
|
245
|
+
self.diag_data_dictionary_spec._resolve_odxlinks(odxlinks)
|
246
|
+
|
247
|
+
for company_data in self.company_datas:
|
248
|
+
company_data._resolve_odxlinks(odxlinks)
|
249
|
+
for functional_class in self.functional_classes:
|
250
|
+
functional_class._resolve_odxlinks(odxlinks)
|
251
|
+
for diag_comm in self.diag_comms:
|
252
|
+
if isinstance(diag_comm, OdxLinkRef):
|
251
253
|
continue
|
252
|
-
|
253
|
-
|
254
|
+
diag_comm._resolve_odxlinks(odxlinks)
|
255
|
+
for request in self.requests:
|
256
|
+
request._resolve_odxlinks(odxlinks)
|
257
|
+
for positive_response in self.positive_responses:
|
258
|
+
positive_response._resolve_odxlinks(odxlinks)
|
259
|
+
for negative_response in self.negative_responses:
|
260
|
+
negative_response._resolve_odxlinks(odxlinks)
|
261
|
+
for global_negative_response in self.global_negative_responses:
|
262
|
+
global_negative_response._resolve_odxlinks(odxlinks)
|
263
|
+
for state_chart in self.state_charts:
|
264
|
+
state_chart._resolve_odxlinks(odxlinks)
|
265
|
+
for additional_audience in self.additional_audiences:
|
266
|
+
additional_audience._resolve_odxlinks(odxlinks)
|
267
|
+
for sdg in self.sdgs:
|
268
|
+
sdg._resolve_odxlinks(odxlinks)
|
269
|
+
for parent_ref in self.parent_refs:
|
270
|
+
parent_ref._resolve_odxlinks(odxlinks)
|
271
|
+
for communication_parameter in self.communication_parameters:
|
272
|
+
communication_parameter._resolve_odxlinks(odxlinks)
|
254
273
|
|
255
274
|
def _resolve_snrefs(self, diag_layer: "DiagLayer") -> None:
|
256
|
-
# do reference resolution
|
257
|
-
|
258
|
-
|
259
|
-
|
260
|
-
|
261
|
-
|
262
|
-
|
263
|
-
|
264
|
-
|
265
|
-
|
266
|
-
|
267
|
-
|
268
|
-
self.state_charts,
|
269
|
-
self.additional_audiences,
|
270
|
-
self.sdgs,
|
271
|
-
self.parent_refs,
|
272
|
-
self.communication_parameters,
|
273
|
-
):
|
274
|
-
if obj is None or isinstance(obj, OdxLinkRef):
|
275
|
+
# do short-name reference resolution
|
276
|
+
if self.admin_data is not None:
|
277
|
+
self.admin_data._resolve_snrefs(diag_layer)
|
278
|
+
if self.diag_data_dictionary_spec is not None:
|
279
|
+
self.diag_data_dictionary_spec._resolve_snrefs(diag_layer)
|
280
|
+
|
281
|
+
for company_data in self.company_datas:
|
282
|
+
company_data._resolve_snrefs(diag_layer)
|
283
|
+
for functional_classe in self.functional_classes:
|
284
|
+
functional_classe._resolve_snrefs(diag_layer)
|
285
|
+
for diag_comm in self.diag_comms:
|
286
|
+
if isinstance(diag_comm, OdxLinkRef):
|
275
287
|
continue
|
276
|
-
|
277
|
-
|
288
|
+
diag_comm._resolve_snrefs(diag_layer)
|
289
|
+
for request in self.requests:
|
290
|
+
request._resolve_snrefs(diag_layer)
|
291
|
+
for positive_response in self.positive_responses:
|
292
|
+
positive_response._resolve_snrefs(diag_layer)
|
293
|
+
for negative_response in self.negative_responses:
|
294
|
+
negative_response._resolve_snrefs(diag_layer)
|
295
|
+
for global_negative_response in self.global_negative_responses:
|
296
|
+
global_negative_response._resolve_snrefs(diag_layer)
|
297
|
+
for state_chart in self.state_charts:
|
298
|
+
state_chart._resolve_snrefs(diag_layer)
|
299
|
+
for additional_audience in self.additional_audiences:
|
300
|
+
additional_audience._resolve_snrefs(diag_layer)
|
301
|
+
for sdg in self.sdgs:
|
302
|
+
sdg._resolve_snrefs(diag_layer)
|
303
|
+
for parent_ref in self.parent_refs:
|
304
|
+
parent_ref._resolve_snrefs(diag_layer)
|
305
|
+
for communication_parameter in self.communication_parameters:
|
306
|
+
communication_parameter._resolve_snrefs(diag_layer)
|
@@ -26,7 +26,7 @@ class DiagnosticTroubleCode(IdentifiableElement):
|
|
26
26
|
|
27
27
|
@property
|
28
28
|
def is_temporary(self) -> bool:
|
29
|
-
return self.is_temporary_raw
|
29
|
+
return self.is_temporary_raw is True
|
30
30
|
|
31
31
|
@staticmethod
|
32
32
|
def from_et(et_element: ElementTree.Element,
|
@@ -69,6 +69,6 @@ class DiagnosticTroubleCode(IdentifiableElement):
|
|
69
69
|
for sdg in self.sdgs:
|
70
70
|
sdg._resolve_odxlinks(odxlinks)
|
71
71
|
|
72
|
-
def _resolve_snrefs(self, diag_layer: "DiagLayer"):
|
72
|
+
def _resolve_snrefs(self, diag_layer: "DiagLayer") -> None:
|
73
73
|
for sdg in self.sdgs:
|
74
74
|
sdg._resolve_snrefs(diag_layer)
|
odxtools/diagservice.py
CHANGED
@@ -8,18 +8,21 @@ from .audience import Audience
|
|
8
8
|
from .createsdgs import create_sdgs_from_et
|
9
9
|
from .element import IdentifiableElement
|
10
10
|
from .exceptions import DecodeError, odxassert, odxrequire
|
11
|
+
from .functionalclass import FunctionalClass
|
11
12
|
from .message import Message
|
12
13
|
from .nameditemlist import NamedItemList
|
13
14
|
from .odxlink import OdxDocFragment, OdxLinkDatabase, OdxLinkId, OdxLinkRef
|
15
|
+
from .odxtypes import ParameterValue
|
14
16
|
from .parameters.parameter import Parameter
|
15
17
|
from .request import Request
|
16
18
|
from .response import Response
|
17
19
|
from .specialdatagroup import SpecialDataGroup
|
20
|
+
from .state import State
|
21
|
+
from .statetransition import StateTransition
|
18
22
|
from .utils import dataclass_fields_asdict
|
19
23
|
|
20
24
|
if TYPE_CHECKING:
|
21
25
|
from .diaglayer import DiagLayer
|
22
|
-
from .endofpdufield import EndOfPduField
|
23
26
|
|
24
27
|
|
25
28
|
@dataclass
|
@@ -96,7 +99,7 @@ class DiagService(IdentifiableElement):
|
|
96
99
|
return self._request
|
97
100
|
|
98
101
|
@property
|
99
|
-
def free_parameters(self) -> List[
|
102
|
+
def free_parameters(self) -> List[Parameter]:
|
100
103
|
"""Return the list of parameters which can be freely specified by
|
101
104
|
the user when encoding the service's request.
|
102
105
|
"""
|
@@ -112,23 +115,23 @@ class DiagService(IdentifiableElement):
|
|
112
115
|
self.request.print_free_parameters_info()
|
113
116
|
|
114
117
|
@property
|
115
|
-
def positive_responses(self) ->
|
118
|
+
def positive_responses(self) -> NamedItemList[Response]:
|
116
119
|
return self._positive_responses
|
117
120
|
|
118
121
|
@property
|
119
|
-
def negative_responses(self) ->
|
122
|
+
def negative_responses(self) -> NamedItemList[Response]:
|
120
123
|
return self._negative_responses
|
121
124
|
|
122
125
|
@property
|
123
|
-
def functional_classes(self):
|
126
|
+
def functional_classes(self) -> NamedItemList[FunctionalClass]:
|
124
127
|
return self._functional_classes
|
125
128
|
|
126
129
|
@property
|
127
|
-
def pre_condition_states(self):
|
130
|
+
def pre_condition_states(self) -> NamedItemList[State]:
|
128
131
|
return self._pre_condition_states
|
129
132
|
|
130
133
|
@property
|
131
|
-
def state_transitions(self):
|
134
|
+
def state_transitions(self) -> NamedItemList[StateTransition]:
|
132
135
|
return self._state_transitions
|
133
136
|
|
134
137
|
def _build_odxlinks(self) -> Dict[OdxLinkId, Any]:
|
@@ -149,11 +152,11 @@ class DiagService(IdentifiableElement):
|
|
149
152
|
[odxlinks.resolve(x, Response) for x in self.neg_response_refs])
|
150
153
|
|
151
154
|
self._functional_classes = NamedItemList(
|
152
|
-
[odxlinks.resolve(
|
155
|
+
[odxlinks.resolve(fc_ref, FunctionalClass) for fc_ref in self.functional_class_refs])
|
153
156
|
self._pre_condition_states = NamedItemList(
|
154
|
-
[odxlinks.resolve(
|
157
|
+
[odxlinks.resolve(st_ref, State) for st_ref in self.pre_condition_state_refs])
|
155
158
|
self._state_transitions = NamedItemList(
|
156
|
-
[odxlinks.resolve(
|
159
|
+
[odxlinks.resolve(stt_ref, StateTransition) for stt_ref in self.state_transition_refs])
|
157
160
|
|
158
161
|
if self.admin_data:
|
159
162
|
self.admin_data._resolve_odxlinks(odxlinks)
|
@@ -174,31 +177,37 @@ class DiagService(IdentifiableElement):
|
|
174
177
|
for sdg in self.sdgs:
|
175
178
|
sdg._resolve_snrefs(diag_layer)
|
176
179
|
|
177
|
-
def decode_message(self,
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
if
|
183
|
-
|
184
|
-
|
185
|
-
" Try calling `database.resolve_odxlinks()`.")
|
180
|
+
def decode_message(self, raw_message: bytes) -> Message:
|
181
|
+
request_prefix = b''
|
182
|
+
candidate_coding_objects: List[Union[Request, Response]] = [
|
183
|
+
*self.positive_responses, *self.negative_responses
|
184
|
+
]
|
185
|
+
if self.request is not None:
|
186
|
+
request_prefix = self.request.coded_const_prefix()
|
187
|
+
candidate_coding_objects.append(self.request)
|
186
188
|
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
if
|
191
|
-
|
189
|
+
coding_objects: List[Union[Request, Response]] = []
|
190
|
+
for candidate_coding_object in candidate_coding_objects:
|
191
|
+
prefix = candidate_coding_object.coded_const_prefix(request_prefix=request_prefix)
|
192
|
+
if len(raw_message) >= len(prefix) and prefix == raw_message[:len(prefix)]:
|
193
|
+
coding_objects.append(candidate_coding_object)
|
192
194
|
|
193
|
-
if len(
|
195
|
+
if len(coding_objects) != 1:
|
194
196
|
raise DecodeError(
|
195
|
-
f"The service {self.short_name} cannot decode the message {
|
196
|
-
|
197
|
-
param_dict =
|
197
|
+
f"The service {self.short_name} cannot decode the message {raw_message.hex()}")
|
198
|
+
coding_object = coding_objects[0]
|
199
|
+
param_dict = coding_object.decode(raw_message)
|
200
|
+
if not isinstance(param_dict, dict):
|
201
|
+
# if this happens, this is probably due to a bug in
|
202
|
+
# coding_object.decode()
|
203
|
+
raise RuntimeError(f"Expected a set of decoded parameters, got {type(param_dict)}")
|
198
204
|
return Message(
|
199
|
-
coded_message=
|
205
|
+
coded_message=raw_message,
|
206
|
+
service=self,
|
207
|
+
coding_object=coding_object,
|
208
|
+
param_dict=param_dict)
|
200
209
|
|
201
|
-
def encode_request(self, **params):
|
210
|
+
def encode_request(self, **params: ParameterValue) -> bytes:
|
202
211
|
"""
|
203
212
|
Composes an UDS request as list of bytes for this service.
|
204
213
|
Parameters:
|
@@ -209,6 +218,9 @@ class DiagService(IdentifiableElement):
|
|
209
218
|
# make sure that all parameters which are required for
|
210
219
|
# encoding are specified (parameters which have a default are
|
211
220
|
# optional)
|
221
|
+
if self.request is None:
|
222
|
+
return b''
|
223
|
+
|
212
224
|
missing_params = {x.short_name
|
213
225
|
for x in self.request.required_parameters}.difference(params.keys())
|
214
226
|
odxassert(not missing_params, f"The parameters {missing_params} are required but missing!")
|
@@ -219,21 +231,27 @@ class DiagService(IdentifiableElement):
|
|
219
231
|
set(params.keys()).issubset(rq_all_param_names),
|
220
232
|
f"Unknown parameters specified for encoding: {params.keys()}, "
|
221
233
|
f"known parameters are: {rq_all_param_names}")
|
222
|
-
return self.request.encode(**params)
|
234
|
+
return self.request.encode(coded_request=None, **params)
|
223
235
|
|
224
|
-
def encode_positive_response(self,
|
236
|
+
def encode_positive_response(self,
|
237
|
+
coded_request: bytes,
|
238
|
+
response_index: int = 0,
|
239
|
+
**params: ParameterValue) -> bytes:
|
225
240
|
# TODO: Should the user decide the positive response or what are the differences?
|
226
241
|
return self.positive_responses[response_index].encode(coded_request, **params)
|
227
242
|
|
228
|
-
def encode_negative_response(self,
|
243
|
+
def encode_negative_response(self,
|
244
|
+
coded_request: bytes,
|
245
|
+
response_index: int = 0,
|
246
|
+
**params: ParameterValue) -> bytes:
|
229
247
|
return self.negative_responses[response_index].encode(coded_request, **params)
|
230
248
|
|
231
|
-
def __call__(self, **params) -> bytes:
|
249
|
+
def __call__(self, **params: ParameterValue) -> bytes:
|
232
250
|
"""Encode a request."""
|
233
251
|
return self.encode_request(**params)
|
234
252
|
|
235
253
|
def __hash__(self) -> int:
|
236
254
|
return hash(self.odx_id)
|
237
255
|
|
238
|
-
def __eq__(self, o:
|
256
|
+
def __eq__(self, o: Any) -> bool:
|
239
257
|
return isinstance(o, DiagService) and self.odx_id == o.odx_id
|
odxtools/docrevision.py
CHANGED
@@ -64,7 +64,7 @@ class DocRevision:
|
|
64
64
|
def _build_odxlinks(self) -> Dict[OdxLinkId, Any]:
|
65
65
|
return {}
|
66
66
|
|
67
|
-
def _resolve_odxlinks(self, odxlinks: OdxLinkDatabase):
|
67
|
+
def _resolve_odxlinks(self, odxlinks: OdxLinkDatabase) -> None:
|
68
68
|
self._team_member: Optional[TeamMember] = None
|
69
69
|
if self.team_member_ref is not None:
|
70
70
|
self._team_member = odxlinks.resolve(self.team_member_ref, TeamMember)
|
odxtools/dopbase.py
CHANGED
@@ -1,11 +1,12 @@
|
|
1
1
|
# SPDX-License-Identifier: MIT
|
2
2
|
from dataclasses import dataclass
|
3
|
-
from typing import TYPE_CHECKING, Any, Dict, List, Optional
|
3
|
+
from typing import TYPE_CHECKING, Any, Dict, List, Optional, Tuple
|
4
4
|
|
5
5
|
from .decodestate import DecodeState
|
6
6
|
from .element import IdentifiableElement
|
7
7
|
from .encodestate import EncodeState
|
8
8
|
from .odxlink import OdxLinkDatabase, OdxLinkId
|
9
|
+
from .odxtypes import ParameterValue
|
9
10
|
from .specialdatagroup import SpecialDataGroup
|
10
11
|
|
11
12
|
if TYPE_CHECKING:
|
@@ -48,15 +49,25 @@ class DopBase(IdentifiableElement):
|
|
48
49
|
for sdg in self.sdgs:
|
49
50
|
sdg._resolve_snrefs(diag_layer)
|
50
51
|
|
52
|
+
def get_static_bit_length(self) -> Optional[int]:
|
53
|
+
return None
|
54
|
+
|
55
|
+
def is_valid_physical_value(self, physical_value: ParameterValue) -> bool:
|
56
|
+
"""Determine if a phyical value can be handled by the DOP
|
57
|
+
"""
|
58
|
+
raise NotImplementedError
|
59
|
+
|
51
60
|
@property
|
52
61
|
def is_visible(self) -> bool:
|
53
62
|
return self.is_visible_raw in (None, True)
|
54
63
|
|
55
|
-
def convert_physical_to_bytes(self, physical_value, encode_state: EncodeState,
|
64
|
+
def convert_physical_to_bytes(self, physical_value: ParameterValue, encode_state: EncodeState,
|
56
65
|
bit_position: int) -> bytes:
|
57
66
|
"""Convert the physical value into bytes."""
|
58
67
|
raise NotImplementedError
|
59
68
|
|
60
|
-
def convert_bytes_to_physical(self,
|
69
|
+
def convert_bytes_to_physical(self,
|
70
|
+
decode_state: DecodeState,
|
71
|
+
bit_position: int = 0) -> Tuple[ParameterValue, int]:
|
61
72
|
"""Extract the bytes from the PDU and convert them to the physical value."""
|
62
73
|
raise NotImplementedError
|
odxtools/dtcdop.py
CHANGED
@@ -1,13 +1,16 @@
|
|
1
1
|
# SPDX-License-Identifier: MIT
|
2
2
|
# from dataclasses import dataclass, field
|
3
3
|
from dataclasses import dataclass
|
4
|
-
from typing import TYPE_CHECKING, Any, Dict, List, Union, cast
|
4
|
+
from typing import TYPE_CHECKING, Any, Dict, List, Tuple, Union, cast
|
5
5
|
|
6
6
|
from .dataobjectproperty import DataObjectProperty
|
7
|
+
from .decodestate import DecodeState
|
7
8
|
from .diagnostictroublecode import DiagnosticTroubleCode
|
9
|
+
from .encodestate import EncodeState
|
8
10
|
from .exceptions import EncodeError, odxassert
|
9
11
|
from .nameditemlist import NamedItemList
|
10
12
|
from .odxlink import OdxLinkDatabase, OdxLinkId, OdxLinkRef
|
13
|
+
from .odxtypes import ParameterValue
|
11
14
|
|
12
15
|
if TYPE_CHECKING:
|
13
16
|
from .diaglayer import DiagLayer
|
@@ -28,8 +31,10 @@ class DtcDop(DataObjectProperty):
|
|
28
31
|
def linked_dtc_dops(self) -> NamedItemList["DtcDop"]:
|
29
32
|
return self._linked_dtc_dops
|
30
33
|
|
31
|
-
def convert_bytes_to_physical(self,
|
32
|
-
|
34
|
+
def convert_bytes_to_physical(self,
|
35
|
+
decode_state: DecodeState,
|
36
|
+
bit_position: int = 0) -> Tuple[ParameterValue, int]:
|
37
|
+
trouble_code, cursor_position = super().convert_bytes_to_physical(
|
33
38
|
decode_state, bit_position=bit_position)
|
34
39
|
|
35
40
|
dtcs = [x for x in self.dtcs if x.trouble_code == trouble_code]
|
@@ -38,7 +43,7 @@ class DtcDop(DataObjectProperty):
|
|
38
43
|
|
39
44
|
if len(dtcs) == 1:
|
40
45
|
# we found exactly one described DTC
|
41
|
-
return dtcs[0],
|
46
|
+
return dtcs[0], cursor_position
|
42
47
|
|
43
48
|
# the DTC was not specified. This probably means that the
|
44
49
|
# diagnostic description file is incomplete. We do not bail
|
@@ -57,9 +62,10 @@ class DtcDop(DataObjectProperty):
|
|
57
62
|
sdgs=[],
|
58
63
|
)
|
59
64
|
|
60
|
-
return dtc,
|
65
|
+
return dtc, cursor_position
|
61
66
|
|
62
|
-
def convert_physical_to_bytes(self, physical_value, encode_state,
|
67
|
+
def convert_physical_to_bytes(self, physical_value: ParameterValue, encode_state: EncodeState,
|
68
|
+
bit_position: int) -> bytes:
|
63
69
|
if isinstance(physical_value, DiagnosticTroubleCode):
|
64
70
|
trouble_code = physical_value.trouble_code
|
65
71
|
elif isinstance(physical_value, int):
|
@@ -71,7 +77,7 @@ class DtcDop(DataObjectProperty):
|
|
71
77
|
trouble_code = dtc.trouble_code
|
72
78
|
else:
|
73
79
|
raise EncodeError(f"The DTC-DOP {self.short_name} expected a"
|
74
|
-
f" DiagnosticTroubleCode but got {physical_value}.")
|
80
|
+
f" DiagnosticTroubleCode but got {physical_value!r}.")
|
75
81
|
|
76
82
|
return super().convert_physical_to_bytes(trouble_code, encode_state, bit_position)
|
77
83
|
|
@@ -84,7 +90,7 @@ class DtcDop(DataObjectProperty):
|
|
84
90
|
|
85
91
|
return odxlinks
|
86
92
|
|
87
|
-
def _resolve_odxlinks(self, odxlinks: OdxLinkDatabase):
|
93
|
+
def _resolve_odxlinks(self, odxlinks: OdxLinkDatabase) -> None:
|
88
94
|
super()._resolve_odxlinks(odxlinks)
|
89
95
|
|
90
96
|
self._dtcs = NamedItemList[DiagnosticTroubleCode]()
|
@@ -99,7 +105,7 @@ class DtcDop(DataObjectProperty):
|
|
99
105
|
linked_dtc_dops = [odxlinks.resolve(x, DtcDop) for x in self.linked_dtc_dop_refs]
|
100
106
|
self._linked_dtc_dops = NamedItemList(linked_dtc_dops)
|
101
107
|
|
102
|
-
def _resolve_snrefs(self, diag_layer: "DiagLayer"):
|
108
|
+
def _resolve_snrefs(self, diag_layer: "DiagLayer") -> None:
|
103
109
|
super()._resolve_snrefs(diag_layer)
|
104
110
|
|
105
111
|
for dtc_proxy in self.dtcs_raw:
|
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, Tuple
|
4
4
|
from xml.etree import ElementTree
|
5
5
|
|
6
6
|
from .decodestate import DecodeState
|
@@ -9,7 +9,7 @@ from .encodestate import EncodeState
|
|
9
9
|
from .exceptions import odxrequire
|
10
10
|
from .field import Field
|
11
11
|
from .odxlink import OdxDocFragment, OdxLinkDatabase, OdxLinkId
|
12
|
-
from .odxtypes import
|
12
|
+
from .odxtypes import ParameterValue
|
13
13
|
from .utils import dataclass_fields_asdict
|
14
14
|
|
15
15
|
if TYPE_CHECKING:
|
@@ -49,11 +49,13 @@ class DynamicLengthField(Field):
|
|
49
49
|
|
50
50
|
def convert_physical_to_bytes(
|
51
51
|
self,
|
52
|
-
physical_value:
|
52
|
+
physical_value: ParameterValue,
|
53
53
|
encode_state: EncodeState,
|
54
54
|
bit_position: int = 0,
|
55
55
|
) -> bytes:
|
56
56
|
raise NotImplementedError()
|
57
57
|
|
58
|
-
def convert_bytes_to_physical(self,
|
58
|
+
def convert_bytes_to_physical(self,
|
59
|
+
decode_state: DecodeState,
|
60
|
+
bit_position: int = 0) -> Tuple[ParameterValue, int]:
|
59
61
|
raise NotImplementedError()
|
odxtools/endofpdufield.py
CHANGED
@@ -1,15 +1,15 @@
|
|
1
1
|
# SPDX-License-Identifier: MIT
|
2
2
|
from copy import copy
|
3
3
|
from dataclasses import dataclass
|
4
|
-
from typing import List, Optional
|
4
|
+
from typing import List, Optional, Tuple
|
5
5
|
from xml.etree import ElementTree
|
6
6
|
|
7
7
|
from .decodestate import DecodeState
|
8
8
|
from .encodestate import EncodeState
|
9
|
-
from .exceptions import odxassert
|
9
|
+
from .exceptions import EncodeError, odxassert, odxraise
|
10
10
|
from .field import Field
|
11
11
|
from .odxlink import OdxDocFragment
|
12
|
-
from .odxtypes import
|
12
|
+
from .odxtypes import ParameterValue
|
13
13
|
from .utils import dataclass_fields_asdict
|
14
14
|
|
15
15
|
|
@@ -42,41 +42,40 @@ class EndOfPduField(Field):
|
|
42
42
|
|
43
43
|
def convert_physical_to_bytes(
|
44
44
|
self,
|
45
|
-
|
45
|
+
physical_values: ParameterValue,
|
46
46
|
encode_state: EncodeState,
|
47
47
|
bit_position: int = 0,
|
48
48
|
) -> bytes:
|
49
49
|
odxassert(
|
50
50
|
bit_position == 0, "End of PDU field must be byte aligned. "
|
51
|
-
"Is there an error in reading the .odx?")
|
52
|
-
if isinstance(
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
for value in physical_value:
|
62
|
-
coded_rpc += self.structure.convert_physical_to_bytes(value, encode_state)
|
63
|
-
return coded_rpc
|
51
|
+
"Is there an error in reading the .odx?", EncodeError)
|
52
|
+
if not isinstance(physical_values, list):
|
53
|
+
odxraise(
|
54
|
+
f"Expected a list of values for structure {self.short_name}, "
|
55
|
+
f"got {type(physical_values)}", EncodeError)
|
56
|
+
|
57
|
+
coded_message = b''
|
58
|
+
for value in physical_values:
|
59
|
+
coded_message += self.structure.convert_physical_to_bytes(value, encode_state)
|
60
|
+
return coded_message
|
64
61
|
|
65
|
-
def convert_bytes_to_physical(self,
|
62
|
+
def convert_bytes_to_physical(self,
|
63
|
+
decode_state: DecodeState,
|
64
|
+
bit_position: int = 0) -> Tuple[ParameterValue, int]:
|
66
65
|
decode_state = copy(decode_state)
|
67
|
-
|
66
|
+
cursor_position = decode_state.cursor_position
|
68
67
|
byte_code = decode_state.coded_message
|
69
68
|
|
70
69
|
value = []
|
71
|
-
while len(byte_code) >
|
70
|
+
while len(byte_code) > cursor_position:
|
72
71
|
# ATTENTION: the ODX specification is very misleading
|
73
72
|
# here: it says that the item is repeated until the end of
|
74
73
|
# the PDU, but it means that DOP of the items that are
|
75
74
|
# repeated are identical, not their values
|
76
|
-
new_value,
|
75
|
+
new_value, cursor_position = self.structure.convert_bytes_to_physical(
|
77
76
|
decode_state, bit_position=bit_position)
|
78
77
|
# Update next byte_position
|
79
|
-
decode_state.
|
78
|
+
decode_state.cursor_position = cursor_position
|
80
79
|
value.append(new_value)
|
81
80
|
|
82
|
-
return value,
|
81
|
+
return value, cursor_position
|
odxtools/environmentdata.py
CHANGED
@@ -7,6 +7,7 @@ from .basicstructure import BasicStructure
|
|
7
7
|
from .createsdgs import create_sdgs_from_et
|
8
8
|
from .element import IdentifiableElement
|
9
9
|
from .exceptions import odxrequire
|
10
|
+
from .nameditemlist import NamedItemList
|
10
11
|
from .odxlink import OdxDocFragment
|
11
12
|
from .odxtypes import odxstr_to_bool
|
12
13
|
from .parameters.createanyparameter import create_any_parameter_from_et
|
@@ -19,10 +20,6 @@ class EnvironmentData(BasicStructure):
|
|
19
20
|
|
20
21
|
dtc_values: List[int]
|
21
22
|
|
22
|
-
def __init__(self, *, dtc_values: List[int], **kwargs):
|
23
|
-
super().__init__(**kwargs)
|
24
|
-
self.dtc_values = dtc_values
|
25
|
-
|
26
23
|
@staticmethod
|
27
24
|
def from_et(et_element: ElementTree.Element,
|
28
25
|
doc_frags: List[OdxDocFragment]) -> "EnvironmentData":
|
@@ -44,7 +41,7 @@ class EnvironmentData(BasicStructure):
|
|
44
41
|
return EnvironmentData(
|
45
42
|
sdgs=sdgs,
|
46
43
|
is_visible_raw=is_visible_raw,
|
47
|
-
parameters=parameters,
|
44
|
+
parameters=NamedItemList(parameters),
|
48
45
|
byte_size=byte_size,
|
49
46
|
dtc_values=dtc_values,
|
50
47
|
**kwargs)
|