odxtools 5.3.1__py3-none-any.whl → 6.0.1__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 (88) hide show
  1. odxtools/__init__.py +1 -1
  2. odxtools/basicstructure.py +76 -91
  3. odxtools/cli/_parser_utils.py +12 -9
  4. odxtools/cli/_print_utils.py +7 -7
  5. odxtools/cli/browse.py +94 -73
  6. odxtools/cli/find.py +42 -59
  7. odxtools/cli/list.py +21 -17
  8. odxtools/cli/snoop.py +19 -18
  9. odxtools/communicationparameterref.py +6 -3
  10. odxtools/companydocinfo.py +2 -2
  11. odxtools/companyrevisioninfo.py +1 -1
  12. odxtools/comparamsubset.py +6 -6
  13. odxtools/complexcomparam.py +1 -1
  14. odxtools/compumethods/compumethod.py +6 -9
  15. odxtools/compumethods/createanycompumethod.py +11 -9
  16. odxtools/compumethods/identicalcompumethod.py +5 -4
  17. odxtools/compumethods/limit.py +9 -9
  18. odxtools/compumethods/linearcompumethod.py +25 -17
  19. odxtools/compumethods/scalelinearcompumethod.py +6 -5
  20. odxtools/compumethods/tabintpcompumethod.py +30 -9
  21. odxtools/compumethods/texttablecompumethod.py +22 -24
  22. odxtools/database.py +5 -5
  23. odxtools/dataobjectproperty.py +10 -23
  24. odxtools/decodestate.py +1 -1
  25. odxtools/determinenumberofitems.py +37 -8
  26. odxtools/diagcodedtype.py +14 -9
  27. odxtools/diagdatadictionaryspec.py +60 -37
  28. odxtools/diaglayer.py +30 -21
  29. odxtools/diaglayercontainer.py +40 -40
  30. odxtools/diaglayerraw.py +92 -63
  31. odxtools/diagnostictroublecode.py +2 -2
  32. odxtools/diagservice.py +53 -35
  33. odxtools/docrevision.py +1 -1
  34. odxtools/dopbase.py +14 -3
  35. odxtools/dtcdop.py +15 -9
  36. odxtools/dynamiclengthfield.py +6 -4
  37. odxtools/endofpdufield.py +22 -23
  38. odxtools/environmentdata.py +2 -5
  39. odxtools/environmentdatadescription.py +6 -4
  40. odxtools/field.py +3 -8
  41. odxtools/isotp_state_machine.py +52 -38
  42. odxtools/leadinglengthinfotype.py +9 -7
  43. odxtools/load_file.py +2 -1
  44. odxtools/load_odx_d_file.py +2 -5
  45. odxtools/load_pdx_file.py +2 -6
  46. odxtools/message.py +11 -3
  47. odxtools/minmaxlengthtype.py +107 -78
  48. odxtools/modification.py +2 -2
  49. odxtools/multiplexer.py +23 -21
  50. odxtools/multiplexerswitchkey.py +37 -8
  51. odxtools/nameditemlist.py +59 -58
  52. odxtools/odxlink.py +4 -2
  53. odxtools/odxtypes.py +4 -3
  54. odxtools/parameterinfo.py +6 -6
  55. odxtools/parameters/codedconstparameter.py +15 -25
  56. odxtools/parameters/createanyparameter.py +1 -1
  57. odxtools/parameters/dynamicparameter.py +6 -5
  58. odxtools/parameters/lengthkeyparameter.py +2 -1
  59. odxtools/parameters/matchingrequestparameter.py +8 -11
  60. odxtools/parameters/nrcconstparameter.py +11 -21
  61. odxtools/parameters/parameter.py +4 -18
  62. odxtools/parameters/parameterwithdop.py +14 -29
  63. odxtools/parameters/physicalconstantparameter.py +7 -9
  64. odxtools/parameters/reservedparameter.py +17 -38
  65. odxtools/parameters/systemparameter.py +6 -5
  66. odxtools/parameters/tableentryparameter.py +6 -5
  67. odxtools/parameters/tablekeyparameter.py +8 -15
  68. odxtools/parameters/tablestructparameter.py +11 -12
  69. odxtools/parameters/valueparameter.py +9 -24
  70. odxtools/paramlengthinfotype.py +11 -9
  71. odxtools/physicaldimension.py +1 -1
  72. odxtools/physicaltype.py +2 -2
  73. odxtools/response.py +7 -3
  74. odxtools/singleecujob.py +48 -22
  75. odxtools/standardlengthtype.py +11 -6
  76. odxtools/uds.py +1 -1
  77. odxtools/unit.py +5 -5
  78. odxtools/unitgroup.py +1 -1
  79. odxtools/unitspec.py +2 -2
  80. odxtools/version.py +13 -3
  81. odxtools/write_pdx_file.py +7 -4
  82. {odxtools-5.3.1.dist-info → odxtools-6.0.1.dist-info}/METADATA +7 -5
  83. {odxtools-5.3.1.dist-info → odxtools-6.0.1.dist-info}/RECORD +87 -88
  84. odxtools/positioneddataobjectproperty.py +0 -74
  85. {odxtools-5.3.1.dist-info → odxtools-6.0.1.dist-info}/LICENSE +0 -0
  86. {odxtools-5.3.1.dist-info → odxtools-6.0.1.dist-info}/WHEEL +0 -0
  87. {odxtools-5.3.1.dist-info → odxtools-6.0.1.dist-info}/entry_points.txt +0 -0
  88. {odxtools-5.3.1.dist-info → odxtools-6.0.1.dist-info}/top_level.txt +0 -0
