odxtools 6.7.1__py3-none-any.whl → 7.0.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (45) hide show
  1. odxtools/basicstructure.py +106 -112
  2. odxtools/dataobjectproperty.py +3 -7
  3. odxtools/decodestate.py +1 -13
  4. odxtools/diagcodedtype.py +9 -96
  5. odxtools/diaglayer.py +45 -0
  6. odxtools/diagservice.py +12 -16
  7. odxtools/dopbase.py +5 -8
  8. odxtools/dtcdop.py +21 -19
  9. odxtools/dynamiclengthfield.py +30 -26
  10. odxtools/encodestate.py +188 -23
  11. odxtools/endofpdufield.py +12 -14
  12. odxtools/environmentdatadescription.py +13 -13
  13. odxtools/leadinglengthinfotype.py +25 -12
  14. odxtools/minmaxlengthtype.py +36 -26
  15. odxtools/multiplexer.py +42 -23
  16. odxtools/multiplexercase.py +1 -1
  17. odxtools/nameditemlist.py +14 -0
  18. odxtools/odxtypes.py +17 -0
  19. odxtools/parameterinfo.py +126 -40
  20. odxtools/parameters/codedconstparameter.py +14 -11
  21. odxtools/parameters/dynamicparameter.py +5 -4
  22. odxtools/parameters/lengthkeyparameter.py +62 -14
  23. odxtools/parameters/matchingrequestparameter.py +23 -11
  24. odxtools/parameters/nrcconstparameter.py +39 -20
  25. odxtools/parameters/parameter.py +29 -42
  26. odxtools/parameters/parameterwithdop.py +5 -8
  27. odxtools/parameters/physicalconstantparameter.py +12 -13
  28. odxtools/parameters/reservedparameter.py +5 -2
  29. odxtools/parameters/systemparameter.py +3 -2
  30. odxtools/parameters/tableentryparameter.py +3 -2
  31. odxtools/parameters/tablekeyparameter.py +79 -30
  32. odxtools/parameters/tablestructparameter.py +62 -42
  33. odxtools/parameters/valueparameter.py +12 -14
  34. odxtools/paramlengthinfotype.py +30 -22
  35. odxtools/request.py +9 -0
  36. odxtools/response.py +5 -13
  37. odxtools/standardlengthtype.py +51 -13
  38. odxtools/staticfield.py +15 -19
  39. odxtools/version.py +2 -2
  40. {odxtools-6.7.1.dist-info → odxtools-7.0.0.dist-info}/METADATA +1 -1
  41. {odxtools-6.7.1.dist-info → odxtools-7.0.0.dist-info}/RECORD +45 -45
  42. {odxtools-6.7.1.dist-info → odxtools-7.0.0.dist-info}/LICENSE +0 -0
  43. {odxtools-6.7.1.dist-info → odxtools-7.0.0.dist-info}/WHEEL +0 -0
  44. {odxtools-6.7.1.dist-info → odxtools-7.0.0.dist-info}/entry_points.txt +0 -0
  45. {odxtools-6.7.1.dist-info → odxtools-7.0.0.dist-info}/top_level.txt +0 -0
@@ -1,14 +1,13 @@
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
6
5
 
7
- from typing_extensions import override
6
+ from typing_extensions import final, override
8
7
 
9
8
  from ..decodestate import DecodeState
10
9
  from ..encodestate import EncodeState
11
- from ..exceptions import DecodeError, EncodeError, OdxWarning, odxraise, odxrequire
10
+ from ..exceptions import DecodeError, EncodeError, odxraise, odxrequire
12
11
  from ..odxlink import OdxDocFragment, OdxLinkDatabase, OdxLinkId, OdxLinkRef
13
12
  from ..odxtypes import ParameterValue
14
13
  from ..utils import dataclass_fields_asdict
@@ -61,15 +60,31 @@ class TableStructParameter(Parameter):
61
60
  self._table_key = odxlinks.resolve(self.table_key_ref, TableKeyParameter)
62
61
 
63
62
  @override
63
+ @final
64
64
  def _resolve_snrefs(self, diag_layer: "DiagLayer") -> None:
