odxtools 6.7.1__py3-none-any.whl → 7.0.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 +106 -112
- odxtools/dataobjectproperty.py +3 -7
- odxtools/decodestate.py +1 -13
- odxtools/diagcodedtype.py +9 -96
- odxtools/diaglayer.py +45 -0
- odxtools/diagservice.py +12 -16
- odxtools/dopbase.py +5 -8
- odxtools/dtcdop.py +21 -19
- odxtools/dynamiclengthfield.py +30 -26
- odxtools/encodestate.py +188 -23
- odxtools/endofpdufield.py +12 -14
- odxtools/environmentdatadescription.py +13 -13
- odxtools/leadinglengthinfotype.py +25 -12
- odxtools/minmaxlengthtype.py +36 -26
- odxtools/multiplexer.py +42 -23
- odxtools/multiplexercase.py +1 -1
- odxtools/nameditemlist.py +14 -0
- odxtools/odxtypes.py +17 -0
- odxtools/parameterinfo.py +126 -40
- odxtools/parameters/codedconstparameter.py +14 -11
- odxtools/parameters/dynamicparameter.py +5 -4
- odxtools/parameters/lengthkeyparameter.py +62 -14
- odxtools/parameters/matchingrequestparameter.py +23 -11
- odxtools/parameters/nrcconstparameter.py +39 -20
- odxtools/parameters/parameter.py +29 -42
- odxtools/parameters/parameterwithdop.py +5 -8
- odxtools/parameters/physicalconstantparameter.py +12 -13
- odxtools/parameters/reservedparameter.py +5 -2
- odxtools/parameters/systemparameter.py +3 -2
- odxtools/parameters/tableentryparameter.py +3 -2
- odxtools/parameters/tablekeyparameter.py +79 -30
- odxtools/parameters/tablestructparameter.py +62 -42
- odxtools/parameters/valueparameter.py +12 -14
- odxtools/paramlengthinfotype.py +30 -22
- odxtools/request.py +9 -0
- odxtools/response.py +5 -13
- odxtools/standardlengthtype.py +51 -13
- odxtools/staticfield.py +15 -19
- odxtools/version.py +2 -2
- {odxtools-6.7.1.dist-info → odxtools-7.0.0.dist-info}/METADATA +1 -1
- {odxtools-6.7.1.dist-info → odxtools-7.0.0.dist-info}/RECORD +45 -45
- {odxtools-6.7.1.dist-info → odxtools-7.0.0.dist-info}/LICENSE +0 -0
- {odxtools-6.7.1.dist-info → odxtools-7.0.0.dist-info}/WHEEL +0 -0
- {odxtools-6.7.1.dist-info → odxtools-7.0.0.dist-info}/entry_points.txt +0 -0
- {odxtools-6.7.1.dist-info → odxtools-7.0.0.dist-info}/top_level.txt +0 -0
odxtools/minmaxlengthtype.py
CHANGED
@@ -2,6 +2,8 @@
|
|
2
2
|
from dataclasses import dataclass
|
3
3
|
from typing import Optional
|
4
4
|
|
5
|
+
from typing_extensions import override
|
6
|
+
|
5
7
|
from .decodestate import DecodeState
|
6
8
|
from .diagcodedtype import DctType, DiagCodedType
|
7
9
|
from .encodestate import EncodeState
|
@@ -51,8 +53,9 @@ class MinMaxLengthType(DiagCodedType):
|
|
51
53
|
termination_sequence = bytes([0xFF, 0xFF])
|
52
54
|
return termination_sequence
|
53
55
|
|
54
|
-
|
55
|
-
|
56
|
+
@override
|
57
|
+
def encode_into_pdu(self, internal_value: AtomicOdxType, encode_state: EncodeState) -> None:
|
58
|
+
|
56
59
|
if not isinstance(internal_value, (bytes, str)):
|
57
60
|
odxraise("MinMaxLengthType is currently only implemented for strings and byte arrays",
|
58
61
|
EncodeError)
|
@@ -62,20 +65,24 @@ class MinMaxLengthType(DiagCodedType):
|
|
62
65
|
else:
|
63
66
|
data_length = len(internal_value)
|
64
67
|
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
68
|
+
orig_cursor = encode_state.cursor_byte_position
|
69
|
+
encode_state.emplace_atomic_value(
|
70
|
+
internal_value=internal_value,
|
71
|
+
used_mask=None,
|
72
|
+
bit_length=8 * data_length,
|
73
|
+
base_data_type=self.base_data_type,
|
74
|
+
is_highlow_byte_order=self.is_highlow_byte_order,
|
75
|
+
)
|
76
|
+
value_len = encode_state.cursor_byte_position - orig_cursor
|
73
77
|
|
74
78
|
# TODO: ensure that the termination delimiter is not
|
75
79
|
# encountered within the encoded value.
|
76
80
|
|
77
|
-
odxassert(
|
78
|
-
|
81
|
+
odxassert(
|
82
|
+
self.termination != "END-OF-PDU" or encode_state.is_end_of_pdu,
|
83
|
+
"Encountered a MIN-MAX-LENGTH type with END-OF-PDU termination "
|
84
|
+
"which is not located at the end of the PDU")
|
85
|
+
if encode_state.is_end_of_pdu or value_len == self.max_length:
|
79
86
|
# All termination types may be ended by the end of the PDU
|
80
87
|
# or once reaching the maximum length. In this case, we
|
81
88
|
# must not add the termination sequence
|
@@ -85,20 +92,23 @@ class MinMaxLengthType(DiagCodedType):
|
|
85
92
|
|
86
93
|
# ensure that we don't try to encode an odd-length
|
87
94
|
# value when using a two-byte terminator
|
88
|
-
odxassert(
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
95
|
+
odxassert(value_len % len(termination_sequence) == 0)
|
96
|
+
|
97
|
+
value_len += len(termination_sequence)
|
98
|
+
encode_state.emplace_bytes(termination_sequence)
|
99
|
+
|
100
|
+
if value_len < self.min_length:
|
101
|
+
odxraise(
|
102
|
+
f"Encoded value for MinMaxLengthType "
|
103
|
+
f"must be at least {self.min_length} bytes long. "
|
104
|
+
f"(Is: {value_len} bytes.)", EncodeError)
|
105
|
+
return
|
106
|
+
elif self.max_length is not None and value_len > self.max_length:
|
107
|
+
odxraise(
|
108
|
+
f"Encoded value for MinMaxLengthType "
|
109
|
+
f"must not be longer than {self.max_length} bytes. "
|
110
|
+
f"(Is: {value_len} bytes.)", EncodeError)
|
111
|
+
return
|
102
112
|
|
103
113
|
def decode_from_pdu(self, decode_state: DecodeState) -> AtomicOdxType:
|
104
114
|
odxassert(decode_state.cursor_bit_position == 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
@@ -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:
|
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/odxtypes.py
CHANGED
@@ -154,6 +154,19 @@ def compare_odx_values(a: AtomicOdxType, b: AtomicOdxType) -> int:
|
|
154
154
|
f"and {type(b).__name__}")
|
155
155
|
|
156
156
|
|
157
|
+
# format specifiers for the data type using the bitstruct module
|
158
|
+
_BITSTRUCT_FORMAT_LETTER_MAP__ = {
|
159
|
+
"A_INT32": "s",
|
160
|
+
"A_UINT32": "u",
|
161
|
+
"A_FLOAT32": "f",
|
162
|
+
"A_FLOAT64": "f",
|
163
|
+
"A_BYTEFIELD": "r",
|
164
|
+
"A_UNICODE2STRING": "r", # UTF-16 strings must be converted explicitly
|
165
|
+
"A_ASCIISTRING": "r",
|
166
|
+
"A_UTF8STRING": "r",
|
167
|
+
}
|
168
|
+
|
169
|
+
|
157
170
|
class DataType(Enum):
|
158
171
|
"""Types for the physical and internal value.
|
159
172
|
|
@@ -181,6 +194,10 @@ class DataType(Enum):
|
|
181
194
|
def python_type(self) -> Type[AtomicOdxType]:
|
182
195
|
return _ODX_TYPE_TO_PYTHON_TYPE[self.value]
|
183
196
|
|
197
|
+
@property
|
198
|
+
def bitstruct_format_letter(self) -> str:
|
199
|
+
return _BITSTRUCT_FORMAT_LETTER_MAP__[self.value]
|
200
|
+
|
184
201
|
def from_string(self, value: str) -> AtomicOdxType:
|
185
202
|
return _PARSE_ODX_TYPE[self.value](value)
|
186
203
|
|
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
|
|
@@ -81,20 +81,23 @@ class CodedConstParameter(Parameter):
|
|
81
81
|
return False
|
82
82
|
|
83
83
|
@override
|
84
|
-
def
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
84
|
+
def _encode_positioned_into_pdu(self, physical_value: Optional[ParameterValue],
|
85
|
+
encode_state: EncodeState) -> None:
|
86
|
+
if physical_value is not None and physical_value != self.coded_value:
|
87
|
+
odxraise(
|
88
|
+
f"Value for constant parameter `{self.short_name}` name can "
|
89
|
+
f"only be specified as {self.coded_value!r} (is: {physical_value!r})", EncodeError)
|
90
|
+
|
91
|
+
internal_value = self.coded_value
|
92
|
+
|
93
|
+
self.diag_coded_type.encode_into_pdu(
|
94
|
+
internal_value=internal_value, encode_state=encode_state)
|
92
95
|
|
93
96
|
@override
|
94
97
|
def _decode_positioned_from_pdu(self, decode_state: DecodeState) -> AtomicOdxType:
|
95
98
|
coded_val = self.diag_coded_type.decode_from_pdu(decode_state)
|
96
99
|
|
97
|
-
# Check if the coded value
|
100
|
+
# Check if the coded value contained by the message is correct.
|
98
101
|
if self.coded_value != coded_val:
|
99
102
|
warnings.warn(
|
100
103
|
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.")
|