odxtools 7.1.1__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/__init__.py +6 -4
- odxtools/additionalaudience.py +3 -5
- odxtools/admindata.py +5 -7
- odxtools/audience.py +3 -5
- odxtools/basecomparam.py +3 -5
- odxtools/basicstructure.py +19 -23
- odxtools/cli/_parser_utils.py +1 -1
- odxtools/cli/_print_utils.py +3 -2
- odxtools/cli/compare.py +1 -1
- odxtools/companydata.py +5 -7
- odxtools/companydocinfo.py +7 -8
- odxtools/companyrevisioninfo.py +3 -5
- odxtools/companyspecificinfo.py +8 -9
- odxtools/comparam.py +4 -6
- odxtools/comparaminstance.py +6 -8
- odxtools/comparamspec.py +14 -13
- odxtools/comparamsubset.py +17 -16
- odxtools/complexcomparam.py +5 -7
- odxtools/compumethods/compuconst.py +31 -0
- odxtools/compumethods/compudefaultvalue.py +27 -0
- odxtools/compumethods/compuinternaltophys.py +39 -0
- odxtools/compumethods/compuinversevalue.py +7 -0
- odxtools/compumethods/compumethod.py +67 -12
- odxtools/compumethods/compuphystointernal.py +39 -0
- odxtools/compumethods/compuscale.py +15 -26
- odxtools/compumethods/createanycompumethod.py +14 -160
- odxtools/compumethods/identicalcompumethod.py +31 -6
- odxtools/compumethods/linearcompumethod.py +69 -189
- odxtools/compumethods/linearsegment.py +191 -0
- odxtools/compumethods/scalelinearcompumethod.py +132 -26
- odxtools/compumethods/tabintpcompumethod.py +119 -99
- odxtools/compumethods/texttablecompumethod.py +107 -43
- odxtools/createanydiagcodedtype.py +10 -67
- odxtools/database.py +84 -72
- odxtools/dataobjectproperty.py +10 -19
- odxtools/decodestate.py +8 -2
- odxtools/description.py +47 -0
- odxtools/determinenumberofitems.py +4 -5
- odxtools/diagcodedtype.py +29 -12
- odxtools/diagcomm.py +10 -6
- odxtools/diagdatadictionaryspec.py +20 -21
- odxtools/diaglayer.py +39 -9
- odxtools/diaglayercontainer.py +17 -11
- odxtools/diaglayerraw.py +20 -21
- odxtools/diagnostictroublecode.py +8 -9
- odxtools/diagservice.py +42 -27
- odxtools/docrevision.py +5 -7
- odxtools/dopbase.py +7 -8
- odxtools/dtcdop.py +44 -22
- odxtools/dynamicendmarkerfield.py +22 -9
- odxtools/dynamiclengthfield.py +5 -11
- odxtools/element.py +4 -3
- odxtools/encodestate.py +14 -2
- odxtools/endofpdufield.py +0 -2
- odxtools/environmentdatadescription.py +137 -19
- odxtools/exceptions.py +11 -2
- odxtools/field.py +9 -9
- odxtools/functionalclass.py +3 -5
- odxtools/inputparam.py +3 -5
- odxtools/leadinglengthinfotype.py +15 -2
- odxtools/loadfile.py +64 -0
- odxtools/minmaxlengthtype.py +20 -2
- odxtools/modification.py +3 -5
- odxtools/multiplexer.py +98 -69
- odxtools/multiplexercase.py +10 -11
- odxtools/multiplexerdefaultcase.py +11 -12
- odxtools/multiplexerswitchkey.py +4 -5
- odxtools/negoutputparam.py +3 -5
- odxtools/odxlink.py +12 -26
- odxtools/odxtypes.py +1 -1
- odxtools/outputparam.py +3 -5
- odxtools/parameterinfo.py +5 -5
- odxtools/parameters/codedconstparameter.py +2 -14
- odxtools/parameters/lengthkeyparameter.py +3 -17
- odxtools/parameters/nrcconstparameter.py +29 -50
- odxtools/parameters/parameter.py +22 -22
- odxtools/parameters/parameterwithdop.py +6 -8
- odxtools/parameters/physicalconstantparameter.py +5 -8
- odxtools/parameters/reservedparameter.py +4 -3
- odxtools/parameters/systemparameter.py +1 -1
- odxtools/parameters/tablekeyparameter.py +6 -9
- odxtools/parameters/tablestructparameter.py +6 -8
- odxtools/parameters/valueparameter.py +5 -8
- odxtools/paramlengthinfotype.py +19 -6
- odxtools/parentref.py +15 -1
- odxtools/physicaldimension.py +3 -5
- odxtools/progcode.py +18 -7
- odxtools/protstack.py +3 -5
- odxtools/relateddoc.py +7 -9
- odxtools/request.py +8 -0
- odxtools/response.py +8 -0
- odxtools/scaleconstr.py +3 -3
- odxtools/singleecujob.py +12 -10
- odxtools/snrefcontext.py +29 -0
- odxtools/specialdata.py +3 -5
- odxtools/specialdatagroup.py +5 -7
- odxtools/specialdatagroupcaption.py +3 -6
- odxtools/standardlengthtype.py +27 -2
- odxtools/state.py +3 -5
- odxtools/statechart.py +9 -11
- odxtools/statetransition.py +4 -9
- odxtools/staticfield.py +4 -8
- odxtools/table.py +7 -8
- odxtools/tablerow.py +7 -6
- odxtools/teammember.py +3 -5
- odxtools/templates/comparam-spec.odx-c.xml.jinja2 +2 -5
- odxtools/templates/comparam-subset.odx-cs.xml.jinja2 +2 -5
- odxtools/templates/macros/printCompanyData.xml.jinja2 +2 -5
- odxtools/templates/macros/printComparamRef.xml.jinja2 +5 -12
- odxtools/templates/macros/printCompuMethod.xml.jinja2 +153 -0
- odxtools/templates/macros/printDOP.xml.jinja2 +10 -132
- odxtools/templates/macros/printDescription.xml.jinja2 +18 -0
- odxtools/templates/macros/printElementId.xml.jinja2 +3 -3
- odxtools/templates/macros/printMux.xml.jinja2 +3 -2
- odxtools/templates/macros/printTable.xml.jinja2 +2 -3
- odxtools/unit.py +3 -5
- odxtools/unitgroup.py +3 -5
- odxtools/unitspec.py +9 -10
- odxtools/utils.py +1 -26
- odxtools/version.py +2 -2
- odxtools/{write_pdx_file.py → writepdxfile.py} +19 -10
- odxtools/xdoc.py +3 -5
- {odxtools-7.1.1.dist-info → odxtools-7.3.0.dist-info}/METADATA +1 -1
- odxtools-7.3.0.dist-info/RECORD +192 -0
- {odxtools-7.1.1.dist-info → odxtools-7.3.0.dist-info}/WHEEL +1 -1
- odxtools/createcompanydatas.py +0 -17
- odxtools/createsdgs.py +0 -19
- odxtools/load_file.py +0 -13
- odxtools/load_odx_d_file.py +0 -6
- odxtools/load_pdx_file.py +0 -8
- odxtools-7.1.1.dist-info/RECORD +0 -186
- /odxtools/templates/{index.xml.xml.jinja2 → index.xml.jinja2} +0 -0
- {odxtools-7.1.1.dist-info → odxtools-7.3.0.dist-info}/LICENSE +0 -0
- {odxtools-7.1.1.dist-info → odxtools-7.3.0.dist-info}/entry_points.txt +0 -0
- {odxtools-7.1.1.dist-info → odxtools-7.3.0.dist-info}/top_level.txt +0 -0
odxtools/diaglayer.py
CHANGED
@@ -1,11 +1,12 @@
|
|
1
1
|
# SPDX-License-Identifier: MIT
|
2
2
|
import re
|
3
3
|
import warnings
|
4
|
-
from copy import copy
|
4
|
+
from copy import copy, deepcopy
|
5
5
|
from dataclasses import dataclass
|
6
6
|
from functools import cached_property
|
7
7
|
from itertools import chain
|
8
|
-
from typing import Any, Callable, Dict, Iterable, List, Optional, Tuple, TypeVar,
|
8
|
+
from typing import (TYPE_CHECKING, Any, Callable, Dict, Iterable, List, Optional, Tuple, TypeVar,
|
9
|
+
Union, cast)
|
9
10
|
from xml.etree import ElementTree
|
10
11
|
|
11
12
|
from deprecation import deprecated
|
@@ -16,6 +17,7 @@ from .companydata import CompanyData
|
|
16
17
|
from .comparaminstance import ComparamInstance
|
17
18
|
from .comparamspec import ComparamSpec
|
18
19
|
from .comparamsubset import ComparamSubset
|
20
|
+
from .description import Description
|
19
21
|
from .diagcomm import DiagComm
|
20
22
|
from .diagdatadictionaryspec import DiagDataDictionarySpec
|
21
23
|
from .diaglayerraw import DiagLayerRaw
|
@@ -33,12 +35,16 @@ from .request import Request
|
|
33
35
|
from .response import Response
|
34
36
|
from .servicebinner import ServiceBinner
|
35
37
|
from .singleecujob import SingleEcuJob
|
38
|
+
from .snrefcontext import SnRefContext
|
36
39
|
from .specialdatagroup import SpecialDataGroup
|
37
40
|
from .statechart import StateChart
|
38
41
|
from .table import Table
|
39
42
|
from .unitgroup import UnitGroup
|
40
43
|
from .unitspec import UnitSpec
|
41
44
|
|
45
|
+
if TYPE_CHECKING:
|
46
|
+
from .database import Database
|
47
|
+
|
42
48
|
TNamed = TypeVar("TNamed", bound=OdxNamed)
|
43
49
|
|
44
50
|
PrefixTree = Dict[int, Union[List[DiagService], "PrefixTree"]]
|
@@ -124,7 +130,22 @@ class DiagLayer:
|
|
124
130
|
|
125
131
|
self.diag_layer_raw._resolve_odxlinks(odxlinks)
|
126
132
|
|
127
|
-
def
|
133
|
+
def __deepcopy__(self, memo: Dict[int, Any]) -> Any:
|
134
|
+
"""Create a deep copy of the diagnostic layer
|
135
|
+
|
136
|
+
Note that the copied diagnostic layer is not fully
|
137
|
+
initialized, so `_finalize_init()` should to be called on it
|
138
|
+
before it can be used normally.
|
139
|
+
"""
|
140
|
+
cls = self.__class__
|
141
|
+
result = cls.__new__(cls)
|
142
|
+
memo[id(self)] = result
|
143
|
+
|
144
|
+
result.diag_layer_raw = deepcopy(self.diag_layer_raw, memo)
|
145
|
+
|
146
|
+
return result
|
147
|
+
|
148
|
+
def _finalize_init(self, database: "Database", odxlinks: OdxLinkDatabase) -> None:
|
128
149
|
"""This method deals with everything inheritance related and
|
129
150
|
-- after the final set of objects covered by the diagnostic
|
130
151
|
layer is determined -- resolves any short name references in
|
@@ -276,7 +297,10 @@ class DiagLayer:
|
|
276
297
|
# by the spec (So far, I haven't found any definitive
|
277
298
|
# statement...)
|
278
299
|
#####
|
279
|
-
|
300
|
+
context = SnRefContext(database=database)
|
301
|
+
context.diag_layer = self
|
302
|
+
self.diag_layer_raw._resolve_snrefs(context)
|
303
|
+
context.diag_layer = None
|
280
304
|
|
281
305
|
#####
|
282
306
|
# <convenience functionality>
|
@@ -309,7 +333,7 @@ class DiagLayer:
|
|
309
333
|
return self.diag_layer_raw.long_name
|
310
334
|
|
311
335
|
@property
|
312
|
-
def description(self) -> Optional[
|
336
|
+
def description(self) -> Optional[Description]:
|
313
337
|
return self.diag_layer_raw.description
|
314
338
|
|
315
339
|
@property
|
@@ -1171,16 +1195,19 @@ class DiagLayer:
|
|
1171
1195
|
for service in candidate_services:
|
1172
1196
|
try:
|
1173
1197
|
decoded_messages.append(service.decode_message(message))
|
1174
|
-
except DecodeError:
|
1198
|
+
except DecodeError as e:
|
1175
1199
|
# check if the message can be decoded as a global
|
1176
1200
|
# negative response for the service
|
1201
|
+
gnr_found = False
|
1177
1202
|
for gnr in self.global_negative_responses:
|
1178
1203
|
try:
|
1179
1204
|
decoded_gnr = gnr.decode(message)
|
1205
|
+
gnr_found = True
|
1180
1206
|
if not isinstance(decoded_gnr, dict):
|
1181
|
-
|
1182
|
-
|
1183
|
-
|
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)
|
1184
1211
|
|
1185
1212
|
decoded_messages.append(
|
1186
1213
|
Message(
|
@@ -1191,6 +1218,9 @@ class DiagLayer:
|
|
1191
1218
|
except DecodeError:
|
1192
1219
|
pass
|
1193
1220
|
|
1221
|
+
if not gnr_found:
|
1222
|
+
raise e
|
1223
|
+
|
1194
1224
|
if len(decoded_messages) == 0:
|
1195
1225
|
raise DecodeError(
|
1196
1226
|
f"None of the services {[x.short_name for x in candidate_services]} could parse {message.hex()}."
|
odxtools/diaglayercontainer.py
CHANGED
@@ -1,13 +1,11 @@
|
|
1
1
|
# SPDX-License-Identifier: MIT
|
2
2
|
from dataclasses import dataclass
|
3
3
|
from itertools import chain
|
4
|
-
from typing import Any, Dict, List, Optional, Union
|
4
|
+
from typing import TYPE_CHECKING, Any, Dict, List, Optional, Union
|
5
5
|
from xml.etree import ElementTree
|
6
6
|
|
7
7
|
from .admindata import AdminData
|
8
8
|
from .companydata import CompanyData
|
9
|
-
from .createcompanydatas import create_company_datas_from_et
|
10
|
-
from .createsdgs import create_sdgs_from_et
|
11
9
|
from .diaglayer import DiagLayer
|
12
10
|
from .element import IdentifiableElement
|
13
11
|
from .exceptions import odxrequire
|
@@ -16,6 +14,9 @@ from .odxlink import OdxDocFragment, OdxLinkDatabase, OdxLinkId
|
|
16
14
|
from .specialdatagroup import SpecialDataGroup
|
17
15
|
from .utils import dataclass_fields_asdict
|
18
16
|
|
17
|
+
if TYPE_CHECKING:
|
18
|
+
from .database import Database
|
19
|
+
|
19
20
|
|
20
21
|
@dataclass
|
21
22
|
class DiagLayerContainer(IdentifiableElement):
|
@@ -48,7 +49,10 @@ class DiagLayerContainer(IdentifiableElement):
|
|
48
49
|
kwargs = dataclass_fields_asdict(IdentifiableElement.from_et(et_element, doc_frags))
|
49
50
|
|
50
51
|
admin_data = AdminData.from_et(et_element.find("ADMIN-DATA"), doc_frags)
|
51
|
-
company_datas =
|
52
|
+
company_datas = NamedItemList([
|
53
|
+
CompanyData.from_et(cde, doc_frags)
|
54
|
+
for cde in et_element.iterfind("COMPANY-DATAS/COMPANY-DATA")
|
55
|
+
])
|
52
56
|
ecu_shared_datas = NamedItemList([
|
53
57
|
DiagLayer.from_et(dl_element, doc_frags)
|
54
58
|
for dl_element in et_element.iterfind("ECU-SHARED-DATAS/ECU-SHARED-DATA")
|
@@ -69,7 +73,9 @@ class DiagLayerContainer(IdentifiableElement):
|
|
69
73
|
DiagLayer.from_et(dl_element, doc_frags)
|
70
74
|
for dl_element in et_element.iterfind("ECU-VARIANTS/ECU-VARIANT")
|
71
75
|
])
|
72
|
-
sdgs =
|
76
|
+
sdgs = [
|
77
|
+
SpecialDataGroup.from_et(sdge, doc_frags) for sdge in et_element.iterfind("SDGS/SDG")
|
78
|
+
]
|
73
79
|
|
74
80
|
return DiagLayerContainer(
|
75
81
|
admin_data=admin_data,
|
@@ -124,17 +130,17 @@ class DiagLayerContainer(IdentifiableElement):
|
|
124
130
|
for ecu_variant in self.ecu_variants:
|
125
131
|
ecu_variant._resolve_odxlinks(odxlinks)
|
126
132
|
|
127
|
-
def _finalize_init(self, odxlinks: OdxLinkDatabase) -> None:
|
133
|
+
def _finalize_init(self, database: "Database", odxlinks: OdxLinkDatabase) -> None:
|
128
134
|
for ecu_shared_data in self.ecu_shared_datas:
|
129
|
-
ecu_shared_data._finalize_init(odxlinks)
|
135
|
+
ecu_shared_data._finalize_init(database, odxlinks)
|
130
136
|
for protocol in self.protocols:
|
131
|
-
protocol._finalize_init(odxlinks)
|
137
|
+
protocol._finalize_init(database, odxlinks)
|
132
138
|
for functional_group in self.functional_groups:
|
133
|
-
functional_group._finalize_init(odxlinks)
|
139
|
+
functional_group._finalize_init(database, odxlinks)
|
134
140
|
for base_variant in self.base_variants:
|
135
|
-
base_variant._finalize_init(odxlinks)
|
141
|
+
base_variant._finalize_init(database, odxlinks)
|
136
142
|
for ecu_variant in self.ecu_variants:
|
137
|
-
ecu_variant._finalize_init(odxlinks)
|
143
|
+
ecu_variant._finalize_init(database, odxlinks)
|
138
144
|
|
139
145
|
@property
|
140
146
|
def diag_layers(self) -> NamedItemList[DiagLayer]:
|
odxtools/diaglayerraw.py
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
# SPDX-License-Identifier: MIT
|
2
2
|
from copy import copy
|
3
3
|
from dataclasses import dataclass
|
4
|
-
from typing import
|
4
|
+
from typing import Any, Dict, List, Optional, Union, cast
|
5
5
|
from xml.etree import ElementTree
|
6
6
|
|
7
7
|
from .additionalaudience import AdditionalAudience
|
@@ -10,7 +10,6 @@ from .companydata import CompanyData
|
|
10
10
|
from .comparaminstance import ComparamInstance
|
11
11
|
from .comparamspec import ComparamSpec
|
12
12
|
from .comparamsubset import ComparamSubset
|
13
|
-
from .createsdgs import create_sdgs_from_et
|
14
13
|
from .diagcomm import DiagComm
|
15
14
|
from .diagdatadictionaryspec import DiagDataDictionarySpec
|
16
15
|
from .diaglayertype import DiagLayerType
|
@@ -26,13 +25,11 @@ from .protstack import ProtStack
|
|
26
25
|
from .request import Request
|
27
26
|
from .response import Response
|
28
27
|
from .singleecujob import SingleEcuJob
|
28
|
+
from .snrefcontext import SnRefContext
|
29
29
|
from .specialdatagroup import SpecialDataGroup
|
30
30
|
from .statechart import StateChart
|
31
31
|
from .utils import dataclass_fields_asdict
|
32
32
|
|
33
|
-
if TYPE_CHECKING:
|
34
|
-
from .diaglayer import DiagLayer
|
35
|
-
|
36
33
|
|
37
34
|
@dataclass
|
38
35
|
class DiagLayerRaw(IdentifiableElement):
|
@@ -153,7 +150,9 @@ class DiagLayerRaw(IdentifiableElement):
|
|
153
150
|
for el in et_element.iterfind("ADDITIONAL-AUDIENCES/ADDITIONAL-AUDIENCE")
|
154
151
|
]
|
155
152
|
|
156
|
-
sdgs =
|
153
|
+
sdgs = [
|
154
|
+
SpecialDataGroup.from_et(sdge, doc_frags) for sdge in et_element.iterfind("SDGS/SDG")
|
155
|
+
]
|
157
156
|
|
158
157
|
parent_refs = [
|
159
158
|
ParentRef.from_et(pr_el, doc_frags)
|
@@ -282,7 +281,7 @@ class DiagLayerRaw(IdentifiableElement):
|
|
282
281
|
for comparam in self.comparams:
|
283
282
|
comparam._resolve_odxlinks(odxlinks)
|
284
283
|
|
285
|
-
def _resolve_snrefs(self,
|
284
|
+
def _resolve_snrefs(self, context: SnRefContext) -> None:
|
286
285
|
self._prot_stack: Optional[ProtStack] = None
|
287
286
|
if self.prot_stack_snref is not None:
|
288
287
|
cp_spec = self.comparam_spec
|
@@ -292,36 +291,36 @@ class DiagLayerRaw(IdentifiableElement):
|
|
292
291
|
|
293
292
|
# do short-name reference resolution
|
294
293
|
if self.admin_data is not None:
|
295
|
-
self.admin_data._resolve_snrefs(
|
294
|
+
self.admin_data._resolve_snrefs(context)
|
296
295
|
if self.diag_data_dictionary_spec is not None:
|
297
|
-
self.diag_data_dictionary_spec._resolve_snrefs(
|
296
|
+
self.diag_data_dictionary_spec._resolve_snrefs(context)
|
298
297
|
|
299
298
|
for company_data in self.company_datas:
|
300
|
-
company_data._resolve_snrefs(
|
299
|
+
company_data._resolve_snrefs(context)
|
301
300
|
for functional_class in self.functional_classes:
|
302
|
-
functional_class._resolve_snrefs(
|
301
|
+
functional_class._resolve_snrefs(context)
|
303
302
|
for diag_comm in self.diag_comms:
|
304
303
|
if isinstance(diag_comm, OdxLinkRef):
|
305
304
|
continue
|
306
|
-
diag_comm._resolve_snrefs(
|
305
|
+
diag_comm._resolve_snrefs(context)
|
307
306
|
for request in self.requests:
|
308
|
-
request._resolve_snrefs(
|
307
|
+
request._resolve_snrefs(context)
|
309
308
|
for positive_response in self.positive_responses:
|
310
|
-
positive_response._resolve_snrefs(
|
309
|
+
positive_response._resolve_snrefs(context)
|
311
310
|
for negative_response in self.negative_responses:
|
312
|
-
negative_response._resolve_snrefs(
|
311
|
+
negative_response._resolve_snrefs(context)
|
313
312
|
for global_negative_response in self.global_negative_responses:
|
314
|
-
global_negative_response._resolve_snrefs(
|
313
|
+
global_negative_response._resolve_snrefs(context)
|
315
314
|
for state_chart in self.state_charts:
|
316
|
-
state_chart._resolve_snrefs(
|
315
|
+
state_chart._resolve_snrefs(context)
|
317
316
|
for additional_audience in self.additional_audiences:
|
318
|
-
additional_audience._resolve_snrefs(
|
317
|
+
additional_audience._resolve_snrefs(context)
|
319
318
|
for sdg in self.sdgs:
|
320
|
-
sdg._resolve_snrefs(
|
319
|
+
sdg._resolve_snrefs(context)
|
321
320
|
for parent_ref in self.parent_refs:
|
322
|
-
parent_ref._resolve_snrefs(
|
321
|
+
parent_ref._resolve_snrefs(context)
|
323
322
|
for comparam in self.comparams:
|
324
|
-
comparam._resolve_snrefs(
|
323
|
+
comparam._resolve_snrefs(context)
|
325
324
|
|
326
325
|
@property
|
327
326
|
def comparam_spec(self) -> Optional[Union[ComparamSpec, ComparamSubset]]:
|
@@ -1,26 +1,23 @@
|
|
1
1
|
# SPDX-License-Identifier: MIT
|
2
2
|
from dataclasses import dataclass
|
3
|
-
from typing import
|
3
|
+
from typing import Any, Dict, List, Optional
|
4
4
|
from xml.etree import ElementTree
|
5
5
|
|
6
|
-
from .createsdgs import create_sdgs_from_et
|
7
6
|
from .element import IdentifiableElement
|
8
7
|
from .exceptions import odxrequire
|
9
8
|
from .odxlink import OdxDocFragment, OdxLinkDatabase, OdxLinkId
|
10
9
|
from .odxtypes import odxstr_to_bool
|
10
|
+
from .snrefcontext import SnRefContext
|
11
11
|
from .specialdatagroup import SpecialDataGroup
|
12
12
|
from .utils import dataclass_fields_asdict
|
13
13
|
|
14
|
-
if TYPE_CHECKING:
|
15
|
-
from .diaglayer import DiagLayer
|
16
|
-
|
17
14
|
|
18
15
|
@dataclass
|
19
16
|
class DiagnosticTroubleCode(IdentifiableElement):
|
20
17
|
trouble_code: int
|
21
18
|
text: Optional[str]
|
22
19
|
display_trouble_code: Optional[str]
|
23
|
-
level:
|
20
|
+
level: Optional[int]
|
24
21
|
is_temporary_raw: Optional[bool]
|
25
22
|
sdgs: List[SpecialDataGroup]
|
26
23
|
|
@@ -43,7 +40,9 @@ class DiagnosticTroubleCode(IdentifiableElement):
|
|
43
40
|
level = None
|
44
41
|
|
45
42
|
is_temporary_raw = odxstr_to_bool(et_element.get("IS-TEMPORARY"))
|
46
|
-
sdgs =
|
43
|
+
sdgs = [
|
44
|
+
SpecialDataGroup.from_et(sdge, doc_frags) for sdge in et_element.iterfind("SDGS/SDG")
|
45
|
+
]
|
47
46
|
|
48
47
|
return DiagnosticTroubleCode(
|
49
48
|
trouble_code=int(odxrequire(et_element.findtext("TROUBLE-CODE"))),
|
@@ -69,6 +68,6 @@ class DiagnosticTroubleCode(IdentifiableElement):
|
|
69
68
|
for sdg in self.sdgs:
|
70
69
|
sdg._resolve_odxlinks(odxlinks)
|
71
70
|
|
72
|
-
def _resolve_snrefs(self,
|
71
|
+
def _resolve_snrefs(self, context: SnRefContext) -> None:
|
73
72
|
for sdg in self.sdgs:
|
74
|
-
sdg._resolve_snrefs(
|
73
|
+
sdg._resolve_snrefs(context)
|
odxtools/diagservice.py
CHANGED
@@ -1,12 +1,12 @@
|
|
1
1
|
# SPDX-License-Identifier: MIT
|
2
2
|
from dataclasses import dataclass
|
3
3
|
from enum import Enum
|
4
|
-
from typing import
|
4
|
+
from typing import Any, Dict, List, Optional, Union, cast
|
5
5
|
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
|
@@ -14,11 +14,9 @@ from .odxtypes import ParameterValue, odxstr_to_bool
|
|
14
14
|
from .parameters.parameter import Parameter
|
15
15
|
from .request import Request
|
16
16
|
from .response import Response
|
17
|
+
from .snrefcontext import SnRefContext
|
17
18
|
from .utils import dataclass_fields_asdict
|
18
19
|
|
19
|
-
if TYPE_CHECKING:
|
20
|
-
from .diaglayer import DiagLayer
|
21
|
-
|
22
20
|
|
23
21
|
class Addressing(Enum):
|
24
22
|
FUNCTIONAL = "FUNCTIONAL"
|
@@ -44,7 +42,7 @@ class DiagService(DiagComm):
|
|
44
42
|
pos_response_refs: List[OdxLinkRef]
|
45
43
|
neg_response_refs: List[OdxLinkRef]
|
46
44
|
|
47
|
-
# TODO: pos_response_suppressable: Optional[PosResponseSuppressable]
|
45
|
+
# TODO: pos_response_suppressable: Optional[PosResponseSuppressable] # (sic!)
|
48
46
|
|
49
47
|
is_cyclic_raw: Optional[bool]
|
50
48
|
is_multiple_raw: Optional[bool]
|
@@ -175,16 +173,21 @@ class DiagService(DiagComm):
|
|
175
173
|
self._negative_responses = NamedItemList[Response](
|
176
174
|
[odxlinks.resolve(x, Response) for x in self.neg_response_refs])
|
177
175
|
|
178
|
-
def _resolve_snrefs(self,
|
179
|
-
|
176
|
+
def _resolve_snrefs(self, context: SnRefContext) -> None:
|
177
|
+
context.diag_service = self
|
178
|
+
|
179
|
+
super()._resolve_snrefs(context)
|
180
180
|
|
181
181
|
for cpr in self.comparam_refs:
|
182
|
-
cpr._resolve_snrefs(
|
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
|
|
189
|
+
context.diag_service = None
|
190
|
+
|
188
191
|
def decode_message(self, raw_message: bytes) -> Message:
|
189
192
|
request_prefix = b''
|
190
193
|
candidate_coding_objects: List[Union[Request, Response]] = [
|
@@ -200,24 +203,36 @@ class DiagService(DiagComm):
|
|
200
203
|
if len(raw_message) >= len(prefix) and prefix == raw_message[:len(prefix)]:
|
201
204
|
coding_objects.append(candidate_coding_object)
|
202
205
|
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
|
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]
|
217
232
|
|
218
233
|
def encode_request(self, **kwargs: ParameterValue) -> bytes:
|
219
|
-
"""
|
220
|
-
|
234
|
+
"""Prepare an array of bytes ready to be send over the wire
|
235
|
+
for the request of this service.
|
221
236
|
"""
|
222
237
|
# make sure that all parameters which are required for
|
223
238
|
# encoding are specified (parameters which have a default are
|
odxtools/docrevision.py
CHANGED
@@ -1,17 +1,15 @@
|
|
1
1
|
# SPDX-License-Identifier: MIT
|
2
2
|
from dataclasses import dataclass
|
3
|
-
from typing import
|
3
|
+
from typing import Any, Dict, List, Optional
|
4
4
|
from xml.etree import ElementTree
|
5
5
|
|
6
6
|
from .companyrevisioninfo import CompanyRevisionInfo
|
7
7
|
from .exceptions import odxrequire
|
8
8
|
from .modification import Modification
|
9
9
|
from .odxlink import OdxDocFragment, OdxLinkDatabase, OdxLinkId, OdxLinkRef
|
10
|
+
from .snrefcontext import SnRefContext
|
10
11
|
from .teammember import TeamMember
|
11
12
|
|
12
|
-
if TYPE_CHECKING:
|
13
|
-
from .diaglayer import DiagLayer
|
14
|
-
|
15
13
|
|
16
14
|
@dataclass
|
17
15
|
class DocRevision:
|
@@ -75,9 +73,9 @@ class DocRevision:
|
|
75
73
|
for mod in self.modifications:
|
76
74
|
mod._resolve_odxlinks(odxlinks)
|
77
75
|
|
78
|
-
def _resolve_snrefs(self,
|
76
|
+
def _resolve_snrefs(self, context: SnRefContext) -> None:
|
79
77
|
for cri in self.company_revision_infos:
|
80
|
-
cri._resolve_snrefs(
|
78
|
+
cri._resolve_snrefs(context)
|
81
79
|
|
82
80
|
for mod in self.modifications:
|
83
|
-
mod._resolve_snrefs(
|
81
|
+
mod._resolve_snrefs(context)
|
odxtools/dopbase.py
CHANGED
@@ -1,21 +1,18 @@
|
|
1
1
|
# SPDX-License-Identifier: MIT
|
2
2
|
from dataclasses import dataclass
|
3
|
-
from typing import
|
3
|
+
from typing import Any, Dict, List, Optional
|
4
4
|
from xml.etree import ElementTree
|
5
5
|
|
6
6
|
from .admindata import AdminData
|
7
|
-
from .createsdgs import create_sdgs_from_et
|
8
7
|
from .decodestate import DecodeState
|
9
8
|
from .element import IdentifiableElement
|
10
9
|
from .encodestate import EncodeState
|
11
10
|
from .odxlink import OdxDocFragment, OdxLinkDatabase, OdxLinkId
|
12
11
|
from .odxtypes import ParameterValue
|
12
|
+
from .snrefcontext import SnRefContext
|
13
13
|
from .specialdatagroup import SpecialDataGroup
|
14
14
|
from .utils import dataclass_fields_asdict
|
15
15
|
|
16
|
-
if TYPE_CHECKING:
|
17
|
-
from .diaglayer import DiagLayer
|
18
|
-
|
19
16
|
|
20
17
|
@dataclass
|
21
18
|
class DopBase(IdentifiableElement):
|
@@ -37,7 +34,9 @@ class DopBase(IdentifiableElement):
|
|
37
34
|
if (admin_data_elem := et_element.find("ADMIN-DATA")) is not None:
|
38
35
|
admin_data = AdminData.from_et(admin_data_elem, doc_frags)
|
39
36
|
|
40
|
-
sdgs =
|
37
|
+
sdgs = [
|
38
|
+
SpecialDataGroup.from_et(sdge, doc_frags) for sdge in et_element.iterfind("SDGS/SDG")
|
39
|
+
]
|
41
40
|
|
42
41
|
return DopBase(admin_data=admin_data, sdgs=sdgs, **kwargs)
|
43
42
|
|
@@ -53,9 +52,9 @@ class DopBase(IdentifiableElement):
|
|
53
52
|
for sdg in self.sdgs:
|
54
53
|
sdg._resolve_odxlinks(odxlinks)
|
55
54
|
|
56
|
-
def _resolve_snrefs(self,
|
55
|
+
def _resolve_snrefs(self, context: SnRefContext) -> None:
|
57
56
|
for sdg in self.sdgs:
|
58
|
-
sdg._resolve_snrefs(
|
57
|
+
sdg._resolve_snrefs(context)
|
59
58
|
|
60
59
|
def get_static_bit_length(self) -> Optional[int]:
|
61
60
|
return None
|
odxtools/dtcdop.py
CHANGED
@@ -1,7 +1,6 @@
|
|
1
1
|
# SPDX-License-Identifier: MIT
|
2
|
-
# from dataclasses import dataclass, field
|
3
2
|
from dataclasses import dataclass
|
4
|
-
from typing import
|
3
|
+
from typing import Any, Dict, List, Optional, Union, cast
|
5
4
|
from xml.etree import ElementTree
|
6
5
|
|
7
6
|
from typing_extensions import override
|
@@ -19,11 +18,9 @@ from .nameditemlist import NamedItemList
|
|
19
18
|
from .odxlink import OdxDocFragment, OdxLinkDatabase, OdxLinkId, OdxLinkRef
|
20
19
|
from .odxtypes import ParameterValue, odxstr_to_bool
|
21
20
|
from .physicaltype import PhysicalType
|
21
|
+
from .snrefcontext import SnRefContext
|
22
22
|
from .utils import dataclass_fields_asdict
|
23
23
|
|
24
|
-
if TYPE_CHECKING:
|
25
|
-
from .diaglayer import DiagLayer
|
26
|
-
|
27
24
|
|
28
25
|
@dataclass
|
29
26
|
class DtcDop(DopBase):
|
@@ -133,24 +130,49 @@ class DtcDop(DopBase):
|
|
133
130
|
sdgs=[],
|
134
131
|
)
|
135
132
|
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
trouble_code = physical_value.trouble_code
|
141
|
-
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):
|
142
137
|
# assume that physical value is the trouble_code
|
143
|
-
|
144
|
-
elif isinstance(
|
138
|
+
return dtc_value
|
139
|
+
elif isinstance(dtc_value, str):
|
145
140
|
# assume that physical value is the short_name
|
146
|
-
dtcs = [dtc for dtc in self.dtcs if dtc.short_name ==
|
147
|
-
|
148
|
-
|
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
|
149
148
|
else:
|
150
|
-
|
151
|
-
|
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)
|
152
175
|
|
153
|
-
internal_trouble_code = self.compu_method.convert_physical_to_internal(trouble_code)
|
154
176
|
self.diag_coded_type.encode_into_pdu(internal_trouble_code, encode_state)
|
155
177
|
|
156
178
|
def _build_odxlinks(self) -> Dict[OdxLinkId, Any]:
|
@@ -177,9 +199,9 @@ class DtcDop(DopBase):
|
|
177
199
|
linked_dtc_dops = [odxlinks.resolve(x, DtcDop) for x in self.linked_dtc_dop_refs]
|
178
200
|
self._linked_dtc_dops = NamedItemList(linked_dtc_dops)
|
179
201
|
|
180
|
-
def _resolve_snrefs(self,
|
181
|
-
super()._resolve_snrefs(
|
202
|
+
def _resolve_snrefs(self, context: SnRefContext) -> None:
|
203
|
+
super()._resolve_snrefs(context)
|
182
204
|
|
183
205
|
for dtc_proxy in self.dtcs_raw:
|
184
206
|
if isinstance(dtc_proxy, DiagnosticTroubleCode):
|
185
|
-
dtc_proxy._resolve_snrefs(
|
207
|
+
dtc_proxy._resolve_snrefs(context)
|