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
@@ -2,6 +2,8 @@
2
2
  from dataclasses import dataclass
3
3
  from typing import Optional
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
@@ -51,8 +53,9 @@ class MinMaxLengthType(DiagCodedType):
51
53
  termination_sequence = bytes([0xFF, 0xFF])
52
54
  return termination_sequence
53
55
 
54
- def convert_internal_to_bytes(self, internal_value: AtomicOdxType, encode_state: EncodeState,
55
- bit_position: int) -> bytes:
56
+ @override
57
+ def encode_into_pdu(self, internal_value: AtomicOdxType, encode_state: EncodeState) -> None:
58
+
56
59
  if not isinstance(internal_value, (bytes, str)):
57
60
  odxraise("MinMaxLengthType is currently only implemented for strings and byte arrays",
58
61
  EncodeError)
@@ -62,20 +65,24 @@ class MinMaxLengthType(DiagCodedType):
62
65
  else:
63
66
  data_length = len(internal_value)
64
67
 
65
- value_bytes = bytearray(
66
- self._encode_internal_value(
67
- internal_value,
68
- bit_position=0,
69
- bit_length=8 * data_length,
70
- base_data_type=self.base_data_type,
71
- is_highlow_byte_order=self.is_highlow_byte_order,
72
- ))
68
+ orig_cursor = encode_state.cursor_byte_position
69
+ encode_state.emplace_atomic_value(
70
+ internal_value=internal_value,
71
+ used_mask=None,
72
+ bit_length=8 * data_length,
73
+ base_data_type=self.base_data_type,
74
+ is_highlow_byte_order=self.is_highlow_byte_order,
75
+ )
76
+ value_len = encode_state.cursor_byte_position - orig_cursor
73
77
 
74
78
  # TODO: ensure that the termination delimiter is not
75
79
  # encountered within the encoded value.
76
80
 
77
- odxassert(self.termination != "END-OF-PDU" or encode_state.is_end_of_pdu)
78
- if encode_state.is_end_of_pdu or len(value_bytes) == self.max_length:
81
+ odxassert(
82
+ self.termination != "END-OF-PDU" or encode_state.is_end_of_pdu,
83
+ "Encountered a MIN-MAX-LENGTH type with END-OF-PDU termination "
84
+ "which is not located at the end of the PDU")
85
+ if encode_state.is_end_of_pdu or value_len == self.max_length:
79
86
  # All termination types may be ended by the end of the PDU
80
87
  # or once reaching the maximum length. In this case, we
81
88
  # must not add the termination sequence
@@ -85,20 +92,23 @@ class MinMaxLengthType(DiagCodedType):
85
92
 
86
93
  # ensure that we don't try to encode an odd-length
87
94
  # value when using a two-byte terminator
88
- odxassert(len(value_bytes) % len(termination_sequence) == 0)
89
-
90
- value_bytes.extend(termination_sequence)
91
-
92
- if len(value_bytes) < self.min_length:
93
- raise EncodeError(f"Encoded value for MinMaxLengthType "
94
- f"must be at least {self.min_length} bytes long. "
95
- f"(Is: {len(value_bytes)} bytes.)")
96
- elif self.max_length is not None and len(value_bytes) > self.max_length:
97
- raise EncodeError(f"Encoded value for MinMaxLengthType "
98
- f"must not be longer than {self.max_length} bytes. "
99
- f"(Is: {len(value_bytes)} bytes.)")
100
-
101
- return value_bytes
95
+ odxassert(value_len % len(termination_sequence) == 0)
96
+
97
+ value_len += len(termination_sequence)
98
+ encode_state.emplace_bytes(termination_sequence)
99
+
100
+ if value_len < self.min_length:
101
+ odxraise(
102
+ f"Encoded value for MinMaxLengthType "
103
+ f"must be at least {self.min_length} bytes long. "
104
+ f"(Is: {value_len} bytes.)", EncodeError)
105
+ return
106
+ elif self.max_length is not None and value_len > self.max_length:
107
+ odxraise(
108
+ f"Encoded value for MinMaxLengthType "
109
+ f"must not be longer than {self.max_length} bytes. "
110
+ f"(Is: {value_len} bytes.)", EncodeError)
111
+ return
102
112
 
