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
@@ -1,36 +1,60 @@
|
|
1
1
|
# SPDX-License-Identifier: MIT
|
2
2
|
from dataclasses import dataclass
|
3
|
-
from typing import
|
3
|
+
from typing import Any, Dict, List, Optional, cast
|
4
4
|
from xml.etree import ElementTree
|
5
5
|
|
6
|
+
from typing_extensions import override
|
7
|
+
|
6
8
|
from .complexdop import ComplexDop
|
9
|
+
from .dataobjectproperty import DataObjectProperty
|
7
10
|
from .decodestate import DecodeState
|
11
|
+
from .dtcdop import DtcDop
|
8
12
|
from .encodestate import EncodeState
|
9
13
|
from .environmentdata import EnvironmentData
|
10
|
-
from .exceptions import
|
14
|
+
from .exceptions import odxraise, odxrequire
|
15
|
+
from .nameditemlist import NamedItemList
|
11
16
|
from .odxlink import OdxDocFragment, OdxLinkDatabase, OdxLinkId, OdxLinkRef
|
12
|
-
from .odxtypes import ParameterValue
|
17
|
+
from .odxtypes import DataType, ParameterValue, ParameterValueDict
|
18
|
+
from .parameters.codedconstparameter import CodedConstParameter
|
19
|
+
from .parameters.parameter import Parameter
|
20
|
+
from .parameters.parameterwithdop import ParameterWithDOP
|
21
|
+
from .parameters.physicalconstantparameter import PhysicalConstantParameter
|
22
|
+
from .parameters.valueparameter import ValueParameter
|
23
|
+
from .snrefcontext import SnRefContext
|
13
24
|
from .utils import dataclass_fields_asdict
|
14
25
|
|
15
|
-
if TYPE_CHECKING:
|
16
|
-
from .diaglayer import DiagLayer
|
17
|
-
|
18
26
|
|
19
27
|
@dataclass
|
20
28
|
class EnvironmentDataDescription(ComplexDop):
|
21
|
-
"""This class represents
|
22
|
-
|
29
|
+
"""This class represents environment data descriptions
|
30
|
+
|
31
|
+
An environment data description provides a list of all environment
|
32
|
+
data objects that are potentially applicable to decode a given
|
33
|
+
response. (If a given environment data object is applicable
|
34
|
+
depends on the value of the DtcDOP that is associated with it.)
|
35
|
+
|
36
|
+
"""
|
37
|
+
|
38
|
+
param_snref: Optional[str]
|
39
|
+
param_snpathref: Optional[str]
|
23
40
|
|
24
41
|
# in ODX 2.0.0, ENV-DATAS seems to be a mandatory
|
25
42
|
# sub-element of ENV-DATA-DESC, in ODX 2.2 it is not
|
26
43
|
# present
|
27
|
-
env_datas:
|
44
|
+
env_datas: NamedItemList[EnvironmentData]
|
28
45
|
env_data_refs: List[OdxLinkRef]
|
29
|
-
param_snref: Optional[str]
|
30
|
-
param_snpathref: Optional[str]
|
31
46
|
|
32
|
-
|
33
|
-
|
47
|
+
@property
|
48
|
+
def param(self) -> Parameter:
|
49
|
+
# the parameter referenced via SNREF cannot be resolved here
|
50
|
+
# because the relevant list of parameters depends on the
|
51
|
+
# concrete codec object processed, whilst an environment data
|
52
|
+
# description object can be featured in an arbitrary number of
|
53
|
+
# responses. Instead, lookup of the appropriate parameter is
|
54
|
+
# done within the encode and decode methods.
|
55
|
+
odxraise("The parameter of ENV-DATA-DESC objects cannot be resolved "
|
56
|
+
"because it depends on the context")
|
57
|
+
return cast(None, Parameter)
|
34
58
|
|
35
59
|
@staticmethod
|
36
60
|
def from_et(et_element: ElementTree.Element,
|
@@ -44,16 +68,20 @@ class EnvironmentDataDescription(ComplexDop):
|
|
44
68
|
param_snpathref = None
|
45
69
|
if (param_snpathref_elem := et_element.find("PARAM-SNPATHREF")) is not None:
|
46
70
|
param_snpathref = odxrequire(param_snpathref_elem.get("SHORT-NAME-PATH"))
|
71
|
+
|
72
|
+
# ODX 2.0 mandates ENV-DATA-DESC to contain a list of
|
73
|
+
# ENV-DATAS and no ENV-DATA-REFS while for ODX 2.2 the
|
74
|
+
# situation is reversed. This means that we will create one
|
75
|
+
# empty and one non-empty list here. (Which is which depends
|
76
|
+
# on the version of the standard used by the file.)
|
47
77
|
env_data_refs = [
|
48
78
|
odxrequire(OdxLinkRef.from_et(env_data_ref, doc_frags))
|
49
79
|
for env_data_ref in et_element.iterfind("ENV-DATA-REFS/ENV-DATA-REF")
|
50
80
|
]
|
51
|
-
|
52
|
-
# ODX 2.0.0 says ENV-DATA-DESC could contain a list of ENV-DATAS
|
53
|
-
env_datas = [
|
81
|
+
env_datas = NamedItemList([
|
54
82
|
EnvironmentData.from_et(env_data_elem, doc_frags)
|
55
83
|
for env_data_elem in et_element.iterfind("ENV-DATAS/ENV-DATA")
|
56
|
-
]
|
84
|
+
])
|
57
85
|
|
58
86
|
return EnvironmentDataDescription(
|
59
87
|
param_snref=param_snref,
|
@@ -65,8 +93,9 @@ class EnvironmentDataDescription(ComplexDop):
|
|
65
93
|
def _build_odxlinks(self) -> Dict[OdxLinkId, Any]:
|
66
94
|
odxlinks = {self.odx_id: self}
|
67
95
|
|
68
|
-
|
69
|
-
|
96
|
+
if not self.env_data_refs:
|
97
|
+
for ed in self.env_datas:
|
98
|
+
odxlinks.update(ed._build_odxlinks())
|
70
99
|
|
71
100
|
return odxlinks
|
72
101
|
|
@@ -74,33 +103,166 @@ class EnvironmentDataDescription(ComplexDop):
|
|
74
103
|
# ODX 2.0 specifies environment data objects here, ODX 2.2
|
75
104
|
# uses references
|
76
105
|
if self.env_data_refs:
|
77
|
-
self.env_datas = [odxlinks.resolve(x) for x in self.env_data_refs]
|
106
|
+
self.env_datas = NamedItemList([odxlinks.resolve(x) for x in self.env_data_refs])
|
78
107
|
else:
|
79
108
|
for ed in self.env_datas:
|
80
109
|
ed._resolve_odxlinks(odxlinks)
|
81
110
|
|
82
|
-
def _resolve_snrefs(self,
|
111
|
+
def _resolve_snrefs(self, context: SnRefContext) -> None:
|
83
112
|
# ODX 2.0 specifies environment data objects here, ODX 2.2
|
84
113
|
# uses references
|
85
114
|
if self.env_data_refs:
|
86
115
|
for ed in self.env_datas:
|
87
|
-
ed._resolve_snrefs(
|
88
|
-
|
89
|
-
def convert_physical_to_bytes(self, physical_value: ParameterValue, encode_state: EncodeState,
|
90
|
-
bit_position: int) -> bytes:
|
91
|
-
"""Convert the physical value into bytes.
|
116
|
+
ed._resolve_snrefs(context)
|
92
117
|
|
93
|
-
|
94
|
-
|
118
|
+
@override
|
119
|
+
def encode_into_pdu(self, physical_value: Optional[ParameterValue],
|
120
|
+
encode_state: EncodeState) -> None:
|
121
|
+
"""Convert a physical value into bytes and emplace them into a PDU.
|
95
122
|
"""
|
96
|
-
raise EncodeError("EnvironmentDataDescription DOPs cannot be encoded or decoded")
|
97
123
|
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
124
|
+
# retrieve the DTC as a numerical value from the referenced
|
125
|
+
# parameter (which must be located somewhere before the
|
126
|
+
# parameter using the environment data description)
|
127
|
+
if self.param_snref is None:
|
128
|
+
odxraise("Specifying the DTC parameter for environment data "
|
129
|
+
"descriptions via SNPATHREF is not supported yet")
|
130
|
+
return None
|
131
|
+
|
132
|
+
numerical_dtc_value: Optional[ParameterValue] = None
|
133
|
+
for prev_param, prev_param_value in reversed(encode_state.journal):
|
134
|
+
if prev_param.short_name == self.param_snref:
|
135
|
+
numerical_dtc_value = self._get_numerical_dtc_from_parameter(
|
136
|
+
prev_param, prev_param_value)
|
137
|
+
break
|
138
|
+
|
139
|
+
if numerical_dtc_value is None:
|
140
|
+
odxraise("Environment data description parameters are only allowed following "
|
141
|
+
"the referenced parameter.")
|
142
|
+
return
|
143
|
+
|
144
|
+
# deal with the "all value" environment data. This holds
|
145
|
+
# parameters that are common to all DTCs. Be aware that the
|
146
|
+
# specification mandates that there is at most one such
|
147
|
+
# environment data object
|
148
|
+
for env_data in self.env_datas:
|
149
|
+
if env_data.all_value:
|
150
|
+
tmp = encode_state.allow_unknown_parameters
|
151
|
+
encode_state.allow_unknown_parameters = True
|
152
|
+
env_data.encode_into_pdu(physical_value, encode_state)
|
153
|
+
encode_state.allow_unknown_parameters = tmp
|
154
|
+
break
|
155
|
+
|
156
|
+
# find the environment data corresponding to the given trouble
|
157
|
+
# code
|
158
|
+
for env_data in self.env_datas:
|
159
|
+
if numerical_dtc_value in env_data.dtc_values:
|
160
|
+
tmp = encode_state.allow_unknown_parameters
|
161
|
+
encode_state.allow_unknown_parameters = True
|
162
|
+
env_data.encode_into_pdu(physical_value, encode_state)
|
163
|
+
encode_state.allow_unknown_parameters = tmp
|
164
|
+
break
|
102
165
|
|
103
|
-
|
104
|
-
|
166
|
+
@override
|
167
|
+
def decode_from_pdu(self, decode_state: DecodeState) -> ParameterValue:
|
168
|
+
"""Extract the bytes from a PDU and convert them to a physical value.
|
105
169
|
"""
|
106
|
-
|
170
|
+
|
171
|
+
# retrieve the DTC as a numerical value from the referenced
|
172
|
+
# parameter (which must be located somewhere before the
|
173
|
+
# parameter using the environment data description)
|
174
|
+
if self.param_snref is None:
|
175
|
+
odxraise("Specifying the DTC parameter for environment data "
|
176
|
+
"descriptions via SNPATHREF is not supported yet")
|
177
|
+
return None
|
178
|
+
|
179
|
+
numerical_dtc_value: Optional[ParameterValue] = None
|
180
|
+
for prev_param, prev_param_value in reversed(decode_state.journal):
|
181
|
+
if prev_param.short_name == self.param_snref:
|
182
|
+
numerical_dtc_value = self._get_numerical_dtc_from_parameter(
|
183
|
+
prev_param, prev_param_value)
|
184
|
+
break
|
185
|
+
|
186
|
+
if numerical_dtc_value is None:
|
187
|
+
odxraise("Environment data description parameters are only allowed following "
|
188
|
+
"the referenced parameter.")
|
189
|
+
return
|
190
|
+
|
191
|
+
result: ParameterValueDict = {}
|
192
|
+
|
193
|
+
# deal with the "all value" environment data. This holds
|
194
|
+
# parameters that are common to all DTCs. Be aware that the
|
195
|
+
# specification mandates that there is at most one such
|
196
|
+
# environment data object
|
197
|
+
for env_data in self.env_datas:
|
198
|
+
if env_data.all_value:
|
199
|
+
tmp = env_data.decode_from_pdu(decode_state)
|
200
|
+
if not isinstance(tmp, dict):
|
201
|
+
odxraise()
|
202
|
+
result.update(tmp)
|
203
|
+
break
|
204
|
+
|
205
|
+
# find the environment data corresponding to the given trouble
|
206
|
+
# code
|
207
|
+
for env_data in self.env_datas:
|
208
|
+
if numerical_dtc_value in env_data.dtc_values:
|
209
|
+
tmp = env_data.decode_from_pdu(decode_state)
|
210
|
+
if not isinstance(tmp, dict):
|
211
|
+
odxraise()
|
212
|
+
result.update(tmp)
|
213
|
+
break
|
214
|
+
|
215
|
+
return result
|
216
|
+
|
217
|
+
def _get_numerical_dtc_from_parameter(self, param: Parameter,
|
218
|
+
param_value: Optional[ParameterValue]) -> int:
|
219
|
+
if isinstance(param, ParameterWithDOP):
|
220
|
+
dop = param.dop
|
221
|
+
if not isinstance(dop, (DataObjectProperty, DtcDop)):
|
222
|
+
odxraise(f"The DOP of the parameter referenced by environment data descriptions "
|
223
|
+
f"must use either be DataObjectProperty or a DtcDop (encountered "
|
224
|
+
f"{type(param).__name__} for parameter '{self.param.short_name}' "
|
225
|
+
f"of ENV-DATA-DESC '{self.short_name}')")
|
226
|
+
return 0
|
227
|
+
|
228
|
+
if dop.diag_coded_type.base_data_type != DataType.A_UINT32:
|
229
|
+
odxraise(f"The data type used by the DOP of the parameter referenced "
|
230
|
+
f"by environment data descriptions must be A_UINT32 "
|
231
|
+
f"(encountered '{dop.diag_coded_type.base_data_type.value}')")
|
232
|
+
|
233
|
+
if param_value is None:
|
234
|
+
if isinstance(param, ValueParameter):
|
235
|
+
param_value = param.physical_default_value
|
236
|
+
elif isinstance(param, PhysicalConstantParameter):
|
237
|
+
param_value = param.physical_constant_value
|
238
|
+
else:
|
239
|
+
odxraise() # make mypy happy...
|
240
|
+
return
|
241
|
+
|
242
|
+
if isinstance(dop, DtcDop):
|
243
|
+
return dop.convert_to_numerical_trouble_code(odxrequire(param_value))
|
244
|
+
elif isinstance(dop, DataObjectProperty):
|
245
|
+
return int(dop.compu_method.convert_physical_to_internal(
|
246
|
+
param_value)) # type: ignore[arg-type]
|
247
|
+
|
248
|
+
odxraise() # not reachable...
|
249
|
+
|
250
|
+
elif isinstance(param, CodedConstParameter):
|
251
|
+
if param.diag_coded_type.base_data_type != DataType.A_UINT32:
|
252
|
+
odxraise(f"The data type used by the parameter referenced "
|
253
|
+
f"by environment data descriptions must be A_UINT32 "
|
254
|
+
f"(encountered '{param.diag_coded_type.base_data_type.value}')")
|
255
|
+
|
256
|
+
return param.coded_value
|
257
|
+
|
258
|
+
if not isinstance(param.coded_value, int):
|
259
|
+
odxraise()
|
260
|
+
|
261
|
+
return param.coded_value
|
262
|
+
|
263
|
+
else:
|
264
|
+
odxraise(f"The parameter referenced by environment data descriptions "
|
265
|
+
f"must be a parameter that either specifies a DOP or a constant "
|
266
|
+
f"(encountered {type(param).__name__} for reference '{self.param_snref}' of "
|
267
|
+
f"ENV-DATA-DESC '{self.short_name}')")
|
268
|
+
return 0
|
odxtools/exceptions.py
CHANGED
@@ -9,13 +9,22 @@ class OdxError(Exception):
|
|
9
9
|
|
10
10
|
|
11
11
|
class EncodeError(Warning, OdxError):
|
12
|
-
"""Encoding of a message to raw data failed
|
12
|
+
"""Encoding of a message to raw data failed"""
|
13
13
|
|
14
14
|
|
15
15
|
class DecodeError(Warning, OdxError):
|
16
16
|
"""Decoding raw data failed."""
|
17
17
|
|
18
18
|
|
19
|
+
class DecodeMismatch(DecodeError):
|
20
|
+
"""Decoding failed because some parameters exhibit an incorrect value
|
21
|
+
|
22
|
+
This is can happen if NRC-CONST or environment data descriptions
|
23
|
+
are present.
|
24
|
+
|
25
|
+
"""
|
26
|
+
|
27
|
+
|
19
28
|
class OdxWarning(Warning):
|
20
29
|
"""Any warning that happens during interacting with diagnostic objects."""
|
21
30
|
|
@@ -40,7 +49,7 @@ def odxraise(message: Optional[str] = None, error_type: Type[Exception] = OdxErr
|
|
40
49
|
else:
|
41
50
|
raise error_type(message)
|
42
51
|
elif message is not None:
|
43
|
-
logger.
|
52
|
+
logger.warning(message)
|
44
53
|
|
45
54
|
|
46
55
|
def odxassert(condition: bool,
|
odxtools/field.py
CHANGED
@@ -1,18 +1,17 @@
|
|
1
|
+
# SPDX-License-Identifier: MIT
|
1
2
|
from dataclasses import dataclass
|
2
|
-
from typing import
|
3
|
+
from typing import List, Optional
|
3
4
|
from xml.etree import ElementTree
|
4
5
|
|
5
6
|
from .basicstructure import BasicStructure
|
6
7
|
from .complexdop import ComplexDop
|
7
8
|
from .environmentdatadescription import EnvironmentDataDescription
|
8
9
|
from .exceptions import odxassert, odxrequire
|
9
|
-
from .odxlink import OdxDocFragment, OdxLinkDatabase, OdxLinkRef
|
10
|
+
from .odxlink import OdxDocFragment, OdxLinkDatabase, OdxLinkRef, resolve_snref
|
10
11
|
from .odxtypes import odxstr_to_bool
|
12
|
+
from .snrefcontext import SnRefContext
|
11
13
|
from .utils import dataclass_fields_asdict
|
12
14
|
|
13
|
-
if TYPE_CHECKING:
|
14
|
-
from .diaglayer import DiagLayer
|
15
|
-
|
16
15
|
|
17
16
|
@dataclass
|
18
17
|
class Field(ComplexDop):
|
@@ -80,12 +79,13 @@ class Field(ComplexDop):
|
|
80
79
|
self._env_data_desc = odxlinks.resolve(self.env_data_desc_ref,
|
81
80
|
EnvironmentDataDescription)
|
82
81
|
|
83
|
-
def _resolve_snrefs(self,
|
82
|
+
def _resolve_snrefs(self, context: SnRefContext) -> None:
|
84
83
|
"""Recursively resolve any short-name references"""
|
84
|
+
ddds = odxrequire(context.diag_layer).diag_data_dictionary_spec
|
85
|
+
|
85
86
|
if self.structure_snref is not None:
|
86
|
-
|
87
|
-
self._structure = odxrequire(structures.get(self.structure_snref))
|
87
|
+
self._structure = resolve_snref(self.structure_snref, ddds.structures, BasicStructure)
|
88
88
|
|
89
89
|
if self.env_data_desc_snref is not None:
|
90
|
-
|
91
|
-
|
90
|
+
self._env_data_desc = resolve_snref(self.env_data_desc_snref, ddds.env_data_descs,
|
91
|
+
EnvironmentDataDescription)
|
odxtools/functionalclass.py
CHANGED
@@ -1,15 +1,13 @@
|
|
1
1
|
# SPDX-License-Identifier: MIT
|
2
2
|
from dataclasses import dataclass
|
3
|
-
from typing import
|
3
|
+
from typing import Any, Dict, List
|
4
4
|
from xml.etree import ElementTree
|
5
5
|
|
6
6
|
from .element import IdentifiableElement
|
7
7
|
from .odxlink import OdxDocFragment, OdxLinkDatabase, OdxLinkId
|
8
|
+
from .snrefcontext import SnRefContext
|
8
9
|
from .utils import dataclass_fields_asdict
|
9
10
|
|
10
|
-
if TYPE_CHECKING:
|
11
|
-
from .diaglayer import DiagLayer
|
12
|
-
|
13
11
|
|
14
12
|
@dataclass
|
15
13
|
class FunctionalClass(IdentifiableElement):
|
@@ -31,5 +29,5 @@ class FunctionalClass(IdentifiableElement):
|
|
31
29
|
def _resolve_odxlinks(self, odxlinks: OdxLinkDatabase) -> None:
|
32
30
|
pass
|
33
31
|
|
34
|
-
def _resolve_snrefs(self,
|
32
|
+
def _resolve_snrefs(self, context: SnRefContext) -> None:
|
35
33
|
pass
|
odxtools/inputparam.py
CHANGED
@@ -1,19 +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
|
-
from deprecation import deprecated
|
7
|
-
|
8
6
|
from .dopbase import DopBase
|
9
7
|
from .element import NamedElement
|
10
8
|
from .exceptions import odxrequire
|
11
9
|
from .odxlink import OdxDocFragment, OdxLinkDatabase, OdxLinkId, OdxLinkRef
|
10
|
+
from .snrefcontext import SnRefContext
|
12
11
|
from .utils import dataclass_fields_asdict
|
13
12
|
|
14
|
-
if TYPE_CHECKING:
|
15
|
-
from .diaglayer import DiagLayer
|
16
|
-
|
17
13
|
|
18
14
|
@dataclass
|
19
15
|
class InputParam(NamedElement):
|
@@ -44,15 +40,10 @@ class InputParam(NamedElement):
|
|
44
40
|
def _resolve_odxlinks(self, odxlinks: OdxLinkDatabase) -> None:
|
45
41
|
self._dop_base = odxlinks.resolve(self.dop_base_ref, DopBase)
|
46
42
|
|
47
|
-
def _resolve_snrefs(self,
|
43
|
+
def _resolve_snrefs(self, context: SnRefContext) -> None:
|
48
44
|
pass
|
49
45
|
|
50
46
|
@property
|
51
47
|
def dop_base(self) -> DopBase:
|
52
48
|
"""The data object property describing this parameter."""
|
53
49
|
return self._dop_base
|
54
|
-
|
55
|
-
@property
|
56
|
-
@deprecated(details="use .dop_base")
|
57
|
-
def dop(self) -> DopBase:
|
58
|
-
return self._dop_base
|
odxtools/internalconstr.py
CHANGED
@@ -4,6 +4,7 @@ from typing import List, Optional
|
|
4
4
|
from xml.etree import ElementTree
|
5
5
|
|
6
6
|
from .compumethods.limit import Limit
|
7
|
+
from .odxlink import OdxDocFragment
|
7
8
|
from .odxtypes import DataType
|
8
9
|
from .scaleconstr import ScaleConstr
|
9
10
|
|
@@ -19,16 +20,24 @@ class InternalConstr:
|
|
19
20
|
upper_limit: Optional[Limit]
|
20
21
|
scale_constrs: List[ScaleConstr]
|
21
22
|
|
23
|
+
value_type: DataType
|
24
|
+
|
22
25
|
@staticmethod
|
23
|
-
def
|
26
|
+
def constr_from_et(et_element: ElementTree.Element, doc_frags: List[OdxDocFragment], *,
|
27
|
+
value_type: DataType) -> "InternalConstr":
|
24
28
|
|
25
|
-
lower_limit = Limit.
|
26
|
-
|
29
|
+
lower_limit = Limit.limit_from_et(
|
30
|
+
et_element.find("LOWER-LIMIT"), doc_frags, value_type=value_type)
|
31
|
+
upper_limit = Limit.limit_from_et(
|
32
|
+
et_element.find("UPPER-LIMIT"), doc_frags, value_type=value_type)
|
27
33
|
|
28
34
|
scale_constrs = [
|
29
|
-
ScaleConstr.
|
35
|
+
ScaleConstr.scale_constr_from_et(sc_el, doc_frags, value_type=value_type)
|
30
36
|
for sc_el in et_element.iterfind("SCALE-CONSTRS/SCALE-CONSTR")
|
31
37
|
]
|
32
38
|
|
33
39
|
return InternalConstr(
|
34
|
-
lower_limit=lower_limit,
|
40
|
+
lower_limit=lower_limit,
|
41
|
+
upper_limit=upper_limit,
|
42
|
+
scale_constrs=scale_constrs,
|
43
|
+
value_type=value_type)
|
odxtools/isotp_state_machine.py
CHANGED
@@ -55,10 +55,13 @@ class IsoTpStateMachine:
|
|
55
55
|
return # unknown CAN ID
|
56
56
|
|
57
57
|
# decode the isotp segment
|
58
|
-
|
58
|
+
frame_type, _ = bitstruct.unpack("u4u4", data)
|
59
|
+
assert isinstance(frame_type, int)
|
60
|
+
|
59
61
|
telegram_len = None
|
60
62
|
if frame_type == IsoTp.FRAME_TYPE_SINGLE:
|
61
63
|
frame_type, telegram_len = bitstruct.unpack("u4u4", data)
|
64
|
+
assert isinstance(telegram_len, int)
|
62
65
|
|
63
66
|
self.on_single_frame(telegram_idx, data[1:1 + telegram_len])
|
64
67
|
self.on_telegram_complete(telegram_idx, data[1:1 + telegram_len])
|
@@ -67,6 +70,7 @@ class IsoTpStateMachine:
|
|
67
70
|
|
68
71
|
elif frame_type == IsoTp.FRAME_TYPE_FIRST:
|
69
72
|
frame_type, telegram_len = bitstruct.unpack("u4u12", data)
|
73
|
+
assert isinstance(telegram_len, int)
|
70
74
|
|
71
75
|
self._telegram_specified_len[telegram_idx] = telegram_len
|
72
76
|
self._telegram_data[telegram_idx] = bytearray(data[2:])
|
@@ -76,11 +80,13 @@ class IsoTpStateMachine:
|
|
76
80
|
|
77
81
|
elif frame_type == IsoTp.FRAME_TYPE_CONSECUTIVE:
|
78
82
|
frame_type, rx_segment_idx = bitstruct.unpack("u4u4", data)
|
83
|
+
assert isinstance(rx_segment_idx, int)
|
79
84
|
|
80
85
|
expected_segment_idx = (self._telegram_last_rx_fragment_idx[telegram_idx] + 1) % 16
|
81
86
|
telegram_data = self._telegram_data[telegram_idx]
|
82
87
|
assert isinstance(telegram_data, bytearray)
|
83
88
|
|
89
|
+
n = -1
|
84
90
|
if expected_segment_idx == rx_segment_idx:
|
85
91
|
self._telegram_last_rx_fragment_idx[telegram_idx] = rx_segment_idx
|
86
92
|
telegram_data += data[1:]
|
@@ -102,6 +108,8 @@ class IsoTpStateMachine:
|
|
102
108
|
|
103
109
|
elif frame_type == IsoTp.FRAME_TYPE_FLOW_CONTROL:
|
104
110
|
frame_type, flow_control_flag = bitstruct.unpack("u4u4", data)
|
111
|
+
assert isinstance(flow_control_flag, int)
|
112
|
+
|
105
113
|
self.on_flow_control_frame(telegram_idx, flow_control_flag)
|
106
114
|
else:
|
107
115
|
self.on_frame_type_error(telegram_idx, frame_type)
|
@@ -139,7 +147,7 @@ class IsoTpStateMachine:
|
|
139
147
|
return
|
140
148
|
|
141
149
|
if m := self.can_normal_frame_re.match(cur_line.strip()):
|
142
|
-
#frame_interface = m.group(1)
|
150
|
+
# frame_interface = m.group(1)
|
143
151
|
frame_id = int(m.group(2), 16)
|
144
152
|
|
145
153
|
frame_data_formatted = m.group(3).strip()
|
@@ -151,7 +159,7 @@ class IsoTpStateMachine:
|
|
151
159
|
elif (m := self.can_log_frame_re.match(
|
152
160
|
cur_line.strip())) or (m := self.can_fd_log_frame_re.match(
|
153
161
|
cur_line.strip())):
|
154
|
-
#frame_interface = m.group(2)
|
162
|
+
# frame_interface = m.group(2)
|
155
163
|
frame_id = int(m.group(2), 16)
|
156
164
|
|
157
165
|
frame_data_formatted = m.group(3).strip()
|
@@ -257,7 +265,7 @@ class IsoTpActiveDecoder(IsoTpStateMachine):
|
|
257
265
|
|
258
266
|
def on_single_frame(self, telegram_idx: int, frame_payload: bytes) -> None:
|
259
267
|
# send ACK
|
260
|
-
#rx_id = self.can_rx_id(telegram_idx)
|
268
|
+
# rx_id = self.can_rx_id(telegram_idx)
|
261
269
|
tx_id = self.can_tx_id(telegram_idx)
|
262
270
|
block_size = 0xFF
|
263
271
|
min_separation_time = 0 # ms
|
@@ -276,7 +284,7 @@ class IsoTpActiveDecoder(IsoTpStateMachine):
|
|
276
284
|
|
277
285
|
def on_first_frame(self, telegram_idx: int, frame_payload: bytes) -> None:
|
278
286
|
# send ACK
|
279
|
-
#rx_id = self.can_rx_id(telegram_idx)
|
287
|
+
# rx_id = self.can_rx_id(telegram_idx)
|
280
288
|
tx_id = self.can_tx_id(telegram_idx)
|
281
289
|
block_size = 0xFF # default value, can be overwritten later
|
282
290
|
min_separation_time = 0 # ms
|
@@ -308,7 +316,7 @@ class IsoTpActiveDecoder(IsoTpStateMachine):
|
|
308
316
|
# send new ACK if necessary
|
309
317
|
block_size = self._block_size[telegram_idx]
|
310
318
|
if block_size is not None and num_received >= block_size:
|
311
|
-
#rx_id = self.can_rx_id(telegram_idx)
|
319
|
+
# rx_id = self.can_rx_id(telegram_idx)
|
312
320
|
tx_id = self.can_tx_id(telegram_idx)
|
313
321
|
min_separation_time = 0 # ms
|
314
322
|
fc_payload = bitstruct.pack(
|
@@ -1,12 +1,17 @@
|
|
1
1
|
# SPDX-License-Identifier: MIT
|
2
2
|
from dataclasses import dataclass
|
3
|
-
from typing import
|
3
|
+
from typing import List
|
4
|
+
from xml.etree import ElementTree
|
5
|
+
|
6
|
+
from typing_extensions import override
|
4
7
|
|
5
8
|
from .decodestate import DecodeState
|
6
9
|
from .diagcodedtype import DctType, DiagCodedType
|
7
10
|
from .encodestate import EncodeState
|
8
|
-
from .exceptions import odxassert, odxraise
|
11
|
+
from .exceptions import EncodeError, odxassert, odxraise, odxrequire
|
12
|
+
from .odxlink import OdxDocFragment
|
9
13
|
from .odxtypes import AtomicOdxType, DataType
|
14
|
+
from .utils import dataclass_fields_asdict
|
10
15
|
|
11
16
|
|
12
17
|
@dataclass
|
@@ -18,6 +23,16 @@ class LeadingLengthInfoType(DiagCodedType):
|
|
18
23
|
#: object.
|
19
24
|
bit_length: int
|
20
25
|
|
26
|
+
@staticmethod
|
27
|
+
@override
|
28
|
+
def from_et(et_element: ElementTree.Element,
|
29
|
+
doc_frags: List[OdxDocFragment]) -> "LeadingLengthInfoType":
|
30
|
+
kwargs = dataclass_fields_asdict(DiagCodedType.from_et(et_element, doc_frags))
|
31
|
+
|
32
|
+
bit_length = int(odxrequire(et_element.findtext("BIT-LENGTH")))
|
33
|
+
|
34
|
+
return LeadingLengthInfoType(bit_length=bit_length, **kwargs)
|
35
|
+
|
21
36
|
def __post_init__(self) -> None:
|
22
37
|
odxassert(self.bit_length > 0,
|
23
38
|
"A Leading length info type with bit length == 0 does not make sense.")
|
@@ -35,36 +50,40 @@ class LeadingLengthInfoType(DiagCodedType):
|
|
35
50
|
def dct_type(self) -> DctType:
|
36
51
|
return "LEADING-LENGTH-INFO-TYPE"
|
37
52
|
|
38
|
-
|
39
|
-
|
40
|
-
# specifier field. This is then followed by the same number of
|
41
|
-
# bytes as the value of this field, i.e., the length of this
|
42
|
-
# DCT is dynamic!
|
43
|
-
return None
|
53
|
+
@override
|
54
|
+
def encode_into_pdu(self, internal_value: AtomicOdxType, encode_state: EncodeState) -> None:
|
44
55
|
|
45
|
-
|
46
|
-
|
56
|
+
if not isinstance(internal_value, (str, bytes)):
|
57
|
+
odxraise(
|
58
|
+
f"LEADING-LENGTH-INFO types can only be used for strings and byte fields, "
|
59
|
+
f"not {type(internal_value).__name__}", EncodeError)
|
60
|
+
return
|
47
61
|
|
48
62
|
byte_length = self._minimal_byte_length_of(internal_value)
|
49
63
|
|
50
|
-
|
51
|
-
|
52
|
-
|
64
|
+
used_mask = None
|
65
|
+
bit_pos = encode_state.cursor_bit_position
|
66
|
+
if encode_state.cursor_bit_position != 0 or (bit_pos + self.bit_length) % 8 != 0:
|
67
|
+
used_mask = (1 << self.bit_length) - 1
|
68
|
+
used_mask <<= bit_pos
|
69
|
+
|
70
|
+
encode_state.emplace_atomic_value(
|
71
|
+
internal_value=byte_length,
|
72
|
+
used_mask=None,
|
53
73
|
bit_length=self.bit_length,
|
54
74
|
base_data_type=DataType.A_UINT32,
|
55
75
|
is_highlow_byte_order=self.is_highlow_byte_order,
|
56
76
|
)
|
57
77
|
|
58
|
-
|
59
|
-
internal_value,
|
60
|
-
|
78
|
+
encode_state.emplace_atomic_value(
|
79
|
+
internal_value=internal_value,
|
80
|
+
used_mask=None,
|
61
81
|
bit_length=8 * byte_length,
|
62
82
|
base_data_type=self.base_data_type,
|
63
83
|
is_highlow_byte_order=self.is_highlow_byte_order,
|
64
84
|
)
|
65
85
|
|
66
|
-
|
67
|
-
|
86
|
+
@override
|
68
87
|
def decode_from_pdu(self, decode_state: DecodeState) -> AtomicOdxType:
|
69
88
|
|
70
89
|
# Extract length of the parameter value
|