65
+ raise RuntimeError(f"Calling TableStructParameter._resolve_snref() is not allowed. "
66
+ f"Use ._table_struct_resolve_snrefs() instead.")
67
+
68
+ def _table_struct_resolve_snrefs(self, diag_layer: "DiagLayer", *,
69
+ param_list: List[Parameter]) -> None:
65
70
  super()._resolve_snrefs(diag_layer)
66
71
 
67
72
  if self.table_key_snref is not None:
68
- warnings.warn(
69
- "Table keys cannot yet be defined using SNREFs"
70
- " in TableStructParameters.",
71
- OdxWarning,
72
- stacklevel=1)
73
+ tk_candidates = [p for p in param_list if p.short_name == self.table_key_snref]
74
+ if len(tk_candidates) > 1:
75
+ odxraise(f"Short name reference '{self.table_key_snref}' could "
76
+ f"not be uniquely resolved.")
77
+ elif len(tk_candidates) == 0:
78
+ odxraise(f"Short name reference '{self.table_key_snref}' could "
79
+ f"not be resolved.")
80
+ return
81
+
82
+ tk = tk_candidates[0]
83
+ if not isinstance(tk, TableKeyParameter):
84
+ odxraise(f"Table struct '{self.short_name}' references non-TableKey parameter "
85
+ f"`{self.table_key_snref}' as its table key.")
86
+
87
+ self._table_key = tk
73
88
 
74
89
  @property
75
90
  def table_key(self) -> TableKeyParameter:
@@ -86,71 +101,76 @@ class TableStructParameter(Parameter):
86
101
  return True
87
102
 
88
103
  @override
89
- def get_coded_value_as_bytes(self, encode_state: EncodeState) -> bytes:
90
- physical_value = encode_state.parameter_values.get(self.short_name)
104
+ def _encode_positioned_into_pdu(self, physical_value: Optional[ParameterValue],
105
+ encode_state: EncodeState) -> None:
91
106
 
92
- if not isinstance(physical_value, tuple) or \
107
+ if not isinstance(physical_value, (tuple, list)) or \
93
108
  len(physical_value) != 2 or \
94
109
  not isinstance(physical_value[0], str):
95
- raise EncodeError(f"The physical value of TableStructParameter 'self.short_name' "
96
- f"must be a tuple with the short name of the selected table "
97
- f"row as the first element and the physical value for the "
98
- f"row's structure or DOP as the second.")
110
+ odxraise(
111
+ f"The physical value of TableStructParameter 'self.short_name' "
112
+ f"must be a tuple containing the short name of the selected table "
113
+ f"row as the first element and the physical value for the "
114
+ f"row's structure or DOP as the second.", EncodeError)
99
115
 
100
116
  tr_short_name = physical_value[0]
101
117
 
102
118
  # make sure that the same table row is selected for all
103
119
  # TABLE-STRUCT parameters that are using the same key
104
120
  tk_short_name = self.table_key.short_name
105
- tk_value = encode_state.parameter_values.get(tk_short_name)
121
+ tk_value = encode_state.table_keys.get(tk_short_name)
106
122
  if tk_value is None:
107
123
  # no value for the key has been set yet. Set it to the
108
124
  # value which we are using right now
109
- encode_state.parameter_values[tk_short_name] = tr_short_name
125
+ encode_state.table_keys[tk_short_name] = tr_short_name
110
126
  elif tk_value != tr_short_name:
111
- raise EncodeError(f"Cannot determine a unique value for table key '{tk_short_name}': "
112
- f"Requested are '{tk_value}' and '{tr_short_name}'")
127
+ odxraise(
128
+ f"Cannot determine a unique value for table key '{tk_short_name}': "
129
+ f"Requested are '{tk_value}' and '{tr_short_name}'", EncodeError)
130
+ return
113
131
 
114
132
  # deal with the static case (i.e., the table row is selected
115
133
  # by the table key object itself)
116
- if self.table_key.table_row is not None and \
117
- self.table_key.table_row.short_name != tr_short_name:
118
- raise EncodeError(f"The selected table row for the {self.short_name} "
119
- f"parameter must be '{self.table_key.table_row.short_name}' "
120
- f"(is: '{tr_short_name}')")
134
+ if self.table_key.table_row is not None:
135
+ if tr_short_name is not None and self.table_key.table_row.short_name != tr_short_name:
136
+ odxraise(
137
+ f"The selected table row for the {self.short_name} "
138
+ f"parameter must be '{self.table_key.table_row.short_name}' "
139
+ f"instead of '{tr_short_name}'", EncodeError)
140
+ return
141
+
142
+ tr_short_name = self.table_key.table_row.short_name
121
143
 