103
113
  def decode_from_pdu(self, decode_state: DecodeState) -> AtomicOdxType:
104
114
  odxassert(decode_state.cursor_bit_position == 0,
odxtools/multiplexer.py CHANGED
@@ -3,6 +3,8 @@ from dataclasses import dataclass
3
3
  from typing import TYPE_CHECKING, Any, Dict, List, Optional, Tuple
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
@@ -34,6 +36,7 @@ class Multiplexer(ComplexDop):
34
36
  is_visible_raw: Optional[bool]
35
37
 
36
38
  @staticmethod
39
+ @override
37
40
  def from_et(et_element: ElementTree.Element, doc_frags: List[OdxDocFragment]) -> "Multiplexer":
38
41
  """Reads a Multiplexer from Diag Layer."""
39
42
  base_obj = ComplexDop.from_et(et_element, doc_frags)
@@ -67,50 +70,63 @@ class Multiplexer(ComplexDop):
67
70
 
68
71
  def _get_case_limits(self, case: MultiplexerCase) -> Tuple[AtomicOdxType, AtomicOdxType]:
69
72
  key_type = self.switch_key.dop.physical_type.base_data_type
70
- lower_limit = key_type.make_from(case.lower_limit)
71
- upper_limit = key_type.make_from(case.upper_limit)
73
+ lower_limit = key_type.make_from(case.lower_limit.value)
74
+ upper_limit = key_type.make_from(case.upper_limit.value)
72
75
  if not isinstance(lower_limit, type(upper_limit)) and not isinstance(
73
76
  upper_limit, type(lower_limit)):
74
77
  odxraise("Upper and lower bounds of limits must compareable")
75
78
  return lower_limit, upper_limit
76
79
 
77
- def convert_physical_to_bytes(self,
78
- physical_value: ParameterValue,
79
- encode_state: EncodeState,
80
- bit_position: int = 0) -> bytes:
80
+ @override
81
+ def encode_into_pdu(self, physical_value: ParameterValue, encode_state: EncodeState) -> None:
81
82
 
82
- if bit_position != 0:
83
- raise EncodeError("Multiplexer must be aligned, i.e. bit_position=0, but "
84
- f"{self.short_name} was passed the bit position {bit_position}")
83
+ if encode_state.cursor_bit_position != 0:
84
+ raise EncodeError(f"Multiplexer must be aligned, i.e. bit_position=0, but "
85
+ f"{self.short_name} was passed the bit position "
86
+ f"{encode_state.cursor_bit_position}")
85
87
 
86
88
  if not isinstance(physical_value, dict) or len(physical_value) != 1:
87
89
  raise EncodeError("""Multiplexer should be defined as a dict
88
90
  with only one key equal to the desired case""")
89
91
 
92
+ orig_origin = encode_state.origin_byte_position
93
+ orig_cursor = encode_state.cursor_byte_position
94
+
95
+ encode_state.origin_byte_position = encode_state.cursor_byte_position
96
+
90
97
  case_name, case_value = next(iter(physical_value.items()))
91
- case_pos = self.byte_position
92
98
 
93
99
  for mux_case in self.cases or []:
94
100
  if mux_case.short_name == case_name:
95
- if mux_case._structure:
96
- case_bytes = mux_case._structure.convert_physical_to_bytes(
97
- case_value, encode_state, 0)
98
- else:
99
- case_bytes = b''
100
-
101
101
  key_value, _ = self._get_case_limits(mux_case)
102
- key_bytes = self.switch_key.dop.convert_physical_to_bytes(
103
- key_value, encode_state, bit_position=self.switch_key.bit_position or 0)
104
102
 
105
- mux_len = max(len(key_bytes), len(case_bytes) + case_pos)
106
- mux_bytes = bytearray(mux_len)
107
- mux_bytes[:len(key_bytes)] = key_bytes
108
- mux_bytes[case_pos:case_pos + len(case_bytes)] = case_bytes
103
+ if self.switch_key.byte_position is not None:
104
+ encode_state.cursor_byte_position = encode_state.origin_byte_position + self.switch_key.byte_position
105
+ encode_state.cursor_bit_position = self.switch_key.bit_position or 0
106
+
107
+ self.switch_key.dop.encode_into_pdu(
108
+ physical_value=key_value, encode_state=encode_state)
109
+
110
+ if self.byte_position is not None:
111
+ encode_state.cursor_byte_position = encode_state.origin_byte_position + self.byte_position
112
+ encode_state.cursor_bit_position = 0
113
+
114
+ if mux_case._structure is None:
115
+ odxraise(f"Multiplexer case '{mux_case.short_name}' does not "
116
+ f"reference a structure.")
117
+ return
118
+
119
+ mux_case.structure.encode_into_pdu(
120
+ physical_value=key_value, encode_state=encode_state)
109
121
 
110
- return bytes(mux_bytes)
122
+ encode_state.origin_byte_position = orig_origin
123
+ encode_state.cursor_byte_position = max(orig_cursor,
124
+ encode_state.cursor_byte_position)
125
+ return
111
126
 
112
127
  raise EncodeError(f"The case {case_name} is not found in Multiplexer {self.short_name}")
113
128
 
129
+ @override
114
130
  def decode_from_pdu(self, decode_state: DecodeState) -> ParameterValue:
115
131
 
116
132
  # multiplexers are structures and thus the origin position
@@ -152,6 +168,7 @@ class Multiplexer(ComplexDop):
152
168
 
153
169
  return mux_value
154
170
 
171
+ @override
155
172
  def _build_odxlinks(self) -> Dict[OdxLinkId, Any]:
156
173
  odxlinks = super()._build_odxlinks()
157
174
 
@@ -161,6 +178,7 @@ class Multiplexer(ComplexDop):
161
178
 
162
179
  return odxlinks
163
180
 
181
+ @override
164
182
  def _resolve_odxlinks(self, odxlinks: OdxLinkDatabase) -> None:
165
183
  super()._resolve_odxlinks(odxlinks)
166
184
 
@@ -172,6 +190,7 @@ class Multiplexer(ComplexDop):
172
190
  mux_case._mux_case_resolve_odxlinks(
173
191
  odxlinks, key_physical_type=self.switch_key.dop.physical_type.base_data_type)
174
192
 
193
+ @override
175
194
  def _resolve_snrefs(self, diag_layer: "DiagLayer") -> None:
176
195
  super()._resolve_snrefs(diag_layer)
177
196
 
@@ -60,7 +60,7 @@ class MultiplexerCase(NamedElement):
60
60
 
61
61
  def _resolve_odxlinks(self, odxlinks: OdxLinkDatabase) -> None:
62
62
  raise RuntimeError("Calling MultiplexerCase._resolve_odxlinks() is not allowed. "
63
- "Use ._mux_case_resolve_odxlinks()().")
63
+ "Use ._mux_case_resolve_odxlinks().")
64
64
 