odxtools/odxlink.py CHANGED
@@ -153,7 +153,7 @@ class OdxLinkRef:
153
153
 
154
154
  # we must reference at to at least of the ID's document
155
155
  # fragments
156
- if not any([ref_doc in odx_id.doc_fragments for ref_doc in self.ref_docs]):
156
+ if not any(ref_doc in odx_id.doc_fragments for ref_doc in self.ref_docs):
157
157
  return False
158
158
 
159
159
  # the local ID of the reference and the object ID must match
@@ -199,6 +199,7 @@ class OdxLinkDatabase:
199
199
  f"Warning: Unknown document fragment {ref_frag} "
200
200
  f"when resolving reference {ref}",
201
201
  OdxWarning,
202
+ stacklevel=1,
202
203
  )
203
204
  continue
204
205
 
@@ -241,6 +242,7 @@ class OdxLinkDatabase:
241
242
  f"Warning: Unknown document fragment {ref_frag} "
242
243
  f"when resolving reference {ref}",
243
244
  OdxWarning,
245
+ stacklevel=1,
244
246
  )
245
247
  continue
246
248
 
@@ -265,6 +267,6 @@ class OdxLinkDatabase:
265
267
  for odx_id, obj in new_entries.items():
266
268
  for doc_frag in odx_id.doc_fragments:
267
269
  if doc_frag not in self._db:
268
- self._db[doc_frag] = dict()
270
+ self._db[doc_frag] = {}
269
271
 
270
272
  self._db[doc_frag][odx_id] = obj
odxtools/odxtypes.py CHANGED
@@ -6,12 +6,13 @@ from xml.etree import ElementTree
6
6
  from .exceptions import odxassert, odxraise, odxrequire
7
7
 
8
8
  if TYPE_CHECKING:
9
- from .parameters.parameter import Parameter
9
+ from odxtools.diagnostictroublecode import DiagnosticTroubleCode
10
+ from odxtools.parameters.parameter import Parameter
10
11
 
11
12
 
12
13
  def bytefield_to_bytearray(bytefield: str) -> bytearray:
13
14
  bytes_string = [bytefield[i:i + 2] for i in range(0, len(bytefield), 2)]
14
- return bytearray(map(lambda x: int(x, 16), bytes_string))
15
+ return bytearray([int(x, 16) for x in bytes_string])
15
16
 
16
17
 
17
18
  AtomicOdxType = Union[str, int, float, bytes]
@@ -27,7 +28,7 @@ ParameterDict = Dict[str, Union["Parameter", "ParameterDict"]]
27
28
  # multiple items, so this can be a list of objects.
28
29
  TableStructParameterValue = Tuple[str, "ParameterValue"]
29
30
  ParameterValue = Union[AtomicOdxType, "ParameterValueDict", TableStructParameterValue,
30
- List["ParameterValue"]]
31
+ List["ParameterValue"], "DiagnosticTroubleCode"]
31
32
  ParameterValueDict = Dict[str, ParameterValue]
32
33
 
33
34
 
odxtools/parameterinfo.py CHANGED
@@ -75,10 +75,10 @@ def parameter_info(param_list: Iterable[Union[Parameter, EndOfPduField]]) -> str
75
75
  else:
76
76
  result += f": <unknown type>"
77
77
 
78
- if dop.bit_length is not None:
79
- result += f"{dop.bit_length}\n"
80
- else:
81
- result += "\n"
78
+ if (bl := dop.get_static_bit_length()) is not None:
79
+ result += f"{bl}"
80
+
81
+ result += "\n"
82
82
 
83
83
  elif isinstance(cm, LinearCompuMethod):
84
84
  result += f": float\n"
@@ -86,8 +86,8 @@ def parameter_info(param_list: Iterable[Union[Parameter, EndOfPduField]]) -> str
86
86
  ul = cm.physical_upper_limit