122
144
  # encode the user specified value using the structure (or DOP)
123
145
  # of the selected table row
124
146
  table = self.table_key.table
125
147
  candidate_trs = [tr for tr in table.table_rows if tr.short_name == tr_short_name]
126
- if len(candidate_trs) != 1:
127
- raise EncodeError(f"Could not uniquely resolve a table row named "
128
- f"'{tr_short_name}' in table '{table.short_name}' ")
148
+ if len(candidate_trs) == 0:
149
+ odxraise(
150
+ f"Could not find a table row named "
151
+ f"'{tr_short_name}' in table '{table.short_name}'", EncodeError)
152
+ return
153
+ elif len(candidate_trs) > 1:
154
+ odxraise(
155
+ f"Found multiple table rows named "
156
+ f"'{tr_short_name}' in table '{table.short_name}'", EncodeError)
157
+
129
158
  tr = candidate_trs[0]
130
159
  tr_value = physical_value[1]
131
160
 
132
- bit_position = self.bit_position or 0
133
161
  if tr.structure is not None:
134
162
  # the selected table row references a structure
135
- inner_encode_state = EncodeState(
136
- coded_message=bytearray(b''),
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)
163
+ tr.structure.encode_into_pdu(tr_value, encode_state)
164
+ return
142
165
 
143
166
  # if the table row does not reference a structure, it must
144
167
  # point to a DOP!
145
168
  if tr.dop is None:
146
- odxraise()
169
+ odxraise(f"Neither a structure nor a DOP has been defined for table row"
170
+ f"'{tr.short_name}'")
171
+ return
147
172
 
148
- return tr.dop.convert_physical_to_bytes(
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)
173
+ tr.dop.encode_into_pdu(tr_value, encode_state)
154
174
 
155
175
  @override
156
176
  def _decode_positioned_from_pdu(self, decode_state: DecodeState) -> ParameterValue:
@@ -7,9 +7,9 @@ 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
14
  from .parameter import ParameterType
15
15
  from .parameterwithdop import ParameterWithDOP
@@ -77,16 +77,14 @@ class ValueParameter(ParameterWithDOP):
77
77
  return True
78
78
 
79
79
  @override
80
- def get_coded_value_as_bytes(self, encode_state: EncodeState) -> bytes:
81
- physical_value = encode_state.parameter_values.get(self.short_name,
82
- self.physical_default_value)
80
+ def _encode_positioned_into_pdu(self, physical_value: Optional[ParameterValue],
81
+ encode_state: EncodeState) -> None:
82
+
83
+ if physical_value is None:
84
+ physical_value = self._physical_default_value
83
85
  if physical_value is None:
