odxtools 7.2.0__py3-none-any.whl → 7.4.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/cli/_print_utils.py +3 -3
- odxtools/cli/browse.py +4 -2
- odxtools/cli/list.py +3 -3
- odxtools/commrelation.py +122 -0
- odxtools/comparaminstance.py +1 -1
- odxtools/comparamspec.py +1 -2
- odxtools/compumethods/linearsegment.py +0 -2
- odxtools/database.py +17 -11
- odxtools/decodestate.py +8 -2
- odxtools/diaglayer.py +23 -17
- odxtools/diaglayerraw.py +116 -23
- odxtools/diagnostictroublecode.py +2 -2
- odxtools/diagservice.py +33 -20
- odxtools/diagvariable.py +104 -0
- odxtools/dtcdop.py +39 -14
- odxtools/dyndefinedspec.py +179 -0
- odxtools/encodestate.py +14 -2
- odxtools/environmentdatadescription.py +137 -16
- odxtools/exceptions.py +10 -1
- odxtools/multiplexer.py +92 -56
- odxtools/multiplexercase.py +6 -5
- odxtools/multiplexerdefaultcase.py +7 -6
- odxtools/odxlink.py +19 -47
- odxtools/odxtypes.py +1 -1
- odxtools/parameterinfo.py +2 -2
- odxtools/parameters/nrcconstparameter.py +28 -37
- odxtools/parameters/systemparameter.py +1 -1
- odxtools/parameters/tablekeyparameter.py +11 -4
- odxtools/servicebinner.py +1 -1
- odxtools/specialdatagroup.py +1 -1
- odxtools/swvariable.py +21 -0
- odxtools/templates/macros/printComparam.xml.jinja2 +4 -2
- odxtools/templates/macros/printCompuMethod.xml.jinja2 +1 -8
- odxtools/templates/macros/printDiagVariable.xml.jinja2 +66 -0
- odxtools/templates/macros/printDynDefinedSpec.xml.jinja2 +48 -0
- odxtools/templates/macros/printEcuVariantPattern.xml.jinja2 +1 -1
- odxtools/templates/macros/printParam.xml.jinja2 +7 -8
- odxtools/templates/macros/printService.xml.jinja2 +3 -2
- odxtools/templates/macros/printSingleEcuJob.xml.jinja2 +2 -2
- odxtools/templates/macros/printVariant.xml.jinja2 +30 -13
- odxtools/variablegroup.py +22 -0
- odxtools/version.py +2 -2
- {odxtools-7.2.0.dist-info → odxtools-7.4.0.dist-info}/METADATA +18 -18
- {odxtools-7.2.0.dist-info → odxtools-7.4.0.dist-info}/RECORD +49 -42
- {odxtools-7.2.0.dist-info → odxtools-7.4.0.dist-info}/WHEEL +1 -1
- {odxtools-7.2.0.dist-info → odxtools-7.4.0.dist-info}/LICENSE +0 -0
- {odxtools-7.2.0.dist-info → odxtools-7.4.0.dist-info}/entry_points.txt +0 -0
- {odxtools-7.2.0.dist-info → odxtools-7.4.0.dist-info}/top_level.txt +0 -0
odxtools/diaglayerraw.py
CHANGED
@@ -14,6 +14,8 @@ from .diagcomm import DiagComm
|
|
14
14
|
from .diagdatadictionaryspec import DiagDataDictionarySpec
|
15
15
|
from .diaglayertype import DiagLayerType
|
16
16
|
from .diagservice import DiagService
|
17
|
+
from .diagvariable import DiagVariable
|
18
|
+
from .dyndefinedspec import DynDefinedSpec
|
17
19
|
from .ecuvariantpattern import EcuVariantPattern
|
18
20
|
from .element import IdentifiableElement
|
19
21
|
from .exceptions import odxassert, odxraise, odxrequire
|
@@ -29,6 +31,7 @@ from .snrefcontext import SnRefContext
|
|
29
31
|
from .specialdatagroup import SpecialDataGroup
|
30
32
|
from .statechart import StateChart
|
31
33
|
from .utils import dataclass_fields_asdict
|
34
|
+
from .variablegroup import VariableGroup
|
32
35
|
|
33
36
|
|
34
37
|
@dataclass
|
@@ -44,7 +47,7 @@ class DiagLayerRaw(IdentifiableElement):
|
|
44
47
|
company_datas: NamedItemList[CompanyData]
|
45
48
|
functional_classes: NamedItemList[FunctionalClass]
|
46
49
|
diag_data_dictionary_spec: Optional[DiagDataDictionarySpec]
|
47
|
-
|
50
|
+
diag_comms_raw: List[Union[OdxLinkRef, DiagComm]]
|
48
51
|
requests: NamedItemList[Request]
|
49
52
|
positive_responses: NamedItemList[Response]
|
50
53
|
negative_responses: NamedItemList[Response]
|
@@ -59,15 +62,36 @@ class DiagLayerRaw(IdentifiableElement):
|
|
59
62
|
# these attributes are only defined for some kinds of diag layers!
|
60
63
|
# TODO: make a proper class hierarchy!
|
61
64
|
parent_refs: List[ParentRef]
|
62
|
-
|
65
|
+
comparam_refs: List[ComparamInstance]
|
63
66
|
ecu_variant_patterns: List[EcuVariantPattern]
|
64
67
|
comparam_spec_ref: Optional[OdxLinkRef]
|
65
68
|
prot_stack_snref: Optional[str]
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
+
diag_variables_raw: List[Union[DiagVariable, OdxLinkRef]]
|
70
|
+
variable_groups: NamedItemList[VariableGroup]
|
71
|
+
dyn_defined_spec: Optional[DynDefinedSpec]
|
69
72
|
# base_variant_patterns: List[EcuVariantPattern] # TODO
|
70
73
|
|
74
|
+
@property
|
75
|
+
def diag_comms(self) -> NamedItemList[DiagComm]:
|
76
|
+
return self._diag_comms
|
77
|
+
|
78
|
+
@property
|
79
|
+
def diag_services(self) -> NamedItemList[DiagService]:
|
80
|
+
return self._diag_services
|
81
|
+
|
82
|
+
@property
|
83
|
+
def services(self) -> NamedItemList[DiagService]:
|
84
|
+
"""This property is an alias for `.diag_services`"""
|
85
|
+
return self._diag_services
|
86
|
+
|
87
|
+
@property
|
88
|
+
def single_ecu_jobs(self) -> NamedItemList[SingleEcuJob]:
|
89
|
+
return self._single_ecu_jobs
|
90
|
+
|
91
|
+
@property
|
92
|
+
def diag_variables(self) -> NamedItemList[DiagVariable]:
|
93
|
+
return self._diag_variables
|
94
|
+
|
71
95
|
@staticmethod
|
72
96
|
def from_et(et_element: ElementTree.Element, doc_frags: List[OdxDocFragment]) -> "DiagLayerRaw":
|
73
97
|
try:
|
@@ -101,7 +125,7 @@ class DiagLayerRaw(IdentifiableElement):
|
|
101
125
|
if (ddds_elem := et_element.find("DIAG-DATA-DICTIONARY-SPEC")) is not None:
|
102
126
|
diag_data_dictionary_spec = DiagDataDictionarySpec.from_et(ddds_elem, doc_frags)
|
103
127
|
|
104
|
-
|
128
|
+
diag_comms_raw: List[Union[OdxLinkRef, DiagComm]] = []
|
105
129
|
if (dc_elems := et_element.find("DIAG-COMMS")) is not None:
|
106
130
|
for dc_proxy_elem in dc_elems:
|
107
131
|
dc: Union[OdxLinkRef, DiagComm]
|
@@ -113,7 +137,7 @@ class DiagLayerRaw(IdentifiableElement):
|
|
113
137
|
odxassert(dc_proxy_elem.tag == "SINGLE-ECU-JOB")
|
114
138
|
dc = SingleEcuJob.from_et(dc_proxy_elem, doc_frags)
|
115
139
|
|
116
|
-
|
140
|
+
diag_comms_raw.append(dc)
|
117
141
|
|
118
142
|
requests = NamedItemList([
|
119
143
|
Request.from_et(rq_elem, doc_frags)
|
@@ -159,7 +183,7 @@ class DiagLayerRaw(IdentifiableElement):
|
|
159
183
|
for pr_el in et_element.iterfind("PARENT-REFS/PARENT-REF")
|
160
184
|
]
|
161
185
|
|
162
|
-
|
186
|
+
comparam_refs = [
|
163
187
|
ComparamInstance.from_et(el, doc_frags)
|
164
188
|
for el in et_element.iterfind("COMPARAM-REFS/COMPARAM-REF")
|
165
189
|
]
|
@@ -178,6 +202,29 @@ class DiagLayerRaw(IdentifiableElement):
|
|
178
202
|
if (prot_stack_snref_elem := et_element.find("PROT-STACK-SNREF")) is not None:
|
179
203
|
prot_stack_snref = odxrequire(prot_stack_snref_elem.get("SHORT-NAME"))
|
180
204
|
|
205
|
+
diag_variables_raw: List[Union[DiagVariable, OdxLinkRef]] = []
|
206
|
+
if (dv_elems := et_element.find("DIAG-VARIABLES")) is not None:
|
207
|
+
for dv_proxy_elem in dv_elems:
|
208
|
+
dv_proxy: Union[OdxLinkRef, DiagVariable]
|
209
|
+
if dv_proxy_elem.tag == "DIAG-VARIABLE-REF":
|
210
|
+
dv_proxy = OdxLinkRef.from_et(dv_proxy_elem, doc_frags)
|
211
|
+
elif dv_proxy_elem.tag == "DIAG-VARIABLE":
|
212
|
+
dv_proxy = DiagVariable.from_et(dv_proxy_elem, doc_frags)
|
213
|
+
else:
|
214
|
+
odxraise("DIAG-VARIABLES tags may only contain "
|
215
|
+
"DIAG-VARIABLE and DIAG-VARIABLE-REF subtags")
|
216
|
+
|
217
|
+
diag_variables_raw.append(dv_proxy)
|
218
|
+
|
219
|
+
variable_groups = NamedItemList([
|
220
|
+
VariableGroup.from_et(vg_elem, doc_frags)
|
221
|
+
for vg_elem in et_element.iterfind("VARIABLE-GROUPS/VARIABLE-GROUP")
|
222
|
+
])
|
223
|
+
|
224
|
+
dyn_defined_spec = None
|
225
|
+
if (dds_elem := et_element.find("DYN-DEFINED-SPEC")) is not None:
|
226
|
+
dyn_defined_spec = DynDefinedSpec.from_et(dds_elem, doc_frags)
|
227
|
+
|
181
228
|
# Create DiagLayer
|
182
229
|
return DiagLayerRaw(
|
183
230
|
variant_type=variant_type,
|
@@ -185,7 +232,7 @@ class DiagLayerRaw(IdentifiableElement):
|
|
185
232
|
company_datas=NamedItemList(company_datas),
|
186
233
|
functional_classes=NamedItemList(functional_classes),
|
187
234
|
diag_data_dictionary_spec=diag_data_dictionary_spec,
|
188
|
-
|
235
|
+
diag_comms_raw=diag_comms_raw,
|
189
236
|
requests=requests,
|
190
237
|
positive_responses=positive_responses,
|
191
238
|
negative_responses=negative_responses,
|
@@ -195,10 +242,13 @@ class DiagLayerRaw(IdentifiableElement):
|
|
195
242
|
additional_audiences=NamedItemList(additional_audiences),
|
196
243
|
sdgs=sdgs,
|
197
244
|
parent_refs=parent_refs,
|
198
|
-
|
245
|
+
comparam_refs=comparam_refs,
|
199
246
|
ecu_variant_patterns=ecu_variant_patterns,
|
200
247
|
comparam_spec_ref=comparam_spec_ref,
|
201
248
|
prot_stack_snref=prot_stack_snref,
|
249
|
+
diag_variables_raw=diag_variables_raw,
|
250
|
+
variable_groups=variable_groups,
|
251
|
+
dyn_defined_spec=dyn_defined_spec,
|
202
252
|
**kwargs)
|
203
253
|
|
204
254
|
def _build_odxlinks(self) -> Dict[OdxLinkId, Any]:
|
@@ -214,10 +264,10 @@ class DiagLayerRaw(IdentifiableElement):
|
|
214
264
|
odxlinks.update(company_data._build_odxlinks())
|
215
265
|
for functional_class in self.functional_classes:
|
216
266
|
odxlinks.update(functional_class._build_odxlinks())
|
217
|
-
for
|
218
|
-
if isinstance(
|
267
|
+
for dc_proxy in self.diag_comms_raw:
|
268
|
+
if isinstance(dc_proxy, OdxLinkRef):
|
219
269
|
continue
|
220
|
-
odxlinks.update(
|
270
|
+
odxlinks.update(dc_proxy._build_odxlinks())
|
221
271
|
for request in self.requests:
|
222
272
|
odxlinks.update(request._build_odxlinks())
|
223
273
|
for positive_response in self.positive_responses:
|
@@ -234,8 +284,13 @@ class DiagLayerRaw(IdentifiableElement):
|
|
234
284
|
odxlinks.update(sdg._build_odxlinks())
|
235
285
|
for parent_ref in self.parent_refs:
|
236
286
|
odxlinks.update(parent_ref._build_odxlinks())
|
237
|
-
for comparam in self.
|
287
|
+
for comparam in self.comparam_refs:
|
238
288
|
odxlinks.update(comparam._build_odxlinks())
|
289
|
+
for dv_proxy in self.diag_variables_raw:
|
290
|
+
if not isinstance(dv_proxy, OdxLinkRef):
|
291
|
+
odxlinks.update(dv_proxy._build_odxlinks())
|
292
|
+
if self.dyn_defined_spec is not None:
|
293
|
+
odxlinks.update(self.dyn_defined_spec._build_odxlinks())
|
239
294
|
|
240
295
|
return odxlinks
|
241
296
|
|
@@ -258,10 +313,28 @@ class DiagLayerRaw(IdentifiableElement):
|
|
258
313
|
company_data._resolve_odxlinks(odxlinks)
|
259
314
|
for functional_class in self.functional_classes:
|
260
315
|
functional_class._resolve_odxlinks(odxlinks)
|
261
|
-
|
262
|
-
|
263
|
-
|
264
|
-
|
316
|
+
|
317
|
+
# resolve references to diagnostic communication objects and
|
318
|
+
# separate them into services and single-ecu jobs
|
319
|
+
self._diag_comms = NamedItemList[DiagComm]()
|
320
|
+
self._diag_services = NamedItemList[DiagService]()
|
321
|
+
self._single_ecu_jobs = NamedItemList[SingleEcuJob]()
|
322
|
+
for dc_proxy in self.diag_comms_raw:
|
323
|
+
if isinstance(dc_proxy, OdxLinkRef):
|
324
|
+
dc = odxlinks.resolve(dc_proxy, DiagComm)
|
325
|
+
else:
|
326
|
+
dc = dc_proxy
|
327
|
+
dc._resolve_odxlinks(odxlinks)
|
328
|
+
|
329
|
+
self._diag_comms.append(dc)
|
330
|
+
|
331
|
+
if isinstance(dc, DiagService):
|
332
|
+
self._diag_services.append(dc)
|
333
|
+
elif isinstance(dc, SingleEcuJob):
|
334
|
+
self._single_ecu_jobs.append(dc)
|
335
|
+
else:
|
336
|
+
odxraise()
|
337
|
+
|
265
338
|
for request in self.requests:
|
266
339
|
request._resolve_odxlinks(odxlinks)
|
267
340
|
for positive_response in self.positive_responses:
|
@@ -278,9 +351,22 @@ class DiagLayerRaw(IdentifiableElement):
|
|
278
351
|
sdg._resolve_odxlinks(odxlinks)
|
279
352
|
for parent_ref in self.parent_refs:
|
280
353
|
parent_ref._resolve_odxlinks(odxlinks)
|
281
|
-
for comparam in self.
|
354
|
+
for comparam in self.comparam_refs:
|
282
355
|
comparam._resolve_odxlinks(odxlinks)
|
283
356
|
|
357
|
+
self._diag_variables: NamedItemList[DiagVariable] = NamedItemList()
|
358
|
+
for dv_proxy in self.diag_variables_raw:
|
359
|
+
if isinstance(dv_proxy, OdxLinkRef):
|
360
|
+
dv = odxlinks.resolve(dv_proxy, DiagVariable)
|
361
|
+
else:
|
362
|
+
dv_proxy._resolve_odxlinks(odxlinks)
|
363
|
+
dv = dv_proxy
|
364
|
+
|
365
|
+
self._diag_variables.append(dv)
|
366
|
+
|
367
|
+
if self.dyn_defined_spec is not None:
|
368
|
+
self.dyn_defined_spec._resolve_odxlinks(odxlinks)
|
369
|
+
|
284
370
|
def _resolve_snrefs(self, context: SnRefContext) -> None:
|
285
371
|
self._prot_stack: Optional[ProtStack] = None
|
286
372
|
if self.prot_stack_snref is not None:
|
@@ -299,10 +385,10 @@ class DiagLayerRaw(IdentifiableElement):
|
|
299
385
|
company_data._resolve_snrefs(context)
|
300
386
|
for functional_class in self.functional_classes:
|
301
387
|
functional_class._resolve_snrefs(context)
|
302
|
-
for
|
303
|
-
if isinstance(
|
388
|
+
for dc_proxy in self.diag_comms_raw:
|
389
|
+
if isinstance(dc_proxy, OdxLinkRef):
|
304
390
|
continue
|
305
|
-
|
391
|
+
dc_proxy._resolve_snrefs(context)
|
306
392
|
for request in self.requests:
|
307
393
|
request._resolve_snrefs(context)
|
308
394
|
for positive_response in self.positive_responses:
|
@@ -319,9 +405,16 @@ class DiagLayerRaw(IdentifiableElement):
|
|
319
405
|
sdg._resolve_snrefs(context)
|
320
406
|
for parent_ref in self.parent_refs:
|
321
407
|
parent_ref._resolve_snrefs(context)
|
322
|
-
for comparam in self.
|
408
|
+
for comparam in self.comparam_refs:
|
323
409
|
comparam._resolve_snrefs(context)
|
324
410
|
|
411
|
+
for dv_proxy in self.diag_variables_raw:
|
412
|
+
if not isinstance(dv_proxy, OdxLinkRef):
|
413
|
+
dv_proxy._resolve_snrefs(context)
|
414
|
+
|
415
|
+
if self.dyn_defined_spec is not None:
|
416
|
+
self.dyn_defined_spec._resolve_snrefs(context)
|
417
|
+
|
325
418
|
@property
|
326
419
|
def comparam_spec(self) -> Optional[Union[ComparamSpec, ComparamSubset]]:
|
327
420
|
return self._comparam_spec
|
@@ -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/diagvariable.py
ADDED
@@ -0,0 +1,104 @@
|
|
1
|
+
# SPDX-License-Identifier: MIT
|
2
|
+
from dataclasses import dataclass
|
3
|
+
from typing import Any, Dict, List, Optional
|
4
|
+
from xml.etree import ElementTree
|
5
|
+
|
6
|
+
from .admindata import AdminData
|
7
|
+
from .commrelation import CommRelation
|
8
|
+
from .element import IdentifiableElement
|
9
|
+
from .exceptions import odxrequire
|
10
|
+
from .nameditemlist import NamedItemList
|
11
|
+
from .odxlink import OdxDocFragment, OdxLinkDatabase, OdxLinkId, OdxLinkRef
|
12
|
+
from .odxtypes import odxstr_to_bool
|
13
|
+
from .snrefcontext import SnRefContext
|
14
|
+
from .specialdatagroup import SpecialDataGroup
|
15
|
+
from .swvariable import SwVariable
|
16
|
+
from .utils import dataclass_fields_asdict
|
17
|
+
from .variablegroup import VariableGroup
|
18
|
+
|
19
|
+
|
20
|
+
@dataclass
|
21
|
+
class DiagVariable(IdentifiableElement):
|
22
|
+
"""Representation of a diagnostic variable
|
23
|
+
"""
|
24
|
+
|
25
|
+
admin_data: Optional[AdminData]
|
26
|
+
variable_group_ref: OdxLinkRef
|
27
|
+
sw_variables: List[SwVariable]
|
28
|
+
comm_relations: List[CommRelation]
|
29
|
+
#snref_to_tablerow: Optional[SnrefToTableRow] # TODO
|
30
|
+
sdgs: List[SpecialDataGroup]
|
31
|
+
is_read_before_write_raw: Optional[bool]
|
32
|
+
|
33
|
+
@property
|
34
|
+
def variable_group(self) -> VariableGroup:
|
35
|
+
return self._variable_group
|
36
|
+
|
37
|
+
@property
|
38
|
+
def is_read_before_write(self) -> bool:
|
39
|
+
return self.is_read_before_write_raw is True
|
40
|
+
|
41
|
+
@staticmethod
|
42
|
+
def from_et(et_element: ElementTree.Element, doc_frags: List[OdxDocFragment]) -> "DiagVariable":
|
43
|
+
kwargs = dataclass_fields_asdict(IdentifiableElement.from_et(et_element, doc_frags))
|
44
|
+
|
45
|
+
admin_data = AdminData.from_et(et_element.find("ADMIN-DATA"), doc_frags)
|
46
|
+
variable_group_ref = odxrequire(
|
47
|
+
OdxLinkRef.from_et(et_element.find("VARIABLE-GROUP-REF"), doc_frags))
|
48
|
+
sw_variables = NamedItemList([
|
49
|
+
SwVariable.from_et(swv_elem, doc_frags)
|
50
|
+
for swv_elem in et_element.iterfind("SW-VARIABLES/SW-VARIABLE")
|
51
|
+
])
|
52
|
+
comm_relations = [
|
53
|
+
CommRelation.from_et(cr_elem, doc_frags)
|
54
|
+
for cr_elem in et_element.iterfind("COMM-RELATIONS/COMM-RELATION")
|
55
|
+
]
|
56
|
+
sdgs = [
|
57
|
+
SpecialDataGroup.from_et(sdge, doc_frags) for sdge in et_element.iterfind("SDGS/SDG")
|
58
|
+
]
|
59
|
+
is_read_before_write_raw = odxstr_to_bool(et_element.get("IS-READ-BEFORE-WRITE"))
|
60
|
+
|
61
|
+
return DiagVariable(
|
62
|
+
admin_data=admin_data,
|
63
|
+
variable_group_ref=variable_group_ref,
|
64
|
+
sw_variables=sw_variables,
|
65
|
+
comm_relations=comm_relations,
|
66
|
+
sdgs=sdgs,
|
67
|
+
is_read_before_write_raw=is_read_before_write_raw,
|
68
|
+
**kwargs)
|
69
|
+
|
70
|
+
def _build_odxlinks(self) -> Dict[OdxLinkId, Any]:
|
71
|
+
result = {self.odx_id: self}
|
72
|
+
|
73
|
+
if self.admin_data is not None:
|
74
|
+
result.update(self.admin_data._build_odxlinks())
|
75
|
+
|
76
|
+
for sdg in self.sdgs:
|
77
|
+
result.update(sdg._build_odxlinks())
|
78
|
+
|
79
|
+
for cr in self.comm_relations:
|
80
|
+
result.update(cr._build_odxlinks())
|
81
|
+
|
82
|
+
return result
|
83
|
+
|
84
|
+
def _resolve_odxlinks(self, odxlinks: OdxLinkDatabase) -> None:
|
85
|
+
self._variable_group = odxlinks.resolve(self.variable_group_ref, VariableGroup)
|
86
|
+
|
87
|
+
if self.admin_data is not None:
|
88
|
+
self.admin_data._resolve_odxlinks(odxlinks)
|
89
|
+
|
90
|
+
for sdg in self.sdgs:
|
91
|
+
sdg._resolve_odxlinks(odxlinks)
|
92
|
+
|
93
|
+
for cr in self.comm_relations:
|
94
|
+
cr._resolve_odxlinks(odxlinks)
|
95
|
+
|
96
|
+
def _resolve_snrefs(self, context: SnRefContext) -> None:
|
97
|
+
if self.admin_data is not None:
|
98
|
+
self.admin_data._resolve_snrefs(context)
|
99
|
+
|
100
|
+
for sdg in self.sdgs:
|
101
|
+
sdg._resolve_snrefs(context)
|
102
|
+
|
103
|
+
for cr in self.comm_relations:
|
104
|
+
cr._resolve_snrefs(context)
|
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]:
|