odxtools 6.7.0__py3-none-any.whl → 9.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 +10 -13
- odxtools/basecomparam.py +3 -5
- odxtools/basicstructure.py +55 -240
- odxtools/cli/_parser_utils.py +1 -1
- odxtools/cli/_print_utils.py +168 -134
- odxtools/cli/browse.py +111 -92
- odxtools/cli/compare.py +90 -71
- odxtools/cli/list.py +24 -15
- odxtools/cli/snoop.py +28 -5
- odxtools/codec.py +211 -0
- odxtools/commrelation.py +122 -0
- 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 +7 -9
- odxtools/comparamspec.py +16 -54
- odxtools/comparamsubset.py +22 -62
- odxtools/complexcomparam.py +5 -7
- odxtools/compumethods/compucodecompumethod.py +63 -0
- odxtools/compumethods/compuconst.py +31 -0
- odxtools/compumethods/compudefaultvalue.py +27 -0
- odxtools/compumethods/compuinternaltophys.py +56 -0
- odxtools/compumethods/compuinversevalue.py +7 -0
- odxtools/compumethods/compumethod.py +93 -12
- odxtools/compumethods/compuphystointernal.py +56 -0
- odxtools/compumethods/compurationalcoeffs.py +20 -9
- odxtools/compumethods/compuscale.py +30 -35
- odxtools/compumethods/createanycompumethod.py +28 -161
- odxtools/compumethods/identicalcompumethod.py +31 -6
- odxtools/compumethods/linearcompumethod.py +69 -189
- odxtools/compumethods/linearsegment.py +190 -0
- odxtools/compumethods/ratfunccompumethod.py +106 -0
- odxtools/compumethods/ratfuncsegment.py +87 -0
- odxtools/compumethods/scalelinearcompumethod.py +132 -26
- odxtools/compumethods/scaleratfunccompumethod.py +113 -0
- odxtools/compumethods/tabintpcompumethod.py +119 -99
- odxtools/compumethods/texttablecompumethod.py +107 -43
- odxtools/createanydiagcodedtype.py +10 -67
- odxtools/database.py +167 -87
- odxtools/dataobjectproperty.py +15 -25
- odxtools/decodestate.py +9 -15
- odxtools/description.py +47 -0
- odxtools/determinenumberofitems.py +4 -5
- odxtools/diagcodedtype.py +36 -106
- odxtools/diagcomm.py +24 -12
- odxtools/diagdatadictionaryspec.py +33 -34
- odxtools/diaglayercontainer.py +46 -54
- odxtools/diaglayers/basevariant.py +128 -0
- odxtools/diaglayers/basevariantraw.py +123 -0
- odxtools/diaglayers/diaglayer.py +432 -0
- odxtools/{diaglayerraw.py → diaglayers/diaglayerraw.py} +105 -120
- odxtools/diaglayers/ecushareddata.py +96 -0
- odxtools/diaglayers/ecushareddataraw.py +87 -0
- odxtools/diaglayers/ecuvariant.py +124 -0
- odxtools/diaglayers/ecuvariantraw.py +129 -0
- odxtools/diaglayers/functionalgroup.py +110 -0
- odxtools/diaglayers/functionalgroupraw.py +106 -0
- odxtools/{diaglayer.py → diaglayers/hierarchyelement.py} +209 -448
- odxtools/diaglayers/hierarchyelementraw.py +58 -0
- odxtools/diaglayers/protocol.py +64 -0
- odxtools/diaglayers/protocolraw.py +91 -0
- odxtools/diagnostictroublecode.py +8 -9
- odxtools/diagservice.py +56 -43
- odxtools/diagvariable.py +113 -0
- odxtools/docrevision.py +5 -7
- odxtools/dopbase.py +15 -17
- odxtools/dtcdop.py +168 -50
- odxtools/dynamicendmarkerfield.py +134 -0
- odxtools/dynamiclengthfield.py +41 -37
- odxtools/dyndefinedspec.py +177 -0
- odxtools/dynenddopref.py +38 -0
- odxtools/ecuvariantmatcher.py +6 -7
- odxtools/element.py +13 -15
- odxtools/encodestate.py +199 -22
- odxtools/endofpdufield.py +31 -18
- odxtools/environmentdata.py +8 -1
- odxtools/environmentdatadescription.py +198 -38
- odxtools/exceptions.py +11 -2
- odxtools/field.py +10 -10
- odxtools/functionalclass.py +3 -5
- odxtools/inputparam.py +3 -12
- odxtools/leadinglengthinfotype.py +37 -18
- odxtools/library.py +66 -0
- odxtools/loadfile.py +64 -0
- odxtools/matchingparameter.py +3 -3
- odxtools/message.py +0 -7
- odxtools/minmaxlengthtype.py +61 -33
- odxtools/modification.py +3 -5
- odxtools/multiplexer.py +128 -73
- odxtools/multiplexercase.py +13 -14
- odxtools/multiplexerdefaultcase.py +15 -12
- odxtools/multiplexerswitchkey.py +4 -5
- odxtools/nameditemlist.py +29 -5
- odxtools/negoutputparam.py +3 -5
- odxtools/odxcategory.py +83 -0
- odxtools/odxlink.py +60 -51
- odxtools/odxtypes.py +37 -5
- odxtools/outputparam.py +4 -15
- odxtools/parameterinfo.py +218 -67
- odxtools/parameters/codedconstparameter.py +16 -24
- odxtools/parameters/dynamicparameter.py +5 -4
- odxtools/parameters/lengthkeyparameter.py +60 -26
- odxtools/parameters/matchingrequestparameter.py +23 -11
- odxtools/parameters/nrcconstparameter.py +45 -46
- odxtools/parameters/parameter.py +54 -56
- odxtools/parameters/parameterwithdop.py +15 -25
- odxtools/parameters/physicalconstantparameter.py +15 -18
- odxtools/parameters/reservedparameter.py +6 -2
- odxtools/parameters/systemparameter.py +55 -11
- odxtools/parameters/tableentryparameter.py +3 -2
- odxtools/parameters/tablekeyparameter.py +103 -49
- odxtools/parameters/tablestructparameter.py +47 -48
- odxtools/parameters/valueparameter.py +16 -20
- odxtools/paramlengthinfotype.py +52 -32
- odxtools/parentref.py +16 -2
- odxtools/physicaldimension.py +3 -8
- odxtools/progcode.py +26 -11
- odxtools/protstack.py +3 -5
- odxtools/py.typed +0 -0
- odxtools/relateddoc.py +7 -9
- odxtools/request.py +120 -10
- odxtools/response.py +123 -23
- odxtools/scaleconstr.py +3 -3
- odxtools/servicebinner.py +1 -1
- odxtools/singleecujob.py +12 -10
- odxtools/snrefcontext.py +29 -0
- odxtools/specialdata.py +3 -5
- odxtools/specialdatagroup.py +7 -9
- odxtools/specialdatagroupcaption.py +3 -6
- odxtools/standardlengthtype.py +80 -14
- odxtools/state.py +3 -5
- odxtools/statechart.py +13 -19
- odxtools/statetransition.py +7 -17
- odxtools/staticfield.py +31 -25
- odxtools/subcomponent.py +288 -0
- odxtools/swvariable.py +21 -0
- odxtools/table.py +7 -8
- odxtools/tablerow.py +19 -11
- odxtools/teammember.py +3 -5
- odxtools/templates/comparam-spec.odx-c.xml.jinja2 +4 -24
- odxtools/templates/comparam-subset.odx-cs.xml.jinja2 +5 -26
- odxtools/templates/diag_layer_container.odx-d.xml.jinja2 +15 -31
- odxtools/templates/{index.xml.xml.jinja2 → index.xml.jinja2} +1 -1
- odxtools/templates/macros/printAudience.xml.jinja2 +1 -1
- odxtools/templates/macros/printBaseVariant.xml.jinja2 +53 -0
- odxtools/templates/macros/printCompanyData.xml.jinja2 +4 -7
- odxtools/templates/macros/printComparam.xml.jinja2 +6 -4
- odxtools/templates/macros/printComparamRef.xml.jinja2 +5 -12
- odxtools/templates/macros/printCompuMethod.xml.jinja2 +147 -0
- odxtools/templates/macros/printDOP.xml.jinja2 +27 -133
- odxtools/templates/macros/printDescription.xml.jinja2 +18 -0
- odxtools/templates/macros/printDiagComm.xml.jinja2 +1 -1
- odxtools/templates/macros/printDiagLayer.xml.jinja2 +222 -0
- odxtools/templates/macros/printDiagVariable.xml.jinja2 +66 -0
- odxtools/templates/macros/printDynDefinedSpec.xml.jinja2 +48 -0
- odxtools/templates/macros/printDynamicEndmarkerField.xml.jinja2 +16 -0
- odxtools/templates/macros/printDynamicLengthField.xml.jinja2 +1 -1
- odxtools/templates/macros/printEcuSharedData.xml.jinja2 +30 -0
- odxtools/templates/macros/printEcuVariant.xml.jinja2 +53 -0
- odxtools/templates/macros/printEcuVariantPattern.xml.jinja2 +1 -1
- odxtools/templates/macros/printElementId.xml.jinja2 +8 -3
- odxtools/templates/macros/printEndOfPdu.xml.jinja2 +1 -1
- odxtools/templates/macros/printEnvDataDesc.xml.jinja2 +1 -1
- odxtools/templates/macros/printFunctionalClass.xml.jinja2 +1 -1
- odxtools/templates/macros/printFunctionalGroup.xml.jinja2 +40 -0
- odxtools/templates/macros/printHierarchyElement.xml.jinja2 +24 -0
- odxtools/templates/macros/printLibrary.xml.jinja2 +21 -0
- odxtools/templates/macros/printMux.xml.jinja2 +4 -3
- odxtools/templates/macros/printOdxCategory.xml.jinja2 +28 -0
- odxtools/templates/macros/printParam.xml.jinja2 +11 -12
- odxtools/templates/macros/printProtStack.xml.jinja2 +1 -1
- odxtools/templates/macros/printProtocol.xml.jinja2 +30 -0
- odxtools/templates/macros/printRequest.xml.jinja2 +1 -1
- odxtools/templates/macros/printResponse.xml.jinja2 +1 -1
- odxtools/templates/macros/printService.xml.jinja2 +3 -2
- odxtools/templates/macros/printSingleEcuJob.xml.jinja2 +5 -26
- odxtools/templates/macros/printSpecialData.xml.jinja2 +1 -1
- odxtools/templates/macros/printState.xml.jinja2 +1 -1
- odxtools/templates/macros/printStateChart.xml.jinja2 +1 -1
- odxtools/templates/macros/printStateTransition.xml.jinja2 +1 -1
- odxtools/templates/macros/printStaticField.xml.jinja2 +1 -1
- odxtools/templates/macros/printStructure.xml.jinja2 +1 -1
- odxtools/templates/macros/printSubComponent.xml.jinja2 +104 -0
- odxtools/templates/macros/printTable.xml.jinja2 +4 -5
- odxtools/templates/macros/printUnitSpec.xml.jinja2 +3 -5
- odxtools/uds.py +2 -10
- odxtools/unit.py +4 -8
- odxtools/unitgroup.py +3 -5
- odxtools/unitspec.py +17 -17
- odxtools/utils.py +38 -20
- odxtools/variablegroup.py +32 -0
- odxtools/version.py +2 -2
- odxtools/{write_pdx_file.py → writepdxfile.py} +20 -10
- odxtools/xdoc.py +3 -5
- {odxtools-6.7.0.dist-info → odxtools-9.3.0.dist-info}/METADATA +20 -21
- odxtools-9.3.0.dist-info/RECORD +228 -0
- {odxtools-6.7.0.dist-info → odxtools-9.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/templates/macros/printVariant.xml.jinja2 +0 -216
- odxtools-6.7.0.dist-info/RECORD +0 -182
- /odxtools/{diaglayertype.py → diaglayers/diaglayertype.py} +0 -0
- {odxtools-6.7.0.dist-info → odxtools-9.3.0.dist-info}/LICENSE +0 -0
- {odxtools-6.7.0.dist-info → odxtools-9.3.0.dist-info}/entry_points.txt +0 -0
- {odxtools-6.7.0.dist-info → odxtools-9.3.0.dist-info}/top_level.txt +0 -0
odxtools/dtcdop.py
CHANGED
@@ -1,9 +1,10 @@
|
|
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
|
|
6
|
+
from typing_extensions import override
|
7
|
+
|
7
8
|
from .compumethods.compumethod import CompuMethod
|
8
9
|
from .compumethods.createanycompumethod import create_any_compu_method_from_et
|
9
10
|
from .createanydiagcodedtype import create_any_diag_coded_type_from_et
|
@@ -12,15 +13,59 @@ from .diagcodedtype import DiagCodedType
|
|
12
13
|
from .diagnostictroublecode import DiagnosticTroubleCode
|
13
14
|
from .dopbase import DopBase
|
14
15
|
from .encodestate import EncodeState
|
15
|
-
from .exceptions import DecodeError, EncodeError, odxassert, odxrequire
|
16
|
+
from .exceptions import DecodeError, EncodeError, odxassert, odxraise, odxrequire
|
16
17
|
from .nameditemlist import NamedItemList
|
17
|
-
from .odxlink import OdxDocFragment, OdxLinkDatabase, OdxLinkId, OdxLinkRef
|
18
|
+
from .odxlink import OdxDocFragment, OdxLinkDatabase, OdxLinkId, OdxLinkRef, resolve_snref
|
18
19
|
from .odxtypes import ParameterValue, odxstr_to_bool
|
19
20
|
from .physicaltype import PhysicalType
|
21
|
+
from .snrefcontext import SnRefContext
|
20
22
|
from .utils import dataclass_fields_asdict
|
21
23
|
|
22
|
-
|
23
|
-
|
24
|
+
|
25
|
+
@dataclass
|
26
|
+
class LinkedDtcDop:
|
27
|
+
not_inherited_dtc_snrefs: List[str]
|
28
|
+
dtc_dop_ref: OdxLinkRef
|
29
|
+
|
30
|
+
@property
|
31
|
+
def dtc_dop(self) -> "DtcDop":
|
32
|
+
return self._dtc_dop
|
33
|
+
|
34
|
+
@property
|
35
|
+
def short_name(self) -> str:
|
36
|
+
return self._dtc_dop.short_name
|
37
|
+
|
38
|
+
@property
|
39
|
+
def not_inherited_dtcs(self) -> NamedItemList[DiagnosticTroubleCode]:
|
40
|
+
return self._not_inherited_dtcs
|
41
|
+
|
42
|
+
@staticmethod
|
43
|
+
def from_et(et_element: ElementTree.Element, doc_frags: List[OdxDocFragment]) -> "LinkedDtcDop":
|
44
|
+
not_inherited_dtc_snrefs = [
|
45
|
+
odxrequire(el.get("SHORT-NAME"))
|
46
|
+
for el in et_element.iterfind("NOT-INHERITED-DTC-SNREFS/"
|
47
|
+
"NOT-INHERITED-DTC-SNREF")
|
48
|
+
]
|
49
|
+
|
50
|
+
dtc_dop_ref = odxrequire(OdxLinkRef.from_et(et_element.find("DTC-DOP-REF"), doc_frags))
|
51
|
+
|
52
|
+
return LinkedDtcDop(
|
53
|
+
not_inherited_dtc_snrefs=not_inherited_dtc_snrefs, dtc_dop_ref=dtc_dop_ref)
|
54
|
+
|
55
|
+
def _build_odxlinks(self) -> Dict[OdxLinkId, Any]:
|
56
|
+
return {}
|
57
|
+
|
58
|
+
def _resolve_odxlinks(self, odxlinks: OdxLinkDatabase) -> None:
|
59
|
+
self._dtc_dop = odxlinks.resolve(self.dtc_dop_ref, DtcDop)
|
60
|
+
|
61
|
+
def _resolve_snrefs(self, context: SnRefContext) -> None:
|
62
|
+
dtc_dop = self._dtc_dop
|
63
|
+
not_inherited_dtcs = [
|
64
|
+
resolve_snref(ni_snref, dtc_dop.dtcs, DiagnosticTroubleCode)
|
65
|
+
for ni_snref in self.not_inherited_dtc_snrefs
|
66
|
+
]
|
67
|
+
|
68
|
+
self._not_inherited_dtcs = NamedItemList(not_inherited_dtcs)
|
24
69
|
|
25
70
|
|
26
71
|
@dataclass
|
@@ -31,9 +76,12 @@ class DtcDop(DopBase):
|
|
31
76
|
physical_type: PhysicalType
|
32
77
|
compu_method: CompuMethod
|
33
78
|
dtcs_raw: List[Union[DiagnosticTroubleCode, OdxLinkRef]]
|
34
|
-
|
79
|
+
linked_dtc_dops_raw: List[LinkedDtcDop]
|
35
80
|
is_visible_raw: Optional[bool]
|
36
81
|
|
82
|
+
def __post_init__(self) -> None:
|
83
|
+
self._init_finished = False
|
84
|
+
|
37
85
|
@staticmethod
|
38
86
|
def from_et(et_element: ElementTree.Element, doc_frags: List[OdxDocFragment]) -> "DtcDop":
|
39
87
|
"""Reads a DTC-DOP."""
|
@@ -57,12 +105,10 @@ class DtcDop(DopBase):
|
|
57
105
|
elif dtc_proxy_elem.tag == "DTC-REF":
|
58
106
|
dtcs_raw.append(OdxLinkRef.from_et(dtc_proxy_elem, doc_frags))
|
59
107
|
|
60
|
-
|
61
|
-
|
62
|
-
OdxLinkRef.from_et(dtc_ref_elem, doc_frags)
|
108
|
+
linked_dtc_dops_raw = [
|
109
|
+
LinkedDtcDop.from_et(dtc_ref_elem, doc_frags)
|
63
110
|
for dtc_ref_elem in et_element.iterfind("LINKED-DTC-DOPS/"
|
64
|
-
"LINKED-DTC-DOP
|
65
|
-
"DTC-DOP-REF")
|
111
|
+
"LINKED-DTC-DOP")
|
66
112
|
]
|
67
113
|
is_visible_raw = odxstr_to_bool(et_element.get("IS-VISIBLE"))
|
68
114
|
|
@@ -71,7 +117,7 @@ class DtcDop(DopBase):
|
|
71
117
|
physical_type=physical_type,
|
72
118
|
compu_method=compu_method,
|
73
119
|
dtcs_raw=dtcs_raw,
|
74
|
-
|
120
|
+
linked_dtc_dops_raw=linked_dtc_dops_raw,
|
75
121
|
is_visible_raw=is_visible_raw,
|
76
122
|
**kwargs)
|
77
123
|
|
@@ -80,24 +126,27 @@ class DtcDop(DopBase):
|
|
80
126
|
return self._dtcs
|
81
127
|
|
82
128
|
@property
|
83
|
-
def
|
84
|
-
return self.
|
129
|
+
def linked_dtc_dops(self) -> NamedItemList[LinkedDtcDop]:
|
130
|
+
return self._linked_dtc_dops
|
85
131
|
|
86
132
|
@property
|
87
|
-
def
|
88
|
-
return self.
|
133
|
+
def is_visible(self) -> bool:
|
134
|
+
return self.is_visible_raw is True
|
89
135
|
|
136
|
+
@override
|
90
137
|
def decode_from_pdu(self, decode_state: DecodeState) -> ParameterValue:
|
91
138
|
|
92
139
|
int_trouble_code = self.diag_coded_type.decode_from_pdu(decode_state)
|
93
140
|
|
94
|
-
if self.compu_method.is_valid_internal_value(int_trouble_code):
|
95
|
-
trouble_code = self.compu_method.convert_internal_to_physical(int_trouble_code)
|
96
|
-
else:
|
141
|
+
if not self.compu_method.is_valid_internal_value(int_trouble_code):
|
97
142
|
# TODO: How to prevent this?
|
98
|
-
|
143
|
+
odxraise(
|
99
144
|
f"DTC-DOP {self.short_name} could not convert the coded value "
|
100
|
-
f" {repr(int_trouble_code)} to physical type {self.physical_type.base_data_type}."
|
145
|
+
f" {repr(int_trouble_code)} to physical type {self.physical_type.base_data_type}.",
|
146
|
+
DecodeError)
|
147
|
+
return
|
148
|
+
|
149
|
+
trouble_code = self.compu_method.convert_internal_to_physical(int_trouble_code)
|
101
150
|
|
102
151
|
assert isinstance(trouble_code, int)
|
103
152
|
|
@@ -110,12 +159,15 @@ class DtcDop(DopBase):
|
|
110
159
|
return dtcs[0]
|
111
160
|
|
112
161
|
# the DTC was not specified. This probably means that the
|
113
|
-
# diagnostic description file is incomplete.
|
114
|
-
|
115
|
-
|
116
|
-
|
162
|
+
# diagnostic description file is incomplete.
|
163
|
+
odxraise(
|
164
|
+
f"Encountered DTC 0x{trouble_code:06x} which has not been defined "
|
165
|
+
f"by the database", DecodeError)
|
166
|
+
|
167
|
+
return DiagnosticTroubleCode(
|
117
168
|
trouble_code=trouble_code,
|
118
169
|
odx_id=cast(OdxLinkId, None),
|
170
|
+
oid=None,
|
119
171
|
short_name=f'DTC_{trouble_code:06x}',
|
120
172
|
long_name=None,
|
121
173
|
description=None,
|
@@ -126,43 +178,70 @@ class DtcDop(DopBase):
|
|
126
178
|
sdgs=[],
|
127
179
|
)
|
128
180
|
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
encode_state: EncodeState,
|
134
|
-
bit_position: int = 0) -> bytes:
|
135
|
-
if isinstance(physical_value, DiagnosticTroubleCode):
|
136
|
-
trouble_code = physical_value.trouble_code
|
137
|
-
elif isinstance(physical_value, int):
|
181
|
+
def convert_to_numerical_trouble_code(self, dtc_value: ParameterValue) -> int:
|
182
|
+
if isinstance(dtc_value, DiagnosticTroubleCode):
|
183
|
+
return dtc_value.trouble_code
|
184
|
+
elif isinstance(dtc_value, int):
|
138
185
|
# assume that physical value is the trouble_code
|
139
|
-
|
140
|
-
elif isinstance(
|
186
|
+
return dtc_value
|
187
|
+
elif isinstance(dtc_value, str):
|
141
188
|
# assume that physical value is the short_name
|
142
|
-
dtcs = [dtc for dtc in self.dtcs if dtc.short_name ==
|
143
|
-
|
144
|
-
|
189
|
+
dtcs = [dtc for dtc in self.dtcs if dtc.short_name == dtc_value]
|
190
|
+
if len(dtcs) != 1:
|
191
|
+
odxraise(f"No DTC named {dtc_value} found for DTC-DOP "
|
192
|
+
f"{self.short_name}.", EncodeError)
|
193
|
+
return cast(int, None)
|
194
|
+
|
195
|
+
return dtcs[0].trouble_code
|
145
196
|
else:
|
146
|
-
|
147
|
-
|
197
|
+
odxraise(
|
198
|
+
f"The DTC-DOP {self.short_name} expected a"
|
199
|
+
f" diagnostic trouble code but got {type(dtc_value).__name__}", EncodeError)
|
200
|
+
return cast(int, None)
|
201
|
+
|
202
|
+
@override
|
203
|
+
def encode_into_pdu(self, physical_value: Optional[ParameterValue],
|
204
|
+
encode_state: EncodeState) -> None:
|
205
|
+
if physical_value is None:
|
206
|
+
odxraise(f"No DTC specified", EncodeError)
|
207
|
+
return
|
208
|
+
|
209
|
+
trouble_code = self.convert_to_numerical_trouble_code(physical_value)
|
148
210
|
|
149
|
-
internal_trouble_code = self.compu_method.convert_physical_to_internal(trouble_code)
|
211
|
+
internal_trouble_code = int(self.compu_method.convert_physical_to_internal(trouble_code))
|
150
212
|
|
151
|
-
|
152
|
-
|
213
|
+
found = False
|
214
|
+
for dtc in self.dtcs:
|
215
|
+
if internal_trouble_code == dtc.trouble_code:
|
216
|
+
found = True
|
217
|
+
break
|
218
|
+
|
219
|
+
if not found:
|
220
|
+
odxraise(
|
221
|
+
f"Unknown diagnostic trouble code {physical_value!r} "
|
222
|
+
f"(0x{internal_trouble_code: 06x}) specified", EncodeError)
|
223
|
+
|
224
|
+
self.diag_coded_type.encode_into_pdu(internal_trouble_code, encode_state)
|
153
225
|
|
154
226
|
def _build_odxlinks(self) -> Dict[OdxLinkId, Any]:
|
155
227
|
odxlinks = super()._build_odxlinks()
|
156
228
|
|
229
|
+
odxlinks.update(self.compu_method._build_odxlinks())
|
230
|
+
|
157
231
|
for dtc_proxy in self.dtcs_raw:
|
158
232
|
if isinstance(dtc_proxy, DiagnosticTroubleCode):
|
159
233
|
odxlinks.update(dtc_proxy._build_odxlinks())
|
160
234
|
|
235
|
+
for linked_dtc_dop in self.linked_dtc_dops_raw:
|
236
|
+
odxlinks.update(linked_dtc_dop._build_odxlinks())
|
237
|
+
|
161
238
|
return odxlinks
|
162
239
|
|
163
240
|
def _resolve_odxlinks(self, odxlinks: OdxLinkDatabase) -> None:
|
164
241
|
super()._resolve_odxlinks(odxlinks)
|
165
242
|
|
243
|
+
self.compu_method._resolve_odxlinks(odxlinks)
|
244
|
+
|
166
245
|
self._dtcs = NamedItemList[DiagnosticTroubleCode]()
|
167
246
|
for dtc_proxy in self.dtcs_raw:
|
168
247
|
if isinstance(dtc_proxy, DiagnosticTroubleCode):
|
@@ -172,12 +251,51 @@ class DtcDop(DopBase):
|
|
172
251
|
dtc = odxlinks.resolve(dtc_proxy, DiagnosticTroubleCode)
|
173
252
|
self._dtcs.append(dtc)
|
174
253
|
|
175
|
-
|
176
|
-
|
254
|
+
for linked_dtc_dop in self.linked_dtc_dops_raw:
|
255
|
+
linked_dtc_dop._resolve_odxlinks(odxlinks)
|
256
|
+
|
257
|
+
def _resolve_snrefs(self, context: SnRefContext) -> None:
|
258
|
+
# hack to avoid initializing the DtcDop object multiple
|
259
|
+
# times. This is required, because the linked DTC DOP feature
|
260
|
+
# requires the "parent" DTC DOPs to be fully initialized but
|
261
|
+
# the standard does not define a formal ordering of DTC DOPs.
|
262
|
+
if self._init_finished:
|
263
|
+
return
|
177
264
|
|
178
|
-
|
179
|
-
|
265
|
+
self._init_finished = True
|
266
|
+
|
267
|
+
super()._resolve_snrefs(context)
|
268
|
+
|
269
|
+
self.compu_method._resolve_snrefs(context)
|
180
270
|
|
181
271
|
for dtc_proxy in self.dtcs_raw:
|
182
272
|
if isinstance(dtc_proxy, DiagnosticTroubleCode):
|
183
|
-
dtc_proxy._resolve_snrefs(
|
273
|
+
dtc_proxy._resolve_snrefs(context)
|
274
|
+
|
275
|
+
for linked_dtc_dop in self.linked_dtc_dops_raw:
|
276
|
+
linked_dtc_dop._resolve_snrefs(context)
|
277
|
+
|
278
|
+
# add the inherited DTCs from linked DTC DOPs. Note that this
|
279
|
+
# requires that there are no cycles in the "link-hierarchy"
|
280
|
+
dtc_short_names = {dtc.short_name for dtc in self._dtcs}
|
281
|
+
for linked_dtc_dop in self.linked_dtc_dops_raw:
|
282
|
+
linked_dtc_dop.dtc_dop._resolve_snrefs(context)
|
283
|
+
|
284
|
+
for dtc in linked_dtc_dop.dtc_dop.dtcs:
|
285
|
+
if dtc.short_name in dtc_short_names:
|
286
|
+
# we already have a DTC with that name. Since we
|
287
|
+
# are not supposed to overwrite the local DTCs, we
|
288
|
+
# skip processing this one. TODO: Are inheritance
|
289
|
+
# conflicts for DTCs allowed?
|
290
|
+
continue
|
291
|
+
|
292
|
+
if dtc.short_name in linked_dtc_dop.not_inherited_dtc_snrefs:
|
293
|
+
# DTC is explicitly not inherited
|
294
|
+
continue
|
295
|
+
|
296
|
+
self._dtcs.append(dtc)
|
297
|
+
dtc_short_names.add(dtc.short_name)
|
298
|
+
|
299
|
+
# at this place, the linked DTC DOPs exhibit .short_name, so
|
300
|
+
# we can create a NamedItemList...
|
301
|
+
self._linked_dtc_dops = NamedItemList(self.linked_dtc_dops_raw)
|
@@ -0,0 +1,134 @@
|
|
1
|
+
# SPDX-License-Identifier: MIT
|
2
|
+
from dataclasses import dataclass
|
3
|
+
from typing import Any, Dict, List, Sequence
|
4
|
+
from xml.etree import ElementTree
|
5
|
+
|
6
|
+
from typing_extensions import override
|
7
|
+
|
8
|
+
from .dataobjectproperty import DataObjectProperty
|
9
|
+
from .decodestate import DecodeState
|
10
|
+
from .dynenddopref import DynEndDopRef
|
11
|
+
from .encodestate import EncodeState
|
12
|
+
from .exceptions import DecodeError, EncodeError, odxassert, odxraise, odxrequire
|
13
|
+
from .field import Field
|
14
|
+
from .odxlink import OdxDocFragment, OdxLinkDatabase, OdxLinkId
|
15
|
+
from .odxtypes import AtomicOdxType, ParameterValue
|
16
|
+
from .snrefcontext import SnRefContext
|
17
|
+
from .utils import dataclass_fields_asdict
|
18
|
+
|
19
|
+
|
20
|
+
@dataclass
|
21
|
+
class DynamicEndmarkerField(Field):
|
22
|
+
"""Array of a structure with variable length determined by a termination sequence"""
|
23
|
+
|
24
|
+
dyn_end_dop_ref: DynEndDopRef
|
25
|
+
|
26
|
+
@staticmethod
|
27
|
+
def from_et(et_element: ElementTree.Element,
|
28
|
+
doc_frags: List[OdxDocFragment]) -> "DynamicEndmarkerField":
|
29
|
+
kwargs = dataclass_fields_asdict(Field.from_et(et_element, doc_frags))
|
30
|
+
|
31
|
+
# ODX 2.0 uses DATA-OBJECT-PROP-REF
|
32
|
+
# ODX 2.2 uses DYN-END-DOP-REF
|
33
|
+
dop_ref = et_element.find("DYN-END-DOP-REF") or et_element.find("DATA-OBJECT-PROP-REF")
|
34
|
+
dyn_end_dop_ref = DynEndDopRef.from_et(odxrequire(dop_ref), doc_frags)
|
35
|
+
|
36
|
+
return DynamicEndmarkerField(dyn_end_dop_ref=dyn_end_dop_ref, **kwargs)
|
37
|
+
|
38
|
+
def _build_odxlinks(self) -> Dict[OdxLinkId, Any]:
|
39
|
+
odxlinks = super()._build_odxlinks()
|
40
|
+
return odxlinks
|
41
|
+
|
42
|
+
def _resolve_odxlinks(self, odxlinks: OdxLinkDatabase) -> None:
|
43
|
+
super()._resolve_odxlinks(odxlinks)
|
44
|
+
|
45
|
+
self._dyn_end_dop = odxlinks.resolve(self.dyn_end_dop_ref, DataObjectProperty)
|
46
|
+
|
47
|
+
tv_string = self.dyn_end_dop_ref.termination_value_raw
|
48
|
+
tv_physical = self._dyn_end_dop.diag_coded_type.base_data_type.from_string(tv_string)
|
49
|
+
|
50
|
+
self._termination_value = tv_physical
|
51
|
+
|
52
|
+
def _resolve_snrefs(self, context: SnRefContext) -> None:
|
53
|
+
super()._resolve_snrefs(context)
|
54
|
+
|
55
|
+
@property
|
56
|
+
def dyn_end_dop(self) -> DataObjectProperty:
|
57
|
+
return self._dyn_end_dop
|
58
|
+
|
59
|
+
@property
|
60
|
+
def termination_value(self) -> AtomicOdxType:
|
61
|
+
return self._termination_value
|
62
|
+
|
63
|
+
@override
|
64
|
+
def encode_into_pdu(self, physical_value: ParameterValue, encode_state: EncodeState) -> None:
|
65
|
+
|
66
|
+
odxassert(encode_state.cursor_bit_position == 0,
|
67
|
+
"No bit position can be specified for dynamic endmarker fields!")
|
68
|
+
if not isinstance(physical_value, Sequence):
|
69
|
+
odxraise(
|
70
|
+
f"Expected a sequence of values for dynamic endmarker field {self.short_name}, "
|
71
|
+
f"got {type(physical_value).__name__}", EncodeError)
|
72
|
+
return
|
73
|
+
|
74
|
+
orig_is_end_of_pdu = encode_state.is_end_of_pdu
|
75
|
+
encode_state.is_end_of_pdu = False
|
76
|
+
for i, item in enumerate(physical_value):
|
77
|
+
if i == len(physical_value) - 1:
|
78
|
+
encode_state.is_end_of_pdu = orig_is_end_of_pdu
|
79
|
+
|
80
|
+
self.structure.encode_into_pdu(item, encode_state)
|
81
|
+
encode_state.is_end_of_pdu = orig_is_end_of_pdu
|
82
|
+
|
83
|
+
if not encode_state.is_end_of_pdu:
|
84
|
+
# only add an endmarker if we are not at the end of the
|
85
|
+
# PDU. note that since section 7.3.6.10.5 of the MCD-2
|
86
|
+
# specification states that the data used by the endmarker
|
87
|
+
# ought to be considered to be not consumed (why?!), we
|
88
|
+
# need to keep the cursor where it is before adding the
|
89
|
+
# endmarker. (we still consider its bits to be used
|
90
|
+
# "used", in order to produce a warning if it is attempted
|
91
|
+
# to be overridden.)
|
92
|
+
tmp_cursor = encode_state.cursor_byte_position
|
93
|
+
self.dyn_end_dop.encode_into_pdu(self.termination_value, encode_state)
|
94
|
+
encode_state.cursor_byte_position = tmp_cursor
|
95
|
+
|
96
|
+
@override
|
97
|
+
def decode_from_pdu(self, decode_state: DecodeState) -> ParameterValue:
|
98
|
+
|
99
|
+
odxassert(decode_state.cursor_bit_position == 0,
|
100
|
+
"No bit position can be specified for dynamic endmarker fields!")
|
101
|
+
|
102
|
+
orig_origin = decode_state.origin_byte_position
|
103
|
+
decode_state.origin_byte_position = decode_state.cursor_byte_position
|
104
|
+
|
105
|
+
result: List[ParameterValue] = []
|
106
|
+
while True:
|
107
|
+
# check if we're at the end of the PDU
|
108
|
+
if decode_state.cursor_byte_position == len(decode_state.coded_message):
|
109
|
+
break
|
110
|
+
|
111
|
+
# check if the cursor currently points to a termination
|
112
|
+
# value
|
113
|
+
tmp_cursor = decode_state.cursor_byte_position
|
114
|
+
try:
|
115
|
+
tv_candidate = self.dyn_end_dop.decode_from_pdu(decode_state)
|
116
|
+
if tv_candidate == self.termination_value:
|
117
|
+
# note that section 7.3.6.10.5 of the MCD-2
|
118
|
+
# specification states that the bytes occupied by
|
119
|
+
# the endmarker ought to be considered to be not
|
120
|
+
# consumed (why?!), i.e., we need to keep the
|
121
|
+
# cursor where it is before adding the
|
122
|
+
# endmarker. (we still consider its to be used
|
123
|
+
# "used", though.)
|
124
|
+
decode_state.cursor_byte_position = tmp_cursor
|
125
|
+
break
|
126
|
+
except DecodeError:
|
127
|
+
pass
|
128
|
+
decode_state.cursor_byte_position = tmp_cursor
|
129
|
+
|
130
|
+
result.append(self.structure.decode_from_pdu(decode_state))
|
131
|
+
|
132
|
+
decode_state.origin_byte_position = orig_origin
|
133
|
+
|
134
|
+
return result
|
odxtools/dynamiclengthfield.py
CHANGED
@@ -1,8 +1,10 @@
|
|
1
1
|
# SPDX-License-Identifier: MIT
|
2
2
|
from dataclasses import dataclass
|
3
|
-
from typing import
|
3
|
+
from typing import Any, Dict, List, Sequence
|
4
4
|
from xml.etree import ElementTree
|
5
5
|
|
6
|
+
from typing_extensions import override
|
7
|
+
|
6
8
|
from .decodestate import DecodeState
|
7
9
|
from .determinenumberofitems import DetermineNumberOfItems
|
8
10
|
from .encodestate import EncodeState
|
@@ -10,11 +12,9 @@ from .exceptions import DecodeError, EncodeError, odxassert, odxraise, odxrequir
|
|
10
12
|
from .field import Field
|
11
13
|
from .odxlink import OdxDocFragment, OdxLinkDatabase, OdxLinkId
|
12
14
|
from .odxtypes import ParameterValue
|
15
|
+
from .snrefcontext import SnRefContext
|
13
16
|
from .utils import dataclass_fields_asdict
|
14
17
|
|
15
|
-
if TYPE_CHECKING:
|
16
|
-
from .diaglayer import DiagLayer
|
17
|
-
|
18
18
|
|
19
19
|
@dataclass
|
20
20
|
class DynamicLengthField(Field):
|
@@ -43,55 +43,60 @@ class DynamicLengthField(Field):
|
|
43
43
|
super()._resolve_odxlinks(odxlinks)
|
44
44
|
self.determine_number_of_items._resolve_odxlinks(odxlinks)
|
45
45
|
|
46
|
-
def _resolve_snrefs(self,
|
47
|
-
super()._resolve_snrefs(
|
48
|
-
self.determine_number_of_items._resolve_snrefs(
|
46
|
+
def _resolve_snrefs(self, context: SnRefContext) -> None:
|
47
|
+
super()._resolve_snrefs(context)
|
48
|
+
self.determine_number_of_items._resolve_snrefs(context)
|
49
49
|
|
50
|
-
|
51
|
-
|
52
|
-
physical_value: ParameterValue,
|
53
|
-
encode_state: EncodeState,
|
54
|
-
bit_position: int = 0,
|
55
|
-
) -> bytes:
|
50
|
+
@override
|
51
|
+
def encode_into_pdu(self, physical_value: ParameterValue, encode_state: EncodeState) -> None:
|
56
52
|
|
57
|
-
odxassert(
|
58
|
-
|
53
|
+
odxassert(encode_state.cursor_bit_position == 0,
|
54
|
+
"No bit position can be specified for dynamic length fields!")
|
55
|
+
|
56
|
+
if not isinstance(physical_value, Sequence):
|
59
57
|
odxraise(
|
60
58
|
f"Expected a list of values for dynamic length field {self.short_name}, "
|
61
59
|
f"got {type(physical_value)}", EncodeError)
|
62
60
|
|
61
|
+
# move the origin to the cursor position
|
62
|
+
orig_origin = encode_state.origin_byte_position
|
63
|
+
encode_state.origin_byte_position = encode_state.cursor_byte_position
|
64
|
+
|
63
65
|
det_num_items = self.determine_number_of_items
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
encode_state.coded_message = bytearray()
|
70
|
-
encode_state.emplace_atomic_value(field_len, self.short_name + ".num_items",
|
71
|
-
det_num_items.byte_position)
|
72
|
-
result = encode_state.coded_message
|
73
|
-
encode_state.coded_message = tmp
|
74
|
-
|
75
|
-
# if required, add padding between the length specifier and
|
76
|
-
# the first item
|
77
|
-
if len(result) < self.offset:
|
78
|
-
result.extend([0] * (self.offset - len(result)))
|
79
|
-
elif len(result) > self.offset:
|
66
|
+
encode_state.cursor_bit_position = self.determine_number_of_items.bit_position or 0
|
67
|
+
encode_state.cursor_byte_position = encode_state.origin_byte_position + det_num_items.byte_position
|
68
|
+
det_num_items.dop.encode_into_pdu(len(physical_value), encode_state)
|
69
|
+
|
70
|
+
if encode_state.cursor_byte_position - encode_state.origin_byte_position > self.offset:
|
80
71
|
odxraise(f"The length specifier of field {self.short_name} overlaps "
|
81
|
-
f"with
|
72
|
+
f"with its first item!")
|
82
73
|
|
83
|
-
|
84
|
-
|
74
|
+
encode_state.cursor_byte_position = encode_state.origin_byte_position + self.offset
|
75
|
+
encode_state.cursor_bit_position = 0
|
85
76
|
|
86
|
-
|
77
|
+
orig_is_end_of_pdu = encode_state.is_end_of_pdu
|
78
|
+
encode_state.is_end_of_pdu = False
|
79
|
+
for i, value in enumerate(physical_value):
|
80
|
+
if i == len(physical_value) - 1:
|
81
|
+
encode_state.is_end_of_pdu = orig_is_end_of_pdu
|
82
|
+
|
83
|
+
self.structure.encode_into_pdu(value, encode_state)
|
84
|
+
encode_state.is_end_of_pdu = orig_is_end_of_pdu
|
85
|
+
|
86
|
+
# ensure the correct message size if the field is empty
|
87
|
+
if len(physical_value) == 0:
|
88
|
+
encode_state.emplace_bytes(b"")
|
89
|
+
|
90
|
+
# move cursor and origin positions
|
91
|
+
encode_state.origin_byte_position = orig_origin
|
87
92
|
|
93
|
+
@override
|
88
94
|
def decode_from_pdu(self, decode_state: DecodeState) -> ParameterValue:
|
89
95
|
|
90
96
|
odxassert(decode_state.cursor_bit_position == 0,
|
91
97
|
"No bit position can be specified for dynamic length fields!")
|
92
98
|
|
93
99
|
orig_origin = decode_state.origin_byte_position
|
94
|
-
orig_cursor = decode_state.cursor_byte_position
|
95
100
|
|
96
101
|
det_num_items = self.determine_number_of_items
|
97
102
|
decode_state.origin_byte_position = decode_state.cursor_byte_position
|
@@ -115,6 +120,5 @@ class DynamicLengthField(Field):
|
|
115
120
|
result.append(self.structure.decode_from_pdu(decode_state))
|
116
121
|
|
117
122
|
decode_state.origin_byte_position = orig_origin
|
118
|
-
decode_state.cursor_byte_position = max(orig_cursor, decode_state.cursor_byte_position)
|
119
123
|
|
120
124
|
return result
|