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/basicstructure.py
CHANGED
@@ -4,6 +4,8 @@ from dataclasses import dataclass
|
|
4
4
|
from typing import TYPE_CHECKING, Any, Dict, List, Optional, cast
|
5
5
|
from xml.etree import ElementTree
|
6
6
|
|
7
|
+
from typing_extensions import override
|
8
|
+
|
7
9
|
from .complexdop import ComplexDop
|
8
10
|
from .dataobjectproperty import DataObjectProperty
|
9
11
|
from .decodestate import DecodeState
|
@@ -54,32 +56,29 @@ class BasicStructure(ComplexDop):
|
|
54
56
|
return 8 * self.byte_size
|
55
57
|
|
56
58
|
cursor = 0
|
57
|
-
|
59
|
+
byte_length = 0
|
58
60
|
for param in self.parameters:
|
59
61
|
param_bit_length = param.get_static_bit_length()
|
60
62
|
if param_bit_length is None:
|
61
63
|
# We were not able to calculate a static bit length
|
62
64
|
return None
|
63
65
|
elif param.byte_position is not None:
|
64
|
-
|
65
|
-
byte_pos = param.byte_position or 0
|
66
|
-
cursor = byte_pos * 8 + bit_pos
|
66
|
+
cursor = param.byte_position
|
67
67
|
|
68
|
-
cursor += param_bit_length
|
69
|
-
|
68
|
+
cursor += ((param.bit_position or 0) + param_bit_length + 7) // 8
|
69
|
+
byte_length = max(byte_length, cursor)
|
70
70
|
|
71
71
|
# Round up to account for padding bits (all structures are
|
72
72
|
# byte aligned)
|
73
|
-
return
|
73
|
+
return byte_length * 8
|
74
74
|
|
75
75
|
def coded_const_prefix(self, request_prefix: bytes = b'') -> bytes:
|
76
|
-
|
77
|
-
|
78
|
-
bytearray(prefix), parameter_values={}, triggering_request=request_prefix)
|
76
|
+
encode_state = EncodeState(coded_message=bytearray(), triggering_request=request_prefix)
|
77
|
+
|
79
78
|
for param in self.parameters:
|
80
|
-
if isinstance(param, (
|
81
|
-
|
82
|
-
|
79
|
+
if (isinstance(param, MatchingRequestParameter) and param.request_byte_position < len(request_prefix)) or \
|
80
|
+
isinstance(param, (CodedConstParameter, NrcConstParameter, PhysicalConstantParameter)):
|
81
|
+
param.encode_into_pdu(physical_value=None, encode_state=encode_state)
|
83
82
|
else:
|
84
83
|
break
|
85
84
|
return encode_state.coded_message
|
@@ -116,58 +115,101 @@ class BasicStructure(ComplexDop):
|
|
116
115
|
|
117
116
|
print(parameter_info(self.free_parameters), end="")
|
118
117
|
|
119
|
-
def
|
120
|
-
|
121
|
-
|
122
|
-
|
118
|
+
def _validate_coded_message_size(self, coded_byte_len: int) -> None:
|
119
|
+
|
120
|
+
if self.byte_size is not None:
|
121
|
+
# We definitely broke something if we didn't respect the explicit byte_size
|
122
|
+
if self.byte_size != coded_byte_len:
|
123
|
+
warnings.warn(
|
124
|
+
"Verification of coded message failed: Incorrect size.",
|
125
|
+
OdxWarning,
|
126
|
+
stacklevel=1)
|
127
|
+
|
128
|
+
return
|
129
|
+
|
130
|
+
bit_length = self.get_static_bit_length()
|
131
|
+
|
132
|
+
if bit_length is None:
|
133
|
+
# Nothing to check
|
134
|
+
return
|
135
|
+
|
136
|
+
if coded_byte_len * 8 != bit_length:
|
137
|
+
# We may have broke something
|
138
|
+
# but it could be that bit_length was mis calculated and not the actual bytes are wrong
|
139
|
+
# Could happen with overlapping parameters and parameters with gaps
|
140
|
+
warnings.warn(
|
141
|
+
"Verification of coded message possibly failed: Size may be incorrect.",
|
142
|
+
OdxWarning,
|
143
|
+
stacklevel=1)
|
123
144
|
|
124
|
-
|
125
|
-
|
145
|
+
@override
|
146
|
+
def encode_into_pdu(self, physical_value: Optional[ParameterValue],
|
147
|
+
encode_state: EncodeState) -> None:
|
148
|
+
if not isinstance(physical_value, dict):
|
149
|
+
odxraise(
|
126
150
|
f"Expected a dictionary for the values of structure {self.short_name}, "
|
127
|
-
f"got {type(
|
151
|
+
f"got {type(physical_value).__name__}", EncodeError)
|
152
|
+
elif encode_state.cursor_bit_position != 0:
|
153
|
+
odxraise(
|
154
|
+
f"Structures must be byte aligned, but "
|
155
|
+
f"{self.short_name} requested to be at bit position "
|
156
|
+
f"{encode_state.cursor_bit_position}", EncodeError)
|
157
|
+
encode_state.bit_position = 0
|
158
|
+
|
159
|
+
orig_cursor = encode_state.cursor_byte_position
|
160
|
+
orig_origin = encode_state.origin_byte_position
|
161
|
+
encode_state.origin_byte_position = encode_state.cursor_byte_position
|
162
|
+
|
163
|
+
orig_is_end_of_pdu = encode_state.is_end_of_pdu
|
164
|
+
encode_state.is_end_of_pdu = False
|
128
165
|
|
129
166
|
# in strict mode, ensure that no values for unknown parameters are specified.
|
130
167
|
if strict_mode:
|
131
|
-
param_names =
|
132
|
-
for
|
133
|
-
if
|
134
|
-
odxraise(f"Value for unknown parameter '{
|
135
|
-
|
136
|
-
encode_state = EncodeState(
|
137
|
-
bytearray(),
|
138
|
-
dict(param_value),
|
139
|
-
triggering_request=triggering_coded_request,
|
140
|
-
is_end_of_pdu=False,
|
141
|
-
)
|
168
|
+
param_names = {param.short_name for param in self.parameters}
|
169
|
+
for param_value_name in physical_value:
|
170
|
+
if param_value_name not in param_names:
|
171
|
+
odxraise(f"Value for unknown parameter '{param_value_name}' specified "
|
172
|
+
f"for structure {self.short_name}")
|
142
173
|
|
143
174
|
for param in self.parameters:
|
144
|
-
if param == self.parameters[-1]:
|
145
|
-
# The last parameter is at the end of
|
146
|
-
# structure itself is at the end of the
|
147
|
-
# This assumes that the last parameter
|
148
|
-
# the ODX is located last in the PDU...
|
149
|
-
encode_state.is_end_of_pdu =
|
150
|
-
|
151
|
-
if isinstance(
|
152
|
-
|
153
|
-
|
154
|
-
# This is a hack to always encode a dummy value for
|
155
|
-
# length- and table keys. since these can be specified
|
175
|
+
if id(param) == id(self.parameters[-1]):
|
176
|
+
# The last parameter of the structure is at the end of
|
177
|
+
# the PDU if the structure itself is at the end of the
|
178
|
+
# PDU. TODO: This assumes that the last parameter
|
179
|
+
# specified in the ODX is located last in the PDU...
|
180
|
+
encode_state.is_end_of_pdu = orig_is_end_of_pdu
|
181
|
+
|
182
|
+
if isinstance(param, (LengthKeyParameter, TableKeyParameter)):
|
183
|
+
# At this point, we encode a placeholder value for length-
|
184
|
+
# and table keys, since these can be specified
|
156
185
|
# implicitly (i.e., by means of parameters that use
|
157
|
-
# these keys)
|
186
|
+
# these keys). To avoid getting an "overlapping
|
158
187
|
# parameter" warning, we must encode a value of zero
|
159
188
|
# into the PDU here and add the real value of the
|
160
|
-
# parameter in a post
|
161
|
-
|
162
|
-
|
163
|
-
|
189
|
+
# parameter in a post-processing step.
|
190
|
+
param.encode_placeholder_into_pdu(
|
191
|
+
physical_value=physical_value.get(param.short_name), encode_state=encode_state)
|
192
|
+
|
164
193
|
continue
|
165
194
|
|
166
|
-
|
195
|
+
if param.is_required and param.short_name not in physical_value:
|
196
|
+
odxraise(f"No value for required parameter {param.short_name} specified",
|
197
|
+
EncodeError)
|
167
198
|
|
168
|
-
|
169
|
-
|
170
|
-
|
199
|
+
param.encode_into_pdu(
|
200
|
+
physical_value=physical_value.get(param.short_name), encode_state=encode_state)
|
201
|
+
|
202
|
+
encode_state.is_end_of_pdu = False
|
203
|
+
if self.byte_size is not None:
|
204
|
+
actual_len = encode_state.cursor_byte_position - encode_state.origin_byte_position
|
205
|
+
if actual_len < self.byte_size:
|
206
|
+
# Padding bytes needed. We add an empty object at the
|
207
|
+
# position directly after the structure and let
|
208
|
+
# EncodeState add the padding as needed.
|
209
|
+
encode_state.cursor_byte_position = encode_state.origin_byte_position + self.byte_size
|
210
|
+
# Padding bytes needed. these count as "used".
|
211
|
+
encode_state.coded_message += b"\x00" * (self.byte_size - actual_len)
|
212
|
+
encode_state.used_mask += b"\xff" * (self.byte_size - actual_len)
|
171
213
|
|
172
214
|
# encode the length- and table keys. This cannot be done above
|
173
215
|
# because we allow these to be defined implicitly (i.e. they
|
@@ -177,56 +219,17 @@ class BasicStructure(ComplexDop):
|
|
177
219
|
# the current parameter is neither a length- nor a table key
|
178
220
|
continue
|
179
221
|
|
180
|
-
# Encode the key parameter into the message
|
181
|
-
|
222
|
+
# Encode the value of the key parameter into the message
|
223
|
+
param.encode_value_into_pdu(encode_state=encode_state)
|
182
224
|
|
183
225
|
# Assert that length is as expected
|
184
|
-
self.
|
185
|
-
|
186
|
-
return bytearray(encode_state.coded_message)
|
187
|
-
|
188
|
-
def _validate_coded_message(self, coded_message: bytes) -> None:
|
189
|
-
|
190
|
-
if self.byte_size is not None:
|
191
|
-
# We definitely broke something if we didn't respect the explicit byte_size
|
192
|
-
odxassert(
|
193
|
-
len(coded_message) == self.byte_size,
|
194
|
-
"Verification of coded message {coded_message.hex()} failed: Incorrect size.")
|
195
|
-
# No need to check further
|
196
|
-
return
|
197
|
-
|
198
|
-
bit_length = self.get_static_bit_length()
|
199
|
-
|
200
|
-
if bit_length is None:
|
201
|
-
# Nothing to check
|
202
|
-
return
|
226
|
+
self._validate_coded_message_size(encode_state.cursor_byte_position -
|
227
|
+
encode_state.origin_byte_position)
|
203
228
|
|
204
|
-
|
205
|
-
|
206
|
-
# but it could be that bit_length was mis calculated and not the actual bytes are wrong
|
207
|
-
# Could happen with overlapping parameters and parameters with gaps
|
208
|
-
warnings.warn(
|
209
|
-
f"Verification of coded message '{coded_message.hex()}' possibly failed: Size may be incorrect.",
|
210
|
-
OdxWarning,
|
211
|
-
stacklevel=1)
|
212
|
-
|
213
|
-
def convert_physical_to_bytes(self,
|
214
|
-
physical_value: ParameterValue,
|
215
|
-
encode_state: EncodeState,
|
216
|
-
bit_position: int = 0) -> bytes:
|
217
|
-
if not isinstance(physical_value, dict):
|
218
|
-
raise EncodeError(
|
219
|
-
f"Expected a dictionary for the values of structure {self.short_name}, "
|
220
|
-
f"got {type(physical_value)}")
|
221
|
-
if bit_position != 0:
|
222
|
-
raise EncodeError("Structures must be aligned, i.e. bit_position=0, but "
|
223
|
-
f"{self.short_name} was passed the bit position {bit_position}")
|
224
|
-
return self.convert_physical_to_internal(
|
225
|
-
physical_value,
|
226
|
-
triggering_coded_request=encode_state.triggering_request,
|
227
|
-
is_end_of_pdu=encode_state.is_end_of_pdu,
|
228
|
-
)
|
229
|
+
encode_state.origin_byte_position = orig_origin
|
230
|
+
encode_state.cursor_byte_position = max(orig_cursor, encode_state.cursor_byte_position)
|
229
231
|
|
232
|
+
@override
|
230
233
|
def decode_from_pdu(self, decode_state: DecodeState) -> ParameterValue:
|
231
234
|
# move the origin since positions specified by sub-parameters of
|
232
235
|
# structures are relative to the beginning of the structure object.
|
@@ -244,19 +247,6 @@ class BasicStructure(ComplexDop):
|
|
244
247
|
|
245
248
|
return result
|
246
249
|
|
247
|
-
def encode(self, coded_request: Optional[bytes] = None, **params: ParameterValue) -> bytes:
|
248
|
-
"""
|
249
|
-
Composes an UDS message as bytes for this service.
|
250
|
-
Parameters:
|
251
|
-
----------
|
252
|
-
coded_request: bytes
|
253
|
-
coded request (only needed when encoding a response)
|
254
|
-
params: dict
|
255
|
-
Parameters of the RPC as mapping from SHORT-NAME of the parameter to the value
|
256
|
-
"""
|
257
|
-
return self.convert_physical_to_internal(
|
258
|
-
params, triggering_coded_request=coded_request, is_end_of_pdu=True)
|
259
|
-
|
260
250
|
def decode(self, message: bytes) -> ParameterValueDict:
|
261
251
|
decode_state = DecodeState(coded_message=message)
|
262
252
|
param_values = self.decode_from_pdu(decode_state)
|
@@ -316,4 +306,4 @@ class BasicStructure(ComplexDop):
|
|
316
306
|
super()._resolve_snrefs(diag_layer)
|
317
307
|
|
318
308
|
for param in self.parameters:
|
319
|
-
param.
|
309
|
+
param._parameter_resolve_snrefs(diag_layer, param_list=self.parameters)
|
odxtools/dataobjectproperty.py
CHANGED
@@ -120,10 +120,7 @@ class DataObjectProperty(DopBase):
|
|
120
120
|
|
121
121
|
return self.compu_method.convert_physical_to_internal(physical_value)
|
122
122
|
|
123
|
-
def
|
124
|
-
physical_value: Any,
|
125
|
-
encode_state: EncodeState,
|
126
|
-
bit_position: int = 0) -> bytes:
|
123
|
+
def encode_into_pdu(self, physical_value: ParameterValue, encode_state: EncodeState) -> None:
|
127
124
|
"""
|
128
125
|
Convert a physical representation of a parameter to a string bytes that can be send over the wire
|
129
126
|
"""
|
@@ -132,9 +129,8 @@ class DataObjectProperty(DopBase):
|
|
132
129
|
f"The value {repr(physical_value)} of type {type(physical_value).__name__}"
|
133
130
|
f" is not a valid.")
|
134
131
|
|
135
|
-
|
136
|
-
|
137
|
-
internal_val, encode_state, bit_position=bit_position)
|
132
|
+
internal_value = self.convert_physical_to_internal(physical_value)
|
133
|
+
self.diag_coded_type.encode_into_pdu(internal_value, encode_state)
|
138
134
|
|
139
135
|
def decode_from_pdu(self, decode_state: DecodeState) -> ParameterValue:
|
140
136
|
"""
|
odxtools/decodestate.py
CHANGED
@@ -15,18 +15,6 @@ except ImportError:
|
|
15
15
|
if TYPE_CHECKING:
|
16
16
|
from .tablerow import TableRow
|
17
17
|
|
18
|
-
# format specifiers for the data type using the bitstruct module
|
19
|
-
ODX_TYPE_TO_FORMAT_LETTER = {
|
20
|
-
DataType.A_INT32: "s",
|
21
|
-
DataType.A_UINT32: "u",
|
22
|
-
DataType.A_FLOAT32: "f",
|
23
|
-
DataType.A_FLOAT64: "f",
|
24
|
-
DataType.A_BYTEFIELD: "r",
|
25
|
-
DataType.A_UNICODE2STRING: "r", # UTF-16 strings must be converted explicitly
|
26
|
-
DataType.A_ASCIISTRING: "r",
|
27
|
-
DataType.A_UTF8STRING: "r",
|
28
|
-
}
|
29
|
-
|
30
18
|
|
31
19
|
@dataclass
|
32
20
|
class DecodeState:
|
@@ -94,7 +82,7 @@ class DecodeState:
|
|
94
82
|
|
95
83
|
padding = (8 - (bit_length + self.cursor_bit_position) % 8) % 8
|
96
84
|
internal_value, = bitstruct.unpack_from(
|
97
|
-
f"{
|
85
|
+
f"{base_data_type.bitstruct_format_letter}{bit_length}",
|
98
86
|
extracted_bytes,
|
99
87
|
offset=padding)
|
100
88
|
|
odxtools/diagcodedtype.py
CHANGED
@@ -1,19 +1,14 @@
|
|
1
1
|
# SPDX-License-Identifier: MIT
|
2
2
|
import abc
|
3
3
|
from dataclasses import dataclass
|
4
|
-
from typing import TYPE_CHECKING, Any, Dict, Literal, Optional, Union
|
4
|
+
from typing import TYPE_CHECKING, Any, Dict, Literal, Optional, Union
|
5
5
|
|
6
|
-
from .decodestate import
|
6
|
+
from .decodestate import DecodeState
|
7
7
|
from .encodestate import EncodeState
|
8
|
-
from .exceptions import
|
8
|
+
from .exceptions import odxassert, odxraise
|
9
9
|
from .odxlink import OdxLinkDatabase, OdxLinkId
|
10
10
|
from .odxtypes import AtomicOdxType, DataType
|
11
11
|
|
12
|
-
try:
|
13
|
-
import bitstruct.c as bitstruct
|
14
|
-
except ImportError:
|
15
|
-
import bitstruct
|
16
|
-
|
17
12
|
if TYPE_CHECKING:
|
18
13
|
from .diaglayer import DiagLayer
|
19
14
|
|
@@ -56,88 +51,6 @@ class DiagCodedType(abc.ABC):
|
|
56
51
|
def is_highlow_byte_order(self) -> bool:
|
57
52
|
return self.is_highlow_byte_order_raw in [None, True]
|
58
53
|
|
59
|
-
@staticmethod
|
60
|
-
def _encode_internal_value(
|
61
|
-
internal_value: AtomicOdxType,
|
62
|
-
bit_position: int,
|
63
|
-
bit_length: int,
|
64
|
-
base_data_type: DataType,
|
65
|
-
is_highlow_byte_order: bool,
|
66
|
-
) -> bytes:
|
67
|
-
"""Convert the internal_value to bytes."""
|
68
|
-
# Check that bytes and strings actually fit into the bit length
|
69
|
-
if base_data_type == DataType.A_BYTEFIELD:
|
70
|
-
if isinstance(internal_value, bytearray):
|
71
|
-
internal_value = bytes(internal_value)
|
72
|
-
if not isinstance(internal_value, bytes):
|
73
|
-
odxraise()
|
74
|
-
if 8 * len(internal_value) > bit_length:
|
75
|
-
raise EncodeError(f"The bytefield {internal_value.hex()} is too large "
|
76
|
-
f"({len(internal_value)} bytes)."
|
77
|
-
f" The maximum length is {bit_length//8}.")
|
78
|
-
if base_data_type == DataType.A_ASCIISTRING:
|
79
|
-
if not isinstance(internal_value, str):
|
80
|
-
odxraise()
|
81
|
-
|
82
|
-
# The spec says ASCII, meaning only byte values 0-127.
|
83
|
-
# But in practice, vendors use iso-8859-1, aka latin-1
|
84
|
-
# reason being iso-8859-1 never fails since it has a valid
|
85
|
-
# character mapping for every possible byte sequence.
|
86
|
-
internal_value = internal_value.encode("iso-8859-1")
|
87
|
-
|
88
|
-
if 8 * len(internal_value) > bit_length:
|
89
|
-
raise EncodeError(f"The string {repr(internal_value)} is too large."
|
90
|
-
f" The maximum number of characters is {bit_length//8}.")
|
91
|
-
elif base_data_type == DataType.A_UTF8STRING:
|
92
|
-
if not isinstance(internal_value, str):
|
93
|
-
odxraise()
|
94
|
-
|
95
|
-
internal_value = internal_value.encode("utf-8")
|
96
|
-
|
97
|
-
if 8 * len(internal_value) > bit_length:
|
98
|
-
raise EncodeError(f"The string {repr(internal_value)} is too large."
|
99
|
-
f" The maximum number of bytes is {bit_length//8}.")
|
100
|
-
|
101
|
-
elif base_data_type == DataType.A_UNICODE2STRING:
|
102
|
-
if not isinstance(internal_value, str):
|
103
|
-
odxraise()
|
104
|
-
|
105
|
-
text_encoding = "utf-16-be" if is_highlow_byte_order else "utf-16-le"
|
106
|
-
internal_value = internal_value.encode(text_encoding)
|
107
|
-
|
108
|
-
if 8 * len(internal_value) > bit_length:
|
109
|
-
raise EncodeError(f"The string {repr(internal_value)} is too large."
|
110
|
-
f" The maximum number of characters is {bit_length//16}.")
|
111
|
-
|
112
|
-
# If the bit length is zero, return empty bytes
|
113
|
-
if bit_length == 0:
|
114
|
-
if (base_data_type.value in [
|
115
|
-
DataType.A_INT32, DataType.A_UINT32, DataType.A_FLOAT32, DataType.A_FLOAT64
|
116
|
-
] and base_data_type.value != 0):
|
117
|
-
raise EncodeError(
|
118
|
-
f"The number {repr(internal_value)} cannot be encoded into {bit_length} bits.")
|
119
|
-
return b''
|
120
|
-
|
121
|
-
char = ODX_TYPE_TO_FORMAT_LETTER[base_data_type]
|
122
|
-
padding = (8 - ((bit_length + bit_position) % 8)) % 8
|
123
|
-
odxassert((0 <= padding and padding < 8 and (padding + bit_length + bit_position) % 8 == 0),
|
124
|
-
f"Incorrect padding {padding}")
|
125
|
-
left_pad = f"p{padding}" if padding > 0 else ""
|
126
|
-
|
127
|
-
# actually encode the value
|
128
|
-
coded = bitstruct.pack(f"{left_pad}{char}{bit_length}", internal_value)
|
129
|
-
|
130
|
-
# apply byte order for numeric objects
|
131
|
-
if not is_highlow_byte_order and base_data_type in [
|
132
|
-
DataType.A_INT32,
|
133
|
-
DataType.A_UINT32,
|
134
|
-
DataType.A_FLOAT32,
|
135
|
-
DataType.A_FLOAT64,
|
136
|
-
]:
|
137
|
-
coded = coded[::-1]
|
138
|
-
|
139
|
-
return cast(bytes, coded)
|
140
|
-
|
141
54
|
def _minimal_byte_length_of(self, internal_value: Union[bytes, str]) -> int:
|
142
55
|
"""Helper method to get the minimal byte length.
|
143
56
|
(needed for LeadingLength- and MinMaxLengthType)
|
@@ -160,11 +73,10 @@ class DiagCodedType(abc.ABC):
|
|
160
73
|
odxassert(
|
161
74
|
byte_length % 2 == 0, f"The bit length of A_UNICODE2STRING must"
|
162
75
|
f" be a multiple of 16 but is {8*byte_length}")
|
76
|
+
|
163
77
|
return byte_length
|
164
78
|
|
165
|
-
|
166
|
-
def convert_internal_to_bytes(self, internal_value: AtomicOdxType, encode_state: EncodeState,
|
167
|
-
bit_position: int) -> bytes:
|
79
|
+
def encode_into_pdu(self, internal_value: AtomicOdxType, encode_state: EncodeState) -> None:
|
168
80
|
"""Encode the internal value.
|
169
81
|
|
170
82
|
Parameters
|
@@ -177,9 +89,9 @@ class DiagCodedType(abc.ABC):
|
|
177
89
|
mapping from ID (of the length key) to bit length
|
178
90
|
(only needed for ParamLengthInfoType)
|
179
91
|
"""
|
180
|
-
|
92
|
+
raise NotImplementedError(
|
93
|
+
f".encode_into_pdu() is not implemented by the class {type(self).__name__}")
|
181
94
|
|
182
|
-
@abc.abstractmethod
|
183
95
|
def decode_from_pdu(self, decode_state: DecodeState) -> AtomicOdxType:
|
184
96
|
"""Decode the parameter value from the coded message.
|
185
97
|
|
@@ -195,4 +107,5 @@ class DiagCodedType(abc.ABC):
|
|
195
107
|
int
|
196
108
|
the next byte position after the extracted parameter
|
197
109
|
"""
|
198
|
-
|
110
|
+
raise NotImplementedError(
|
111
|
+
f".decode_from_pdu() is not implemented by the class {type(self).__name__}")
|
odxtools/diagcomm.py
CHANGED
@@ -11,7 +11,7 @@ from .element import IdentifiableElement
|
|
11
11
|
from .exceptions import odxraise, odxrequire
|
12
12
|
from .functionalclass import FunctionalClass
|
13
13
|
from .nameditemlist import NamedItemList
|
14
|
-
from .odxlink import OdxDocFragment, OdxLinkDatabase, OdxLinkId, OdxLinkRef
|
14
|
+
from .odxlink import OdxDocFragment, OdxLinkDatabase, OdxLinkId, OdxLinkRef, resolve_snref
|
15
15
|
from .odxtypes import odxstr_to_bool
|
16
16
|
from .specialdatagroup import SpecialDataGroup
|
17
17
|
from .state import State
|
@@ -211,5 +211,13 @@ class DiagComm(IdentifiableElement):
|
|
211
211
|
for sdg in self.sdgs:
|
212
212
|
sdg._resolve_snrefs(diag_layer)
|
213
213
|
|
214
|
-
|
215
|
-
|
214
|
+
if TYPE_CHECKING:
|
215
|
+
self._protocols = NamedItemList([
|
216
|
+
resolve_snref(prot_snref, diag_layer.protocols, DiagLayer)
|
217
|
+
for prot_snref in self.protocol_snrefs
|
218
|
+
])
|
219
|
+
else:
|
220
|
+
self._protocols = NamedItemList([
|
221
|
+
resolve_snref(prot_snref, diag_layer.protocols)
|
222
|
+
for prot_snref in self.protocol_snrefs
|
223
|
+
])
|
@@ -10,6 +10,7 @@ from .createsdgs import create_sdgs_from_et
|
|
10
10
|
from .dataobjectproperty import DataObjectProperty
|
11
11
|
from .dopbase import DopBase
|
12
12
|
from .dtcdop import DtcDop
|
13
|
+
from .dynamicendmarkerfield import DynamicEndmarkerField
|
13
14
|
from .dynamiclengthfield import DynamicLengthField
|
14
15
|
from .endofpdufield import EndOfPduField
|
15
16
|
from .environmentdata import EnvironmentData
|
@@ -37,7 +38,7 @@ class DiagDataDictionarySpec:
|
|
37
38
|
structures: NamedItemList[BasicStructure]
|
38
39
|
static_fields: NamedItemList[StaticField]
|
39
40
|
dynamic_length_fields: NamedItemList[DynamicLengthField]
|
40
|
-
|
41
|
+
dynamic_endmarker_fields: NamedItemList[DynamicEndmarkerField]
|
41
42
|
end_of_pdu_fields: NamedItemList[EndOfPduField]
|
42
43
|
muxs: NamedItemList[Multiplexer]
|
43
44
|
env_datas: NamedItemList[EnvironmentData]
|
@@ -54,7 +55,7 @@ class DiagDataDictionarySpec:
|
|
54
55
|
self.structures,
|
55
56
|
self.static_fields,
|
56
57
|
self.dynamic_length_fields,
|
57
|
-
|
58
|
+
self.dynamic_endmarker_fields,
|
58
59
|
self.end_of_pdu_fields,
|
59
60
|
self.muxs,
|
60
61
|
self.env_datas,
|
@@ -99,11 +100,10 @@ class DiagDataDictionarySpec:
|
|
99
100
|
for dl_element in et_element.iterfind("DYNAMIC-LENGTH-FIELDS/DYNAMIC-LENGTH-FIELD")
|
100
101
|
]
|
101
102
|
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
#]
|
103
|
+
dynamic_endmarker_fields = [
|
104
|
+
DynamicEndmarkerField.from_et(dl_element, doc_frags) for dl_element in
|
105
|
+
et_element.iterfind("DYNAMIC-ENDMARKER-FIELDS/DYNAMIC-ENDMARKER-FIELD")
|
106
|
+
]
|
107
107
|
|
108
108
|
end_of_pdu_fields = [
|
109
109
|
EndOfPduField.from_et(eofp_element, doc_frags)
|
@@ -145,7 +145,7 @@ class DiagDataDictionarySpec:
|
|
145
145
|
structures=NamedItemList(structures),
|
146
146
|
static_fields=NamedItemList(static_fields),
|
147
147
|
dynamic_length_fields=NamedItemList(dynamic_length_fields),
|
148
|
-
|
148
|
+
dynamic_endmarker_fields=NamedItemList(dynamic_endmarker_fields),
|
149
149
|
end_of_pdu_fields=NamedItemList(end_of_pdu_fields),
|
150
150
|
muxs=NamedItemList(muxs),
|
151
151
|
env_datas=NamedItemList(env_datas),
|
@@ -172,8 +172,8 @@ class DiagDataDictionarySpec:
|
|
172
172
|
odxlinks.update(static_field._build_odxlinks())
|
173
173
|
for dynamic_length_field in self.dynamic_length_fields:
|
174
174
|
odxlinks.update(dynamic_length_field._build_odxlinks())
|
175
|
-
|
176
|
-
|
175
|
+
for dynamic_endmarker_field in self.dynamic_endmarker_fields:
|
176
|
+
odxlinks.update(dynamic_endmarker_field._build_odxlinks())
|
177
177
|
for end_of_pdu_field in self.end_of_pdu_fields:
|
178
178
|
odxlinks.update(end_of_pdu_field._build_odxlinks())
|
179
179
|
for mux in self.muxs:
|
@@ -204,8 +204,8 @@ class DiagDataDictionarySpec:
|
|
204
204
|
static_field._resolve_odxlinks(odxlinks)
|
205
205
|
for dynamic_length_field in self.dynamic_length_fields:
|
206
206
|
dynamic_length_field._resolve_odxlinks(odxlinks)
|
207
|
-
|
208
|
-
|
207
|
+
for dynamic_endmarker_field in self.dynamic_endmarker_fields:
|
208
|
+
dynamic_endmarker_field._resolve_odxlinks(odxlinks)
|
209
209
|
for end_of_pdu_field in self.end_of_pdu_fields:
|
210
210
|
end_of_pdu_field._resolve_odxlinks(odxlinks)
|
211
211
|
for mux in self.muxs:
|
@@ -234,8 +234,8 @@ class DiagDataDictionarySpec:
|
|
234
234
|
static_field._resolve_snrefs(diag_layer)
|
235
235
|
for dynamic_length_field in self.dynamic_length_fields:
|
236
236
|
dynamic_length_field._resolve_snrefs(diag_layer)
|
237
|
-
|
238
|
-
|
237
|
+
for dynamic_endmarker_field in self.dynamic_endmarker_fields:
|
238
|
+
dynamic_endmarker_field._resolve_snrefs(diag_layer)
|
239
239
|
for end_of_pdu_field in self.end_of_pdu_fields:
|
240
240
|
end_of_pdu_field._resolve_snrefs(diag_layer)
|
241
241
|
for mux in self.muxs:
|