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
@@ -3,12 +3,12 @@ from dataclasses import dataclass
|
|
3
3
|
from typing import TYPE_CHECKING, Any, Dict, List, Optional
|
4
4
|
from xml.etree import ElementTree
|
5
5
|
|
6
|
-
from typing_extensions import override
|
6
|
+
from typing_extensions import final, override
|
7
7
|
|
8
8
|
from ..decodestate import DecodeState
|
9
9
|
from ..encodestate import EncodeState
|
10
10
|
from ..exceptions import DecodeError, EncodeError, odxraise, odxrequire
|
11
|
-
from ..odxlink import OdxDocFragment, OdxLinkDatabase, OdxLinkId, OdxLinkRef
|
11
|
+
from ..odxlink import OdxDocFragment, OdxLinkDatabase, OdxLinkId, OdxLinkRef, resolve_snref
|
12
12
|
from ..odxtypes import ParameterValue
|
13
13
|
from ..utils import dataclass_fields_asdict
|
14
14
|
from .parameter import Parameter, ParameterType
|
@@ -94,19 +94,19 @@ class TableKeyParameter(Parameter):
|
|
94
94
|
self._table = self._table_row.table
|
95
95
|
|
96
96
|
@override
|
97
|
-
def
|
98
|
-
|
97
|
+
def _parameter_resolve_snrefs(self, diag_layer: "DiagLayer", *,
|
98
|
+
param_list: List[Parameter]) -> None:
|
99
|
+
super()._parameter_resolve_snrefs(diag_layer, param_list=param_list)
|
99
100
|
|
100
101
|
if self.table_snref is not None:
|
101
|
-
|
102
|
-
self._table =
|
102
|
+
tables = diag_layer.diag_data_dictionary_spec.tables
|
103
|
+
self._table = resolve_snref(self.table_snref, tables, Table)
|
103
104
|
if self.table_row_snref is not None:
|
104
105
|
# make sure that we know the table to which the table row
|
105
106
|
# SNREF is relative to.
|
106
|
-
table = odxrequire(
|
107
|
-
|
108
|
-
|
109
|
-
self._table_row = table.table_rows[self.table_row_snref]
|
107
|
+
table = odxrequire(self._table,
|
108
|
+
"If a table-row is referenced, a table must also be referenced.")
|
109
|
+
self._table_row = resolve_snref(self.table_row_snref, table.table_rows, TableRow)
|
110
110
|
|
111
111
|
@property
|
112
112
|
def table(self) -> "Table":
|
@@ -133,46 +133,95 @@ class TableKeyParameter(Parameter):
|
|
133
133
|
return True
|
134
134
|
|
135
135
|
@override
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
if
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
136
|
+
@final
|
137
|
+
def _encode_positioned_into_pdu(self, physical_value: Optional[ParameterValue],
|
138
|
+
encode_state: EncodeState) -> None:
|
139
|
+
# if you get this exception, you ought to use
|
140
|
+
# `.encode_placeholder_into_pdu()` followed by (after the
|
141
|
+
# value of the table key has been determined)
|
142
|
+
# `.encode_value_into_pdu()`.
|
143
|
+
raise RuntimeError("_encode_positioned_into_pdu() cannot be called for table keys.")
|
144
|
+
|
145
|
+
def encode_placeholder_into_pdu(self, physical_value: Optional[ParameterValue],
|
146
|
+
encode_state: EncodeState) -> None:
|
147
|
+
|
148
|
+
if physical_value is not None:
|
144
149
|
key_dop = self.table.key_dop
|
145
150
|
if key_dop is None:
|
146
|
-
|
147
|
-
|
148
|
-
|
151
|
+
odxraise(
|
152
|
+
f"Table '{self.table.short_name}' does not define "
|
153
|
+
f"a KEY-DOP, but is used by TABLE-KEY parameter "
|
154
|
+
f"'{self.short_name}'", EncodeError)
|
155
|
+
return
|
156
|
+
|
157
|
+
if not isinstance(physical_value, str):
|
158
|
+
odxraise(f"Invalid type for for table key '{self.short_name}' specified. "
|
159
|
+
f"(expect name of table row.)")
|
160
|
+
|
161
|
+
tkv = encode_state.table_keys.get(self.short_name)
|
162
|
+
if tkv is not None and tkv != physical_value:
|
163
|
+
odxraise(f"Got conflicting values for table key {self.short_name}: "
|
164
|
+
f"{tkv} and {physical_value!r}")
|
165
|
+
|
166
|
+
encode_state.table_keys[self.short_name] = physical_value
|
167
|
+
|
168
|
+
orig_pos = encode_state.cursor_byte_position
|
169
|
+
pos = encode_state.cursor_byte_position
|
170
|
+
if self.byte_position is not None:
|
171
|
+
pos = encode_state.origin_byte_position + self.byte_position
|
172
|
+
encode_state.key_pos[self.short_name] = pos
|
173
|
+
encode_state.cursor_byte_position = pos
|
174
|
+
encode_state.cursor_bit_position = self.bit_position or 0
|
149
175
|
|
150
|
-
|
151
|
-
|
152
|
-
|
176
|
+
key_dop = self.table.key_dop
|
177
|
+
if key_dop is None:
|
178
|
+
odxraise(f"No KEY-DOP specified for table {self.table.short_name}")
|
179
|
+
return
|
180
|
+
|
181
|
+
sz = key_dop.get_static_bit_length()
|
182
|
+
if sz is None:
|
183
|
+
odxraise("The DOP of table key {self.short_name} must exhibit a fixed size.",
|
184
|
+
EncodeError)
|
185
|
+
return
|
186
|
+
|
187
|
+
# emplace a value of zero into the encode state, but pretend the bits not to be used
|
188
|
+
n = sz + encode_state.cursor_bit_position
|
189
|
+
tmp_val = b'\x00' * ((n + 7) // 8)
|
190
|
+
encode_state.emplace_bytes(tmp_val, obj_used_mask=tmp_val)
|
191
|
+
|
192
|
+
encode_state.cursor_byte_position = max(orig_pos, encode_state.cursor_byte_position)
|
193
|
+
encode_state.cursor_bit_position = 0
|
194
|
+
|
195
|
+
def encode_value_into_pdu(self, encode_state: EncodeState) -> None:
|
153
196
|
|
154
|
-
|
197
|
+
key_dop = self.table.key_dop
|
198
|
+
if key_dop is None:
|
199
|
+
odxraise(
|
200
|
+
f"Table '{self.table.short_name}' does not define "
|
201
|
+
f"a KEY-DOP, but is used by TABLE-KEY parameter "
|
202
|
+
f"'{self.short_name}'", EncodeError)
|
203
|
+
return
|
204
|
+
|
205
|
+
if self.short_name not in encode_state.table_keys:
|
206
|
+
odxraise(f"Table key {self.short_name} has not been defined before "
|
207
|
+
f"it is required.", EncodeError)
|
208
|
+
return
|
209
|
+
else:
|
210
|
+
tr_short_name = encode_state.table_keys[self.short_name]
|
155
211
|
|
156
|
-
# the table key
|
157
|
-
# into the PDU.
|
212
|
+
# We need to encode the table key using the associated DOP into the PDU.
|
158
213
|
tr_candidates = [x for x in self.table.table_rows if x.short_name == tr_short_name]
|
159
214
|
if len(tr_candidates) == 0:
|
160
|
-
|
215
|
+
odxraise(f"No table row with short name '{tr_short_name}' found", EncodeError)
|
216
|
+
return
|
161
217
|
elif len(tr_candidates) > 1:
|
162
|
-
|
218
|
+
odxraise(f"Multiple rows exhibiting short name '{tr_short_name}'", EncodeError)
|
163
219
|
tr = tr_candidates[0]
|
164
220
|
|
165
|
-
|
166
|
-
|
167
|
-
raise EncodeError(f"Table '{self.table.short_name}' does not define "
|
168
|
-
f"a KEY-DOP, but is used in TABLE-KEY parameter "
|
169
|
-
f"'{self.short_name}'")
|
170
|
-
bit_position = 0 if self.bit_position is None else self.bit_position
|
171
|
-
return key_dop.convert_physical_to_bytes(tr.key, encode_state, bit_position=bit_position)
|
221
|
+
encode_state.cursor_byte_position = encode_state.key_pos[self.short_name]
|
222
|
+
encode_state.cursor_bit_position = self.bit_position or 0
|
172
223
|
|
173
|
-
|
174
|
-
def encode_into_pdu(self, encode_state: EncodeState) -> bytes:
|
175
|
-
return super().encode_into_pdu(encode_state)
|
224
|
+
key_dop.encode_into_pdu(encode_state=encode_state, physical_value=odxrequire(tr.key))
|
176
225
|
|
177
226
|
@override
|
178
227
|
def _decode_positioned_from_pdu(self, decode_state: DecodeState) -> ParameterValue:
|
@@ -1,5 +1,4 @@
|
|
1
1
|
# SPDX-License-Identifier: MIT
|
2
|
-
import warnings
|
3
2
|
from dataclasses import dataclass
|
4
3
|
from typing import TYPE_CHECKING, Any, Dict, List, Optional, cast
|
5
4
|
from xml.etree import ElementTree
|
@@ -8,8 +7,8 @@ from typing_extensions import override
|
|
8
7
|
|
9
8
|
from ..decodestate import DecodeState
|
10
9
|
from ..encodestate import EncodeState
|
11
|
-
from ..exceptions import DecodeError, EncodeError,
|
12
|
-
from ..odxlink import OdxDocFragment, OdxLinkDatabase, OdxLinkId, OdxLinkRef
|
10
|
+
from ..exceptions import DecodeError, EncodeError, odxraise, odxrequire
|
11
|
+
from ..odxlink import OdxDocFragment, OdxLinkDatabase, OdxLinkId, OdxLinkRef, resolve_snref
|
13
12
|
from ..odxtypes import ParameterValue
|
14
13
|
from ..utils import dataclass_fields_asdict
|
15
14
|
from .parameter import Parameter, ParameterType
|
@@ -61,15 +60,12 @@ class TableStructParameter(Parameter):
|
|
61
60
|
self._table_key = odxlinks.resolve(self.table_key_ref, TableKeyParameter)
|
62
61
|
|
63
62
|
@override
|
64
|
-
def
|
65
|
-
|
63
|
+
def _parameter_resolve_snrefs(self, diag_layer: "DiagLayer", *,
|
64
|
+
param_list: List[Parameter]) -> None:
|
65
|
+
super()._parameter_resolve_snrefs(diag_layer, param_list=param_list)
|
66
66
|
|
67
67
|
if self.table_key_snref is not None:
|
68
|
-
|
69
|
-
"Table keys cannot yet be defined using SNREFs"
|
70
|
-
" in TableStructParameters.",
|
71
|
-
OdxWarning,
|
72
|
-
stacklevel=1)
|
68
|
+
self._table_key = resolve_snref(self.table_key_snref, param_list, TableKeyParameter)
|
73
69
|
|
74
70
|
@property
|
75
71
|
def table_key(self) -> TableKeyParameter:
|
@@ -86,71 +82,76 @@ class TableStructParameter(Parameter):
|
|
86
82
|
return True
|
87
83
|
|
88
84
|
@override
|
89
|
-
def
|
90
|
-
|
85
|
+
def _encode_positioned_into_pdu(self, physical_value: Optional[ParameterValue],
|
86
|
+
encode_state: EncodeState) -> None:
|
91
87
|
|
92
|
-
if not isinstance(physical_value, tuple) or \
|
88
|
+
if not isinstance(physical_value, (tuple, list)) or \
|
93
89
|
len(physical_value) != 2 or \
|
94
90
|
not isinstance(physical_value[0], str):
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
91
|
+
odxraise(
|
92
|
+
f"The physical value of TableStructParameter 'self.short_name' "
|
93
|
+
f"must be a tuple containing the short name of the selected table "
|
94
|
+
f"row as the first element and the physical value for the "
|
95
|
+
f"row's structure or DOP as the second.", EncodeError)
|
99
96
|
|
100
97
|
tr_short_name = physical_value[0]
|
101
98
|
|
102
99
|
# make sure that the same table row is selected for all
|
103
100
|
# TABLE-STRUCT parameters that are using the same key
|
104
101
|
tk_short_name = self.table_key.short_name
|
105
|
-
tk_value = encode_state.
|
102
|
+
tk_value = encode_state.table_keys.get(tk_short_name)
|
106
103
|
if tk_value is None:
|
107
104
|
# no value for the key has been set yet. Set it to the
|
108
105
|
# value which we are using right now
|
109
|
-
encode_state.
|
106
|
+
encode_state.table_keys[tk_short_name] = tr_short_name
|
110
107
|
elif tk_value != tr_short_name:
|
111
|
-
|
112
|
-
|
108
|
+
odxraise(
|
109
|
+
f"Cannot determine a unique value for table key '{tk_short_name}': "
|
110
|
+
f"Requested are '{tk_value}' and '{tr_short_name}'", EncodeError)
|
111
|
+
return
|
113
112
|
|
114
113
|
# deal with the static case (i.e., the table row is selected
|
115
114
|
# by the table key object itself)
|
116
|
-
if self.table_key.table_row is not None
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
115
|
+
if self.table_key.table_row is not None:
|
116
|
+
if tr_short_name is not None and self.table_key.table_row.short_name != tr_short_name:
|
117
|
+
odxraise(
|
118
|
+
f"The selected table row for the {self.short_name} "
|
119
|
+
f"parameter must be '{self.table_key.table_row.short_name}' "
|
120
|
+
f"instead of '{tr_short_name}'", EncodeError)
|
121
|
+
return
|
122
|
+
|
123
|
+
tr_short_name = self.table_key.table_row.short_name
|
121
124
|
|
122
125
|
# encode the user specified value using the structure (or DOP)
|
123
126
|
# of the selected table row
|
124
127
|
table = self.table_key.table
|
125
128
|
candidate_trs = [tr for tr in table.table_rows if tr.short_name == tr_short_name]
|
126
|
-
if len(candidate_trs)
|
127
|
-
|
128
|
-
|
129
|
+
if len(candidate_trs) == 0:
|
130
|
+
odxraise(
|
131
|
+
f"Could not find a table row named "
|
132
|
+
f"'{tr_short_name}' in table '{table.short_name}'", EncodeError)
|
133
|
+
return
|
134
|
+
elif len(candidate_trs) > 1:
|
135
|
+
odxraise(
|
136
|
+
f"Found multiple table rows named "
|
137
|
+
f"'{tr_short_name}' in table '{table.short_name}'", EncodeError)
|
138
|
+
|
129
139
|
tr = candidate_trs[0]
|
130
140
|
tr_value = physical_value[1]
|
131
141
|
|
132
|
-
bit_position = self.bit_position or 0
|
133
142
|
if tr.structure is not None:
|
134
143
|
# the selected table row references a structure
|
135
|
-
|
136
|
-
|
137
|
-
parameter_values=tr_value,
|
138
|
-
triggering_request=encode_state.triggering_request)
|
139
|
-
|
140
|
-
return tr.structure.convert_physical_to_bytes(
|
141
|
-
tr_value, inner_encode_state, bit_position=bit_position)
|
144
|
+
tr.structure.encode_into_pdu(tr_value, encode_state)
|
145
|
+
return
|
142
146
|
|
143
147
|
# if the table row does not reference a structure, it must
|
144
148
|
# point to a DOP!
|
145
149
|
if tr.dop is None:
|
146
|
-
odxraise(
|
150
|
+
odxraise(f"Neither a structure nor a DOP has been defined for table row"
|
151
|
+
f"'{tr.short_name}'")
|
152
|
+
return
|
147
153
|
|
148
|
-
|
149
|
-
tr_value, encode_state=encode_state, bit_position=bit_position)
|
150
|
-
|
151
|
-
@override
|
152
|
-
def encode_into_pdu(self, encode_state: EncodeState) -> bytes:
|
153
|
-
return super().encode_into_pdu(encode_state)
|
154
|
+
tr.dop.encode_into_pdu(tr_value, encode_state)
|
154
155
|
|
155
156
|
@override
|
156
157
|
def _decode_positioned_from_pdu(self, decode_state: DecodeState) -> ParameterValue:
|
@@ -7,11 +7,11 @@ from typing_extensions import override
|
|
7
7
|
|
8
8
|
from ..dataobjectproperty import DataObjectProperty
|
9
9
|
from ..encodestate import EncodeState
|
10
|
-
from ..exceptions import odxraise, odxrequire
|
10
|
+
from ..exceptions import EncodeError, odxraise, odxrequire
|
11
11
|
from ..odxlink import OdxDocFragment, OdxLinkDatabase, OdxLinkId
|
12
|
-
from ..odxtypes import AtomicOdxType
|
12
|
+
from ..odxtypes import AtomicOdxType, ParameterValue
|
13
13
|
from ..utils import dataclass_fields_asdict
|
14
|
-
from .parameter import ParameterType
|
14
|
+
from .parameter import Parameter, ParameterType
|
15
15
|
from .parameterwithdop import ParameterWithDOP
|
16
16
|
|
17
17
|
if TYPE_CHECKING:
|
@@ -50,8 +50,9 @@ class ValueParameter(ParameterWithDOP):
|
|
50
50
|
super()._resolve_odxlinks(odxlinks)
|
51
51
|
|
52
52
|
@override
|
53
|
-
def
|
54
|
-
|
53
|
+
def _parameter_resolve_snrefs(self, diag_layer: "DiagLayer", *,
|
54
|
+
param_list: List[Parameter]) -> None:
|
55
|
+
super()._parameter_resolve_snrefs(diag_layer, param_list=param_list)
|
55
56
|
|
56
57
|
if self.physical_default_value_raw is not None:
|
57
58
|
dop = odxrequire(self.dop)
|
@@ -77,16 +78,14 @@ class ValueParameter(ParameterWithDOP):
|
|
77
78
|
return True
|
78
79
|
|
79
80
|
@override
|
80
|
-
def
|
81
|
-
|
82
|
-
|
81
|
+
def _encode_positioned_into_pdu(self, physical_value: Optional[ParameterValue],
|
82
|
+
encode_state: EncodeState) -> None:
|
83
|
+
|
84
|
+
if physical_value is None:
|
85
|
+
physical_value = self._physical_default_value
|
83
86
|
if physical_value is None:
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
bit_position_int = self.bit_position if self.bit_position is not None else 0
|
91
|
-
return dop.convert_physical_to_bytes(
|
92
|
-
physical_value, encode_state=encode_state, bit_position=bit_position_int)
|
87
|
+
odxraise(
|
88
|
+
f"A value for parameter '{self.short_name}' must be specified"
|
89
|
+
f" because the parameter does not exhibit a default.", EncodeError)
|
90
|
+
|
91
|
+
self.dop.encode_into_pdu(physical_value, encode_state=encode_state)
|
odxtools/paramlengthinfotype.py
CHANGED
@@ -2,10 +2,12 @@
|
|
2
2
|
from dataclasses import dataclass
|
3
3
|
from typing import TYPE_CHECKING, Any, Dict, cast
|
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
|
8
|
-
from .exceptions import odxraise
|
10
|
+
from .exceptions import EncodeError, odxraise
|
9
11
|
from .odxlink import OdxLinkDatabase, OdxLinkId, OdxLinkRef
|
10
12
|
from .odxtypes import AtomicOdxType, DataType
|
11
13
|
|
@@ -43,35 +45,43 @@ class ParamLengthInfoType(DiagCodedType):
|
|
43
45
|
def length_key(self) -> "LengthKeyParameter":
|
44
46
|
return self._length_key
|
45
47
|
|
46
|
-
|
47
|
-
|
48
|
-
bit_length = encode_state.
|
48
|
+
@override
|
49
|
+
def encode_into_pdu(self, internal_value: AtomicOdxType, encode_state: EncodeState) -> None:
|
50
|
+
bit_length = encode_state.length_keys.get(self.length_key.short_name)
|
49
51
|
|
50
52
|
if bit_length is None:
|
53
|
+
# the length key is implicit, i.e., we need to set the
|
54
|
+
# value for the length key in the encode_state based on
|
55
|
+
# the value passed here.
|
51
56
|
if self.base_data_type in [
|
52
57
|
DataType.A_BYTEFIELD,
|
53
58
|
DataType.A_ASCIISTRING,
|
54
59
|
DataType.A_UTF8STRING,
|
55
60
|
]:
|
56
|
-
bit_length = 8 * len(internal_value)
|
57
|
-
|
58
|
-
bit_length = 16 * len(internal_value)
|
59
|
-
|
60
|
-
if self.base_data_type in [DataType.A_INT32, DataType.A_UINT32]:
|
61
|
+
bit_length = 8 * len(cast(str, internal_value))
|
62
|
+
elif self.base_data_type in [DataType.A_UNICODE2STRING]:
|
63
|
+
bit_length = 16 * len(cast(str, internal_value))
|
64
|
+
elif self.base_data_type in [DataType.A_INT32, DataType.A_UINT32]:
|
61
65
|
bit_length = int(internal_value).bit_length()
|
62
66
|
if self.base_data_type == DataType.A_INT32:
|
63
67
|
bit_length += 1
|
64
68
|
# Round up
|
65
69
|
bit_length = ((bit_length + 7) // 8) * 8
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
70
|
+
elif self.base_data_type == DataType.A_FLOAT32:
|
71
|
+
bit_length = 32
|
72
|
+
elif self.base_data_type == DataType.A_FLOAT64:
|
73
|
+
bit_length = 64
|
74
|
+
else:
|
75
|
+
odxraise(
|
76
|
+
f"Cannot determine size of an object of type "
|
77
|
+
f"{self.base_data_type.value}", EncodeError)
|
78
|
+
return
|
79
|
+
|
80
|
+
encode_state.length_keys[self.length_key.short_name] = bit_length
|
81
|
+
|
82
|
+
encode_state.emplace_atomic_value(
|
83
|
+
internal_value=internal_value,
|
84
|
+
used_mask=None,
|
75
85
|
bit_length=bit_length,
|
76
86
|
base_data_type=self.base_data_type,
|
77
87
|
is_highlow_byte_order=self.is_highlow_byte_order,
|
@@ -82,7 +92,7 @@ class ParamLengthInfoType(DiagCodedType):
|
|
82
92
|
if self.length_key.short_name not in decode_state.length_keys:
|
83
93
|
odxraise(f"Unspecified mandatory length key parameter "
|
84
94
|
f"{self.length_key.short_name}")
|
85
|
-
decode_state.cursor_bit_position =
|
95
|
+
decode_state.cursor_bit_position = 0
|
86
96
|
return cast(None, AtomicOdxType)
|
87
97
|
|
88
98
|
bit_length = decode_state.length_keys[self.length_key.short_name]
|
@@ -91,10 +101,8 @@ class ParamLengthInfoType(DiagCodedType):
|
|
91
101
|
bit_length = 0
|
92
102
|
|
93
103
|
# Extract the internal value and return.
|
94
|
-
|
104
|
+
return decode_state.extract_atomic_value(
|
95
105
|
bit_length,
|
96
106
|
self.base_data_type,
|
97
107
|
self.is_highlow_byte_order,
|
98
108
|
)
|
99
|
-
|
100
|
-
return value
|
odxtools/request.py
CHANGED
@@ -4,7 +4,9 @@ from typing import List
|
|
4
4
|
from xml.etree import ElementTree
|
5
5
|
|
6
6
|
from .basicstructure import BasicStructure
|
7
|
+
from .encodestate import EncodeState
|
7
8
|
from .odxlink import OdxDocFragment
|
9
|
+
from .odxtypes import ParameterValue
|
8
10
|
from .utils import dataclass_fields_asdict
|
9
11
|
|
10
12
|
|
@@ -20,3 +22,10 @@ class Request(BasicStructure):
|
|
20
22
|
kwargs = dataclass_fields_asdict(BasicStructure.from_et(et_element, doc_frags))
|
21
23
|
|
22
24
|
return Request(**kwargs)
|
25
|
+
|
26
|
+
def encode(self, **kwargs: ParameterValue) -> bytes:
|
27
|
+
encode_state = EncodeState(is_end_of_pdu=True)
|
28
|
+
|
29
|
+
self.encode_into_pdu(physical_value=kwargs, encode_state=encode_state)
|
30
|
+
|
31
|
+
return encode_state.coded_message
|
odxtools/response.py
CHANGED
@@ -5,10 +5,10 @@ from typing import List, Optional, cast
|
|
5
5
|
from xml.etree import ElementTree
|
6
6
|
|
7
7
|
from .basicstructure import BasicStructure
|
8
|
+
from .encodestate import EncodeState
|
8
9
|
from .exceptions import odxraise
|
9
10
|
from .odxlink import OdxDocFragment
|
10
11
|
from .odxtypes import ParameterValue
|
11
|
-
from .parameters.matchingrequestparameter import MatchingRequestParameter
|
12
12
|
from .utils import dataclass_fields_asdict
|
13
13
|
|
14
14
|
|
@@ -38,17 +38,9 @@ class Response(BasicStructure):
|
|
38
38
|
|
39
39
|
return Response(response_type=response_type, **kwargs)
|
40
40
|
|
41
|
-
def encode(self, coded_request: Optional[bytes] = None, **
|
42
|
-
|
43
|
-
# Extract MATCHING-REQUEST-PARAMs from the coded
|
44
|
-
# request. TODO: this should be done by
|
45
|
-
# MatchingRequestParam itself!
|
46
|
-
for param in self.parameters:
|
47
|
-
if isinstance(param, MatchingRequestParameter):
|
48
|
-
byte_pos = param.request_byte_position
|
49
|
-
byte_length = param.byte_length
|
41
|
+
def encode(self, coded_request: Optional[bytes] = None, **kwargs: ParameterValue) -> bytes:
|
42
|
+
encode_state = EncodeState(triggering_request=coded_request, is_end_of_pdu=True)
|
50
43
|
|
51
|
-
|
52
|
-
params[param.short_name] = val
|
44
|
+
self.encode_into_pdu(physical_value=kwargs, encode_state=encode_state)
|
53
45
|
|
54
|
-
return
|
46
|
+
return encode_state.coded_message
|
odxtools/standardlengthtype.py
CHANGED
@@ -1,11 +1,13 @@
|
|
1
1
|
# SPDX-License-Identifier: MIT
|
2
2
|
from dataclasses import dataclass
|
3
|
-
from typing import Optional
|
3
|
+
from typing import Literal, Optional
|
4
|
+
|
5
|
+
from typing_extensions import override
|
4
6
|
|
5
7
|
from .decodestate import DecodeState
|
6
8
|
from .diagcodedtype import DctType, DiagCodedType
|
7
9
|
from .encodestate import EncodeState
|
8
|
-
from .exceptions import odxassert, odxraise
|
10
|
+
from .exceptions import odxassert, odxraise, odxrequire
|
9
11
|
from .odxtypes import AtomicOdxType, DataType
|
10
12
|
|
11
13
|
|
@@ -20,6 +22,10 @@ class StandardLengthType(DiagCodedType):
|
|
20
22
|
def dct_type(self) -> DctType:
|
21
23
|
return "STANDARD-LENGTH-TYPE"
|
22
24
|
|
25
|
+
@property
|
26
|
+
def is_condensed(self) -> bool:
|
27
|
+
return self.is_condensed_raw is True
|
28
|
+
|
23
29
|
def __post_init__(self) -> None:
|
24
30
|
if self.bit_mask is not None:
|
25
31
|
maskable_types = (DataType.A_UINT32, DataType.A_INT32, DataType.A_BYTEFIELD)
|
@@ -28,11 +34,43 @@ class StandardLengthType(DiagCodedType):
|
|
28
34
|
'Can not apply a bit_mask on a value of type {self.base_data_type}',
|
29
35
|
)
|
30
36
|
|
37
|
+
def __get_raw_mask(self, internal_value: AtomicOdxType) -> Optional[bytes]:
|
38
|
+
"""Returns a byte field where all bits that are used by the
|
39
|
+
DiagCoded type are set and all unused ones are not set.
|
40
|
+
|
41
|
+
If `None` is returned, all bits are used.
|
42
|
+
"""
|
43
|
+
if self.bit_mask is None:
|
44
|
+
return None
|
45
|
+
|
46
|
+
if self.is_condensed:
|
47
|
+
odxraise("Condensed bit masks are not yet supported", NotImplementedError)
|
48
|
+
return
|
49
|
+
|
50
|
+
endianness: Literal["little", "big"] = "big"
|
51
|
+
if not self.is_highlow_byte_order and self.base_data_type in [
|
52
|
+
DataType.A_INT32, DataType.A_UINT32, DataType.A_FLOAT32, DataType.A_FLOAT64
|
53
|
+
]:
|
54
|
+
# TODO (?): Technically, little endian A_UNICODE2STRING
|
55
|
+
# objects require a byte swap for each 16 bit letter, and
|
56
|
+
# thus also for the mask. I somehow doubt that this has
|
57
|
+
# been anticipated by the standard, though...
|
58
|
+
endianness = "little"
|
59
|
+
|
60
|
+
sz: int
|
61
|
+
if isinstance(internal_value, (bytes, bytearray)):
|
62
|
+
sz = len(internal_value)
|
63
|
+
else:
|
64
|
+
sz = (odxrequire(self.get_static_bit_length()) + 7) // 8
|
65
|
+
|
66
|
+
return self.bit_mask.to_bytes(sz, endianness)
|
67
|
+
|
31
68
|
def __apply_mask(self, internal_value: AtomicOdxType) -> AtomicOdxType:
|
32
69
|
if self.bit_mask is None:
|
33
70
|
return internal_value
|
34
|
-
if self.
|
35
|
-
|
71
|
+
if self.is_condensed:
|
72
|
+
odxraise("Serialization of condensed bit mask is not supported", NotImplementedError)
|
73
|
+
return
|
36
74
|
if isinstance(internal_value, int):
|
37
75
|
return internal_value & self.bit_mask
|
38
76
|
if isinstance(internal_value, bytes):
|
@@ -46,16 +84,16 @@ class StandardLengthType(DiagCodedType):
|
|
46
84
|
def get_static_bit_length(self) -> Optional[int]:
|
47
85
|
return self.bit_length
|
48
86
|
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
self.__apply_mask(internal_value),
|
53
|
-
|
54
|
-
self.bit_length,
|
55
|
-
self.base_data_type,
|
56
|
-
is_highlow_byte_order=self.is_highlow_byte_order
|
57
|
-
)
|
87
|
+
@override
|
88
|
+
def encode_into_pdu(self, internal_value: AtomicOdxType, encode_state: EncodeState) -> None:
|
89
|
+
encode_state.emplace_atomic_value(
|
90
|
+
internal_value=self.__apply_mask(internal_value),
|
91
|
+
used_mask=self.__get_raw_mask(internal_value),
|
92
|
+
bit_length=self.bit_length,
|
93
|
+
base_data_type=self.base_data_type,
|
94
|
+
is_highlow_byte_order=self.is_highlow_byte_order)
|
58
95
|
|
96
|
+
@override
|
59
97
|
def decode_from_pdu(self, decode_state: DecodeState) -> AtomicOdxType:
|
60
98
|
internal_value = decode_state.extract_atomic_value(
|
61
99
|
self.bit_length,
|
odxtools/statechart.py
CHANGED
@@ -6,7 +6,7 @@ from xml.etree import ElementTree
|
|
6
6
|
from .element import IdentifiableElement
|
7
7
|
from .exceptions import odxrequire
|
8
8
|
from .nameditemlist import NamedItemList
|
9
|
-
from .odxlink import OdxDocFragment, OdxLinkDatabase, OdxLinkId
|
9
|
+
from .odxlink import OdxDocFragment, OdxLinkDatabase, OdxLinkId, resolve_snref
|
10
10
|
from .state import State
|
11
11
|
from .statetransition import StateTransition
|
12
12
|
from .utils import dataclass_fields_asdict
|
@@ -68,19 +68,15 @@ class StateChart(IdentifiableElement):
|
|
68
68
|
for st in self.states:
|
69
69
|
st._resolve_odxlinks(odxlinks)
|
70
70
|
|
71
|
-
|
72
|
-
#
|
71
|
+
def _resolve_snrefs(self, diag_layer: "DiagLayer") -> None:
|
72
|
+
# For now, we assume that the start state short name reference
|
73
|
+
# points to a local state of the state chart. TODO: The XSD
|
73
74
|
# allows to define state charts without any states, yet the
|
74
75
|
# start state SNREF is mandatory. Is this a gap in the spec or
|
75
76
|
# does it allow "foreign" start states? If the latter, what
|
76
77
|
# does that mean?
|
77
|
-
self._start_state
|
78
|
-
for st in self.states:
|
79
|
-
if st.short_name == self.start_state_snref:
|
80
|
-
self._start_state = st
|
81
|
-
break
|
78
|
+
self._start_state = resolve_snref(self.start_state_snref, self.states, State)
|
82
79
|
|
83
|
-
def _resolve_snrefs(self, diag_layer: "DiagLayer") -> None:
|
84
80
|
for st in self.states:
|
85
81
|
st._resolve_snrefs(diag_layer)
|
86
82
|
|