65
65
  def _mux_case_resolve_odxlinks(self, odxlinks: OdxLinkDatabase, *,
66
66
  key_physical_type: DataType) -> None:
odxtools/nameditemlist.py CHANGED
@@ -1,5 +1,6 @@
1
1
  # SPDX-License-Identifier: MIT
2
2
  import abc
3
+ from copy import deepcopy
3
4
  from keyword import iskeyword
4
5
  from typing import (Any, Collection, Dict, Iterable, List, Optional, Protocol, SupportsIndex, Tuple,
5
6
  TypeVar, Union, cast, overload, runtime_checkable)
@@ -175,6 +176,19 @@ class ItemAttributeList(List[T]):
175
176
  def __repr__(self) -> str:
176
177
  return f"{type(self).__name__}([{', '.join([repr(x) for x in self])}])"
177
178
 
179
+ def __copy__(self) -> Any:
180
+ return self.__class__(list(self))
181
+
182
+ def __deepcopy__(self, memo: Dict[int, Any]) -> Any:
183
+ cls = self.__class__
184
+ result = cls.__new__(cls)
185
+ memo[id(self)] = result
186
+ result._item_dict = {}
187
+ for x in self:
188
+ result.append(deepcopy(x, memo))
189
+
190
+ return result
191
+
178
192
 