84
- raise TypeError(f"A value for parameter '{self.short_name}' must be specified"
85
- f" as the parameter does not exhibit a default.")
86
- dop = odxrequire(
87
- self.dop,
88
- f"Param {self.short_name} does not have a DOP. Maybe resolving references failed?")
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)
86
+ odxraise(
87
+ f"A value for parameter '{self.short_name}' must be specified"
88
+ f" because the parameter does not exhibit a default.", EncodeError)
89
+
90
+ self.dop.encode_into_pdu(physical_value, encode_state=encode_state)
@@ -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
- def convert_internal_to_bytes(self, internal_value: AtomicOdxType, encode_state: EncodeState,
47
- bit_position: int) -> bytes:
48
- bit_length = encode_state.parameter_values.get(self.length_key.short_name, None)
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) # type: ignore[arg-type]
57
- if self.base_data_type in [DataType.A_UNICODE2STRING]:
58
- bit_length = 16 * len(internal_value) # type: ignore[arg-type]
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
- encode_state.parameter_values[self.length_key.short_name] = bit_length
68
-
69
- if bit_length is None:
70
- odxraise()
71
-
72
- return self._encode_internal_value(
73
- internal_value,
74
- bit_position=bit_position,
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 = None
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
- value = decode_state.extract_atomic_value(
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, **params: ParameterValue) -> bytes:
42
- if coded_request is not None:
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
- val = coded_request[byte_pos:byte_pos + byte_length]
52
- params[param.short_name] = val
44
+ self.encode_into_pdu(physical_value=kwargs, encode_state=encode_state)
53
45
 
54
- return super().encode(coded_request=coded_request, **params)
46
+ return encode_state.coded_message
@@ -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.is_condensed_raw is True:
35
- raise NotImplementedError("Serialization of condensed bit mask is not supported")
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
- def convert_internal_to_bytes(self, internal_value: AtomicOdxType, encode_state: EncodeState,
50
- bit_position: int) -> bytes:
51
- return self._encode_internal_value(
52
- self.__apply_mask(internal_value),
53
- bit_position,
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/staticfield.py CHANGED
@@ -48,36 +48,32 @@ class StaticField(Field):
48
48
  super()._resolve_snrefs(diag_layer)
49
49
 
50
50
  @override
51
- def convert_physical_to_bytes(
52
- self,
53
- physical_value: ParameterValue,
54
- encode_state: EncodeState,
55
- bit_position: int = 0,
56
- ) -> bytes:
51
+ def encode_into_pdu(self, physical_value: ParameterValue, encode_state: EncodeState) -> None:
52
+
57
53
  if not isinstance(physical_value,
58
54
  (tuple, list)) or len(physical_value) != self.fixed_number_of_items:
59
55
  odxraise(f"Value for static field '{self.short_name}' "
60
56
  f"must be a list of size {self.fixed_number_of_items}")
61
57
 
62
- result = bytearray()
63
58
  for val in physical_value:
64
59
  if not isinstance(val, dict):
65
60
  odxraise(f"The individual parameter values for static field '{self.short_name}' "
66
61
  f"must be dictionaries for structure '{self.structure.short_name}'")
67
62
 
68
- data = self.structure.convert_physical_to_bytes(val, encode_state)
69
-
70
- if len(data) > self.item_byte_size:
71
- odxraise(f"Insufficient item byte size for static field {self.short_name}: "
72
- f"Is {self.item_byte_size} bytes, but need at least {len(data)} bytes")
73
- data = data[:self.item_byte_size]
74
- elif len(data) < self.item_byte_size:
63
+ pos_before = encode_state.cursor_byte_position
64
+ self.structure.encode_into_pdu(val, encode_state)
65
+ pos_after = encode_state.cursor_byte_position
66
+
67
+ if pos_after - pos_before > self.item_byte_size:
68
+ odxraise(
69
+ f"Insufficient item byte size for static field {self.short_name}: "
70
+ f"Is {self.item_byte_size} bytes, but need at least {pos_after - pos_before} bytes"
71
+ )
72
+ encode_state.cursor_byte_position = pos_before + self.item_byte_size
73
+ elif pos_after - pos_before < self.item_byte_size:
75
74
  # add some padding bytes
76
- data = data.ljust(self.item_byte_size, b'\x00')
77
-
78
- result += data
79
-
80
- return result
75
+ encode_state.emplace_bytes(b'\x00' * (self.item_byte_size -
76
+ (pos_after - pos_before)))
81
77
 
82
78
  @override
83
79
  def decode_from_pdu(self, decode_state: DecodeState) -> ParameterValue:
odxtools/version.py CHANGED
@@ -12,5 +12,5 @@ __version__: str
12
12
  __version_tuple__: VERSION_TUPLE
13
13
  version_tuple: VERSION_TUPLE
14
14
 
15
- __version__ = version = '6.7.1'
16
- __version_tuple__ = version_tuple = (6, 7, 1)
15
+ __version__ = version = '7.0.0'
16
+ __version_tuple__ = version_tuple = (7, 0, 0)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: odxtools
3
- Version: 6.7.1
3
+ Version: 7.0.0
4
4
  Summary: Utilities to work with the ODX standard for automotive diagnostics
5
5
  Author-email: Katrin Bauer <katrin.bauer@mbition.io>, Andreas Lauser <andreas.lauser@mbition.io>, Ayoub Kaanich <kayoub5@live.com>
6
6
  Maintainer-email: Andreas Lauser <andreas.lauser@mbition.io>, Ayoub Kaanich <kayoub5@live.com>