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
@@ -1,13 +1,13 @@
|
|
1
1
|
# SPDX-License-Identifier: MIT
|
2
2
|
from dataclasses import dataclass
|
3
|
-
from typing import TYPE_CHECKING, Any, Dict, List
|
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
|
-
from ..exceptions import odxraise, odxrequire
|
10
|
+
from ..exceptions import EncodeError, odxraise, odxrequire
|
11
11
|
from ..odxlink import OdxDocFragment, OdxLinkDatabase, OdxLinkId
|
12
12
|
from ..odxtypes import ParameterValue
|
13
13
|
from ..utils import dataclass_fields_asdict
|
@@ -77,17 +77,65 @@ class LengthKeyParameter(ParameterWithDOP):
|
|
77
77
|
return True
|
78
78
|
|
79
79
|
@override
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
def
|
90
|
-
|
80
|
+
@final
|
81
|
+
def _encode_positioned_into_pdu(self, physical_value: Optional[ParameterValue],
|
82
|
+
encode_state: EncodeState) -> None:
|
83
|
+
# if you get this exception, you ought to use
|
84
|
+
# `.encode_placeholder_into_pdu()` followed by (after the
|
85
|
+
# value of the length key has been determined)
|
86
|
+
# `.encode_value_into_pdu()`.
|
87
|
+
raise RuntimeError("_encode_positioned_into_pdu() cannot be called for length keys.")
|
88
|
+
|
89
|
+
def encode_placeholder_into_pdu(self, physical_value: Optional[ParameterValue],
|
90
|
+
encode_state: EncodeState) -> None:
|
91
|
+
|
92
|
+
if physical_value is not None:
|
93
|
+
if not self.dop.is_valid_physical_value(physical_value):
|
94
|
+
odxraise(f"Invalid explicitly specified physical value '{physical_value!r}' "
|
95
|
+
f"for length key '{self.short_name}'.")
|
96
|
+
|
97
|
+
lkv = encode_state.length_keys.get(self.short_name)
|
98
|
+
if lkv is not None and lkv != physical_value:
|
99
|
+
odxraise(f"Got conflicting values for length key {self.short_name}: "
|
100
|
+
f"{lkv} and {physical_value!r}")
|
101
|
+
|
102
|
+
if not isinstance(physical_value, int):
|
103
|
+
odxraise(
|
104
|
+
f"Value of length key {self.short_name} is of type {type(physical_value).__name__} "
|
105
|
+
f"instead of int")
|
106
|
+
|
107
|
+
encode_state.length_keys[self.short_name] = physical_value
|
108
|
+
|
109
|
+
orig_cursor = encode_state.cursor_byte_position
|
110
|
+
pos = encode_state.cursor_byte_position
|
111
|
+
if self.byte_position is not None:
|
112
|
+
pos = encode_state.origin_byte_position + self.byte_position
|
113
|
+
encode_state.key_pos[self.short_name] = pos
|
114
|
+
encode_state.cursor_byte_position = pos
|
115
|
+
encode_state.cursor_bit_position = self.bit_position or 0
|
116
|
+
|
117
|
+
# emplace a value of zero into the encode state, but pretend the bits not to be used
|
118
|
+
n = odxrequire(self.dop.get_static_bit_length()) + encode_state.cursor_bit_position
|
119
|
+
tmp_val = b'\x00' * ((n + 7) // 8)
|
120
|
+
encode_state.emplace_bytes(tmp_val, obj_used_mask=tmp_val)
|
121
|
+
|
122
|
+
encode_state.cursor_byte_position = max(encode_state.cursor_byte_position, orig_cursor)
|
123
|
+
encode_state.cursor_bit_position = 0
|
124
|
+
|
125
|
+
def encode_value_into_pdu(self, encode_state: EncodeState) -> None:
|
126
|
+
|
127
|
+
if self.short_name not in encode_state.length_keys:
|
128
|
+
odxraise(
|
129
|
+
f"Length key {self.short_name} has not been defined before "
|
130
|
+
f"it is required.", EncodeError)
|
131
|
+
return
|
132
|
+
else:
|
133
|
+
physical_value = encode_state.length_keys[self.short_name]
|
134
|
+
|
135
|
+
encode_state.cursor_byte_position = encode_state.key_pos[self.short_name]
|
136
|
+
encode_state.cursor_bit_position = self.bit_position or 0
|
137
|
+
|
138
|
+
self.dop.encode_into_pdu(encode_state=encode_state, physical_value=physical_value)
|
91
139
|
|
92
140
|
@override
|
93
141
|
def _decode_positioned_from_pdu(self, decode_state: DecodeState) -> ParameterValue:
|
@@ -7,7 +7,7 @@ from typing_extensions import override
|
|
7
7
|
|
8
8
|
from ..decodestate import DecodeState
|
9
9
|
from ..encodestate import EncodeState
|
10
|
-
from ..exceptions import EncodeError, odxrequire
|
10
|
+
from ..exceptions import EncodeError, odxraise, odxrequire
|
11
11
|
from ..odxlink import OdxDocFragment
|
12
12
|
from ..odxtypes import DataType, ParameterValue
|
13
13
|
from ..utils import dataclass_fields_asdict
|
@@ -52,19 +52,31 @@ class MatchingRequestParameter(Parameter):
|
|
52
52
|
return False
|
53
53
|
|
54
54
|
@override
|
55
|
-
def
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
55
|
+
def _encode_positioned_into_pdu(self, physical_value: Optional[ParameterValue],
|
56
|
+
encode_state: EncodeState) -> None:
|
57
|
+
if encode_state.triggering_request is None:
|
58
|
+
odxraise(
|
59
|
+
f"Parameter '{self.short_name}' is of matching request type,"
|
60
|
+
f" but no original request has been specified.", EncodeError)
|
61
|
+
return
|
62
|
+
|
63
|
+
rq_pos = self.request_byte_position
|
64
|
+
rq_len = self.byte_length
|
65
|
+
|
66
|
+
if len(encode_state.triggering_request) < rq_pos + rq_len:
|
67
|
+
odxraise(
|
68
|
+
f"Specified triggering request 0x{encode_state.triggering_request.hex()} "
|
69
|
+
f"is not long enough to encode matching request parameter "
|
70
|
+
f"'{self.short_name}': Have {len(encode_state.triggering_request)} "
|
71
|
+
f"bytes, need at least {rq_pos + rq_len} bytes", EncodeError)
|
72
|
+
return
|
73
|
+
|
74
|
+
encode_state.emplace_bytes(encode_state.triggering_request[rq_pos:rq_pos + rq_len],
|
75
|
+
self.short_name)
|
62
76
|
|
63
77
|
@override
|
64
78
|
def _decode_positioned_from_pdu(self, decode_state: DecodeState) -> ParameterValue:
|
65
|
-
|
79
|
+
return decode_state.extract_atomic_value(
|
66
80
|
bit_length=self.byte_length * 8,
|
67
81
|
base_data_type=DataType.A_UINT32,
|
68
82
|
is_highlow_byte_order=False)
|
69
|
-
|
70
|
-
return result
|
@@ -1,7 +1,7 @@
|
|
1
1
|
# SPDX-License-Identifier: MIT
|
2
2
|
import warnings
|
3
3
|
from dataclasses import dataclass
|
4
|
-
from typing import TYPE_CHECKING, Any, Dict, List, Optional
|
4
|
+
from typing import TYPE_CHECKING, Any, Dict, List, Optional, cast
|
5
5
|
from xml.etree import ElementTree
|
6
6
|
|
7
7
|
from typing_extensions import override
|
@@ -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, EncodeError, 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
|
|
@@ -22,13 +22,19 @@ if TYPE_CHECKING:
|
|
22
22
|
|
23
23
|
@dataclass
|
24
24
|
class NrcConstParameter(Parameter):
|
25
|
-
"""A param of type NRC-CONST defines a set of values to be matched.
|
25
|
+
"""A param of type NRC-CONST defines a set of values to be matched for a negative response to apply.
|
26
26
|
|
27
|
-
|
28
|
-
|
29
|
-
|
27
|
+
The behaviour of NRC-CONST parameters is similar to CODED-CONST
|
28
|
+
parameters in that they allow to specify which coding objects
|
29
|
+
apply to a binary string, but in contrast to CODED-CONST
|
30
|
+
parameters they allow to specify multiple values. Thus, the value
|
31
|
+
of a CODED-CONST parameter is usually set using an overlapping
|
32
|
+
VALUE parameter. Since NRC-CONST parameters can only be specified
|
33
|
+
for negative responses, they can thus be regarded as a multiplexer
|
34
|
+
mechanism that is specific to negative responses.
|
30
35
|
|
31
36
|
See ASAM MCD-2 D (ODX), p. 77-79.
|
37
|
+
|
32
38
|
"""
|
33
39
|
|
34
40
|
diag_coded_type: DiagCodedType
|
@@ -91,21 +97,34 @@ class NrcConstParameter(Parameter):
|
|
91
97
|
return False
|
92
98
|
|
93
99
|
@override
|
94
|
-
def
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
100
|
+
def _encode_positioned_into_pdu(self, physical_value: Optional[ParameterValue],
|
101
|
+
encode_state: EncodeState) -> None:
|
102
|
+
coded_value: ParameterValue
|
103
|
+
if physical_value is not None:
|
104
|
+
if physical_value not in self.coded_values:
|
105
|
+
odxraise(
|
106
|
+
f"The value of parameter '{self.short_name}' must "
|
107
|
+
f" be one of {self.coded_values} (is: {physical_value!r})", EncodeError)
|
108
|
+
coded_value = self.coded_values[0]
|
99
109
|
else:
|
100
|
-
coded_value =
|
110
|
+
coded_value = physical_value
|
101
111
|
else:
|
102
|
-
# If the user
|
103
|
-
#
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
112
|
+
# If the user did not select a value, the value of the
|
113
|
+
# this parameter is set by another parameter which
|
114
|
+
# overlaps with it. We thus just move the cursor.
|
115
|
+
bit_pos = encode_state.cursor_bit_position
|
116
|
+
bit_len = self.diag_coded_type.get_static_bit_length()
|
117
|
+
|
118
|
+
if bit_len is None:
|
119
|
+
odxraise("The diag coded type of NRC-CONST parameters must "
|
120
|
+
"exhibit a static size")
|
121
|
+
return
|
122
|
+
|
123
|
+
encode_state.cursor_byte_position += (bit_pos + bit_len + 7) // 8
|
124
|
+
encode_state.cursor_bit_position = 0
|
125
|
+
return
|
126
|
+
|
127
|
+
self.diag_coded_type.encode_into_pdu(cast(AtomicOdxType, coded_value), encode_state)
|
109
128
|
|
110
129
|
@override
|
111
130
|
def _decode_positioned_from_pdu(self, decode_state: DecodeState) -> AtomicOdxType:
|
odxtools/parameters/parameter.py
CHANGED
@@ -117,12 +117,35 @@ class Parameter(NamedElement):
|
|
117
117
|
"""
|
118
118
|
raise NotImplementedError(".is_settable is not implemented by the concrete parameter class")
|
119
119
|
|
120
|
-
|
121
|
-
|
122
|
-
|
120
|
+
@final
|
121
|
+
def encode_into_pdu(self, physical_value: Optional[ParameterValue],
|
122
|
+
encode_state: EncodeState) -> None:
|
123
|
+
"""Convert a physical value into its encoded form and place it into the PDU
|
124
|
+
|
125
|
+
Also, adapt the `encode_state` so that it points to where the next
|
126
|
+
parameter is located (if the parameter does not explicitly specify a
|
127
|
+
position)
|
123
128
|
"""
|
129
|
+
|
130
|
+
orig_cursor = encode_state.cursor_byte_position
|
131
|
+
if self.byte_position is not None:
|
132
|
+
encode_state.cursor_byte_position = encode_state.origin_byte_position + self.byte_position
|
133
|
+
|
134
|
+
encode_state.cursor_bit_position = self.bit_position or 0
|
135
|
+
|
136
|
+
self._encode_positioned_into_pdu(physical_value, encode_state)
|
137
|
+
|
138
|
+
encode_state.cursor_byte_position = max(encode_state.cursor_byte_position, orig_cursor)
|
139
|
+
encode_state.cursor_bit_position = 0
|
140
|
+
|
141
|
+
def _encode_positioned_into_pdu(self, physical_value: Optional[ParameterValue],
|
142
|
+
encode_state: EncodeState) -> None:
|
143
|
+
"""Method which actually encodes the parameter
|
144
|
+
|
145
|
+
Its location is managed by `Parameter`."""
|
124
146
|
raise NotImplementedError(
|
125
|
-
"
|
147
|
+
f"Required method '_encode_positioned_into_pdu()' not implemented by "
|
148
|
+
f"child class {type(self).__name__}")
|
126
149
|
|
127
150
|
@final
|
128
151
|
def decode_from_pdu(self, decode_state: DecodeState) -> ParameterValue:
|
@@ -144,41 +167,5 @@ class Parameter(NamedElement):
|
|
144
167
|
|
145
168
|
Its location is managed by `Parameter`."""
|
146
169
|
raise NotImplementedError(
|
147
|
-
"Required method '_decode_positioned_from_pdu()' not implemented by
|
148
|
-
|
149
|
-
def encode_into_pdu(self, encode_state: EncodeState) -> bytes:
|
150
|
-
"""Encode the value of a parameter into a binary blob and return it
|
151
|
-
|
152
|
-
If the byte position of the parameter is not defined,
|
153
|
-
the byte code is appended to the blob.
|
154
|
-
|
155
|
-
Technical note for subclasses: The default implementation
|
156
|
-
tries to compute the coded value via
|
157
|
-
`self.get_coded_value_as_bytes(encoded_state)` and inserts it
|
158
|
-
into the PDU. Thus it usually suffices to overwrite
|
159
|
-
`get_coded_value_as_bytes()` instead of `encode_into_pdu()`.
|
160
|
-
|
161
|
-
Parameters:
|
162
|
-
----------
|
163
|
-
encode_state: EncodeState, i.e. a named tuple with attributes
|
164
|
-
* coded_message: bytes, the message encoded so far
|
165
|
-
* parameter_values: List[ParameterValuePairs]
|
166
|
-
* triggering_coded_request: bytes
|
167
|
-
|
168
|
-
Returns:
|
169
|
-
-------
|
170
|
-
bytes
|
171
|
-
the message's blob after adding the encoded parameter into it
|
172
|
-
|
173
|
-
"""
|
174
|
-
msg_blob = encode_state.coded_message
|
175
|
-
param_blob = self.get_coded_value_as_bytes(encode_state)
|
176
|
-
|
177
|
-
if self.byte_position is not None:
|
178
|
-
byte_position = self.byte_position
|
179
|
-
else:
|
180
|
-
byte_position = len(msg_blob)
|
181
|
-
|
182
|
-
encode_state.emplace_atomic_value(param_blob, self.short_name, byte_position)
|
183
|
-
|
184
|
-
return encode_state.coded_message
|
170
|
+
f"Required method '_decode_positioned_from_pdu()' not implemented by "
|
171
|
+
f"child class {type(self).__name__}")
|
@@ -1,6 +1,6 @@
|
|
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, cast
|
4
4
|
from xml.etree import ElementTree
|
5
5
|
|
6
6
|
from typing_extensions import override
|
@@ -12,7 +12,7 @@ from ..dtcdop import DtcDop
|
|
12
12
|
from ..encodestate import EncodeState
|
13
13
|
from ..exceptions import odxassert, odxrequire
|
14
14
|
from ..odxlink import OdxDocFragment, OdxLinkDatabase, OdxLinkId, OdxLinkRef
|
15
|
-
from ..odxtypes import ParameterValue
|
15
|
+
from ..odxtypes import AtomicOdxType, ParameterValue
|
16
16
|
from ..physicaltype import PhysicalType
|
17
17
|
from ..utils import dataclass_fields_asdict
|
18
18
|
from .parameter import Parameter
|
@@ -91,12 +91,9 @@ class ParameterWithDOP(Parameter):
|
|
91
91
|
return None
|
92
92
|
|
93
93
|
@override
|
94
|
-
def
|
95
|
-
|
96
|
-
physical_value
|
97
|
-
bit_position_int = self.bit_position if self.bit_position is not None else 0
|
98
|
-
return dop.convert_physical_to_bytes(
|
99
|
-
physical_value, encode_state, bit_position=bit_position_int)
|
94
|
+
def _encode_positioned_into_pdu(self, physical_value: Optional[ParameterValue],
|
95
|
+
encode_state: EncodeState) -> None:
|
96
|
+
self.dop.encode_into_pdu(cast(AtomicOdxType, physical_value), encode_state)
|
100
97
|
|
101
98
|
@override
|
102
99
|
def _decode_positioned_from_pdu(self, decode_state: DecodeState) -> ParameterValue:
|
@@ -1,6 +1,6 @@
|
|
1
1
|
# SPDX-License-Identifier: MIT
|
2
2
|
from dataclasses import dataclass
|
3
|
-
from typing import TYPE_CHECKING, Any, Dict, List
|
3
|
+
from typing import TYPE_CHECKING, Any, Dict, List, Optional
|
4
4
|
from xml.etree import ElementTree
|
5
5
|
|
6
6
|
from typing_extensions import override
|
@@ -8,7 +8,7 @@ from typing_extensions import override
|
|
8
8
|
from ..dataobjectproperty import DataObjectProperty
|
9
9
|
from ..decodestate import DecodeState
|
10
10
|
from ..encodestate import EncodeState
|
11
|
-
from ..exceptions import DecodeError, odxraise, odxrequire
|
11
|
+
from ..exceptions import DecodeError, EncodeError, odxraise, odxrequire
|
12
12
|
from ..odxlink import OdxDocFragment, OdxLinkDatabase, OdxLinkId
|
13
13
|
from ..odxtypes import ParameterValue
|
14
14
|
from ..utils import dataclass_fields_asdict
|
@@ -74,17 +74,15 @@ class PhysicalConstantParameter(ParameterWithDOP):
|
|
74
74
|
return False
|
75
75
|
|
76
76
|
@override
|
77
|
-
def
|
78
|
-
|
79
|
-
if
|
80
|
-
|
81
|
-
|
82
|
-
f"
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
return dop.convert_physical_to_bytes(
|
87
|
-
self.physical_constant_value, encode_state, bit_position=bit_position_int)
|
77
|
+
def _encode_positioned_into_pdu(self, physical_value: Optional[ParameterValue],
|
78
|
+
encode_state: EncodeState) -> None:
|
79
|
+
if physical_value is not None and physical_value != self.physical_constant_value:
|
80
|
+
odxraise(
|
81
|
+
f"Value for constant parameter `{self.short_name}` name can "
|
82
|
+
f"only be specified as {self.physical_constant_value!r} (is: {physical_value!r})",
|
83
|
+
EncodeError)
|
84
|
+
|
85
|
+
self.dop.encode_into_pdu(self.physical_constant_value, encode_state)
|
88
86
|
|
89
87
|
@override
|
90
88
|
def _decode_positioned_from_pdu(self, decode_state: DecodeState) -> ParameterValue:
|
@@ -99,4 +97,5 @@ class PhysicalConstantParameter(ParameterWithDOP):
|
|
99
97
|
f"{self.physical_constant_value!r} but got {phys_val!r} "
|
100
98
|
f"at byte position {decode_state.cursor_byte_position} "
|
101
99
|
f"in coded message {decode_state.coded_message.hex()}.", DecodeError)
|
100
|
+
|
102
101
|
return phys_val
|
@@ -49,8 +49,11 @@ class ReservedParameter(Parameter):
|
|
49
49
|
return self.bit_length
|
50
50
|
|
51
51
|
@override
|
52
|
-
def
|
53
|
-
|
52
|
+
def _encode_positioned_into_pdu(self, physical_value: Optional[ParameterValue],
|
53
|
+
encode_state: EncodeState) -> None:
|
54
|
+
raw_data = (0).to_bytes((encode_state.cursor_bit_position + self.bit_length + 7) // 8,
|
55
|
+
"big")
|
56
|
+
encode_state.emplace_bytes(raw_data, self.short_name)
|
54
57
|
|
55
58
|
@override
|
56
59
|
def _decode_positioned_from_pdu(self, decode_state: DecodeState) -> ParameterValue:
|
@@ -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
|
@@ -46,7 +46,8 @@ class SystemParameter(ParameterWithDOP):
|
|
46
46
|
raise NotImplementedError("SystemParameter.is_settable is not implemented yet.")
|
47
47
|
|
48
48
|
@override
|
49
|
-
def
|
49
|
+
def _encode_positioned_into_pdu(self, physical_value: Optional[ParameterValue],
|
50
|
+
encode_state: EncodeState) -> None:
|
50
51
|
raise NotImplementedError("Encoding a SystemParameter is not implemented yet.")
|
51
52
|
|
52
53
|
@override
|
@@ -1,6 +1,6 @@
|
|
1
1
|
# SPDX-License-Identifier: MIT
|
2
2
|
from dataclasses import dataclass
|
3
|
-
from typing import TYPE_CHECKING, List
|
3
|
+
from typing import TYPE_CHECKING, List, Optional
|
4
4
|
from xml.etree import ElementTree
|
5
5
|
|
6
6
|
from typing_extensions import override
|
@@ -59,7 +59,8 @@ class TableEntryParameter(Parameter):
|
|
59
59
|
raise NotImplementedError("TableEntryParameter.is_settable is not implemented yet.")
|
60
60
|
|
61
61
|
@override
|
62
|
-
def
|
62
|
+
def _encode_positioned_into_pdu(self, physical_value: Optional[ParameterValue],
|
63
|
+
encode_state: EncodeState) -> None:
|
63
64
|
raise NotImplementedError("Encoding a TableEntryParameter is not implemented yet.")
|
64
65
|
|
65
66
|
@property
|
@@ -3,7 +3,7 @@ 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
|
@@ -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:
|