odxtools 6.7.1__py3-none-any.whl → 7.1.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/basicstructure.py +102 -112
- odxtools/dataobjectproperty.py +3 -7
- odxtools/decodestate.py +1 -13
- odxtools/diagcodedtype.py +9 -96
- odxtools/diagcomm.py +11 -3
- odxtools/diagdatadictionaryspec.py +14 -14
- odxtools/diaglayer.py +52 -1
- odxtools/diaglayerraw.py +13 -7
- odxtools/diagservice.py +12 -16
- odxtools/dopbase.py +5 -8
- odxtools/dtcdop.py +21 -19
- odxtools/dynamicendmarkerfield.py +119 -0
- odxtools/dynamiclengthfield.py +39 -29
- odxtools/dynenddopref.py +38 -0
- odxtools/encodestate.py +188 -23
- odxtools/endofpdufield.py +33 -18
- odxtools/environmentdata.py +8 -1
- odxtools/environmentdatadescription.py +21 -15
- odxtools/field.py +4 -3
- odxtools/leadinglengthinfotype.py +25 -12
- odxtools/matchingparameter.py +2 -2
- odxtools/minmaxlengthtype.py +36 -26
- odxtools/multiplexer.py +42 -23
- odxtools/multiplexercase.py +3 -3
- odxtools/multiplexerdefaultcase.py +7 -3
- odxtools/nameditemlist.py +14 -0
- odxtools/odxlink.py +38 -4
- odxtools/odxtypes.py +20 -2
- odxtools/parameterinfo.py +126 -40
- odxtools/parameters/codedconstparameter.py +17 -13
- odxtools/parameters/dynamicparameter.py +5 -4
- odxtools/parameters/lengthkeyparameter.py +66 -17
- odxtools/parameters/matchingrequestparameter.py +23 -11
- odxtools/parameters/nrcconstparameter.py +42 -22
- odxtools/parameters/parameter.py +35 -42
- odxtools/parameters/parameterwithdop.py +15 -22
- odxtools/parameters/physicalconstantparameter.py +16 -16
- odxtools/parameters/reservedparameter.py +5 -2
- odxtools/parameters/systemparameter.py +3 -2
- odxtools/parameters/tableentryparameter.py +3 -2
- odxtools/parameters/tablekeyparameter.py +88 -39
- odxtools/parameters/tablestructparameter.py +45 -44
- odxtools/parameters/valueparameter.py +16 -17
- odxtools/paramlengthinfotype.py +30 -22
- odxtools/request.py +9 -0
- odxtools/response.py +5 -13
- odxtools/standardlengthtype.py +51 -13
- odxtools/statechart.py +5 -9
- odxtools/statetransition.py +3 -8
- odxtools/staticfield.py +30 -20
- odxtools/tablerow.py +5 -3
- odxtools/templates/macros/printDynamicEndmarkerField.xml.jinja2 +16 -0
- odxtools/templates/macros/printVariant.xml.jinja2 +8 -0
- odxtools/version.py +2 -2
- {odxtools-6.7.1.dist-info → odxtools-7.1.0.dist-info}/METADATA +1 -1
- {odxtools-6.7.1.dist-info → odxtools-7.1.0.dist-info}/RECORD +60 -57
- {odxtools-6.7.1.dist-info → odxtools-7.1.0.dist-info}/LICENSE +0 -0
- {odxtools-6.7.1.dist-info → odxtools-7.1.0.dist-info}/WHEEL +0 -0
- {odxtools-6.7.1.dist-info → odxtools-7.1.0.dist-info}/entry_points.txt +0 -0
- {odxtools-6.7.1.dist-info → odxtools-7.1.0.dist-info}/top_level.txt +0 -0
odxtools/multiplexer.py
CHANGED
@@ -3,6 +3,8 @@ from dataclasses import dataclass
|
|
3
3
|
from typing import TYPE_CHECKING, Any, Dict, List, Optional, Tuple
|
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
|
@@ -34,6 +36,7 @@ class Multiplexer(ComplexDop):
|
|
34
36
|
is_visible_raw: Optional[bool]
|
35
37
|
|
36
38
|
@staticmethod
|
39
|
+
@override
|
37
40
|
def from_et(et_element: ElementTree.Element, doc_frags: List[OdxDocFragment]) -> "Multiplexer":
|
38
41
|
"""Reads a Multiplexer from Diag Layer."""
|
39
42
|
base_obj = ComplexDop.from_et(et_element, doc_frags)
|
@@ -67,50 +70,63 @@ class Multiplexer(ComplexDop):
|
|
67
70
|
|
68
71
|
def _get_case_limits(self, case: MultiplexerCase) -> Tuple[AtomicOdxType, AtomicOdxType]:
|
69
72
|
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)
|
73
|
+
lower_limit = key_type.make_from(case.lower_limit.value)
|
74
|
+
upper_limit = key_type.make_from(case.upper_limit.value)
|
72
75
|
if not isinstance(lower_limit, type(upper_limit)) and not isinstance(
|
73
76
|
upper_limit, type(lower_limit)):
|
74
77
|
odxraise("Upper and lower bounds of limits must compareable")
|
75
78
|
return lower_limit, upper_limit
|
76
79
|
|
77
|
-
|
78
|
-
|
79
|
-
encode_state: EncodeState,
|
80
|
-
bit_position: int = 0) -> bytes:
|
80
|
+
@override
|
81
|
+
def encode_into_pdu(self, physical_value: ParameterValue, encode_state: EncodeState) -> None:
|
81
82
|
|
82
|
-
if
|
83
|
-
raise EncodeError("Multiplexer must be aligned, i.e. bit_position=0, but "
|
84
|
-
f"{self.short_name} was passed the bit position
|
83
|
+
if encode_state.cursor_bit_position != 0:
|
84
|
+
raise EncodeError(f"Multiplexer must be aligned, i.e. bit_position=0, but "
|
85
|
+
f"{self.short_name} was passed the bit position "
|
86
|
+
f"{encode_state.cursor_bit_position}")
|
85
87
|
|
86
88
|
if not isinstance(physical_value, dict) or len(physical_value) != 1:
|
87
89
|
raise EncodeError("""Multiplexer should be defined as a dict
|
88
90
|
with only one key equal to the desired case""")
|
89
91
|
|
92
|
+
orig_origin = encode_state.origin_byte_position
|
93
|
+
orig_cursor = encode_state.cursor_byte_position
|
94
|
+
|
95
|
+
encode_state.origin_byte_position = encode_state.cursor_byte_position
|
96
|
+
|
90
97
|
case_name, case_value = next(iter(physical_value.items()))
|
91
|
-
case_pos = self.byte_position
|
92
98
|
|
93
99
|
for mux_case in self.cases or []:
|
94
100
|
if mux_case.short_name == case_name:
|
95
|
-
if mux_case._structure:
|
96
|
-
case_bytes = mux_case._structure.convert_physical_to_bytes(
|
97
|
-
case_value, encode_state, 0)
|
98
|
-
else:
|
99
|
-
case_bytes = b''
|
100
|
-
|
101
101
|
key_value, _ = self._get_case_limits(mux_case)
|
102
|
-
key_bytes = self.switch_key.dop.convert_physical_to_bytes(
|
103
|
-
key_value, encode_state, bit_position=self.switch_key.bit_position or 0)
|
104
102
|
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
103
|
+
if self.switch_key.byte_position is not None:
|
104
|
+
encode_state.cursor_byte_position = encode_state.origin_byte_position + self.switch_key.byte_position
|
105
|
+
encode_state.cursor_bit_position = self.switch_key.bit_position or 0
|
106
|
+
|
107
|
+
self.switch_key.dop.encode_into_pdu(
|
108
|
+
physical_value=key_value, encode_state=encode_state)
|
109
|
+
|
110
|
+
if self.byte_position is not None:
|
111
|
+
encode_state.cursor_byte_position = encode_state.origin_byte_position + self.byte_position
|
112
|
+
encode_state.cursor_bit_position = 0
|
113
|
+
|
114
|
+
if mux_case._structure is None:
|
115
|
+
odxraise(f"Multiplexer case '{mux_case.short_name}' does not "
|
116
|
+
f"reference a structure.")
|
117
|
+
return
|
118
|
+
|
119
|
+
mux_case.structure.encode_into_pdu(
|
120
|
+
physical_value=key_value, encode_state=encode_state)
|
109
121
|
|
110
|
-
|
122
|
+
encode_state.origin_byte_position = orig_origin
|
123
|
+
encode_state.cursor_byte_position = max(orig_cursor,
|
124
|
+
encode_state.cursor_byte_position)
|
125
|
+
return
|
111
126
|
|
112
127
|
raise EncodeError(f"The case {case_name} is not found in Multiplexer {self.short_name}")
|
113
128
|
|
129
|
+
@override
|
114
130
|
def decode_from_pdu(self, decode_state: DecodeState) -> ParameterValue:
|
115
131
|
|
116
132
|
# multiplexers are structures and thus the origin position
|
@@ -152,6 +168,7 @@ class Multiplexer(ComplexDop):
|
|
152
168
|
|
153
169
|
return mux_value
|
154
170
|
|
171
|
+
@override
|
155
172
|
def _build_odxlinks(self) -> Dict[OdxLinkId, Any]:
|
156
173
|
odxlinks = super()._build_odxlinks()
|
157
174
|
|
@@ -161,6 +178,7 @@ class Multiplexer(ComplexDop):
|
|
161
178
|
|
162
179
|
return odxlinks
|
163
180
|
|
181
|
+
@override
|
164
182
|
def _resolve_odxlinks(self, odxlinks: OdxLinkDatabase) -> None:
|
165
183
|
super()._resolve_odxlinks(odxlinks)
|
166
184
|
|
@@ -172,6 +190,7 @@ class Multiplexer(ComplexDop):
|
|
172
190
|
mux_case._mux_case_resolve_odxlinks(
|
173
191
|
odxlinks, key_physical_type=self.switch_key.dop.physical_type.base_data_type)
|
174
192
|
|
193
|
+
@override
|
175
194
|
def _resolve_snrefs(self, diag_layer: "DiagLayer") -> None:
|
176
195
|
super()._resolve_snrefs(diag_layer)
|
177
196
|
|
odxtools/multiplexercase.py
CHANGED
@@ -7,7 +7,7 @@ from .basicstructure import BasicStructure
|
|
7
7
|
from .compumethods.limit import Limit
|
8
8
|
from .element import NamedElement
|
9
9
|
from .exceptions import odxrequire
|
10
|
-
from .odxlink import OdxDocFragment, OdxLinkDatabase, OdxLinkId, OdxLinkRef
|
10
|
+
from .odxlink import OdxDocFragment, OdxLinkDatabase, OdxLinkId, OdxLinkRef, resolve_snref
|
11
11
|
from .odxtypes import AtomicOdxType, DataType
|
12
12
|
from .utils import dataclass_fields_asdict
|
13
13
|
|
@@ -60,7 +60,7 @@ class MultiplexerCase(NamedElement):
|
|
60
60
|
|
61
61
|
def _resolve_odxlinks(self, odxlinks: OdxLinkDatabase) -> None:
|
62
62
|
raise RuntimeError("Calling MultiplexerCase._resolve_odxlinks() is not allowed. "
|
63
|
-
"Use ._mux_case_resolve_odxlinks()
|
63
|
+
"Use ._mux_case_resolve_odxlinks().")
|
64
64
|
|
65
65
|
def _mux_case_resolve_odxlinks(self, odxlinks: OdxLinkDatabase, *,
|
66
66
|
key_physical_type: DataType) -> None:
|
@@ -73,7 +73,7 @@ class MultiplexerCase(NamedElement):
|
|
73
73
|
def _resolve_snrefs(self, diag_layer: "DiagLayer") -> None:
|
74
74
|
if self.structure_snref:
|
75
75
|
ddds = diag_layer.diag_data_dictionary_spec
|
76
|
-
self._structure =
|
76
|
+
self._structure = resolve_snref(self.structure_snref, ddds.structures, BasicStructure)
|
77
77
|
|
78
78
|
def applies(self, value: AtomicOdxType) -> bool:
|
79
79
|
return self.lower_limit.complies_to_lower(value) \
|
@@ -6,7 +6,7 @@ from xml.etree import ElementTree
|
|
6
6
|
from .basicstructure import BasicStructure
|
7
7
|
from .element import NamedElement
|
8
8
|
from .exceptions import odxrequire
|
9
|
-
from .odxlink import OdxDocFragment, OdxLinkDatabase, OdxLinkId, OdxLinkRef
|
9
|
+
from .odxlink import OdxDocFragment, OdxLinkDatabase, OdxLinkId, OdxLinkRef, resolve_snref
|
10
10
|
from .utils import dataclass_fields_asdict
|
11
11
|
|
12
12
|
if TYPE_CHECKING:
|
@@ -20,7 +20,7 @@ class MultiplexerDefaultCase(NamedElement):
|
|
20
20
|
structure_snref: Optional[str]
|
21
21
|
|
22
22
|
def __post_init__(self) -> None:
|
23
|
-
self._structure:
|
23
|
+
self._structure: BasicStructure
|
24
24
|
|
25
25
|
@staticmethod
|
26
26
|
def from_et(et_element: ElementTree.Element,
|
@@ -46,4 +46,8 @@ class MultiplexerDefaultCase(NamedElement):
|
|
46
46
|
def _resolve_snrefs(self, diag_layer: "DiagLayer") -> None:
|
47
47
|
if self.structure_snref:
|
48
48
|
ddds = diag_layer.diag_data_dictionary_spec
|
49
|
-
self._structure =
|
49
|
+
self._structure = resolve_snref(self.structure_snref, ddds.structures, BasicStructure)
|
50
|
+
|
51
|
+
@property
|
52
|
+
def structure(self) -> BasicStructure:
|
53
|
+
return self._structure
|
odxtools/nameditemlist.py
CHANGED
@@ -1,5 +1,6 @@
|
|
1
1
|
# SPDX-License-Identifier: MIT
|
2
2
|
import abc
|
3
|
+
from copy import deepcopy
|
3
4
|
from keyword import iskeyword
|
4
5
|
from typing import (Any, Collection, Dict, Iterable, List, Optional, Protocol, SupportsIndex, Tuple,
|
5
6
|
TypeVar, Union, cast, overload, runtime_checkable)
|
@@ -175,6 +176,19 @@ class ItemAttributeList(List[T]):
|
|
175
176
|
def __repr__(self) -> str:
|
176
177
|
return f"{type(self).__name__}([{', '.join([repr(x) for x in self])}])"
|
177
178
|
|
179
|
+
def __copy__(self) -> Any:
|
180
|
+
return self.__class__(list(self))
|
181
|
+
|
182
|
+
def __deepcopy__(self, memo: Dict[int, Any]) -> Any:
|
183
|
+
cls = self.__class__
|
184
|
+
result = cls.__new__(cls)
|
185
|
+
memo[id(self)] = result
|
186
|
+
result._item_dict = {}
|
187
|
+
for x in self:
|
188
|
+
result.append(deepcopy(x, memo))
|
189
|
+
|
190
|
+
return result
|
191
|
+
|
178
192
|
|
179
193
|
class NamedItemList(ItemAttributeList[T]):
|
180
194
|
|
odxtools/odxlink.py
CHANGED
@@ -1,10 +1,11 @@
|
|
1
1
|
# SPDX-License-Identifier: MIT
|
2
2
|
import warnings
|
3
3
|
from dataclasses import dataclass
|
4
|
-
from typing import Any, Dict, List, Optional, Type, TypeVar, overload
|
4
|
+
from typing import Any, Dict, Iterable, List, Optional, Type, TypeVar, overload
|
5
5
|
from xml.etree import ElementTree
|
6
6
|
|
7
|
-
from .exceptions import OdxWarning, odxassert
|
7
|
+
from .exceptions import OdxWarning, odxassert, odxraise
|
8
|
+
from .nameditemlist import OdxNamed, TNamed
|
8
9
|
|
9
10
|
|
10
11
|
@dataclass(frozen=True)
|
@@ -210,8 +211,10 @@ class OdxLinkDatabase:
|
|
210
211
|
|
211
212
|
return obj
|
212
213
|
|
213
|
-
|
214
|
-
|
214
|
+
odxraise(
|
215
|
+
f"ODXLINK reference {ref} could not be resolved for any "
|
216
|
+
f"of the document fragments {ref.ref_docs}", KeyError)
|
217
|
+
return None
|
215
218
|
|
216
219
|
@overload
|
217
220
|
def resolve_lenient(self, ref: OdxLinkRef, expected_type: None = None) -> Any:
|
@@ -270,3 +273,34 @@ class OdxLinkDatabase:
|
|
270
273
|
self._db[doc_frag] = {}
|
271
274
|
|
272
275
|
self._db[doc_frag][odx_id] = obj
|
276
|
+
|
277
|
+
|
278
|
+
@overload
|
279
|
+
def resolve_snref(target_short_name: str,
|
280
|
+
items: Iterable[OdxNamed],
|
281
|
+
expected_type: None = None) -> Any:
|
282
|
+
"""Resolve a short name reference given a sequence of candidate objects"""
|
283
|
+
...
|
284
|
+
|
285
|
+
|
286
|
+
@overload
|
287
|
+
def resolve_snref(target_short_name: str, items: Iterable[OdxNamed],
|
288
|
+
expected_type: Type[TNamed]) -> TNamed:
|
289
|
+
...
|
290
|
+
|
291
|
+
|
292
|
+
def resolve_snref(target_short_name: str,
|
293
|
+
items: Iterable[OdxNamed],
|
294
|
+
expected_type: Any = None) -> Any:
|
295
|
+
candidates = [x for x in items if x.short_name == target_short_name]
|
296
|
+
|
297
|
+
if not candidates:
|
298
|
+
odxraise(f"Cannot resolve short name reference to '{target_short_name}'")
|
299
|
+
return None
|
300
|
+
elif len(candidates) > 1:
|
301
|
+
odxraise(f"Cannot uniquely resolve short name reference to '{target_short_name}'")
|
302
|
+
elif expected_type is not None and not isinstance(candidates[0], expected_type):
|
303
|
+
odxraise(f"Reference '{target_short_name}' points to a {type(candidates[0]).__name__}"
|
304
|
+
f"object while expecting {expected_type.__name__}")
|
305
|
+
|
306
|
+
return candidates[0]
|
odxtools/odxtypes.py
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
# SPDX-License-Identifier: MIT
|
2
2
|
from enum import Enum
|
3
|
-
from typing import TYPE_CHECKING, Any, Callable, Dict,
|
3
|
+
from typing import (TYPE_CHECKING, Any, Callable, Dict, Iterable, Optional, Tuple, Type, Union,
|
4
|
+
overload)
|
4
5
|
from xml.etree import ElementTree
|
5
6
|
|
6
7
|
from .exceptions import odxassert, odxraise, odxrequire
|
@@ -28,7 +29,7 @@ ParameterDict = Dict[str, Union["Parameter", "ParameterDict"]]
|
|
28
29
|
# multiple items, so this can be a list of objects.
|
29
30
|
TableStructParameterValue = Tuple[str, "ParameterValue"]
|
30
31
|
ParameterValue = Union[AtomicOdxType, "ParameterValueDict", TableStructParameterValue,
|
31
|
-
|
32
|
+
Iterable["ParameterValue"], "DiagnosticTroubleCode"]
|
32
33
|
ParameterValueDict = Dict[str, ParameterValue]
|
33
34
|
|
34
35
|
|
@@ -154,6 +155,19 @@ def compare_odx_values(a: AtomicOdxType, b: AtomicOdxType) -> int:
|
|
154
155
|
f"and {type(b).__name__}")
|
155
156
|
|
156
157
|
|
158
|
+
# format specifiers for the data type using the bitstruct module
|
159
|
+
_BITSTRUCT_FORMAT_LETTER_MAP__ = {
|
160
|
+
"A_INT32": "s",
|
161
|
+
"A_UINT32": "u",
|
162
|
+
"A_FLOAT32": "f",
|
163
|
+
"A_FLOAT64": "f",
|
164
|
+
"A_BYTEFIELD": "r",
|
165
|
+
"A_UNICODE2STRING": "r", # UTF-16 strings must be converted explicitly
|
166
|
+
"A_ASCIISTRING": "r",
|
167
|
+
"A_UTF8STRING": "r",
|
168
|
+
}
|
169
|
+
|
170
|
+
|
157
171
|
class DataType(Enum):
|
158
172
|
"""Types for the physical and internal value.
|
159
173
|
|
@@ -181,6 +195,10 @@ class DataType(Enum):
|
|
181
195
|
def python_type(self) -> Type[AtomicOdxType]:
|
182
196
|
return _ODX_TYPE_TO_PYTHON_TYPE[self.value]
|
183
197
|
|
198
|
+
@property
|
199
|
+
def bitstruct_format_letter(self) -> str:
|
200
|
+
return _BITSTRUCT_FORMAT_LETTER_MAP__[self.value]
|
201
|
+
|
184
202
|
def from_string(self, value: str) -> AtomicOdxType:
|
185
203
|
return _PARSE_ODX_TYPE[self.value](value)
|
186
204
|
|
odxtools/parameterinfo.py
CHANGED
@@ -1,105 +1,191 @@
|
|
1
1
|
# SPDX-License-Identifier: MIT
|
2
|
-
import
|
3
|
-
from
|
2
|
+
import textwrap
|
3
|
+
from io import StringIO
|
4
|
+
from typing import Iterable
|
4
5
|
|
5
6
|
from .compumethods.identicalcompumethod import IdenticalCompuMethod
|
6
7
|
from .compumethods.limit import IntervalType
|
7
8
|
from .compumethods.linearcompumethod import LinearCompuMethod
|
8
9
|
from .compumethods.texttablecompumethod import TexttableCompuMethod
|
9
10
|
from .dataobjectproperty import DataObjectProperty
|
11
|
+
from .dtcdop import DtcDop
|
12
|
+
from .dynamiclengthfield import DynamicLengthField
|
10
13
|
from .endofpdufield import EndOfPduField
|
14
|
+
from .exceptions import odxrequire
|
15
|
+
from .multiplexer import Multiplexer
|
11
16
|
from .odxtypes import DataType
|
12
17
|
from .parameters.codedconstparameter import CodedConstParameter
|
13
18
|
from .parameters.matchingrequestparameter import MatchingRequestParameter
|
19
|
+
from .parameters.nrcconstparameter import NrcConstParameter
|
14
20
|
from .parameters.parameter import Parameter
|
15
21
|
from .parameters.parameterwithdop import ParameterWithDOP
|
16
22
|
from .parameters.reservedparameter import ReservedParameter
|
23
|
+
from .parameters.tablekeyparameter import TableKeyParameter
|
24
|
+
from .parameters.tablestructparameter import TableStructParameter
|
25
|
+
from .paramlengthinfotype import ParamLengthInfoType
|
26
|
+
from .staticfield import StaticField
|
17
27
|
|
18
28
|
|
19
|
-
def parameter_info(param_list: Iterable[
|
20
|
-
|
29
|
+
def parameter_info(param_list: Iterable[Parameter], quoted_names: bool = False) -> str:
|
30
|
+
q = "'" if quoted_names else ""
|
31
|
+
of = StringIO()
|
21
32
|
for param in param_list:
|
22
33
|
if isinstance(param, CodedConstParameter):
|
23
|
-
|
34
|
+
of.write(f"{q}{param.short_name}{q}: const = {param._coded_value_str}\n")
|
24
35
|
continue
|
25
36
|
elif isinstance(param, MatchingRequestParameter):
|
26
|
-
|
37
|
+
of.write(f"{q}{param.short_name}{q}: <matches request>\n")
|
38
|
+
continue
|
39
|
+
elif isinstance(param, NrcConstParameter):
|
40
|
+
of.write(f"{q}{param.short_name}{q}: const; choices = {param.coded_values}\n")
|
27
41
|
continue
|
28
42
|
elif isinstance(param, ReservedParameter):
|
29
|
-
|
43
|
+
of.write(f"{q}{param.short_name}{q}: <reserved>\n")
|
44
|
+
continue
|
45
|
+
elif isinstance(param, TableKeyParameter):
|
46
|
+
of.write(
|
47
|
+
f"{q}{param.short_name}{q}: <optional> table key; table = '{param.table.short_name}'; choices:\n"
|
48
|
+
)
|
49
|
+
for tr in param.table.table_rows:
|
50
|
+
of.write(f" '{tr.short_name}',\n")
|
51
|
+
|
52
|
+
continue
|
53
|
+
elif isinstance(param, TableStructParameter):
|
54
|
+
of.write(
|
55
|
+
f"{q}{param.short_name}{q}: table struct; key = '{param.table_key.short_name}'; choices:\n"
|
56
|
+
)
|
57
|
+
for tr in param.table_key.table.table_rows:
|
58
|
+
of.write(f" ('{tr.short_name}',\n")
|
59
|
+
of.write(f" {{\n")
|
60
|
+
of.write(
|
61
|
+
textwrap.indent(
|
62
|
+
parameter_info(odxrequire(tr.structure).parameters, True), " "))
|
63
|
+
of.write(f" }}),\n")
|
64
|
+
|
30
65
|
continue
|
31
66
|
elif not isinstance(param, ParameterWithDOP):
|
32
|
-
|
67
|
+
of.write(
|
68
|
+
f"{q}{param.short_name}{q}: <unhandled parameter type '{type(param).__name__}'>\n")
|
33
69
|
continue
|
34
70
|
|
35
71
|
dop = param.dop
|
36
|
-
|
37
72
|
if isinstance(dop, EndOfPduField):
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
73
|
+
of.write(f"{q}{param.short_name}{q}: list({{\n")
|
74
|
+
of.write(textwrap.indent(parameter_info(dop.structure.parameters, True), " "))
|
75
|
+
of.write(f"}})\n")
|
76
|
+
continue
|
77
|
+
elif isinstance(dop, StaticField):
|
78
|
+
of.write(f"{q}{param.short_name}{q}: length={dop.fixed_number_of_items}; list({{\n")
|
79
|
+
of.write(textwrap.indent(parameter_info(dop.structure.parameters, True), " "))
|
80
|
+
of.write(f"}})\n")
|
81
|
+
continue
|
82
|
+
elif isinstance(dop, DynamicLengthField):
|
83
|
+
of.write(f"{q}{param.short_name}{q}: list({{\n")
|
84
|
+
of.write(textwrap.indent(parameter_info(dop.structure.parameters, True), " "))
|
85
|
+
of.write(f"}})\n")
|
86
|
+
continue
|
87
|
+
elif isinstance(dop, ParamLengthInfoType):
|
88
|
+
of.write(f"{q}{param.short_name}{q}: ")
|
89
|
+
of.write("<optional> ")
|
90
|
+
of.write(f"int; length_key='{dop.length_key.short_name}'\n")
|
91
|
+
continue
|
92
|
+
elif isinstance(dop, DtcDop):
|
93
|
+
of.write(f"{q}{param.short_name}{q}: ")
|
94
|
+
of.write(f"DTC; choices:\n")
|
95
|
+
for dtc in dop.dtcs:
|
96
|
+
if dtc.display_trouble_code is not None:
|
97
|
+
dtc_desc = dtc.text and f"; \"{dtc.text}\""
|
98
|
+
of.write(
|
99
|
+
f" '{dtc.display_trouble_code}' (0x{dtc.trouble_code:06x}{dtc_desc})\n")
|
100
|
+
else:
|
101
|
+
dtc_desc = dtc.text and f" (\"{dtc.text}\")"
|
102
|
+
of.write(f" 0x{dtc.trouble_code:06x}{dtc_desc}\n")
|
103
|
+
continue
|
104
|
+
elif isinstance(dop, Multiplexer):
|
105
|
+
of.write(f"{q}{param.short_name}{q}: ")
|
106
|
+
if dop.default_case is not None:
|
107
|
+
of.write(f"<optional>")
|
108
|
+
of.write(f"multiplexer; choices:\n")
|
109
|
+
for mux_case in dop.cases:
|
110
|
+
of.write(f" ({repr(mux_case.short_name)}, {{\n")
|
111
|
+
of.write(
|
112
|
+
textwrap.indent(parameter_info(mux_case.structure.parameters, True), " "))
|
113
|
+
of.write(f" }})\n")
|
43
114
|
continue
|
44
115
|
|
45
|
-
|
116
|
+
of.write(f"{q}{param.short_name}{q}")
|
46
117
|
|
47
118
|
if dop is None:
|
48
|
-
|
119
|
+
of.write(": <no DOP>\n")
|
49
120
|
continue
|
50
121
|
elif not isinstance(dop, DataObjectProperty):
|
51
|
-
|
122
|
+
of.write(f": <unhandled DOP '{type(dop).__name__}'>\n")
|
52
123
|
continue
|
53
124
|
|
54
125
|
if (cm := dop.compu_method) is None:
|
55
|
-
|
126
|
+
of.write(": <no compu method>\n")
|
56
127
|
continue
|
57
128
|
|
58
129
|
if isinstance(cm, TexttableCompuMethod):
|
59
|
-
|
130
|
+
of.write(f": enum; choices:\n")
|
60
131
|
for scale in cm.internal_to_phys:
|
61
|
-
|
132
|
+
val_str = ""
|
133
|
+
if scale.lower_limit is not None:
|
134
|
+
val_str = f"({repr(scale.lower_limit.value)})"
|
135
|
+
of.write(f" {repr(scale.compu_const)}{val_str}\n")
|
62
136
|
|
63
137
|
elif isinstance(cm, IdenticalCompuMethod):
|
64
138
|
bdt = dop.physical_type.base_data_type
|
65
139
|
if bdt in (DataType.A_UTF8STRING, DataType.A_UNICODE2STRING, DataType.A_ASCIISTRING):
|
66
|
-
|
67
|
-
elif bdt
|
68
|
-
|
140
|
+
of.write(f": str")
|
141
|
+
elif bdt == DataType.A_BYTEFIELD:
|
142
|
+
of.write(f": bytes")
|
69
143
|
elif bdt.name.startswith("A_FLOAT"):
|
70
|
-
|
144
|
+
of.write(f": float")
|
71
145
|
elif bdt.name.startswith("A_UINT"):
|
72
|
-
|
146
|
+
of.write(f": uint")
|
73
147
|
elif bdt.name.startswith("A_INT"):
|
74
|
-
|
148
|
+
of.write(f": int")
|
75
149
|
else:
|
76
|
-
|
77
|
-
|
78
|
-
if (bl := dop.get_static_bit_length()) is not None:
|
79
|
-
result += f"{bl}"
|
150
|
+
of.write(f": <unknown type {{ bdt.name }}>")
|
80
151
|
|
81
|
-
|
152
|
+
of.write("\n")
|
82
153
|
|
83
154
|
elif isinstance(cm, LinearCompuMethod):
|
84
|
-
|
155
|
+
bdt = dop.physical_type.base_data_type
|
156
|
+
if bdt in (DataType.A_UTF8STRING, DataType.A_UNICODE2STRING, DataType.A_ASCIISTRING):
|
157
|
+
of.write(f": str")
|
158
|
+
elif bdt in (DataType.A_BYTEFIELD,):
|
159
|
+
of.write(f": bytes")
|
160
|
+
elif bdt.name.startswith("A_FLOAT"):
|
161
|
+
of.write(f": float")
|
162
|
+
elif bdt.name.startswith("A_UINT"):
|
163
|
+
of.write(f": uint")
|
164
|
+
elif bdt.name.startswith("A_INT"):
|
165
|
+
of.write(f": int")
|
166
|
+
else:
|
167
|
+
of.write(f": <unknown type>")
|
168
|
+
|
85
169
|
ll = cm.physical_lower_limit
|
86
170
|
ul = cm.physical_upper_limit
|
87
|
-
if ll is None:
|
88
|
-
ll_str = "(inf"
|
171
|
+
if ll is None or ll.interval_type == IntervalType.INFINITE:
|
172
|
+
ll_str = "(-inf"
|
89
173
|
else:
|
90
|
-
ll_delim = '
|
174
|
+
ll_delim = '(' if ll.interval_type == IntervalType.OPEN else '['
|
91
175
|
ll_str = f"{ll_delim}{ll._value!r}"
|
92
176
|
|
93
|
-
if ul is None:
|
177
|
+
if ul is None or ul.interval_type == IntervalType.INFINITE:
|
94
178
|
ul_str = "inf)"
|
95
179
|
else:
|
96
|
-
ul_delim = '
|
180
|
+
ul_delim = ')' if ul.interval_type == IntervalType.OPEN else ']'
|
97
181
|
ul_str = f"{ul._value!r}{ul_delim}"
|
98
|
-
|
182
|
+
of.write(f"; range: {ll_str}, {ul_str}")
|
99
183
|
|
100
184
|
unit = dop.unit
|
101
185
|
unit_str = unit.display_name if unit is not None else None
|
102
186
|
if unit_str is not None:
|
103
|
-
|
187
|
+
of.write(f"; unit: {unit_str}")
|
188
|
+
|
189
|
+
of.write("\n")
|
104
190
|
|
105
|
-
return
|
191
|
+
return of.getvalue()
|
@@ -10,9 +10,9 @@ from ..createanydiagcodedtype import create_any_diag_coded_type_from_et
|
|
10
10
|
from ..decodestate import DecodeState
|
11
11
|
from ..diagcodedtype import DiagCodedType
|
12
12
|
from ..encodestate import EncodeState
|
13
|
-
from ..exceptions import DecodeError, odxrequire
|
13
|
+
from ..exceptions import DecodeError, EncodeError, odxraise, odxrequire
|
14
14
|
from ..odxlink import OdxDocFragment, OdxLinkDatabase, OdxLinkId
|
15
|
-
from ..odxtypes import AtomicOdxType, DataType
|
15
|
+
from ..odxtypes import AtomicOdxType, DataType, ParameterValue
|
16
16
|
from ..utils import dataclass_fields_asdict
|
17
17
|
from .parameter import Parameter, ParameterType
|
18
18
|
|
@@ -59,8 +59,9 @@ class CodedConstParameter(Parameter):
|
|
59
59
|
super()._resolve_odxlinks(odxlinks)
|
60
60
|
|
61
61
|
@override
|
62
|
-
def
|
63
|
-
|
62
|
+
def _parameter_resolve_snrefs(self, diag_layer: "DiagLayer", *,
|
63
|
+
param_list: List[Parameter]) -> None:
|
64
|
+
super()._parameter_resolve_snrefs(diag_layer, param_list=param_list)
|
64
65
|
|
65
66
|
@override
|
66
67
|
def get_static_bit_length(self) -> Optional[int]:
|
@@ -81,20 +82,23 @@ class CodedConstParameter(Parameter):
|
|
81
82
|
return False
|
82
83
|
|
83
84
|
@override
|
84
|
-
def
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
85
|
+
def _encode_positioned_into_pdu(self, physical_value: Optional[ParameterValue],
|
86
|
+
encode_state: EncodeState) -> None:
|
87
|
+
if physical_value is not None and physical_value != self.coded_value:
|
88
|
+
odxraise(
|
89
|
+
f"Value for constant parameter `{self.short_name}` name can "
|
90
|
+
f"only be specified as {self.coded_value!r} (is: {physical_value!r})", EncodeError)
|
91
|
+
|
92
|
+
internal_value = self.coded_value
|
93
|
+
|
94
|
+
self.diag_coded_type.encode_into_pdu(
|
95
|
+
internal_value=internal_value, encode_state=encode_state)
|
92
96
|
|
93
97
|
@override
|
94
98
|
def _decode_positioned_from_pdu(self, decode_state: DecodeState) -> AtomicOdxType:
|
95
99
|
coded_val = self.diag_coded_type.decode_from_pdu(decode_state)
|
96
100
|
|
97
|
-
# Check if the coded value
|
101
|
+
# Check if the coded value contained by the message is correct.
|
98
102
|
if self.coded_value != coded_val:
|
99
103
|
warnings.warn(
|
100
104
|
f"Coded constant parameter does not match! "
|
@@ -1,6 +1,6 @@
|
|
1
1
|
# SPDX-License-Identifier: MIT
|
2
2
|
from dataclasses import dataclass
|
3
|
-
from typing import List
|
3
|
+
from typing import List, Optional
|
4
4
|
from xml.etree import ElementTree
|
5
5
|
|
6
6
|
from typing_extensions import override
|
@@ -41,9 +41,10 @@ class DynamicParameter(Parameter):
|
|
41
41
|
raise NotImplementedError(".is_settable for a DynamicParameter")
|
42
42
|
|
43
43
|
@override
|
44
|
-
def
|
45
|
-
|
44
|
+
def _encode_positioned_into_pdu(self, physical_value: Optional[ParameterValue],
|
45
|
+
encode_state: EncodeState) -> None:
|
46
|
+
raise NotImplementedError("Encoding DynamicParameter is not implemented yet.")
|
46
47
|
|
47
48
|
@override
|
48
49
|
def _decode_positioned_from_pdu(self, decode_state: DecodeState) -> ParameterValue:
|
49
|
-
raise NotImplementedError("Decoding
|
50
|
+
raise NotImplementedError("Decoding DynamicParameter is not implemented yet.")
|