179
193
  class NamedItemList(ItemAttributeList[T]):
180
194
 
odxtools/odxtypes.py CHANGED
@@ -154,6 +154,19 @@ def compare_odx_values(a: AtomicOdxType, b: AtomicOdxType) -> int:
154
154
  f"and {type(b).__name__}")
155
155
 
156
156
 
157
+ # format specifiers for the data type using the bitstruct module
158
+ _BITSTRUCT_FORMAT_LETTER_MAP__ = {
159
+ "A_INT32": "s",
160
+ "A_UINT32": "u",
161
+ "A_FLOAT32": "f",
162
+ "A_FLOAT64": "f",
163
+ "A_BYTEFIELD": "r",
164
+ "A_UNICODE2STRING": "r", # UTF-16 strings must be converted explicitly
165
+ "A_ASCIISTRING": "r",
166
+ "A_UTF8STRING": "r",
167
+ }
168
+
169
+
157
170
  class DataType(Enum):
158
171
  """Types for the physical and internal value.
159
172
 
@@ -181,6 +194,10 @@ class DataType(Enum):
181
194
  def python_type(self) -> Type[AtomicOdxType]:
182
195
  return _ODX_TYPE_TO_PYTHON_TYPE[self.value]
183
196
 
197
+ @property
198
+ def bitstruct_format_letter(self) -> str:
199
+ return _BITSTRUCT_FORMAT_LETTER_MAP__[self.value]
200
+
184
201
  def from_string(self, value: str) -> AtomicOdxType:
185
202
  return _PARSE_ODX_TYPE[self.value](value)
186
203
 
odxtools/parameterinfo.py CHANGED
@@ -1,105 +1,191 @@
1
1
  # SPDX-License-Identifier: MIT
2
- import re
3
- from typing import Iterable, Union
2
+ import textwrap
3
+ from io import StringIO
4
+ from typing import Iterable
4
5
 
5
6
  from .compumethods.identicalcompumethod import IdenticalCompuMethod
6
7
  from .compumethods.limit import IntervalType
7
8
  from .compumethods.linearcompumethod import LinearCompuMethod
8
9
  from .compumethods.texttablecompumethod import TexttableCompuMethod
9
10
  from .dataobjectproperty import DataObjectProperty
11
+ from .dtcdop import DtcDop
12
+ from .dynamiclengthfield import DynamicLengthField
10
13
  from .endofpdufield import EndOfPduField
14
+ from .exceptions import odxrequire
15
+ from .multiplexer import Multiplexer
11
16
  from .odxtypes import DataType
12
17
  from .parameters.codedconstparameter import CodedConstParameter
13
18
  from .parameters.matchingrequestparameter import MatchingRequestParameter
19
+ from .parameters.nrcconstparameter import NrcConstParameter
14
20
  from .parameters.parameter import Parameter
15
21
  from .parameters.parameterwithdop import ParameterWithDOP
16
22
  from .parameters.reservedparameter import ReservedParameter
23
+ from .parameters.tablekeyparameter import TableKeyParameter
24
+ from .parameters.tablestructparameter import TableStructParameter
25
+ from .paramlengthinfotype import ParamLengthInfoType
26
+ from .staticfield import StaticField
17
27
 
18
28
 
19
- def parameter_info(param_list: Iterable[Union[Parameter, EndOfPduField]]) -> str:
20
- result = ""
29
+ def parameter_info(param_list: Iterable[Parameter], quoted_names: bool = False) -> str:
30
+ q = "'" if quoted_names else ""
31
+ of = StringIO()
21
32
  for param in param_list:
22
33
  if isinstance(param, CodedConstParameter):
23
- result += f"{param.short_name} : const = {param._coded_value_str}\n"
34
+ of.write(f"{q}{param.short_name}{q}: const = {param._coded_value_str}\n")
24
35
  continue
25
36
  elif isinstance(param, MatchingRequestParameter):
26
- result += f"{param.short_name} : <matches request>\n"
37
+ of.write(f"{q}{param.short_name}{q}: <matches request>\n")
38
+ continue
39
+ elif isinstance(param, NrcConstParameter):
40
+ of.write(f"{q}{param.short_name}{q}: const; choices = {param.coded_values}\n")
27
41
  continue
28
42
  elif isinstance(param, ReservedParameter):
29
- result += f"{param.short_name} : <reserved>\n"
43
+ of.write(f"{q}{param.short_name}{q}: <reserved>\n")
44
+ continue
45
+ elif isinstance(param, TableKeyParameter):
46
+ of.write(
47
+ f"{q}{param.short_name}{q}: <optional> table key; table = '{param.table.short_name}'; choices:\n"
48
+ )
49
+ for tr in param.table.table_rows:
50
+ of.write(f" '{tr.short_name}',\n")
51
+
52
+ continue
53
+ elif isinstance(param, TableStructParameter):
54
+ of.write(
55
+ f"{q}{param.short_name}{q}: table struct; key = '{param.table_key.short_name}'; choices:\n"
56
+ )
57
+ for tr in param.table_key.table.table_rows:
58
+ of.write(f" ('{tr.short_name}',\n")
59
+ of.write(f" {{\n")
60
+ of.write(
61
+ textwrap.indent(
62
+ parameter_info(odxrequire(tr.structure).parameters, True), " "))
63
+ of.write(f" }}),\n")
64
+
30
65
  continue
31
66
  elif not isinstance(param, ParameterWithDOP):
32
- result += f"{param.short_name} : <unhandled parameter type>\n"
67
+ of.write(
68
+ f"{q}{param.short_name}{q}: <unhandled parameter type '{type(param).__name__}'>\n")
33
69
  continue
34
70
 
35
71
  dop = param.dop
36
-
37
72
  if isinstance(dop, EndOfPduField):
38
- result += f"{param.short_name} : <optional> list({{\n"
39
- tmp = parameter_info(dop.structure.parameters).strip()
40
- tmp = re.sub("^", " ", tmp)
41
- result += tmp + "\n"
42
- result += f"}})\n"
73
+ of.write(f"{q}{param.short_name}{q}: list({{\n")
74
+ of.write(textwrap.indent(parameter_info(dop.structure.parameters, True), " "))
75
+ of.write(f"}})\n")
76
+ continue
77
+ elif isinstance(dop, StaticField):
78
+ of.write(f"{q}{param.short_name}{q}: length={dop.fixed_number_of_items}; list({{\n")
79
+ of.write(textwrap.indent(parameter_info(dop.structure.parameters, True), " "))
80
+ of.write(f"}})\n")
81
+ continue
82
+ elif isinstance(dop, DynamicLengthField):
83
+ of.write(f"{q}{param.short_name}{q}: list({{\n")
84
+ of.write(textwrap.indent(parameter_info(dop.structure.parameters, True), " "))
85
+ of.write(f"}})\n")
86
+ continue
87
+ elif isinstance(dop, ParamLengthInfoType):
88
+ of.write(f"{q}{param.short_name}{q}: ")
89
+ of.write("<optional> ")
90
+ of.write(f"int; length_key='{dop.length_key.short_name}'\n")
91
+ continue
92
+ elif isinstance(dop, DtcDop):
93
+ of.write(f"{q}{param.short_name}{q}: ")
94
+ of.write(f"DTC; choices:\n")
95
+ for dtc in dop.dtcs:
96
+ if dtc.display_trouble_code is not None:
97
+ dtc_desc = dtc.text and f"; \"{dtc.text}\""
98
+ of.write(
99
+ f" '{dtc.display_trouble_code}' (0x{dtc.trouble_code:06x}{dtc_desc})\n")
100
+ else:
101
+ dtc_desc = dtc.text and f" (\"{dtc.text}\")"
102
+ of.write(f" 0x{dtc.trouble_code:06x}{dtc_desc}\n")
103
+ continue
104
+ elif isinstance(dop, Multiplexer):
105
+ of.write(f"{q}{param.short_name}{q}: ")
106
+ if dop.default_case is not None:
107
+ of.write(f"<optional>")
108
+ of.write(f"multiplexer; choices:\n")
109
+ for mux_case in dop.cases:
110
+ of.write(f" ({repr(mux_case.short_name)}, {{\n")
111
+ of.write(
112
+ textwrap.indent(parameter_info(mux_case.structure.parameters, True), " "))
113
+ of.write(f" }})\n")
43
114
  continue
44
115
 
45
- result += f"{param.short_name}"
116
+ of.write(f"{q}{param.short_name}{q}")
46
117
 
47
118
  if dop is None:
48
- result += ": <no DOP>\n"
119
+ of.write(": <no DOP>\n")
49
120
  continue
50
121
  elif not isinstance(dop, DataObjectProperty):
51
- result += ": <unhandled DOP>\n"
122
+ of.write(f": <unhandled DOP '{type(dop).__name__}'>\n")
52
123
  continue
53
124
 
54
125
  if (cm := dop.compu_method) is None:
55
- result += ": <no compu method>\n"
126
+ of.write(": <no compu method>\n")
56
127
  continue
57
128
 
58
129
  if isinstance(cm, TexttableCompuMethod):
59
- result += f": enum; choices:\n"
130
+ of.write(f": enum; choices:\n")
60
131
  for scale in cm.internal_to_phys:
61
- result += f" '{str(scale.compu_const)}'\n"
132
+ val_str = ""
133
+ if scale.lower_limit is not None:
134
+ val_str = f"({repr(scale.lower_limit.value)})"
135
+ of.write(f" {repr(scale.compu_const)}{val_str}\n")
62
136
 
63
137
  elif isinstance(cm, IdenticalCompuMethod):
64
138
  bdt = dop.physical_type.base_data_type
65
139
  if bdt in (DataType.A_UTF8STRING, DataType.A_UNICODE2STRING, DataType.A_ASCIISTRING):
66
- result += f": str"
67
- elif bdt in (DataType.A_BYTEFIELD,):
68
- result += f": bytes"
140
+ of.write(f": str")
141
+ elif bdt == DataType.A_BYTEFIELD:
142
+ of.write(f": bytes")
69
143
  elif bdt.name.startswith("A_FLOAT"):
70
- result += f": float"
144
+ of.write(f": float")
71
145
  elif bdt.name.startswith("A_UINT"):
72
- result += f": uint"
146
+ of.write(f": uint")
73
147
  elif bdt.name.startswith("A_INT"):
74
- result += f": int"
148
+ of.write(f": int")
75
149
  else:
76
- result += f": <unknown type>"
77
-
78
- if (bl := dop.get_static_bit_length()) is not None:
79
- result += f"{bl}"
150
+ of.write(f": <unknown type {{ bdt.name }}>")
80
151
 
81
- result += "\n"
152
+ of.write("\n")
82
153
 
83
154
  elif isinstance(cm, LinearCompuMethod):
84
- result += f": float\n"
155
+ bdt = dop.physical_type.base_data_type
156
+ if bdt in (DataType.A_UTF8STRING, DataType.A_UNICODE2STRING, DataType.A_ASCIISTRING):
157
+ of.write(f": str")
158
+ elif bdt in (DataType.A_BYTEFIELD,):
159
+ of.write(f": bytes")
160
+ elif bdt.name.startswith("A_FLOAT"):
161
+ of.write(f": float")
162
+ elif bdt.name.startswith("A_UINT"):
163
+ of.write(f": uint")
164
+ elif bdt.name.startswith("A_INT"):
165
+ of.write(f": int")
166
+ else:
167
+ of.write(f": <unknown type>")
168
+
85
169
  ll = cm.physical_lower_limit
86
170
  ul = cm.physical_upper_limit
87
- if ll is None:
88
- ll_str = "(inf"
171
+ if ll is None or ll.interval_type == IntervalType.INFINITE:
172
+ ll_str = "(-inf"
89
173
  else:
90
- ll_delim = '[' if ll.interval_type == IntervalType.CLOSED else '('
174
+ ll_delim = '(' if ll.interval_type == IntervalType.OPEN else '['
91
175
  ll_str = f"{ll_delim}{ll._value!r}"
92
176
 
93
- if ul is None:
177
+ if ul is None or ul.interval_type == IntervalType.INFINITE:
94
178
  ul_str = "inf)"
95
179
  else:
96
- ul_delim = ']' if ul.interval_type == IntervalType.CLOSED else ')'
180
+ ul_delim = ')' if ul.interval_type == IntervalType.OPEN else ']'
97
181
  ul_str = f"{ul._value!r}{ul_delim}"
98
- result += f" range: {ll_str}, {ul_str}\n"
182
+ of.write(f"; range: {ll_str}, {ul_str}")
99
183
 
100
184
  unit = dop.unit
101
185
  unit_str = unit.display_name if unit is not None else None
102
186
  if unit_str is not None:
103
- result += f" unit: {unit_str}\n"
187
+ of.write(f"; unit: {unit_str}")
188
+
189
+ of.write("\n")
104
190
 
105
- return result
191
+ return of.getvalue()
@@ -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, 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
 
@@ -81,20 +81,23 @@ class CodedConstParameter(Parameter):
81
81
  return False
82
82
 
83
83
  @override
84
- def get_coded_value_as_bytes(self, encode_state: EncodeState) -> bytes:
85
- if (self.short_name in encode_state.parameter_values and
86
- encode_state.parameter_values[self.short_name] != self.coded_value):
87
- raise TypeError(f"The parameter '{self.short_name}' is constant {self._coded_value_str}"
88
- " and thus can not be changed.")
89
- bit_position_int = self.bit_position if self.bit_position is not None else 0
90
- return self.diag_coded_type.convert_internal_to_bytes(
91
- self.coded_value, encode_state=encode_state, bit_position=bit_position_int)
84
+ def _encode_positioned_into_pdu(self, physical_value: Optional[ParameterValue],
85
+ encode_state: EncodeState) -> None:
86
+ if physical_value is not None and physical_value != self.coded_value:
87
+ odxraise(
88
+ f"Value for constant parameter `{self.short_name}` name can "
89
+ f"only be specified as {self.coded_value!r} (is: {physical_value!r})", EncodeError)
90
+
91
+ internal_value = self.coded_value
92
+
93
+ self.diag_coded_type.encode_into_pdu(
94
+ internal_value=internal_value, encode_state=encode_state)
92
95
 
93
96
  @override
94
97
  def _decode_positioned_from_pdu(self, decode_state: DecodeState) -> AtomicOdxType:
95
98
  coded_val = self.diag_coded_type.decode_from_pdu(decode_state)
96
99
 
97
- # Check if the coded value in the message is correct.
100
+ # Check if the coded value contained by the message is correct.
98
101
  if self.coded_value != coded_val:
99
102
  warnings.warn(
100
103
  f"Coded constant parameter does not match! "
@@ -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
@@ -41,9 +41,10 @@ class DynamicParameter(Parameter):
41
41
  raise NotImplementedError(".is_settable for a DynamicParameter")
42
42
 
43
43
  @override
44
- def get_coded_value_as_bytes(self, encode_state: EncodeState) -> bytes:
45
- raise NotImplementedError("Encoding a DynamicParameter is not implemented yet.")
44
+ def _encode_positioned_into_pdu(self, physical_value: Optional[ParameterValue],
45
+ encode_state: EncodeState) -> None:
46
+ raise NotImplementedError("Encoding DynamicParameter is not implemented yet.")
46
47
 
47
48
  @override
48
49
  def _decode_positioned_from_pdu(self, decode_state: DecodeState) -> ParameterValue:
49
- raise NotImplementedError("Decoding a DynamicParameter is not implemented yet.")
50
+ raise NotImplementedError("Decoding DynamicParameter is not implemented yet.")