odxtools 6.7.0__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/compumethods/createanycompumethod.py +1 -1
- 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.0.dist-info → odxtools-7.0.0.dist-info}/METADATA +1 -1
- {odxtools-6.7.0.dist-info → odxtools-7.0.0.dist-info}/RECORD +46 -46
- {odxtools-6.7.0.dist-info → odxtools-7.0.0.dist-info}/LICENSE +0 -0
- {odxtools-6.7.0.dist-info → odxtools-7.0.0.dist-info}/WHEEL +0 -0
- {odxtools-6.7.0.dist-info → odxtools-7.0.0.dist-info}/entry_points.txt +0 -0
- {odxtools-6.7.0.dist-info → odxtools-7.0.0.dist-info}/top_level.txt +0 -0
odxtools/dtcdop.py
CHANGED
@@ -4,6 +4,8 @@ from dataclasses import dataclass
|
|
4
4
|
from typing import TYPE_CHECKING, Any, Dict, List, Optional, Union, cast
|
5
5
|
from xml.etree import ElementTree
|
6
6
|
|
7
|
+
from typing_extensions import override
|
8
|
+
|
7
9
|
from .compumethods.compumethod import CompuMethod
|
8
10
|
from .compumethods.createanycompumethod import create_any_compu_method_from_et
|
9
11
|
from .createanydiagcodedtype import create_any_diag_coded_type_from_et
|
@@ -12,7 +14,7 @@ from .diagcodedtype import DiagCodedType
|
|
12
14
|
from .diagnostictroublecode import DiagnosticTroubleCode
|
13
15
|
from .dopbase import DopBase
|
14
16
|
from .encodestate import EncodeState
|
15
|
-
from .exceptions import DecodeError, EncodeError, odxassert, odxrequire
|
17
|
+
from .exceptions import DecodeError, EncodeError, odxassert, odxraise, odxrequire
|
16
18
|
from .nameditemlist import NamedItemList
|
17
19
|
from .odxlink import OdxDocFragment, OdxLinkDatabase, OdxLinkId, OdxLinkRef
|
18
20
|
from .odxtypes import ParameterValue, odxstr_to_bool
|
@@ -87,17 +89,20 @@ class DtcDop(DopBase):
|
|
87
89
|
def linked_dtc_dops(self) -> NamedItemList["DtcDop"]:
|
88
90
|
return self._linked_dtc_dops
|
89
91
|
|
92
|
+
@override
|
90
93
|
def decode_from_pdu(self, decode_state: DecodeState) -> ParameterValue:
|
91
94
|
|
92
95
|
int_trouble_code = self.diag_coded_type.decode_from_pdu(decode_state)
|
93
96
|
|
94
|
-
if self.compu_method.is_valid_internal_value(int_trouble_code):
|
95
|
-
trouble_code = self.compu_method.convert_internal_to_physical(int_trouble_code)
|
96
|
-
else:
|
97
|
+
if not self.compu_method.is_valid_internal_value(int_trouble_code):
|
97
98
|
# TODO: How to prevent this?
|
98
|
-
|
99
|
+
odxraise(
|
99
100
|
f"DTC-DOP {self.short_name} could not convert the coded value "
|
100
|
-
f" {repr(int_trouble_code)} to physical type {self.physical_type.base_data_type}."
|
101
|
+
f" {repr(int_trouble_code)} to physical type {self.physical_type.base_data_type}.",
|
102
|
+
DecodeError)
|
103
|
+
return
|
104
|
+
|
105
|
+
trouble_code = self.compu_method.convert_internal_to_physical(int_trouble_code)
|
101
106
|
|
102
107
|
assert isinstance(trouble_code, int)
|
103
108
|
|
@@ -110,10 +115,12 @@ class DtcDop(DopBase):
|
|
110
115
|
return dtcs[0]
|
111
116
|
|
112
117
|
# the DTC was not specified. This probably means that the
|
113
|
-
# diagnostic description file is incomplete.
|
114
|
-
|
115
|
-
|
116
|
-
|
118
|
+
# diagnostic description file is incomplete.
|
119
|
+
odxraise(
|
120
|
+
f"Encountered DTC 0x{trouble_code:06x} which has not been defined "
|
121
|
+
f"by the database", DecodeError)
|
122
|
+
|
123
|
+
return DiagnosticTroubleCode(
|
117
124
|
trouble_code=trouble_code,
|
118
125
|
odx_id=cast(OdxLinkId, None),
|
119
126
|
short_name=f'DTC_{trouble_code:06x}',
|
@@ -126,12 +133,9 @@ class DtcDop(DopBase):
|
|
126
133
|
sdgs=[],
|
127
134
|
)
|
128
135
|
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
physical_value: ParameterValue,
|
133
|
-
encode_state: EncodeState,
|
134
|
-
bit_position: int = 0) -> bytes:
|
136
|
+
@override
|
137
|
+
def encode_into_pdu(self, physical_value: Optional[ParameterValue],
|
138
|
+
encode_state: EncodeState) -> None:
|
135
139
|
if isinstance(physical_value, DiagnosticTroubleCode):
|
136
140
|
trouble_code = physical_value.trouble_code
|
137
141
|
elif isinstance(physical_value, int):
|
@@ -147,9 +151,7 @@ class DtcDop(DopBase):
|
|
147
151
|
f" DiagnosticTroubleCode but got {physical_value!r}.")
|
148
152
|
|
149
153
|
internal_trouble_code = self.compu_method.convert_physical_to_internal(trouble_code)
|
150
|
-
|
151
|
-
return self.diag_coded_type.convert_internal_to_bytes(
|
152
|
-
internal_trouble_code, encode_state=encode_state, bit_position=bit_position)
|
154
|
+
self.diag_coded_type.encode_into_pdu(internal_trouble_code, encode_state)
|
153
155
|
|
154
156
|
def _build_odxlinks(self) -> Dict[OdxLinkId, Any]:
|
155
157
|
odxlinks = super()._build_odxlinks()
|
odxtools/dynamiclengthfield.py
CHANGED
@@ -3,6 +3,8 @@ from dataclasses import dataclass
|
|
3
3
|
from typing import TYPE_CHECKING, Any, Dict, List
|
4
4
|
from xml.etree import ElementTree
|
5
5
|
|
6
|
+
from typing_extensions import override
|
7
|
+
|
6
8
|
from .decodestate import DecodeState
|
7
9
|
from .determinenumberofitems import DetermineNumberOfItems
|
8
10
|
from .encodestate import EncodeState
|
@@ -47,44 +49,46 @@ class DynamicLengthField(Field):
|
|
47
49
|
super()._resolve_snrefs(diag_layer)
|
48
50
|
self.determine_number_of_items._resolve_snrefs(diag_layer)
|
49
51
|
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
encode_state
|
54
|
-
|
55
|
-
) -> bytes:
|
52
|
+
@override
|
53
|
+
def encode_into_pdu(self, physical_value: ParameterValue, encode_state: EncodeState) -> None:
|
54
|
+
|
55
|
+
odxassert(encode_state.cursor_bit_position == 0,
|
56
|
+
"No bit position can be specified for dynamic length fields!")
|
56
57
|
|
57
|
-
odxassert(bit_position == 0, "No bit position can be specified for dynamic length fields!")
|
58
58
|
if not isinstance(physical_value, list):
|
59
59
|
odxraise(
|
60
60
|
f"Expected a list of values for dynamic length field {self.short_name}, "
|
61
61
|
f"got {type(physical_value)}", EncodeError)
|
62
62
|
|
63
|
+
# move the origin to the cursor position
|
64
|
+
orig_cursor = encode_state.cursor_byte_position
|
65
|
+
orig_origin = encode_state.origin_byte_position
|
66
|
+
encode_state.origin_byte_position = encode_state.cursor_byte_position
|
67
|
+
|
63
68
|
det_num_items = self.determine_number_of_items
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
encode_state.coded_message = bytearray()
|
70
|
-
encode_state.emplace_atomic_value(field_len, self.short_name + ".num_items",
|
71
|
-
det_num_items.byte_position)
|
72
|
-
result = encode_state.coded_message
|
73
|
-
encode_state.coded_message = tmp
|
74
|
-
|
75
|
-
# if required, add padding between the length specifier and
|
76
|
-
# the first item
|
77
|
-
if len(result) < self.offset:
|
78
|
-
result.extend([0] * (self.offset - len(result)))
|
79
|
-
elif len(result) > self.offset:
|
69
|
+
encode_state.cursor_bit_position = self.determine_number_of_items.bit_position or 0
|
70
|
+
encode_state.cursor_byte_position = encode_state.origin_byte_position + det_num_items.byte_position
|
71
|
+
det_num_items.dop.encode_into_pdu(len(physical_value), encode_state)
|
72
|
+
|
73
|
+
if encode_state.cursor_byte_position - encode_state.origin_byte_position > self.offset:
|
80
74
|
odxraise(f"The length specifier of field {self.short_name} overlaps "
|
81
|
-
f"with
|
75
|
+
f"with its first item!")
|
76
|
+
|
77
|
+
encode_state.cursor_byte_position = encode_state.origin_byte_position + self.offset
|
78
|
+
encode_state.cursor_bit_position = 0
|
82
79
|
|
83
80
|
for value in physical_value:
|
84
|
-
|
81
|
+
self.structure.encode_into_pdu(value, encode_state)
|
85
82
|
|
86
|
-
|
83
|
+
# ensure the correct message size if the field is empty
|
84
|
+
if len(physical_value) == 0:
|
85
|
+
encode_state.emplace_bytes(b"")
|
86
|
+
|
87
|
+
# move cursor and origin positions
|
88
|
+
encode_state.origin_byte_position = orig_origin
|
89
|
+
encode_state.cursor_byte_position = max(orig_cursor, encode_state.cursor_byte_position)
|
87
90
|
|
91
|
+
@override
|
88
92
|
def decode_from_pdu(self, decode_state: DecodeState) -> ParameterValue:
|
89
93
|
|
90
94
|
odxassert(decode_state.cursor_bit_position == 0,
|
odxtools/encodestate.py
CHANGED
@@ -1,12 +1,15 @@
|
|
1
1
|
# SPDX-License-Identifier: MIT
|
2
2
|
import warnings
|
3
3
|
from dataclasses import dataclass, field
|
4
|
-
from typing import
|
4
|
+
from typing import Dict, Optional
|
5
5
|
|
6
|
-
from .exceptions import OdxWarning
|
6
|
+
from .exceptions import EncodeError, OdxWarning, odxassert, odxraise
|
7
|
+
from .odxtypes import AtomicOdxType, DataType
|
7
8
|
|
8
|
-
|
9
|
-
|
9
|
+
try:
|
10
|
+
import bitstruct.c as bitstruct
|
11
|
+
except ImportError:
|
12
|
+
import bitstruct
|
10
13
|
|
11
14
|
|
12
15
|
@dataclass
|
@@ -15,10 +18,23 @@ class EncodeState:
|
|
15
18
|
"""
|
16
19
|
|
17
20
|
#: payload that has been constructed so far
|
18
|
-
coded_message: bytearray
|
21
|
+
coded_message: bytearray = field(default_factory=bytearray)
|
19
22
|
|
20
|
-
#:
|
21
|
-
|
23
|
+
#: the bits of the payload that are used
|
24
|
+
used_mask: bytearray = field(default_factory=bytearray)
|
25
|
+
|
26
|
+
#: The absolute position in bytes from the beginning of the PDU to
|
27
|
+
#: which relative positions refer to, e.g., the beginning of the
|
28
|
+
#: structure.
|
29
|
+
origin_byte_position: int = 0
|
30
|
+
|
31
|
+
#: The absolute position in bytes from the beginning of the PDU
|
32
|
+
#: where the next object ought to be placed into the PDU
|
33
|
+
cursor_byte_position: int = 0
|
34
|
+
|
35
|
+
#: The bit position [0-7] where the next object ought to be
|
36
|
+
#: placed into the PDU
|
37
|
+
cursor_bit_position: int = 0
|
22
38
|
|
23
39
|
#: If encoding a response: request that triggered the response
|
24
40
|
triggering_request: Optional[bytes] = None
|
@@ -28,31 +44,180 @@ class EncodeState:
|
|
28
44
|
length_keys: Dict[str, int] = field(default_factory=dict)
|
29
45
|
|
30
46
|
#: Mapping from the short name of a table-key parameter to the
|
31
|
-
#: corresponding row of the table (specified by
|
32
|
-
|
47
|
+
#: short name of the corresponding row of the table (specified by
|
48
|
+
#: TableKeyParameter)
|
49
|
+
table_keys: Dict[str, str] = field(default_factory=dict)
|
50
|
+
|
51
|
+
#: The cursor position where a given length- or table key is located
|
52
|
+
#: in the PDU
|
53
|
+
key_pos: Dict[str, int] = field(default_factory=dict)
|
33
54
|
|
34
55
|
#: Flag whether we are currently the last parameter of the PDU
|
35
|
-
#: (needed for MinMaxLengthType)
|
36
|
-
is_end_of_pdu: bool =
|
56
|
+
#: (needed for MinMaxLengthType, EndOfPduField, etc.)
|
57
|
+
is_end_of_pdu: bool = True
|
58
|
+
|
59
|
+
def __post_init__(self) -> None:
|
60
|
+
# if a coded message has been specified, but no used_mask, we
|
61
|
+
# assume that all of the bits of the coded message are
|
62
|
+
# currently used.
|
63
|
+
if len(self.coded_message) > len(self.used_mask):
|
64
|
+
self.used_mask += b'\xff' * (len(self.coded_message) - len(self.used_mask))
|
65
|
+
if len(self.coded_message) < len(self.used_mask):
|
66
|
+
odxraise(f"The specified bit mask 0x{self.used_mask.hex()} for used bits "
|
67
|
+
f"is not suitable for representing the coded_message "
|
68
|
+
f"0x{self.coded_message.hex()}")
|
69
|
+
self.used_mask = self.used_mask[:len(self.coded_message)]
|
70
|
+
|
71
|
+
def emplace_atomic_value(
|
72
|
+
self,
|
73
|
+
*,
|
74
|
+
internal_value: AtomicOdxType,
|
75
|
+
bit_length: int,
|
76
|
+
base_data_type: DataType,
|
77
|
+
is_highlow_byte_order: bool,
|
78
|
+
used_mask: Optional[bytes],
|
79
|
+
) -> None:
|
80
|
+
"""Convert the internal_value to bytes and emplace this into the PDU"""
|
81
|
+
|
82
|
+
raw_value: AtomicOdxType
|
83
|
+
|
84
|
+
# Check that bytes and strings actually fit into the bit length
|
85
|
+
if base_data_type == DataType.A_BYTEFIELD:
|
86
|
+
if not isinstance(internal_value, bytes):
|
87
|
+
odxraise()
|
88
|
+
if 8 * len(internal_value) > bit_length:
|
89
|
+
raise EncodeError(f"The bytefield {internal_value.hex()} is too large "
|
90
|
+
f"({len(internal_value)} bytes)."
|
91
|
+
f" The maximum length is {bit_length//8}.")
|
92
|
+
raw_value = internal_value
|
93
|
+
elif base_data_type == DataType.A_ASCIISTRING:
|
94
|
+
if not isinstance(internal_value, str):
|
95
|
+
odxraise()
|
96
|
+
|
97
|
+
# The spec says ASCII, meaning only byte values 0-127.
|
98
|
+
# But in practice, vendors use iso-8859-1, aka latin-1
|
99
|
+
# reason being iso-8859-1 never fails since it has a valid
|
100
|
+
# character mapping for every possible byte sequence.
|
101
|
+
raw_value = internal_value.encode("iso-8859-1")
|
102
|
+
|
103
|
+
if 8 * len(raw_value) > bit_length:
|
104
|
+
raise EncodeError(f"The string {repr(internal_value)} is too large."
|
105
|
+
f" The maximum number of characters is {bit_length//8}.")
|
106
|
+
elif base_data_type == DataType.A_UTF8STRING:
|
107
|
+
if not isinstance(internal_value, str):
|
108
|
+
odxraise()
|
109
|
+
|
110
|
+
raw_value = internal_value.encode("utf-8")
|
111
|
+
|
112
|
+
if 8 * len(raw_value) > bit_length:
|
113
|
+
raise EncodeError(f"The string {repr(internal_value)} is too large."
|
114
|
+
f" The maximum number of bytes is {bit_length//8}.")
|
115
|
+
|
116
|
+
elif base_data_type == DataType.A_UNICODE2STRING:
|
117
|
+
if not isinstance(internal_value, str):
|
118
|
+
odxraise()
|
37
119
|
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
120
|
+
text_encoding = "utf-16-be" if is_highlow_byte_order else "utf-16-le"
|
121
|
+
raw_value = internal_value.encode(text_encoding)
|
122
|
+
|
123
|
+
if 8 * len(raw_value) > bit_length:
|
124
|
+
raise EncodeError(f"The string {repr(internal_value)} is too large."
|
125
|
+
f" The maximum number of characters is {bit_length//16}.")
|
126
|
+
else:
|
127
|
+
raw_value = internal_value
|
128
|
+
|
129
|
+
# If the bit length is zero, return empty bytes
|
130
|
+
if bit_length == 0:
|
131
|
+
if (base_data_type.value in [
|
132
|
+
DataType.A_INT32, DataType.A_UINT32, DataType.A_FLOAT32, DataType.A_FLOAT64
|
133
|
+
] and base_data_type.value != 0):
|
134
|
+
odxraise(
|
135
|
+
f"The number {repr(internal_value)} cannot be encoded into {bit_length} bits.",
|
136
|
+
EncodeError)
|
137
|
+
self.emplace_bytes(b'')
|
138
|
+
return
|
139
|
+
|
140
|
+
char = base_data_type.bitstruct_format_letter
|
141
|
+
padding = (8 - ((bit_length + self.cursor_bit_position) % 8)) % 8
|
142
|
+
odxassert((0 <= padding and padding < 8 and
|
143
|
+
(padding + bit_length + self.cursor_bit_position) % 8 == 0),
|
144
|
+
f"Incorrect padding {padding}")
|
145
|
+
left_pad = f"p{padding}" if padding > 0 else ""
|
146
|
+
|
147
|
+
# actually encode the value
|
148
|
+
coded = bitstruct.pack(f"{left_pad}{char}{bit_length}", raw_value)
|
149
|
+
|
150
|
+
# create the raw mask of used bits for numeric objects
|
151
|
+
used_mask_raw = used_mask
|
152
|
+
if base_data_type in [DataType.A_INT32, DataType.A_UINT32
|
153
|
+
] and (self.cursor_bit_position != 0 or
|
154
|
+
(self.cursor_bit_position + bit_length) % 8 != 0):
|
155
|
+
if used_mask is None:
|
156
|
+
tmp = (1 << bit_length) - 1
|
157
|
+
else:
|
158
|
+
tmp = int.from_bytes(used_mask, "big")
|
159
|
+
tmp <<= self.cursor_bit_position
|
160
|
+
|
161
|
+
used_mask_raw = tmp.to_bytes((self.cursor_bit_position + bit_length + 7) // 8, "big")
|
162
|
+
|
163
|
+
# apply byte order to numeric objects
|
164
|
+
if not is_highlow_byte_order and base_data_type in [
|
165
|
+
DataType.A_INT32, DataType.A_UINT32, DataType.A_FLOAT32, DataType.A_FLOAT64
|
166
|
+
]:
|
167
|
+
coded = coded[::-1]
|
168
|
+
|
169
|
+
if used_mask_raw is not None:
|
170
|
+
used_mask_raw = used_mask_raw[::-1]
|
171
|
+
|
172
|
+
self.cursor_bit_position = 0
|
173
|
+
self.emplace_bytes(coded, obj_used_mask=used_mask_raw)
|
174
|
+
|
175
|
+
def emplace_bytes(self,
|
176
|
+
new_data: bytes,
|
177
|
+
obj_name: Optional[str] = None,
|
178
|
+
obj_used_mask: Optional[bytes] = None) -> None:
|
179
|
+
if self.cursor_bit_position != 0:
|
180
|
+
odxraise("EncodeState.emplace_bytes can only be called "
|
181
|
+
"for a bit position of 0!", RuntimeError)
|
182
|
+
|
183
|
+
pos = self.cursor_byte_position
|
44
184
|
|
45
185
|
# Make blob longer if necessary
|
46
186
|
min_length = pos + len(new_data)
|
47
187
|
if len(self.coded_message) < min_length:
|
48
|
-
|
188
|
+
pad = b'\x00' * (min_length - len(self.coded_message))
|
189
|
+
self.coded_message += pad
|
190
|
+
self.used_mask += pad
|
191
|
+
|
192
|
+
if obj_used_mask is None:
|
193
|
+
# Happy path for when no obj_used_mask has been
|
194
|
+
# specified. In this case we assume that all bits of the
|
195
|
+
# new data to be emplaced are used.
|
196
|
+
n = len(new_data)
|
49
197
|
|
50
|
-
|
51
|
-
# insert byte value
|
52
|
-
if self.coded_message[byte_idx_rpc] & new_data[byte_idx_val] != 0:
|
198
|
+
if self.used_mask[pos:pos + n] != b'\x00' * n:
|
53
199
|
warnings.warn(
|
54
|
-
f"
|
200
|
+
f"Overlapping objects detected in between bytes {pos} and "
|
201
|
+
f"{pos+n}",
|
55
202
|
OdxWarning,
|
56
203
|
stacklevel=1,
|
57
204
|
)
|
58
|
-
self.coded_message[
|
205
|
+
self.coded_message[pos:pos + n] = new_data
|
206
|
+
self.used_mask[pos:pos + n] = b'\xff' * n
|
207
|
+
else:
|
208
|
+
# insert data the hard way, i.e. we have to look at each
|
209
|
+
# individual byte to determine if it has already been used
|
210
|
+
# somewhere else (it would be nice if bytearrays supported
|
211
|
+
# bitwise operations!)
|
212
|
+
for i in range(len(new_data)):
|
213
|
+
if self.used_mask[pos + i] & obj_used_mask[i] != 0:
|
214
|
+
warnings.warn(
|
215
|
+
f"Overlapping objects detected at position {pos + i}",
|
216
|
+
OdxWarning,
|
217
|
+
stacklevel=1,
|
218
|
+
)
|
219
|
+
self.coded_message[pos + i] &= ~obj_used_mask[i]
|
220
|
+
self.coded_message[pos + i] |= new_data[i] & obj_used_mask[i]
|
221
|
+
self.used_mask[pos + i] |= obj_used_mask[i]
|
222
|
+
|
223
|
+
self.cursor_byte_position += len(new_data)
|
odxtools/endofpdufield.py
CHANGED
@@ -3,6 +3,8 @@ from dataclasses import dataclass
|
|
3
3
|
from typing import List, Optional
|
4
4
|
from xml.etree import ElementTree
|
5
5
|
|
6
|
+
from typing_extensions import override
|
7
|
+
|
6
8
|
from .decodestate import DecodeState
|
7
9
|
from .encodestate import EncodeState
|
8
10
|
from .exceptions import EncodeError, odxassert, odxraise
|
@@ -39,26 +41,22 @@ class EndOfPduField(Field):
|
|
39
41
|
|
40
42
|
return eopf
|
41
43
|
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
encode_state
|
46
|
-
|
47
|
-
) -> bytes:
|
44
|
+
@override
|
45
|
+
def encode_into_pdu(self, physical_value: Optional[ParameterValue],
|
46
|
+
encode_state: EncodeState) -> None:
|
47
|
+
odxassert(not encode_state.cursor_bit_position,
|
48
|
+
"No bit position can be specified for end-of-pdu fields!")
|
48
49
|
|
49
|
-
odxassert(
|
50
|
-
bit_position == 0, "End of PDU field must be byte aligned. "
|
51
|
-
"Is there an error in reading the .odx?", EncodeError)
|
52
50
|
if not isinstance(physical_value, list):
|
53
51
|
odxraise(
|
54
|
-
f"
|
55
|
-
f"
|
52
|
+
f"Invalid type {type(physical_value).__name__} of physical "
|
53
|
+
f"value for end-of-pdu field, expected a list", EncodeError)
|
54
|
+
return
|
56
55
|
|
57
|
-
coded_message = b''
|
58
56
|
for value in physical_value:
|
59
|
-
|
60
|
-
return coded_message
|
57
|
+
self.structure.encode_into_pdu(value, encode_state)
|
61
58
|
|
59
|
+
@override
|
62
60
|
def decode_from_pdu(self, decode_state: DecodeState) -> ParameterValue:
|
63
61
|
odxassert(not decode_state.cursor_bit_position,
|
64
62
|
"No bit position can be specified for end-of-pdu fields!")
|
@@ -1,13 +1,15 @@
|
|
1
1
|
# SPDX-License-Identifier: MIT
|
2
2
|
from dataclasses import dataclass
|
3
|
-
from typing import TYPE_CHECKING, Any, Dict, List, Optional
|
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
|
7
|
+
|
6
8
|
from .complexdop import ComplexDop
|
7
9
|
from .decodestate import DecodeState
|
8
10
|
from .encodestate import EncodeState
|
9
11
|
from .environmentdata import EnvironmentData
|
10
|
-
from .exceptions import
|
12
|
+
from .exceptions import odxraise, odxrequire
|
11
13
|
from .odxlink import OdxDocFragment, OdxLinkDatabase, OdxLinkId, OdxLinkRef
|
12
14
|
from .odxtypes import ParameterValue
|
13
15
|
from .utils import dataclass_fields_asdict
|
@@ -86,23 +88,21 @@ class EnvironmentDataDescription(ComplexDop):
|
|
86
88
|
for ed in self.env_datas:
|
87
89
|
ed._resolve_snrefs(diag_layer)
|
88
90
|
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
"""Convert the physical value into bytes.
|
91
|
+
@override
|
92
|
+
def encode_into_pdu(self, physical_value: Optional[ParameterValue],
|
93
|
+
encode_state: EncodeState) -> None:
|
94
|
+
"""Convert a physical value into bytes and emplace them into a PDU.
|
94
95
|
|
95
96
|
Since environmental data is supposed to never appear on the
|
96
97
|
wire, this method just raises an EncodeError exception.
|
97
98
|
"""
|
98
|
-
|
99
|
+
odxraise("EnvironmentDataDescription DOPs cannot be encoded or decoded")
|
99
100
|
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
"""Extract the bytes from the PDU and convert them to the physical value.
|
101
|
+
@override
|
102
|
+
def decode_from_pdu(self, decode_state: DecodeState) -> ParameterValue:
|
103
|
+
"""Extract the bytes from a PDU and convert them to a physical value.
|
104
104
|
|
105
105
|
Since environmental data is supposed to never appear on the
|
106
106
|
wire, this method just raises an DecodeError exception.
|
107
107
|
"""
|
108
|
-
|
108
|
+
odxraise("EnvironmentDataDescription DOPs cannot be encoded or decoded")
|
@@ -1,11 +1,13 @@
|
|
1
1
|
# SPDX-License-Identifier: MIT
|
2
2
|
from dataclasses import dataclass
|
3
|
-
from typing import
|
3
|
+
from typing import 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 EncodeError, odxassert, odxraise
|
9
11
|
from .odxtypes import AtomicOdxType, DataType
|
10
12
|
|
11
13
|
|
@@ -42,29 +44,40 @@ class LeadingLengthInfoType(DiagCodedType):
|
|
42
44
|
# DCT is dynamic!
|
43
45
|
return None
|
44
46
|
|
45
|
-
|
46
|
-
|
47
|
+
@override
|
48
|
+
def encode_into_pdu(self, internal_value: AtomicOdxType, encode_state: EncodeState) -> None:
|
49
|
+
|
50
|
+
if not isinstance(internal_value, (str, bytes)):
|
51
|
+
odxraise(
|
52
|
+
f"LEADING-LENGTH-INFO types can only be used for strings and byte fields, "
|
53
|
+
f"not {type(internal_value).__name__}", EncodeError)
|
54
|
+
return
|
47
55
|
|
48
56
|
byte_length = self._minimal_byte_length_of(internal_value)
|
49
57
|
|
50
|
-
|
51
|
-
|
52
|
-
|
58
|
+
used_mask = None
|
59
|
+
bit_pos = encode_state.cursor_bit_position
|
60
|
+
if encode_state.cursor_bit_position != 0 or (bit_pos + self.bit_length) % 8 != 0:
|
61
|
+
used_mask = (1 << self.bit_length) - 1
|
62
|
+
used_mask <<= bit_pos
|
63
|
+
|
64
|
+
encode_state.emplace_atomic_value(
|
65
|
+
internal_value=byte_length,
|
66
|
+
used_mask=None,
|
53
67
|
bit_length=self.bit_length,
|
54
68
|
base_data_type=DataType.A_UINT32,
|
55
69
|
is_highlow_byte_order=self.is_highlow_byte_order,
|
56
70
|
)
|
57
71
|
|
58
|
-
|
59
|
-
internal_value,
|
60
|
-
|
72
|
+
encode_state.emplace_atomic_value(
|
73
|
+
internal_value=internal_value,
|
74
|
+
used_mask=None,
|
61
75
|
bit_length=8 * byte_length,
|
62
76
|
base_data_type=self.base_data_type,
|
63
77
|
is_highlow_byte_order=self.is_highlow_byte_order,
|
64
78
|
)
|
65
79
|
|
66
|
-
|
67
|
-
|
80
|
+
@override
|
68
81
|
def decode_from_pdu(self, decode_state: DecodeState) -> AtomicOdxType:
|
69
82
|
|
70
83
|
# Extract length of the parameter value
|