87
87
  result += (f" range: "
88
88
  f"{'[' if ll.interval_type == IntervalType.CLOSED else '('}"
89
- f"{ll.value}, "
90
- f"{ul.value}"
89
+ f"{ll.value!r}, "
90
+ f"{ul.value!r}"
91
91
  f"{']' if ul.interval_type == IntervalType.CLOSED else ')'}\n")
92
92
 
93
93
  unit = dop.unit
@@ -2,7 +2,7 @@
2
2
  import warnings
3
3
  from copy import copy
4
4
  from dataclasses import dataclass
5
- from typing import TYPE_CHECKING, Any, Dict
5
+ from typing import TYPE_CHECKING, Any, Dict, Optional, Tuple
6
6
 
7
7
  from ..decodestate import DecodeState
8
8
  from ..diagcodedtype import DiagCodedType
@@ -39,9 +39,8 @@ class CodedConstParameter(Parameter):
39
39
  def _resolve_snrefs(self, diag_layer: "DiagLayer") -> None:
40
40
  super()._resolve_snrefs(diag_layer)
41
41
 
42
- @property
43
- def bit_length(self):
44
- return self.diag_coded_type.bit_length
42
+ def get_static_bit_length(self) -> Optional[int]:
43
+ return getattr(self.diag_coded_type, "bit_length", None)
45
44
 
46
45
  @property
47
46
  def internal_data_type(self) -> DataType:
@@ -55,10 +54,7 @@ class CodedConstParameter(Parameter):
55
54
  def is_settable(self) -> bool:
56
55
  return False
57
56
 
58
- def get_coded_value(self):
59
- return self.coded_value
60
-
61
- def get_coded_value_as_bytes(self, encode_state: EncodeState):
57
+ def get_coded_value_as_bytes(self, encode_state: EncodeState) -> bytes:
62
58
  if (self.short_name in encode_state.parameter_values and
63
59
  encode_state.parameter_values[self.short_name] != self.coded_value):
