odxtools 6.6.1__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 +7 -5
- odxtools/additionalaudience.py +3 -5
- odxtools/admindata.py +5 -7
- odxtools/audience.py +10 -13
- odxtools/basecomparam.py +3 -5
- odxtools/basicstructure.py +55 -241
- odxtools/cli/_parser_utils.py +16 -1
- odxtools/cli/_print_utils.py +169 -134
- odxtools/cli/browse.py +127 -103
- odxtools/cli/compare.py +114 -87
- odxtools/cli/decode.py +2 -1
- odxtools/cli/dummy_sub_parser.py +3 -1
- odxtools/cli/find.py +2 -1
- odxtools/cli/list.py +26 -16
- odxtools/cli/main.py +1 -0
- odxtools/cli/snoop.py +32 -6
- 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 +14 -14
- 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 +94 -15
- odxtools/compumethods/compuphystointernal.py +56 -0
- odxtools/compumethods/compurationalcoeffs.py +20 -9
- odxtools/compumethods/compuscale.py +67 -32
- odxtools/compumethods/createanycompumethod.py +31 -172
- odxtools/compumethods/identicalcompumethod.py +31 -6
- odxtools/compumethods/limit.py +70 -36
- odxtools/compumethods/linearcompumethod.py +70 -181
- 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 +123 -92
- odxtools/compumethods/texttablecompumethod.py +117 -57
- odxtools/createanydiagcodedtype.py +10 -67
- odxtools/database.py +167 -87
- odxtools/dataobjectproperty.py +25 -32
- odxtools/decodestate.py +14 -17
- odxtools/description.py +47 -0
- odxtools/determinenumberofitems.py +4 -5
- odxtools/diagcodedtype.py +37 -106
- odxtools/diagcomm.py +24 -12
- odxtools/diagdatadictionaryspec.py +120 -96
- 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/diaglayertype.py +42 -0
- 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} +273 -472
- 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 +57 -44
- odxtools/diagvariable.py +113 -0
- odxtools/docrevision.py +5 -7
- odxtools/dopbase.py +15 -15
- odxtools/dtcdop.py +170 -50
- odxtools/dynamicendmarkerfield.py +134 -0
- odxtools/dynamiclengthfield.py +47 -42
- 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 -36
- odxtools/exceptions.py +11 -2
- odxtools/field.py +10 -10
- odxtools/functionalclass.py +3 -5
- odxtools/inputparam.py +3 -12
- odxtools/internalconstr.py +14 -5
- odxtools/isotp_state_machine.py +14 -6
- 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 +135 -75
- odxtools/multiplexercase.py +39 -18
- odxtools/multiplexerdefaultcase.py +15 -12
- odxtools/multiplexerswitchkey.py +4 -5
- odxtools/nameditemlist.py +33 -8
- odxtools/negoutputparam.py +3 -5
- odxtools/odxcategory.py +83 -0
- odxtools/odxlink.py +62 -53
- odxtools/odxtypes.py +93 -8
- odxtools/outputparam.py +5 -16
- odxtools/parameterinfo.py +219 -61
- odxtools/parameters/codedconstparameter.py +45 -32
- odxtools/parameters/createanyparameter.py +19 -193
- odxtools/parameters/dynamicparameter.py +25 -4
- odxtools/parameters/lengthkeyparameter.py +83 -25
- odxtools/parameters/matchingrequestparameter.py +48 -18
- odxtools/parameters/nrcconstparameter.py +76 -54
- odxtools/parameters/parameter.py +97 -73
- odxtools/parameters/parameterwithdop.py +41 -38
- odxtools/parameters/physicalconstantparameter.py +41 -20
- odxtools/parameters/reservedparameter.py +36 -18
- odxtools/parameters/systemparameter.py +74 -7
- odxtools/parameters/tableentryparameter.py +47 -7
- odxtools/parameters/tablekeyparameter.py +142 -55
- odxtools/parameters/tablestructparameter.py +79 -58
- odxtools/parameters/valueparameter.py +39 -21
- odxtools/paramlengthinfotype.py +56 -33
- odxtools/parentref.py +20 -3
- 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 +14 -8
- 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 +8 -18
- odxtools/staticfield.py +107 -0
- odxtools/subcomponent.py +288 -0
- odxtools/swvariable.py +21 -0
- odxtools/table.py +9 -9
- odxtools/tablerow.py +30 -15
- 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 -137
- 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 +5 -3
- odxtools/templates/macros/printOdxCategory.xml.jinja2 +28 -0
- odxtools/templates/macros/printParam.xml.jinja2 +18 -19
- 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 +15 -0
- 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} +22 -12
- odxtools/xdoc.py +3 -5
- {odxtools-6.6.1.dist-info → odxtools-9.3.0.dist-info}/METADATA +44 -33
- odxtools-9.3.0.dist-info/RECORD +228 -0
- {odxtools-6.6.1.dist-info → odxtools-9.3.0.dist-info}/WHEEL +1 -1
- odxtools/createcompanydatas.py +0 -17
- odxtools/createsdgs.py +0 -19
- odxtools/diaglayertype.py +0 -30
- 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 -208
- odxtools-6.6.1.dist-info/RECORD +0 -180
- {odxtools-6.6.1.dist-info → odxtools-9.3.0.dist-info}/LICENSE +0 -0
- {odxtools-6.6.1.dist-info → odxtools-9.3.0.dist-info}/entry_points.txt +0 -0
- {odxtools-6.6.1.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."""
|
@@ -46,8 +94,8 @@ class DtcDop(DopBase):
|
|
46
94
|
compu_method = create_any_compu_method_from_et(
|
47
95
|
odxrequire(et_element.find("COMPU-METHOD")),
|
48
96
|
doc_frags,
|
49
|
-
diag_coded_type.base_data_type,
|
50
|
-
physical_type.base_data_type,
|
97
|
+
internal_type=diag_coded_type.base_data_type,
|
98
|
+
physical_type=physical_type.base_data_type,
|
51
99
|
)
|
52
100
|
dtcs_raw: List[Union[DiagnosticTroubleCode, OdxLinkRef]] = []
|
53
101
|
if (dtcs_elem := et_element.find("DTCS")) is not None:
|
@@ -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
|
-
cast(OdxLinkRef, 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,41 +178,70 @@ class DtcDop(DopBase):
|
|
126
178
|
sdgs=[],
|
127
179
|
)
|
128
180
|
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
if isinstance(physical_value, DiagnosticTroubleCode):
|
134
|
-
trouble_code = physical_value.trouble_code
|
135
|
-
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):
|
136
185
|
# assume that physical value is the trouble_code
|
137
|
-
|
138
|
-
elif isinstance(
|
186
|
+
return dtc_value
|
187
|
+
elif isinstance(dtc_value, str):
|
139
188
|
# assume that physical value is the short_name
|
140
|
-
dtcs = [dtc for dtc in self.dtcs if dtc.short_name ==
|
141
|
-
|
142
|
-
|
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
|
143
196
|
else:
|
144
|
-
|
145
|
-
|
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)
|
146
210
|
|
147
|
-
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))
|
148
212
|
|
149
|
-
|
150
|
-
|
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)
|
151
225
|
|
152
226
|
def _build_odxlinks(self) -> Dict[OdxLinkId, Any]:
|
153
227
|
odxlinks = super()._build_odxlinks()
|
154
228
|
|
229
|
+
odxlinks.update(self.compu_method._build_odxlinks())
|
230
|
+
|
155
231
|
for dtc_proxy in self.dtcs_raw:
|
156
232
|
if isinstance(dtc_proxy, DiagnosticTroubleCode):
|
157
233
|
odxlinks.update(dtc_proxy._build_odxlinks())
|
158
234
|
|
235
|
+
for linked_dtc_dop in self.linked_dtc_dops_raw:
|
236
|
+
odxlinks.update(linked_dtc_dop._build_odxlinks())
|
237
|
+
|
159
238
|
return odxlinks
|
160
239
|
|
161
240
|
def _resolve_odxlinks(self, odxlinks: OdxLinkDatabase) -> None:
|
162
241
|
super()._resolve_odxlinks(odxlinks)
|
163
242
|
|
243
|
+
self.compu_method._resolve_odxlinks(odxlinks)
|
244
|
+
|
164
245
|
self._dtcs = NamedItemList[DiagnosticTroubleCode]()
|
165
246
|
for dtc_proxy in self.dtcs_raw:
|
166
247
|
if isinstance(dtc_proxy, DiagnosticTroubleCode):
|
@@ -170,12 +251,51 @@ class DtcDop(DopBase):
|
|
170
251
|
dtc = odxlinks.resolve(dtc_proxy, DiagnosticTroubleCode)
|
171
252
|
self._dtcs.append(dtc)
|
172
253
|
|
173
|
-
|
174
|
-
|
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
|
175
264
|
|
176
|
-
|
177
|
-
|
265
|
+
self._init_finished = True
|
266
|
+
|
267
|
+
super()._resolve_snrefs(context)
|
268
|
+
|
269
|
+
self.compu_method._resolve_snrefs(context)
|
178
270
|
|
179
271
|
for dtc_proxy in self.dtcs_raw:
|
180
272
|
if isinstance(dtc_proxy, DiagnosticTroubleCode):
|
181
|
-
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
|
+
|
50
|
+
@override
|
51
|
+
def encode_into_pdu(self, physical_value: ParameterValue, encode_state: EncodeState) -> None:
|
49
52
|
|
50
|
-
|
51
|
-
|
52
|
-
physical_value: ParameterValue,
|
53
|
-
encode_state: EncodeState,
|
54
|
-
bit_position: int = 0,
|
55
|
-
) -> bytes:
|
53
|
+
odxassert(encode_state.cursor_bit_position == 0,
|
54
|
+
"No bit position can be specified for dynamic length fields!")
|
56
55
|
|
57
|
-
|
58
|
-
if not isinstance(physical_value, list):
|
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"")
|
87
89
|
|
90
|
+
# move cursor and origin positions
|
91
|
+
encode_state.origin_byte_position = orig_origin
|
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
|
@@ -99,6 +104,7 @@ class DynamicLengthField(Field):
|
|
99
104
|
decode_state.cursor_bit_position = det_num_items.bit_position or 0
|
100
105
|
|
101
106
|
n = det_num_items.dop.decode_from_pdu(decode_state)
|
107
|
+
result: List[ParameterValue] = []
|
102
108
|
|
103
109
|
if not isinstance(n, int):
|
104
110
|
odxraise(f"Number of items specified by a dynamic length field {self.short_name} "
|
@@ -107,13 +113,12 @@ class DynamicLengthField(Field):
|
|
107
113
|
odxraise(
|
108
114
|
f"Number of items specified by a dynamic length field {self.short_name} "
|
109
115
|
f"must be positive (is: {n})", DecodeError)
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
116
|
+
n = 0
|
117
|
+
|
118
|
+
decode_state.cursor_byte_position = decode_state.origin_byte_position + self.offset
|
119
|
+
for _ in range(n):
|
120
|
+
result.append(self.structure.decode_from_pdu(decode_state))
|
115
121
|
|
116
122
|
decode_state.origin_byte_position = orig_origin
|
117
|
-
decode_state.cursor_byte_position = max(orig_cursor, decode_state.cursor_byte_position)
|
118
123
|
|
119
124
|
return result
|