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/matchingparameter.py
CHANGED
@@ -12,7 +12,7 @@ from .utils import is_short_name, is_short_name_path
|
|
12
12
|
class MatchingParameter:
|
13
13
|
"""According to ISO 22901, a MatchingParameter contains a string value identifying
|
14
14
|
the active ECU variant. Moreover, it references a DIAG-COMM via snref and one of its
|
15
|
-
|
15
|
+
positive response's OUT-PARAM-IF via snref or snpathref.
|
16
16
|
|
17
17
|
Unlike other parameters defined in the `parameters` package, a MatchingParameter is
|
18
18
|
not transferred over the network.
|
@@ -32,8 +32,8 @@ class MatchingParameter:
|
|
32
32
|
doc_frags: List[OdxDocFragment]) -> "MatchingParameter":
|
33
33
|
|
34
34
|
expected_value = odxrequire(et_element.findtext("EXPECTED-VALUE"))
|
35
|
-
|
36
|
-
diag_comm_snref = odxrequire(
|
35
|
+
diag_comm_snref_el = odxrequire(et_element.find("DIAG-COMM-SNREF"))
|
36
|
+
diag_comm_snref = odxrequire(diag_comm_snref_el.get("SHORT-NAME"))
|
37
37
|
out_param_snref_el = et_element.find("OUT-PARAM-IF-SNREF")
|
38
38
|
out_param_snpathref_el = et_element.find("OUT-PARAM-IF-SNPATHREF")
|
39
39
|
out_param_if = None
|
odxtools/message.py
CHANGED
@@ -2,8 +2,6 @@
|
|
2
2
|
from dataclasses import dataclass
|
3
3
|
from typing import TYPE_CHECKING, Union
|
4
4
|
|
5
|
-
from deprecation import deprecated
|
6
|
-
|
7
5
|
from .odxtypes import ParameterValue, ParameterValueDict
|
8
6
|
|
9
7
|
if TYPE_CHECKING:
|
@@ -29,8 +27,3 @@ class Message:
|
|
29
27
|
|
30
28
|
def __getitem__(self, key: str) -> ParameterValue:
|
31
29
|
return self.param_dict[key]
|
32
|
-
|
33
|
-
@property
|
34
|
-
@deprecated("use .coding_object") # type: ignore[misc]
|
35
|
-
def structure(self) -> Union["Request", "Response"]:
|
36
|
-
return self.coding_object
|
odxtools/minmaxlengthtype.py
CHANGED
@@ -1,12 +1,17 @@
|
|
1
1
|
# SPDX-License-Identifier: MIT
|
2
2
|
from dataclasses import dataclass
|
3
|
-
from typing import Optional
|
3
|
+
from typing import List, Optional
|
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 DecodeError, EncodeError, odxassert, odxraise
|
11
|
+
from .exceptions import DecodeError, 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
|
@@ -15,6 +20,25 @@ class MinMaxLengthType(DiagCodedType):
|
|
15
20
|
max_length: Optional[int]
|
16
21
|
termination: str
|
17
22
|
|
23
|
+
@property
|
24
|
+
def dct_type(self) -> DctType:
|
25
|
+
return "MIN-MAX-LENGTH-TYPE"
|
26
|
+
|
27
|
+
@staticmethod
|
28
|
+
@override
|
29
|
+
def from_et(et_element: ElementTree.Element,
|
30
|
+
doc_frags: List[OdxDocFragment]) -> "MinMaxLengthType":
|
31
|
+
kwargs = dataclass_fields_asdict(DiagCodedType.from_et(et_element, doc_frags))
|
32
|
+
|
33
|
+
min_length = int(odxrequire(et_element.findtext("MIN-LENGTH")))
|
34
|
+
max_length = None
|
35
|
+
if et_element.find("MAX-LENGTH") is not None:
|
36
|
+
max_length = int(odxrequire(et_element.findtext("MAX-LENGTH")))
|
37
|
+
termination = odxrequire(et_element.get("TERMINATION"))
|
38
|
+
|
39
|
+
return MinMaxLengthType(
|
40
|
+
min_length=min_length, max_length=max_length, termination=termination, **kwargs)
|
41
|
+
|
18
42
|
def __post_init__(self) -> None:
|
19
43
|
odxassert(self.max_length is None or self.min_length <= self.max_length)
|
20
44
|
odxassert(
|
@@ -30,10 +54,6 @@ class MinMaxLengthType(DiagCodedType):
|
|
30
54
|
"END-OF-PDU",
|
31
55
|
], f"A min-max length type cannot have the termination {self.termination}")
|
32
56
|
|
33
|
-
@property
|
34
|
-
def dct_type(self) -> DctType:
|
35
|
-
return "MIN-MAX-LENGTH-TYPE"
|
36
|
-
|
37
57
|
def __termination_sequence(self) -> bytes:
|
38
58
|
"""Returns the termination byte sequence if it isn't defined."""
|
39
59
|
# The termination sequence is actually not specified by ASAM
|
@@ -51,9 +71,10 @@ class MinMaxLengthType(DiagCodedType):
|
|
51
71
|
termination_sequence = bytes([0xFF, 0xFF])
|
52
72
|
return termination_sequence
|
53
73
|
|
54
|
-
|
55
|
-
|
56
|
-
|
74
|
+
@override
|
75
|
+
def encode_into_pdu(self, internal_value: AtomicOdxType, encode_state: EncodeState) -> None:
|
76
|
+
|
77
|
+
if not isinstance(internal_value, (bytes, str, bytearray)):
|
57
78
|
odxraise("MinMaxLengthType is currently only implemented for strings and byte arrays",
|
58
79
|
EncodeError)
|
59
80
|
|
@@ -62,20 +83,24 @@ class MinMaxLengthType(DiagCodedType):
|
|
62
83
|
else:
|
63
84
|
data_length = len(internal_value)
|
64
85
|
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
86
|
+
orig_cursor = encode_state.cursor_byte_position
|
87
|
+
encode_state.emplace_atomic_value(
|
88
|
+
internal_value=internal_value,
|
89
|
+
used_mask=None,
|
90
|
+
bit_length=8 * data_length,
|
91
|
+
base_data_type=self.base_data_type,
|
92
|
+
is_highlow_byte_order=self.is_highlow_byte_order,
|
93
|
+
)
|
94
|
+
value_len = encode_state.cursor_byte_position - orig_cursor
|
73
95
|
|
74
96
|
# TODO: ensure that the termination delimiter is not
|
75
97
|
# encountered within the encoded value.
|
76
98
|
|
77
|
-
odxassert(
|
78
|
-
|
99
|
+
odxassert(
|
100
|
+
self.termination != "END-OF-PDU" or encode_state.is_end_of_pdu,
|
101
|
+
"Encountered a MIN-MAX-LENGTH type with END-OF-PDU termination "
|
102
|
+
"which is not located at the end of the PDU")
|
103
|
+
if encode_state.is_end_of_pdu or value_len == self.max_length:
|
79
104
|
# All termination types may be ended by the end of the PDU
|
80
105
|
# or once reaching the maximum length. In this case, we
|
81
106
|
# must not add the termination sequence
|
@@ -85,20 +110,23 @@ class MinMaxLengthType(DiagCodedType):
|
|
85
110
|
|
86
111
|
# ensure that we don't try to encode an odd-length
|
87
112
|
# value when using a two-byte terminator
|
88
|
-
odxassert(
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
113
|
+
odxassert(value_len % len(termination_sequence) == 0)
|
114
|
+
|
115
|
+
value_len += len(termination_sequence)
|
116
|
+
encode_state.emplace_bytes(termination_sequence)
|
117
|
+
|
118
|
+
if value_len < self.min_length:
|
119
|
+
odxraise(
|
120
|
+
f"Encoded value for MinMaxLengthType "
|
121
|
+
f"must be at least {self.min_length} bytes long. "
|
122
|
+
f"(Is: {value_len} bytes.)", EncodeError)
|
123
|
+
return
|
124
|
+
elif self.max_length is not None and value_len > self.max_length:
|
125
|
+
odxraise(
|
126
|
+
f"Encoded value for MinMaxLengthType "
|
127
|
+
f"must not be longer than {self.max_length} bytes. "
|
128
|
+
f"(Is: {value_len} bytes.)", EncodeError)
|
129
|
+
return
|
102
130
|
|
103
131
|
def decode_from_pdu(self, decode_state: DecodeState) -> AtomicOdxType:
|
104
132
|
odxassert(decode_state.cursor_bit_position == 0,
|
odxtools/modification.py
CHANGED
@@ -1,12 +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, Optional
|
4
4
|
from xml.etree import ElementTree
|
5
5
|
|
6
6
|
from .odxlink import OdxDocFragment, OdxLinkDatabase, OdxLinkId
|
7
|
-
|
8
|
-
if TYPE_CHECKING:
|
9
|
-
from .diaglayer import DiagLayer
|
7
|
+
from .snrefcontext import SnRefContext
|
10
8
|
|
11
9
|
|
12
10
|
@dataclass
|
@@ -27,5 +25,5 @@ class Modification:
|
|
27
25
|
def _resolve_odxlinks(self, odxlinks: OdxLinkDatabase) -> None:
|
28
26
|
pass
|
29
27
|
|
30
|
-
def _resolve_snrefs(self,
|
28
|
+
def _resolve_snrefs(self, context: SnRefContext) -> None:
|
31
29
|
pass
|
odxtools/multiplexer.py
CHANGED
@@ -1,22 +1,23 @@
|
|
1
1
|
# SPDX-License-Identifier: MIT
|
2
2
|
from dataclasses import dataclass
|
3
|
-
from typing import
|
3
|
+
from typing import Any, Dict, List, Optional, Tuple, Union, cast
|
4
4
|
from xml.etree import ElementTree
|
5
5
|
|
6
|
+
from typing_extensions import override
|
7
|
+
|
6
8
|
from .complexdop import ComplexDop
|
7
9
|
from .decodestate import DecodeState
|
8
10
|
from .encodestate import EncodeState
|
9
|
-
from .exceptions import DecodeError, EncodeError, odxraise, odxrequire
|
11
|
+
from .exceptions import DecodeError, EncodeError, odxassert, odxraise, odxrequire
|
10
12
|
from .multiplexercase import MultiplexerCase
|
11
13
|
from .multiplexerdefaultcase import MultiplexerDefaultCase
|
12
14
|
from .multiplexerswitchkey import MultiplexerSwitchKey
|
15
|
+
from .nameditemlist import NamedItemList
|
13
16
|
from .odxlink import OdxDocFragment, OdxLinkDatabase, OdxLinkId
|
14
17
|
from .odxtypes import AtomicOdxType, ParameterValue, odxstr_to_bool
|
18
|
+
from .snrefcontext import SnRefContext
|
15
19
|
from .utils import dataclass_fields_asdict
|
16
20
|
|
17
|
-
if TYPE_CHECKING:
|
18
|
-
from .diaglayer import DiagLayer
|
19
|
-
|
20
21
|
|
21
22
|
@dataclass
|
22
23
|
class Multiplexer(ComplexDop):
|
@@ -30,10 +31,11 @@ class Multiplexer(ComplexDop):
|
|
30
31
|
byte_position: int
|
31
32
|
switch_key: MultiplexerSwitchKey
|
32
33
|
default_case: Optional[MultiplexerDefaultCase]
|
33
|
-
cases:
|
34
|
+
cases: NamedItemList[MultiplexerCase]
|
34
35
|
is_visible_raw: Optional[bool]
|
35
36
|
|
36
37
|
@staticmethod
|
38
|
+
@override
|
37
39
|
def from_et(et_element: ElementTree.Element, doc_frags: List[OdxDocFragment]) -> "Multiplexer":
|
38
40
|
"""Reads a Multiplexer from Diag Layer."""
|
39
41
|
base_obj = ComplexDop.from_et(et_element, doc_frags)
|
@@ -47,9 +49,8 @@ class Multiplexer(ComplexDop):
|
|
47
49
|
if (dc_elem := et_element.find("DEFAULT-CASE")) is not None:
|
48
50
|
default_case = MultiplexerDefaultCase.from_et(dc_elem, doc_frags)
|
49
51
|
|
50
|
-
cases =
|
51
|
-
|
52
|
-
cases = [MultiplexerCase.from_et(el, doc_frags) for el in cases_elem.iterfind("CASE")]
|
52
|
+
cases = NamedItemList(
|
53
|
+
[MultiplexerCase.from_et(el, doc_frags) for el in et_element.iterfind("CASES/CASE")])
|
53
54
|
|
54
55
|
is_visible_raw = odxstr_to_bool(et_element.get("IS-VISIBLE"))
|
55
56
|
|
@@ -67,91 +68,143 @@ class Multiplexer(ComplexDop):
|
|
67
68
|
|
68
69
|
def _get_case_limits(self, case: MultiplexerCase) -> Tuple[AtomicOdxType, AtomicOdxType]:
|
69
70
|
key_type = self.switch_key.dop.physical_type.base_data_type
|
70
|
-
lower_limit = key_type.make_from(case.lower_limit)
|
71
|
-
upper_limit = key_type.make_from(case.upper_limit)
|
71
|
+
lower_limit = key_type.make_from(case.lower_limit.value)
|
72
|
+
upper_limit = key_type.make_from(case.upper_limit.value)
|
72
73
|
if not isinstance(lower_limit, type(upper_limit)) and not isinstance(
|
73
74
|
upper_limit, type(lower_limit)):
|
74
75
|
odxraise("Upper and lower bounds of limits must compareable")
|
75
76
|
return lower_limit, upper_limit
|
76
77
|
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
78
|
+
@override
|
79
|
+
def encode_into_pdu(self, physical_value: ParameterValue, encode_state: EncodeState) -> None:
|
80
|
+
|
81
|
+
if encode_state.cursor_bit_position != 0:
|
82
|
+
raise EncodeError(f"Multiplexer parameters must be aligned, i.e. bit_position=0, but "
|
83
|
+
f"{self.short_name} was passed the bit position "
|
84
|
+
f"{encode_state.cursor_bit_position}")
|
85
|
+
|
86
|
+
orig_origin = encode_state.origin_byte_position
|
87
|
+
encode_state.origin_byte_position = encode_state.cursor_byte_position
|
88
|
+
|
89
|
+
if isinstance(physical_value, (list, tuple)) and len(physical_value) == 2:
|
90
|
+
case_spec, case_value = physical_value
|
91
|
+
elif isinstance(physical_value, dict) and len(physical_value) == 1:
|
92
|
+
case_spec, case_value = next(iter(physical_value.items()))
|
93
|
+
else:
|
94
|
+
raise EncodeError(
|
95
|
+
f"Values of multiplexer parameters must be defined as a "
|
96
|
+
f"(case_name, content_value) tuple instead of as '{physical_value!r}'")
|
97
|
+
|
98
|
+
mux_case: Union[MultiplexerCase, MultiplexerDefaultCase]
|
99
|
+
applicable_cases: List[Union[MultiplexerCase, MultiplexerDefaultCase]]
|
100
|
+
|
101
|
+
if isinstance(case_spec, str):
|
102
|
+
applicable_cases = [x for x in self.cases if x.short_name == case_spec]
|
103
|
+
if not applicable_cases and self.default_case:
|
104
|
+
applicable_cases.append(self.default_case)
|
105
|
+
if len(applicable_cases) == 0:
|
106
|
+
raise EncodeError(
|
107
|
+
f"Multiplexer {self.short_name} does not know any case called {case_spec}")
|
108
|
+
|
109
|
+
odxassert(len(applicable_cases) == 1)
|
110
|
+
mux_case = applicable_cases[0]
|
111
|
+
if isinstance(mux_case, MultiplexerCase):
|
101
112
|
key_value, _ = self._get_case_limits(mux_case)
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
113
|
+
else:
|
114
|
+
key_value = 0
|
115
|
+
elif isinstance(case_spec, int):
|
116
|
+
applicable_cases = []
|
117
|
+
for x in self.cases:
|
118
|
+
lower, upper = cast(Tuple[int, int], self._get_case_limits(x))
|
119
|
+
if lower <= case_spec and case_spec <= upper:
|
120
|
+
applicable_cases.append(x)
|
121
|
+
|
122
|
+
if len(applicable_cases) == 0:
|
123
|
+
if self.default_case is None:
|
124
|
+
raise EncodeError(
|
125
|
+
f"Multiplexer {self.short_name} does not know any case called {case_spec}")
|
126
|
+
mux_case = self.default_case
|
127
|
+
key_value = case_spec
|
128
|
+
else:
|
129
|
+
mux_case = applicable_cases[0]
|
130
|
+
key_value = case_spec
|
131
|
+
elif isinstance(case_spec, MultiplexerCase):
|
132
|
+
mux_case = case_spec
|
133
|
+
key_value, _ = self._get_case_limits(mux_case)
|
134
|
+
elif case_spec is None:
|
135
|
+
if self.default_case is None:
|
136
|
+
raise EncodeError(f"Multiplexer {self.short_name} does not define a default case")
|
137
|
+
key_value = 0
|
138
|
+
else:
|
139
|
+
raise EncodeError(f"Illegal case specification '{case_spec}' for "
|
140
|
+
f"multiplexer {self.short_name}")
|
141
|
+
|
142
|
+
# the byte position of the switch key is relative to
|
143
|
+
# the multiplexer's position
|
144
|
+
encode_state.cursor_byte_position = encode_state.origin_byte_position + self.switch_key.byte_position
|
145
|
+
encode_state.cursor_bit_position = self.switch_key.bit_position or 0
|
146
|
+
self.switch_key.dop.encode_into_pdu(physical_value=key_value, encode_state=encode_state)
|
147
|
+
encode_state.cursor_bit_position = 0
|
148
|
+
|
149
|
+
if mux_case.structure is not None:
|
150
|
+
# the byte position of the content is specified by the
|
151
|
+
# BYTE-POSITION attribute of the multiplexer
|
152
|
+
encode_state.cursor_byte_position = encode_state.origin_byte_position + self.byte_position
|
153
|
+
mux_case.structure.encode_into_pdu(physical_value=case_value, encode_state=encode_state)
|
154
|
+
|
155
|
+
encode_state.origin_byte_position = orig_origin
|
156
|
+
|
157
|
+
@override
|
114
158
|
def decode_from_pdu(self, decode_state: DecodeState) -> ParameterValue:
|
115
|
-
|
116
|
-
# multiplexers are structures and thus the origin position
|
117
|
-
# must be moved to the start of the multiplexer
|
118
159
|
orig_origin = decode_state.origin_byte_position
|
119
|
-
orig_cursor = decode_state.cursor_byte_position
|
120
|
-
if self.byte_position is not None:
|
121
|
-
decode_state.cursor_byte_position = decode_state.origin_byte_position + self.byte_position
|
122
160
|
decode_state.origin_byte_position = decode_state.cursor_byte_position
|
123
161
|
|
162
|
+
# Decode the switch key. Its BYTE-POSITION is relative to the
|
163
|
+
# that of the multiplexer.
|
164
|
+
if self.switch_key.byte_position is not None:
|
165
|
+
decode_state.cursor_byte_position = decode_state.origin_byte_position + self.switch_key.byte_position
|
166
|
+
decode_state.cursor_bit_position = self.switch_key.bit_position or 0
|
124
167
|
key_value = self.switch_key.dop.decode_from_pdu(decode_state)
|
168
|
+
decode_state.cursor_bit_position = 0
|
125
169
|
|
126
170
|
if not isinstance(key_value, int):
|
127
171
|
odxraise(f"Multiplexer keys must be integers (is '{type(key_value).__name__}'"
|
128
172
|
f" for multiplexer '{self.short_name}')")
|
129
173
|
|
130
|
-
|
131
|
-
|
132
|
-
|
174
|
+
# "If a matching CASE is found, the referenced STRUCTURE is
|
175
|
+
# analyzed at the BYTE-POSITION (child element of MUX)
|
176
|
+
# relatively to the byte position of the MUX."
|
177
|
+
decode_state.cursor_byte_position = decode_state.origin_byte_position + self.byte_position
|
178
|
+
|
179
|
+
applicable_case: Optional[Union[MultiplexerCase, MultiplexerDefaultCase]] = None
|
180
|
+
for mux_case in self.cases:
|
133
181
|
lower, upper = self._get_case_limits(mux_case)
|
134
182
|
if lower <= key_value and key_value <= upper: # type: ignore[operator]
|
135
|
-
|
136
|
-
case_value = mux_case._structure.decode_from_pdu(decode_state)
|
183
|
+
applicable_case = mux_case
|
137
184
|
break
|
138
185
|
|
139
|
-
if
|
140
|
-
|
141
|
-
|
186
|
+
if applicable_case is None:
|
187
|
+
applicable_case = self.default_case
|
188
|
+
|
189
|
+
if applicable_case is None:
|
190
|
+
odxraise(
|
191
|
+
f"Cannot find an applicable case for value {key_value} in "
|
192
|
+
f"multiplexer {self.short_name}", DecodeError)
|
193
|
+
decode_state.origin_byte_position = orig_origin
|
194
|
+
return (None, None)
|
142
195
|
|
143
|
-
if
|
144
|
-
|
145
|
-
|
196
|
+
if applicable_case.structure is not None:
|
197
|
+
case_value = applicable_case.structure.decode_from_pdu(decode_state)
|
198
|
+
else:
|
199
|
+
case_value = {}
|
146
200
|
|
147
|
-
|
201
|
+
result = (applicable_case.short_name, case_value)
|
148
202
|
|
149
|
-
# go back to the original origin
|
150
203
|
decode_state.origin_byte_position = orig_origin
|
151
|
-
decode_state.cursor_byte_position = max(orig_cursor, decode_state.cursor_byte_position)
|
152
204
|
|
153
|
-
return
|
205
|
+
return result
|
154
206
|
|
207
|
+
@override
|
155
208
|
def _build_odxlinks(self) -> Dict[OdxLinkId, Any]:
|
156
209
|
odxlinks = super()._build_odxlinks()
|
157
210
|
|
@@ -161,6 +214,7 @@ class Multiplexer(ComplexDop):
|
|
161
214
|
|
162
215
|
return odxlinks
|
163
216
|
|
217
|
+
@override
|
164
218
|
def _resolve_odxlinks(self, odxlinks: OdxLinkDatabase) -> None:
|
165
219
|
super()._resolve_odxlinks(odxlinks)
|
166
220
|
|
@@ -172,12 +226,13 @@ class Multiplexer(ComplexDop):
|
|
172
226
|
mux_case._mux_case_resolve_odxlinks(
|
173
227
|
odxlinks, key_physical_type=self.switch_key.dop.physical_type.base_data_type)
|
174
228
|
|
175
|
-
|
176
|
-
|
229
|
+
@override
|
230
|
+
def _resolve_snrefs(self, context: SnRefContext) -> None:
|
231
|
+
super()._resolve_snrefs(context)
|
177
232
|
|
178
|
-
self.switch_key._resolve_snrefs(
|
233
|
+
self.switch_key._resolve_snrefs(context)
|
179
234
|
if self.default_case is not None:
|
180
|
-
self.default_case._resolve_snrefs(
|
235
|
+
self.default_case._resolve_snrefs(context)
|
181
236
|
|
182
237
|
for mux_case in self.cases:
|
183
|
-
mux_case._resolve_snrefs(
|
238
|
+
mux_case._resolve_snrefs(context)
|
odxtools/multiplexercase.py
CHANGED
@@ -1,23 +1,21 @@
|
|
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 .basicstructure import BasicStructure
|
7
6
|
from .compumethods.limit import Limit
|
8
7
|
from .element import NamedElement
|
9
8
|
from .exceptions import odxrequire
|
10
|
-
from .odxlink import OdxDocFragment, OdxLinkDatabase, OdxLinkId, OdxLinkRef
|
9
|
+
from .odxlink import OdxDocFragment, OdxLinkDatabase, OdxLinkId, OdxLinkRef, resolve_snref
|
11
10
|
from .odxtypes import AtomicOdxType, DataType
|
11
|
+
from .snrefcontext import SnRefContext
|
12
|
+
from .structure import Structure
|
12
13
|
from .utils import dataclass_fields_asdict
|
13
14
|
|
14
|
-
if TYPE_CHECKING:
|
15
|
-
from .diaglayer import DiagLayer
|
16
|
-
|
17
15
|
|
18
16
|
@dataclass
|
19
17
|
class MultiplexerCase(NamedElement):
|
20
|
-
"""This class represents a
|
18
|
+
"""This class represents a case which represents a range of keys of a multiplexer."""
|
21
19
|
|
22
20
|
structure_ref: Optional[OdxLinkRef]
|
23
21
|
structure_snref: Optional[str]
|
@@ -25,7 +23,7 @@ class MultiplexerCase(NamedElement):
|
|
25
23
|
upper_limit: Limit
|
26
24
|
|
27
25
|
def __post_init__(self) -> None:
|
28
|
-
self._structure:
|
26
|
+
self._structure: Optional[Structure]
|
29
27
|
|
30
28
|
@staticmethod
|
31
29
|
def from_et(et_element: ElementTree.Element,
|
@@ -60,25 +58,26 @@ class MultiplexerCase(NamedElement):
|
|
60
58
|
|
61
59
|
def _resolve_odxlinks(self, odxlinks: OdxLinkDatabase) -> None:
|
62
60
|
raise RuntimeError("Calling MultiplexerCase._resolve_odxlinks() is not allowed. "
|
63
|
-
"Use ._mux_case_resolve_odxlinks()
|
61
|
+
"Use ._mux_case_resolve_odxlinks().")
|
64
62
|
|
65
63
|
def _mux_case_resolve_odxlinks(self, odxlinks: OdxLinkDatabase, *,
|
66
64
|
key_physical_type: DataType) -> None:
|
65
|
+
self._structure = None
|
67
66
|
if self.structure_ref:
|
68
|
-
self._structure = odxlinks.resolve(self.structure_ref)
|
67
|
+
self._structure = odxlinks.resolve(self.structure_ref, Structure)
|
69
68
|
|
70
69
|
self.lower_limit.set_value_type(key_physical_type)
|
71
70
|
self.upper_limit.set_value_type(key_physical_type)
|
72
71
|
|
73
|
-
def _resolve_snrefs(self,
|
72
|
+
def _resolve_snrefs(self, context: SnRefContext) -> None:
|
74
73
|
if self.structure_snref:
|
75
|
-
ddds = diag_layer.diag_data_dictionary_spec
|
76
|
-
self._structure =
|
74
|
+
ddds = odxrequire(context.diag_layer).diag_data_dictionary_spec
|
75
|
+
self._structure = resolve_snref(self.structure_snref, ddds.structures, Structure)
|
77
76
|
|
78
77
|
def applies(self, value: AtomicOdxType) -> bool:
|
79
78
|
return self.lower_limit.complies_to_lower(value) \
|
80
79
|
and self.upper_limit.complies_to_upper(value)
|
81
80
|
|
82
81
|
@property
|
83
|
-
def structure(self) ->
|
82
|
+
def structure(self) -> Optional[Structure]:
|
84
83
|
return self._structure
|
@@ -1,17 +1,15 @@
|
|
1
1
|
# SPDX-License-Identifier: MIT
|
2
2
|
from dataclasses import dataclass
|
3
|
-
from typing import
|
3
|
+
from typing import Any, Dict, List, Optional
|
4
4
|
from xml.etree import ElementTree
|
5
5
|
|
6
|
-
from .basicstructure import BasicStructure
|
7
6
|
from .element import NamedElement
|
8
7
|
from .exceptions import odxrequire
|
9
|
-
from .odxlink import OdxDocFragment, OdxLinkDatabase, OdxLinkId, OdxLinkRef
|
8
|
+
from .odxlink import OdxDocFragment, OdxLinkDatabase, OdxLinkId, OdxLinkRef, resolve_snref
|
9
|
+
from .snrefcontext import SnRefContext
|
10
|
+
from .structure import Structure
|
10
11
|
from .utils import dataclass_fields_asdict
|
11
12
|
|
12
|
-
if TYPE_CHECKING:
|
13
|
-
from .diaglayer import DiagLayer
|
14
|
-
|
15
13
|
|
16
14
|
@dataclass
|
17
15
|
class MultiplexerDefaultCase(NamedElement):
|
@@ -20,12 +18,12 @@ class MultiplexerDefaultCase(NamedElement):
|
|
20
18
|
structure_snref: Optional[str]
|
21
19
|
|
22
20
|
def __post_init__(self) -> None:
|
23
|
-
self._structure: Optional[
|
21
|
+
self._structure: Optional[Structure]
|
24
22
|
|
25
23
|
@staticmethod
|
26
24
|
def from_et(et_element: ElementTree.Element,
|
27
25
|
doc_frags: List[OdxDocFragment]) -> "MultiplexerDefaultCase":
|
28
|
-
"""Reads a
|
26
|
+
"""Reads a default case for a multiplexer."""
|
29
27
|
kwargs = dataclass_fields_asdict(NamedElement.from_et(et_element, doc_frags))
|
30
28
|
|
31
29
|
structure_ref = OdxLinkRef.from_et(et_element.find("STRUCTURE-REF"), doc_frags)
|
@@ -40,10 +38,15 @@ class MultiplexerDefaultCase(NamedElement):
|
|
40
38
|
return {}
|
41
39
|
|
42
40
|
def _resolve_odxlinks(self, odxlinks: OdxLinkDatabase) -> None:
|
41
|
+
self._structure = None
|
43
42
|
if self.structure_ref is not None:
|
44
|
-
self._structure = odxlinks.resolve(self.structure_ref)
|
43
|
+
self._structure = odxlinks.resolve(self.structure_ref, Structure)
|
45
44
|
|
46
|
-
def _resolve_snrefs(self,
|
45
|
+
def _resolve_snrefs(self, context: SnRefContext) -> None:
|
47
46
|
if self.structure_snref:
|
48
|
-
ddds = diag_layer.diag_data_dictionary_spec
|
49
|
-
self._structure =
|
47
|
+
ddds = odxrequire(context.diag_layer).diag_data_dictionary_spec
|
48
|
+
self._structure = resolve_snref(self.structure_snref, ddds.structures, Structure)
|
49
|
+
|
50
|
+
@property
|
51
|
+
def structure(self) -> Optional[Structure]:
|
52
|
+
return self._structure
|
odxtools/multiplexerswitchkey.py
CHANGED
@@ -1,13 +1,12 @@
|
|
1
|
+
# SPDX-License-Identifier: MIT
|
1
2
|
from dataclasses import dataclass
|
2
|
-
from typing import
|
3
|
+
from typing import Any, Dict, List, Optional
|
3
4
|
from xml.etree import ElementTree
|
4
5
|
|
5
6
|
from .dataobjectproperty import DataObjectProperty
|
6
7
|
from .exceptions import odxrequire
|
7
8
|
from .odxlink import OdxDocFragment, OdxLinkDatabase, OdxLinkId, OdxLinkRef
|
8
|
-
|
9
|
-
if TYPE_CHECKING:
|
10
|
-
from .diaglayer import DiagLayer
|
9
|
+
from .snrefcontext import SnRefContext
|
11
10
|
|
12
11
|
|
13
12
|
@dataclass
|
@@ -39,7 +38,7 @@ class MultiplexerSwitchKey:
|
|
39
38
|
def _resolve_odxlinks(self, odxlinks: OdxLinkDatabase) -> None:
|
40
39
|
self._dop = odxlinks.resolve(self.dop_ref, DataObjectProperty)
|
41
40
|
|
42
|
-
def _resolve_snrefs(self,
|
41
|
+
def _resolve_snrefs(self, context: SnRefContext) -> None:
|
43
42
|
pass
|
44
43
|
|
45
44
|
@property
|