64
60
  raise TypeError(f"The parameter '{self.short_name}' is constant {self._coded_value_str}"
@@ -67,15 +63,15 @@ class CodedConstParameter(Parameter):
67
63
  return self.diag_coded_type.convert_internal_to_bytes(
68
64
  self.coded_value, encode_state=encode_state, bit_position=bit_position_int)
69
65
 
70
- def decode_from_pdu(self, decode_state: DecodeState):
66
+ def decode_from_pdu(self, decode_state: DecodeState) -> Tuple[AtomicOdxType, int]:
71
67
  decode_state = copy(decode_state)
72
- if self.byte_position is not None and self.byte_position != decode_state.next_byte_position:
68
+ if self.byte_position is not None and self.byte_position != decode_state.cursor_position:
73
69
  # Update byte position
74
- decode_state.next_byte_position = self.byte_position
70
+ decode_state.cursor_position = self.byte_position
75
71
 
76
72
  # Extract coded values
77
73
  bit_position_int = self.bit_position if self.bit_position is not None else 0
78
- coded_val, next_byte_position = self.diag_coded_type.convert_bytes_to_internal(
74
+ coded_val, cursor_position = self.diag_coded_type.convert_bytes_to_internal(
79
75
  decode_state, bit_position=bit_position_int)
80
76
 
81
77
  # Check if the coded value in the message is correct.
@@ -84,25 +80,19 @@ class CodedConstParameter(Parameter):
84
80
  f"Coded constant parameter does not match! "
85
81
  f"The parameter {self.short_name} expected coded "
86
82
  f"value {str(self._coded_value_str)} but got {str(coded_val)} "
87
- f"at byte position {decode_state.next_byte_position} "
83
+ f"at byte position {decode_state.cursor_position} "
88
84
  f"in coded message {decode_state.coded_message.hex()}.",
89
85
  DecodeError,
86
+ stacklevel=1,
90
87
  )
91
88
 
92
- return coded_val, next_byte_position
93
-
94
- def _as_dict(self):
95
- d = super()._as_dict()
96
- if self.bit_length is not None:
97
- d["bit_length"] = self.bit_length
98
- d["coded_value"] = hex(self.coded_value)
99
- return d
89
+ return coded_val, cursor_position
100
90
 
101
91
  @property
102
- def _coded_value_str(self):
103
- if isinstance(self.coded_value, int):
104
- return str(self.coded_value)
105
- return self.coded_value.hex()
92
+ def _coded_value_str(self) -> str:
93
+ if isinstance(self.coded_value, bytes):
94
+ return self.coded_value.hex()
95
+ return str(self.coded_value)
106
96
 
107
97
  def get_description_of_valid_values(self) -> str:
108
98
  """return a human-understandable description of valid physical values"""
@@ -115,7 +115,7 @@ def create_any_parameter_from_et(et_element: ElementTree.Element,
115
115
  bit_length = int(odxrequire(et_element.findtext("BIT-LENGTH")))
116
116
 
117
117
  return ReservedParameter(
118
- bit_length_raw=bit_length,
118
+ bit_length=bit_length,
119
119
  semantic=semantic,
120
120
  byte_position=byte_position,
121
121
  bit_position=bit_position,
@@ -1,6 +1,10 @@
1
1
  # SPDX-License-Identifier: MIT
2
2
  from dataclasses import dataclass
3
+ from typing import Tuple
3
4
 
5
+ from ..decodestate import DecodeState
6
+ from ..encodestate import EncodeState
7
+ from ..odxtypes import ParameterValue
4
8
  from .parameter import Parameter, ParameterType
5
9
 
6
10
 
@@ -19,11 +23,8 @@ class DynamicParameter(Parameter):
19
23
  def is_settable(self) -> bool:
20
24
  raise NotImplementedError(".is_settable for a DynamicParameter")
21
25
 
22
- def get_coded_value(self):
26
+ def get_coded_value_as_bytes(self, encode_state: EncodeState) -> bytes:
23
27
  raise NotImplementedError("Encoding a DynamicParameter is not implemented yet.")
24
28
 
25
- def get_coded_value_as_bytes(self):
26
- raise NotImplementedError("Encoding a DynamicParameter is not implemented yet.")
27
-
28
- def decode_from_pdu(self, coded_message, default_byte_position=None):
29
+ def decode_from_pdu(self, decode_state: DecodeState) -> Tuple[ParameterValue, int]:
29
30
  raise NotImplementedError("Decoding a DynamicParameter is not implemented yet.")
@@ -6,6 +6,7 @@ from ..decodestate import DecodeState
6
6
  from ..encodestate import EncodeState
7
7
  from ..exceptions import odxrequire
8
8
  from ..odxlink import OdxLinkDatabase, OdxLinkId
9
+ from ..odxtypes import ParameterValue
9
10
  from .parameter import ParameterType
10
11
  from .parameterwithdop import ParameterWithDOP
11
12
 
@@ -65,5 +66,5 @@ class LengthKeyParameter(ParameterWithDOP):
65
66
  def encode_into_pdu(self, encode_state: EncodeState) -> bytes:
66
67
  return super().encode_into_pdu(encode_state)
67
68
 
68
- def decode_from_pdu(self, decode_state: DecodeState) -> Tuple[Any, int]:
69
+ def decode_from_pdu(self, decode_state: DecodeState) -> Tuple[ParameterValue, int]:
69
70
  return super().decode_from_pdu(decode_state)
@@ -1,9 +1,11 @@
1
1
  # SPDX-License-Identifier: MIT
2
2
  from dataclasses import dataclass
3
+ from typing import Optional, Tuple
3
4
 
4
5
  from ..decodestate import DecodeState
5
6
  from ..encodestate import EncodeState
6
7
  from ..exceptions import EncodeError
8
+ from ..odxtypes import ParameterValue
7
9
  from .parameter import Parameter, ParameterType
8
10
 
9
11
 
@@ -16,8 +18,7 @@ class MatchingRequestParameter(Parameter):
16
18
  def parameter_type(self) -> ParameterType:
17
19
  return "MATCHING-REQUEST-PARAM"
18
20
 
19
- @property
20
- def bit_length(self):
21
+ def get_static_bit_length(self) -> Optional[int]:
21
22
  return 8 * self.byte_length
22
23
 
23
24
  @property
@@ -28,10 +29,7 @@ class MatchingRequestParameter(Parameter):
28
29
  def is_settable(self) -> bool:
29
30
  return False
30
31
 
31
- def get_coded_value(self, request_value=None):
32
- return request_value
33
-
34
- def get_coded_value_as_bytes(self, encode_state: EncodeState):
32
+ def get_coded_value_as_bytes(self, encode_state: EncodeState) -> bytes:
35
33
  if not encode_state.triggering_request:
36
34
  raise EncodeError(f"Parameter '{self.short_name}' is of matching request type,"
37
35
  " but no original request has been specified.")
@@ -39,12 +37,11 @@ class MatchingRequestParameter(Parameter):
39
37
  .request_byte_position:self.request_byte_position +
40
38
  self.byte_length]
41
39
 
42
- def decode_from_pdu(self, decode_state: DecodeState):
40
+ def decode_from_pdu(self, decode_state: DecodeState) -> Tuple[ParameterValue, int]:
43
41
  byte_position = (
44
- self.byte_position
45
- if self.byte_position is not None else decode_state.next_byte_position)
46
- bit_position = self.bit_position if self.bit_position is not None else 0
47
- byte_length = (self.bit_length + bit_position + 7) // 8
42
+ self.byte_position if self.byte_position is not None else decode_state.cursor_position)
43
+ bit_position = self.bit_position or 0
44
+ byte_length = (8 * self.byte_length + bit_position + 7) // 8
48
45
  val_as_bytes = decode_state.coded_message[byte_position:byte_position + byte_length]
49
46
 
50
47
  return val_as_bytes, byte_position + byte_length
@@ -2,7 +2,7 @@
2
2
  import warnings
3
3
  from copy import copy
4
4
  from dataclasses import dataclass
5
- from typing import TYPE_CHECKING, Any, Dict, List
5
+ from typing import TYPE_CHECKING, Any, Dict, List, Optional, Tuple
6
6
 
7
7
  from ..decodestate import DecodeState
8
8
  from ..diagcodedtype import DiagCodedType
@@ -47,9 +47,8 @@ class NrcConstParameter(Parameter):
47
47
  def _resolve_snrefs(self, diag_layer: "DiagLayer") -> None:
48
48
  super()._resolve_snrefs(diag_layer)
49
49
 
50
- @property
51
- def bit_length(self):
52
- return self.diag_coded_type.bit_length
50
+ def get_static_bit_length(self) -> Optional[int]:
51
+ return self.diag_coded_type.get_static_bit_length()
53
52
 
54
53
  @property
55
54
  def internal_data_type(self) -> DataType:
@@ -63,10 +62,7 @@ class NrcConstParameter(Parameter):
63
62
  def is_settable(self) -> bool:
64
63
  return False
65
64
 
66
- def get_coded_value(self):
67
- return self.coded_value
68
-
69
- def get_coded_value_as_bytes(self, encode_state: EncodeState):
65
+ def get_coded_value_as_bytes(self, encode_state: EncodeState) -> bytes:
70
66
  if self.short_name in encode_state.parameter_values:
71
67
  if encode_state.parameter_values[self.short_name] not in self.coded_values:
72
68
  raise EncodeError(f"The parameter '{self.short_name}' must have"
@@ -82,15 +78,15 @@ class NrcConstParameter(Parameter):
82
78
  return self.diag_coded_type.convert_internal_to_bytes(
83
79
  coded_value, encode_state, bit_position=bit_position_int)
84
80
 
85
- def decode_from_pdu(self, decode_state: DecodeState):
81
+ def decode_from_pdu(self, decode_state: DecodeState) -> Tuple[AtomicOdxType, int]:
86
82
  decode_state = copy(decode_state)
87
- if self.byte_position is not None and self.byte_position != decode_state.next_byte_position:
83
+ if self.byte_position is not None and self.byte_position != decode_state.cursor_position:
88
84
  # Update byte position
89
- decode_state.next_byte_position = self.byte_position
85
+ decode_state.cursor_position = self.byte_position
90
86
 
91
87
  # Extract coded values
92
88
  bit_position_int = self.bit_position if self.bit_position is not None else 0
93
- coded_value, next_byte_position = self.diag_coded_type.convert_bytes_to_internal(
89
+ coded_value, cursor_position = self.diag_coded_type.convert_bytes_to_internal(
94
90
  decode_state, bit_position=bit_position_int)
95
91
 
96
92
  # Check if the coded value in the message is correct.
@@ -99,19 +95,13 @@ class NrcConstParameter(Parameter):
99
95
  f"Coded constant parameter does not match! "
100
96
  f"The parameter {self.short_name} expected a coded "
101
97
  f"value in {str(self.coded_values)} but got {str(coded_value)} "
102
- f"at byte position {decode_state.next_byte_position} "
98
+ f"at byte position {decode_state.cursor_position} "
103
99
  f"in coded message {decode_state.coded_message.hex()}.",
104
100
  DecodeError,
101
+ stacklevel=1,
105
102
  )
106
103
 
107
- return coded_value, next_byte_position
108
-
109
- def _as_dict(self):
110
- d = super()._as_dict()
111
- if self.bit_length is not None:
112
- d["bit_length"] = self.bit_length
113
- d["coded_values"] = self.coded_values
114
- return d
104
+ return coded_value, cursor_position
115
105
 
116
106
  def get_description_of_valid_values(self) -> str:
117
107
  """return a human-understandable description of valid physical values"""
@@ -9,6 +9,7 @@ from ..element import NamedElement
9
9
  from ..encodestate import EncodeState
10
10
  from ..exceptions import OdxWarning
11
11
  from ..odxlink import OdxLinkDatabase, OdxLinkId
12
+ from ..odxtypes import ParameterValue
12
13
  from ..specialdatagroup import SpecialDataGroup
13
14
 
14
15
  if TYPE_CHECKING:
@@ -58,8 +59,7 @@ class Parameter(NamedElement, abc.ABC):
58
59
  def parameter_type(self) -> ParameterType:
59
60
  pass
60
61
 
61
- @property
62
- def bit_length(self) -> Optional[int]:
62
+ def get_static_bit_length(self) -> Optional[int]:
63
63
  return None
64
64
 
65
65
  @property
@@ -85,10 +85,6 @@ class Parameter(NamedElement, abc.ABC):
85
85
  """
86
86
  raise NotImplementedError
87
87
 
88
- @abc.abstractmethod
89
- def get_coded_value(self):
90
- pass
91
-
92
88
  @abc.abstractmethod
93
89
  def get_coded_value_as_bytes(self, encode_state: EncodeState) -> bytes:
94
90
  """Get the coded value of the parameter given the encode state.
@@ -97,7 +93,7 @@ class Parameter(NamedElement, abc.ABC):
97
93
  pass
98
94
 
99
95
  @abc.abstractmethod
100
- def decode_from_pdu(self, decode_state: DecodeState) -> Tuple[Any, int]:
96
+ def decode_from_pdu(self, decode_state: DecodeState) -> Tuple[ParameterValue, int]:
101
97
  """Decode the parameter value from the coded message.
102
98
 
103
99
  If the parameter does have a byte position property, the coded bytes the parameter covers are extracted
@@ -178,18 +174,8 @@ class Parameter(NamedElement, abc.ABC):
178
174
  warnings.warn(
179
175
  f"Parameter {self.short_name} overlaps with another parameter (bytes are already set)",
180
176
  OdxWarning,
177
+ stacklevel=1,
181
178
  )
182
179
  result_blob[byte_idx_rpc] |= new_data[byte_idx_val]
183
180
 
184
181
  return result_blob
185
-
186
- def _as_dict(self):
187
- """
188
- Mostly for pretty printing purposes (specifically not for reconstructing the object)
189
- """
190
- d = {"short_name": self.short_name, "type": self.parameter_type, "semantic": self.semantic}
191
- if self.byte_position is not None:
192
- d["byte_position"] = self.byte_position
193
- if self.bit_position is not None:
194
- d["bit_position"] = self.bit_position
195
- return d
@@ -10,6 +10,7 @@ from ..dtcdop import DtcDop
10
10
  from ..encodestate import EncodeState
11
11
  from ..exceptions import odxassert, odxrequire
12
12
  from ..odxlink import OdxLinkDatabase, OdxLinkId, OdxLinkRef
13
+ from ..odxtypes import ParameterValue
13
14
  from ..physicaltype import PhysicalType
14
15
  from .parameter import Parameter
15
16
 
@@ -54,15 +55,16 @@ class ParameterWithDOP(Parameter):
54
55
  spec.env_datas.get(self.dop_snref))
55
56
 
56
57
  @property
57
- def dop(self) -> Optional[DopBase]:
58
+ def dop(self) -> DopBase:
58
59
  """may be a DataObjectProperty, a Structure or None"""
59
60
 
60
- return self._dop
61
+ return odxrequire(
62
+ self._dop, "Specifying a data object property is mandatory but it "
63
+ "could not be resolved")
61
64
 
62
- @property
63
- def bit_length(self):
64
- if self.dop is not None:
65
- return self.dop.bit_length
65
+ def get_static_bit_length(self) -> Optional[int]:
66
+ if self._dop is not None:
67
+ return self._dop.get_static_bit_length()
66
68
  else:
67
69
  return None
68
70
 
@@ -73,38 +75,21 @@ class ParameterWithDOP(Parameter):
73
75
  else:
74
76
  return None
75
77
 
76
- def get_coded_value(self, physical_value=None):
77
- return self.dop.convert_physical_to_internal(physical_value)
78
-
79
- def get_coded_value_as_bytes(self, encode_state: EncodeState):
78
+ def get_coded_value_as_bytes(self, encode_state: EncodeState) -> bytes:
80
79
  dop = odxrequire(self.dop, "Reference to DOP is not resolved")
81
80
  physical_value = encode_state.parameter_values[self.short_name]
82
81
  bit_position_int = self.bit_position if self.bit_position is not None else 0
83
82
  return dop.convert_physical_to_bytes(
84
83
  physical_value, encode_state, bit_position=bit_position_int)
85
84
 
86
- def decode_from_pdu(self, decode_state: DecodeState) -> Tuple[Any, int]:
87
- dop = odxrequire(self.dop, "Reference to DOP is not resolved")
85
+ def decode_from_pdu(self, decode_state: DecodeState) -> Tuple[ParameterValue, int]:
88
86
  decode_state = copy(decode_state)
89
- if self.byte_position is not None and self.byte_position != decode_state.next_byte_position:
90
- decode_state.next_byte_position = self.byte_position
87
+ if self.byte_position is not None and self.byte_position != decode_state.cursor_position:
88
+ decode_state.cursor_position = self.byte_position
91
89
 
92
90
  # Use DOP to decode
93
91
  bit_position_int = self.bit_position if self.bit_position is not None else 0
94
- phys_val, next_byte_position = dop.convert_bytes_to_physical(
92
+ phys_val, cursor_position = self.dop.convert_bytes_to_physical(
95
93
  decode_state, bit_position=bit_position_int)
96
94
 
97
- return phys_val, next_byte_position
98
-
99
- def _as_dict(self):
100
- d = super()._as_dict()
101
- if self.dop is not None:
102
- if self.bit_length is not None:
103
- d["bit_length"] = self.bit_length
104
- d["dop_ref"] = OdxLinkRef.from_id(self.dop.odx_id)
105
- elif self.dop_ref is not None:
106
- d["dop_ref"] = self.dop_ref
107
- elif self.dop_snref is not None:
108
- d["dop_snref"] = self.dop_snref
109
-
110
- return d
95
+ return phys_val, cursor_position
@@ -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
4
+ from typing import TYPE_CHECKING, Any, Dict, Tuple
5
5
 
6
6
  from ..dataobjectproperty import DataObjectProperty
7
7
  from ..decodestate import DecodeState
@@ -52,10 +52,7 @@ class PhysicalConstantParameter(ParameterWithDOP):
52
52
  def is_settable(self) -> bool:
53
53
  return False
54
54
 
55
- def get_coded_value(self):
56
- return self.dop.convert_physical_to_internal(self.physical_constant_value)
57
-
58
- def get_coded_value_as_bytes(self, encode_state: EncodeState):
55
+ def get_coded_value_as_bytes(self, encode_state: EncodeState) -> bytes:
59
56
  dop = odxrequire(self.dop, "Reference to DOP is not resolved")
60
57
  if (self.short_name in encode_state.parameter_values and
61
58
  encode_state.parameter_values[self.short_name] != self.physical_constant_value):
@@ -67,17 +64,18 @@ class PhysicalConstantParameter(ParameterWithDOP):
67
64
  return dop.convert_physical_to_bytes(
68
65
  self.physical_constant_value, encode_state, bit_position=bit_position_int)
69
66
 
70
- def decode_from_pdu(self, decode_state: DecodeState):
67
+ def decode_from_pdu(self, decode_state: DecodeState) -> Tuple[ParameterValue, int]:
71
68
  # Decode value
72
- phys_val, next_byte_position = super().decode_from_pdu(decode_state)
69
+ phys_val, cursor_position = super().decode_from_pdu(decode_state)
73
70
 
74
71
  # Check if decoded value matches expected value
75
72
  if phys_val != self.physical_constant_value:
76
73
  warnings.warn(
77
74
  f"Physical constant parameter does not match! "
78
75
  f"The parameter {self.short_name} expected physical value {self.physical_constant_value!r} but got {phys_val!r} "
79
- f"at byte position {next_byte_position} "
76
+ f"at byte position {cursor_position} "
80
77
  f"in coded message {decode_state.coded_message.hex()}.",
81
78
  DecodeError,
79
+ stacklevel=1,
82
80
  )
83
- return phys_val, next_byte_position
81
+ return phys_val, cursor_position
@@ -1,15 +1,16 @@
1
1
  # SPDX-License-Identifier: MIT
2
- import warnings
3
2
  from dataclasses import dataclass
3
+ from typing import Optional, Tuple, cast
4
4
 
5
5
  from ..decodestate import DecodeState
6
- from ..exceptions import DecodeError
6
+ from ..encodestate import EncodeState
7
+ from ..odxtypes import ParameterValue
7
8
  from .parameter import Parameter, ParameterType
8
9
 
9
10
 
10
11
  @dataclass
11
12
  class ReservedParameter(Parameter):
12
- bit_length_raw: int
13
+ bit_length: int
13
14
 
14
15
  @property
15
16
  def parameter_type(self) -> ParameterType:
@@ -23,44 +24,22 @@ class ReservedParameter(Parameter):
23
24
  def is_settable(self) -> bool:
24
25
  return False
25
26
 
26
- @property
27
- def bit_length(self) -> int:
28
- # this is a bit hacky: the parent class already specifies
29
- # bit_length as a function property, and we need to change
30
- # this to return the value from the XML here. Since function
31
- # attributes cannot be overridden by non-function ones, we
32
- # need to take the "bit_length_raw" detour...
33
- return self.bit_length_raw
34
-
35
- def get_coded_value(self):
36
- return 0
27
+ def get_static_bit_length(self) -> Optional[int]:
28
+ return self.bit_length
37
29
 
38
- def get_coded_value_as_bytes(self, encode_state):
30
+ def get_coded_value_as_bytes(self, encode_state: EncodeState) -> bytes:
39
31
  bit_position_int = self.bit_position if self.bit_position is not None else 0
40
- return int(0).to_bytes((self.bit_length + bit_position_int + 7) // 8, "big")
32
+ return (0).to_bytes((self.bit_length + bit_position_int + 7) // 8, "big")
41
33
 
42
- def decode_from_pdu(self, decode_state: DecodeState):
34
+ def decode_from_pdu(self, decode_state: DecodeState) -> Tuple[ParameterValue, int]:
43
35
  byte_position = (
44
- self.byte_position
45
- if self.byte_position is not None else decode_state.next_byte_position)
46
- bit_position_int = self.bit_position if self.bit_position is not None else 0
47
- byte_length = (self.bit_length_raw + bit_position_int + 7) // 8
48
- val_as_bytes = decode_state.coded_message[byte_position:byte_position + byte_length]
49
- next_byte_position = byte_position + byte_length
50
-
51
- # Check that reserved bits are 0
52
- expected = sum(
53
- 2**i for i in range(bit_position_int, bit_position_int + self.bit_length_raw))
54
- actual = int.from_bytes(val_as_bytes, "big")
36
+ self.byte_position if self.byte_position is not None else decode_state.cursor_position)
37
+ abs_bit_position = byte_position * 8 + (self.bit_position or 0)
38
+ bit_length = self.bit_length
55
39
 
56
- # Bit-wise compare if reserved bits are 0.
57
- if expected & actual != 0:
58
- warnings.warn(
59
- f"Reserved bits must be Zero! "
60
- f"The parameter {self.short_name} expected {self.bit_length} bits to be Zero starting at bit position {bit_position_int} "
61
- f"at byte position {byte_position} "
62
- f"in coded message {decode_state.coded_message.hex()}.",
63
- DecodeError,
64
- )
40
+ # the cursor points to the first byte which has not been fully
41
+ # consumed
42
+ cursor_position = (abs_bit_position + bit_length) // 8
65
43
 
66
- return None, next_byte_position
44
+ # ignore the value of the parameter data
45
+ return cast(int, None), cursor_position
@@ -1,6 +1,10 @@
1
1
  # SPDX-License-Identifier: MIT
2
2
  from dataclasses import dataclass
3
+ from typing import Tuple
3
4
 
5
+ from ..decodestate import DecodeState
6
+ from ..encodestate import EncodeState
7
+ from ..odxtypes import ParameterValue
4
8
  from .parameter import ParameterType
5
9
  from .parameterwithdop import ParameterWithDOP
6
10
 
@@ -21,11 +25,8 @@ class SystemParameter(ParameterWithDOP):
21
25
  def is_settable(self) -> bool:
22
26
  raise NotImplementedError("SystemParameter.is_settable is not implemented yet.")
23
27
 
24
- def get_coded_value(self):
28
+ def get_coded_value_as_bytes(self, encode_state: EncodeState) -> bytes:
25
29
  raise NotImplementedError("Encoding a SystemParameter is not implemented yet.")
26
30
 
27
- def get_coded_value_as_bytes(self):
28
- raise NotImplementedError("Encoding a SystemParameter is not implemented yet.")
29
-
30
- def decode_from_pdu(self, coded_message, default_byte_position=None):
31
+ def decode_from_pdu(self, decode_state: DecodeState) -> Tuple[ParameterValue, int]:
31
32
  raise NotImplementedError("Decoding a SystemParameter is not implemented yet.")
@@ -1,7 +1,11 @@
1
1
  # SPDX-License-Identifier: MIT
2
2
  from dataclasses import dataclass
3
+ from typing import Tuple
3
4
 
5
+ from ..decodestate import DecodeState
6
+ from ..encodestate import EncodeState
4
7
  from ..odxlink import OdxLinkRef
8
+ from ..odxtypes import ParameterValue
5
9
  from .parameter import Parameter, ParameterType
6
10
 
7
11
 
@@ -22,11 +26,8 @@ class TableEntryParameter(Parameter):
22
26
  def is_settable(self) -> bool:
23
27
  raise NotImplementedError("TableKeyParameter.is_settable is not implemented yet.")
24
28
 
25
- def get_coded_value(self):
29
+ def get_coded_value_as_bytes(self, encode_state: EncodeState) -> bytes:
26
30
  raise NotImplementedError("Encoding a TableKeyParameter is not implemented yet.")
27
31
 
28
- def get_coded_value_as_bytes(self):
29
- raise NotImplementedError("Encoding a TableKeyParameter is not implemented yet.")
30
-
31
- def decode_from_pdu(self, coded_message, default_byte_position=None):
32
+ def decode_from_pdu(self, decode_state: DecodeState) -> Tuple[ParameterValue, int]:
32
33
  raise NotImplementedError("Decoding a TableKeyParameter is not implemented yet.")