odxtools 6.6.1__py3-none-any.whl → 6.7.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 (81) hide show
  1. odxtools/__init__.py +5 -5
  2. odxtools/basicstructure.py +7 -8
  3. odxtools/cli/_parser_utils.py +15 -0
  4. odxtools/cli/_print_utils.py +4 -3
  5. odxtools/cli/browse.py +19 -14
  6. odxtools/cli/compare.py +24 -16
  7. odxtools/cli/decode.py +2 -1
  8. odxtools/cli/dummy_sub_parser.py +3 -1
  9. odxtools/cli/find.py +2 -1
  10. odxtools/cli/list.py +2 -1
  11. odxtools/cli/main.py +1 -0
  12. odxtools/cli/snoop.py +4 -1
  13. odxtools/comparaminstance.py +7 -5
  14. odxtools/compumethods/compumethod.py +2 -4
  15. odxtools/compumethods/compuscale.py +45 -5
  16. odxtools/compumethods/createanycompumethod.py +28 -36
  17. odxtools/compumethods/limit.py +70 -36
  18. odxtools/compumethods/linearcompumethod.py +68 -59
  19. odxtools/compumethods/tabintpcompumethod.py +19 -8
  20. odxtools/compumethods/texttablecompumethod.py +32 -36
  21. odxtools/dataobjectproperty.py +13 -10
  22. odxtools/decodestate.py +6 -3
  23. odxtools/determinenumberofitems.py +1 -1
  24. odxtools/diagcodedtype.py +5 -4
  25. odxtools/diagdatadictionaryspec.py +108 -83
  26. odxtools/diaglayer.py +75 -35
  27. odxtools/diaglayertype.py +17 -5
  28. odxtools/diagservice.py +1 -1
  29. odxtools/dopbase.py +4 -2
  30. odxtools/dtcdop.py +7 -5
  31. odxtools/dynamiclengthfield.py +6 -5
  32. odxtools/endofpdufield.py +4 -4
  33. odxtools/environmentdatadescription.py +4 -2
  34. odxtools/inputparam.py +1 -1
  35. odxtools/internalconstr.py +14 -5
  36. odxtools/isotp_state_machine.py +14 -6
  37. odxtools/message.py +1 -1
  38. odxtools/multiplexer.py +18 -13
  39. odxtools/multiplexercase.py +27 -5
  40. odxtools/multiplexerswitchkey.py +1 -1
  41. odxtools/nameditemlist.py +7 -6
  42. odxtools/odxlink.py +2 -2
  43. odxtools/odxtypes.py +56 -3
  44. odxtools/outputparam.py +2 -2
  45. odxtools/parameterinfo.py +12 -5
  46. odxtools/parameters/codedconstparameter.py +33 -12
  47. odxtools/parameters/createanyparameter.py +19 -193
  48. odxtools/parameters/dynamicparameter.py +21 -1
  49. odxtools/parameters/lengthkeyparameter.py +28 -4
  50. odxtools/parameters/matchingrequestparameter.py +27 -9
  51. odxtools/parameters/nrcconstparameter.py +34 -11
  52. odxtools/parameters/parameter.py +58 -32
  53. odxtools/parameters/parameterwithdop.py +28 -15
  54. odxtools/parameters/physicalconstantparameter.py +28 -4
  55. odxtools/parameters/reservedparameter.py +32 -18
  56. odxtools/parameters/systemparameter.py +25 -2
  57. odxtools/parameters/tableentryparameter.py +45 -6
  58. odxtools/parameters/tablekeyparameter.py +43 -10
  59. odxtools/parameters/tablestructparameter.py +36 -14
  60. odxtools/parameters/valueparameter.py +24 -2
  61. odxtools/paramlengthinfotype.py +4 -1
  62. odxtools/parentref.py +4 -1
  63. odxtools/scaleconstr.py +11 -5
  64. odxtools/statetransition.py +1 -1
  65. odxtools/staticfield.py +101 -0
  66. odxtools/table.py +2 -1
  67. odxtools/tablerow.py +11 -4
  68. odxtools/templates/macros/printDOP.xml.jinja2 +30 -34
  69. odxtools/templates/macros/printMux.xml.jinja2 +3 -2
  70. odxtools/templates/macros/printParam.xml.jinja2 +9 -9
  71. odxtools/templates/macros/printStaticField.xml.jinja2 +15 -0
  72. odxtools/templates/macros/printVariant.xml.jinja2 +8 -0
  73. odxtools/uds.py +2 -2
  74. odxtools/version.py +2 -2
  75. odxtools/write_pdx_file.py +3 -3
  76. {odxtools-6.6.1.dist-info → odxtools-6.7.1.dist-info}/METADATA +28 -16
  77. {odxtools-6.6.1.dist-info → odxtools-6.7.1.dist-info}/RECORD +81 -79
  78. {odxtools-6.6.1.dist-info → odxtools-6.7.1.dist-info}/WHEEL +1 -1
  79. {odxtools-6.6.1.dist-info → odxtools-6.7.1.dist-info}/LICENSE +0 -0
  80. {odxtools-6.6.1.dist-info → odxtools-6.7.1.dist-info}/entry_points.txt +0 -0
  81. {odxtools-6.6.1.dist-info → odxtools-6.7.1.dist-info}/top_level.txt +0 -0
@@ -1,13 +1,17 @@
1
1
  # SPDX-License-Identifier: MIT
2
2
  import warnings
3
3
  from dataclasses import dataclass
4
- from typing import TYPE_CHECKING, Any, Dict, Optional, cast
4
+ from typing import TYPE_CHECKING, Any, Dict, List, Optional, cast
5
+ from xml.etree import ElementTree
6
+
7
+ from typing_extensions import override
5
8
 
6
9
  from ..decodestate import DecodeState
7
10
  from ..encodestate import EncodeState
8
- from ..exceptions import EncodeError, OdxWarning, odxraise
9
- from ..odxlink import OdxLinkDatabase, OdxLinkId, OdxLinkRef
11
+ from ..exceptions import DecodeError, EncodeError, OdxWarning, odxraise, odxrequire
12
+ from ..odxlink import OdxDocFragment, OdxLinkDatabase, OdxLinkId, OdxLinkRef
10
13
  from ..odxtypes import ParameterValue
14
+ from ..utils import dataclass_fields_asdict
11
15
  from .parameter import Parameter, ParameterType
12
16
  from .tablekeyparameter import TableKeyParameter
13
17
 
@@ -21,23 +25,42 @@ class TableStructParameter(Parameter):
21
25
  table_key_ref: Optional[OdxLinkRef]
22
26
  table_key_snref: Optional[str]
23
27
 
28
+ @staticmethod
29
+ @override
30
+ def from_et(et_element: ElementTree.Element,
31
+ doc_frags: List[OdxDocFragment]) -> "TableStructParameter":
32
+
33
+ kwargs = dataclass_fields_asdict(Parameter.from_et(et_element, doc_frags))
34
+
35
+ table_key_ref = OdxLinkRef.from_et(et_element.find("TABLE-KEY-REF"), doc_frags)
36
+ table_key_snref = None
37
+ if (table_key_snref_elem := et_element.find("TABLE-KEY-SNREF")) is not None:
38
+ table_key_snref = odxrequire(table_key_snref_elem.get("SHORT-NAME"))
39
+
40
+ return TableStructParameter(
41
+ table_key_ref=table_key_ref, table_key_snref=table_key_snref, **kwargs)
42
+
24
43
  def __post_init__(self) -> None:
25
44
  if self.table_key_ref is None and self.table_key_snref is None:
26
45
  odxraise("Either table_key_ref or table_key_snref must be defined.")
27
46
 
28
47
  @property
48
+ @override
29
49
  def parameter_type(self) -> ParameterType:
30
50
  return "TABLE-STRUCT"
31
51
 
52
+ @override
32
53
  def _build_odxlinks(self) -> Dict[OdxLinkId, Any]:
33
54
  return super()._build_odxlinks()
34
55
 
56
+ @override
35
57
  def _resolve_odxlinks(self, odxlinks: OdxLinkDatabase) -> None:
36
58
  super()._resolve_odxlinks(odxlinks)
37
59
 
38
60
  if self.table_key_ref is not None:
39
- self._table_key = odxlinks.resolve(self.table_key_ref)
61
+ self._table_key = odxlinks.resolve(self.table_key_ref, TableKeyParameter)
40
62
 
63
+ @override
41
64
  def _resolve_snrefs(self, diag_layer: "DiagLayer") -> None:
42
65
  super()._resolve_snrefs(diag_layer)
43
66
 
@@ -53,13 +76,16 @@ class TableStructParameter(Parameter):
53
76
  return self._table_key
54
77
 
55
78
  @property
79
+ @override
56
80
  def is_required(self) -> bool:
57
81
  return True
58
82
 
59
83
  @property
84
+ @override
60
85
  def is_settable(self) -> bool:
61
86
  return True
62
87
 
88
+ @override
63
89
  def get_coded_value_as_bytes(self, encode_state: EncodeState) -> bytes:
64
90
  physical_value = encode_state.parameter_values.get(self.short_name)
65
91
 
@@ -122,22 +148,21 @@ class TableStructParameter(Parameter):
122
148
  return tr.dop.convert_physical_to_bytes(
123
149
  tr_value, encode_state=encode_state, bit_position=bit_position)
124
150
 
151
+ @override
125
152
  def encode_into_pdu(self, encode_state: EncodeState) -> bytes:
126
153
  return super().encode_into_pdu(encode_state)
127
154
 
128
- def decode_from_pdu(self, decode_state: DecodeState) -> ParameterValue:
129
- orig_cursor = decode_state.cursor_byte_position
130
- if self.byte_position is not None:
131
- decode_state.cursor_byte_position = decode_state.origin_byte_position + self.byte_position
132
-
155
+ @override
156
+ def _decode_positioned_from_pdu(self, decode_state: DecodeState) -> ParameterValue:
133
157
  # find the selected table row
134
158
  key_name = self.table_key.short_name
135
159
 
136
160
  decode_state.table_keys[key_name]
137
161
  table_row = decode_state.table_keys.get(key_name)
138
162
  if table_row is None:
139
- raise odxraise(f"No table key '{key_name}' found when decoding "
140
- f"table struct parameter '{str(self.short_name)}'")
163
+ odxraise(
164
+ f"No table key '{key_name}' found when decoding "
165
+ f"table struct parameter '{str(self.short_name)}'", DecodeError)
141
166
  dummy_val = cast(str, None), cast(int, None)
142
167
  return dummy_val
143
168
 
@@ -145,14 +170,11 @@ class TableStructParameter(Parameter):
145
170
  if table_row.dop is not None:
146
171
  dop = table_row.dop
147
172
  val = dop.decode_from_pdu(decode_state)
148
- decode_state.cursor_byte_position = max(decode_state.cursor_byte_position, orig_cursor)
149
173
  return (table_row.short_name, val)
150
174
  elif table_row.structure is not None:
151
175
  val = table_row.structure.decode_from_pdu(decode_state)
152
- decode_state.cursor_byte_position = max(decode_state.cursor_byte_position, orig_cursor)
153
176
  return (table_row.short_name, val)
154
177
  else:
155
178
  # the table row associated with the key neither defines a
156
179
  # DOP nor a structure -> ignore it
157
- decode_state.cursor_byte_position = max(decode_state.cursor_byte_position, orig_cursor)
158
180
  return (table_row.short_name, cast(int, None))
@@ -1,12 +1,16 @@
1
1
  # SPDX-License-Identifier: MIT
2
2
  from dataclasses import dataclass
3
- from typing import TYPE_CHECKING, Any, Dict, Optional
3
+ from typing import TYPE_CHECKING, Any, Dict, List, Optional
4
+ from xml.etree import ElementTree
5
+
6
+ from typing_extensions import override
4
7
 
5
8
  from ..dataobjectproperty import DataObjectProperty
6
9
  from ..encodestate import EncodeState
7
10
  from ..exceptions import odxraise, odxrequire
8
- from ..odxlink import OdxLinkDatabase, OdxLinkId
11
+ from ..odxlink import OdxDocFragment, OdxLinkDatabase, OdxLinkId
9
12
  from ..odxtypes import AtomicOdxType
13
+ from ..utils import dataclass_fields_asdict
10
14
  from .parameter import ParameterType
11
15
  from .parameterwithdop import ParameterWithDOP
12
16
 
@@ -21,16 +25,31 @@ class ValueParameter(ParameterWithDOP):
21
25
  def __post_init__(self) -> None:
22
26
  self._physical_default_value: Optional[AtomicOdxType] = None
23
27
 
28
+ @staticmethod
29
+ @override
30
+ def from_et(et_element: ElementTree.Element,
31
+ doc_frags: List[OdxDocFragment]) -> "ValueParameter":
32
+
33
+ kwargs = dataclass_fields_asdict(ParameterWithDOP.from_et(et_element, doc_frags))
34
+
35
+ physical_default_value_raw = et_element.findtext("PHYSICAL-DEFAULT-VALUE")
36
+
37
+ return ValueParameter(physical_default_value_raw=physical_default_value_raw, **kwargs)
38
+
24
39
  @property
40
+ @override
25
41
  def parameter_type(self) -> ParameterType:
26
42
  return "VALUE"
27
43
 
44
+ @override
28
45
  def _build_odxlinks(self) -> Dict[OdxLinkId, Any]:
29
46
  return super()._build_odxlinks()
30
47
 
48
+ @override
31
49
  def _resolve_odxlinks(self, odxlinks: OdxLinkDatabase) -> None:
32
50
  super()._resolve_odxlinks(odxlinks)
33
51
 
52
+ @override
34
53
  def _resolve_snrefs(self, diag_layer: "DiagLayer") -> None:
35
54
  super()._resolve_snrefs(diag_layer)
36
55
 
@@ -48,13 +67,16 @@ class ValueParameter(ParameterWithDOP):
48
67
  return self._physical_default_value
49
68
 
50
69
  @property
70
+ @override
51
71
  def is_required(self) -> bool:
52
72
  return self._physical_default_value is None
53
73
 
54
74
  @property
75
+ @override
55
76
  def is_settable(self) -> bool:
56
77
  return True
57
78
 
79
+ @override
58
80
  def get_coded_value_as_bytes(self, encode_state: EncodeState) -> bytes:
59
81
  physical_value = encode_state.parameter_values.get(self.short_name,
60
82
  self.physical_default_value)
@@ -30,7 +30,10 @@ class ParamLengthInfoType(DiagCodedType):
30
30
  """Recursively resolve any odxlinks references"""
31
31
  super()._resolve_odxlinks(odxlinks)
32
32
 
33
- self._length_key = odxlinks.resolve(self.length_key_ref)
33
+ if TYPE_CHECKING:
34
+ self._length_key = odxlinks.resolve(self.length_key_ref, LengthKeyParameter)
35
+ else:
36
+ self._length_key = odxlinks.resolve(self.length_key_ref)
34
37
 
35
38
  def _resolve_snrefs(self, diag_layer: "DiagLayer") -> None:
36
39
  """Recursively resolve any short-name references"""
odxtools/parentref.py CHANGED
@@ -71,7 +71,10 @@ class ParentRef:
71
71
  return {}
72
72
 
73
73
  def _resolve_odxlinks(self, odxlinks: OdxLinkDatabase) -> None:
74
- self._layer = odxlinks.resolve(self.layer_ref)
74
+ if TYPE_CHECKING:
75
+ self._layer = odxlinks.resolve(self.layer_ref, DiagLayer)
76
+ else:
77
+ self._layer = odxlinks.resolve(self.layer_ref)
75
78
 
76
79
  def _resolve_snrefs(self, diag_layer: "DiagLayer") -> None:
77
80
  pass
odxtools/scaleconstr.py CHANGED
@@ -1,11 +1,12 @@
1
1
  # SPDX-License-Identifier: MIT
2
2
  from dataclasses import dataclass
3
3
  from enum import Enum
4
- from typing import Optional
4
+ from typing import List, Optional
5
5
  from xml.etree import ElementTree
6
6
 
7
7
  from .compumethods.limit import Limit
8
8
  from .exceptions import odxraise, odxrequire
9
+ from .odxlink import OdxDocFragment
9
10
  from .odxtypes import DataType
10
11
  from .utils import create_description_from_et
11
12
 
@@ -27,14 +28,18 @@ class ScaleConstr:
27
28
  lower_limit: Optional[Limit]
28
29
  upper_limit: Optional[Limit]
29
30
  validity: ValidType
31
+ value_type: DataType
30
32
 
31
33
  @staticmethod
32
- def from_et(et_element: ElementTree.Element, internal_type: DataType) -> "ScaleConstr":
34
+ def scale_constr_from_et(et_element: ElementTree.Element, doc_frags: List[OdxDocFragment], *,
35
+ value_type: DataType) -> "ScaleConstr":
33
36
  short_label = et_element.findtext("SHORT-LABEL")
34
37
  description = create_description_from_et(et_element.find("DESC"))
35
38
 
36
- lower_limit = Limit.from_et(et_element.find("LOWER-LIMIT"), internal_type=internal_type)
37
- upper_limit = Limit.from_et(et_element.find("UPPER-LIMIT"), internal_type=internal_type)
39
+ lower_limit = Limit.limit_from_et(
40
+ et_element.find("LOWER-LIMIT"), doc_frags, value_type=value_type)
41
+ upper_limit = Limit.limit_from_et(
42
+ et_element.find("UPPER-LIMIT"), doc_frags, value_type=value_type)
38
43
 
39
44
  validity_str = odxrequire(et_element.get("VALIDITY"))
40
45
  try:
@@ -47,4 +52,5 @@ class ScaleConstr:
47
52
  description=description,
48
53
  lower_limit=lower_limit,
49
54
  upper_limit=upper_limit,
50
- validity=validity)
55
+ validity=validity,
56
+ value_type=value_type)
@@ -20,7 +20,7 @@ class StateTransition(IdentifiableElement):
20
20
  """
21
21
  source_snref: str
22
22
  target_snref: str
23
- #external_access_method: Optional[ExternalAccessMethod] # TODO
23
+ # external_access_method: Optional[ExternalAccessMethod] # TODO
24
24
 
25
25
  @property
26
26
  def source_state(self) -> State:
@@ -0,0 +1,101 @@
1
+ # SPDX-License-Identifier: MIT
2
+ from dataclasses import dataclass
3
+ from typing import TYPE_CHECKING, Any, Dict, List
4
+ from xml.etree import ElementTree
5
+
6
+ from typing_extensions import override
7
+
8
+ from .decodestate import DecodeState
9
+ from .encodestate import EncodeState
10
+ from .exceptions import odxassert, odxraise, odxrequire
11
+ from .field import Field
12
+ from .odxlink import OdxDocFragment, OdxLinkDatabase, OdxLinkId
13
+ from .odxtypes import ParameterValue
14
+ from .utils import dataclass_fields_asdict
15
+
16
+ if TYPE_CHECKING:
17
+ from .diaglayer import DiagLayer
18
+
19
+
20
+ @dataclass
21
+ class StaticField(Field):
22
+ """Array of a fixed number of structure objects"""
23
+ fixed_number_of_items: int
24
+ item_byte_size: int
25
+
26
+ @staticmethod
27
+ @override
28
+ def from_et(et_element: ElementTree.Element, doc_frags: List[OdxDocFragment]) -> "StaticField":
29
+ kwargs = dataclass_fields_asdict(Field.from_et(et_element, doc_frags))
30
+
31
+ fixed_number_of_items = int(odxrequire(et_element.findtext('FIXED-NUMBER-OF-ITEMS')))
32
+ item_byte_size = int(odxrequire(et_element.findtext('ITEM-BYTE-SIZE')))
33
+
34
+ return StaticField(
35
+ fixed_number_of_items=fixed_number_of_items, item_byte_size=item_byte_size, **kwargs)
36
+
37
+ @override
38
+ def _build_odxlinks(self) -> Dict[OdxLinkId, Any]:
39
+ odxlinks = super()._build_odxlinks()
40
+ return odxlinks
41
+
42
+ @override
43
+ def _resolve_odxlinks(self, odxlinks: OdxLinkDatabase) -> None:
44
+ super()._resolve_odxlinks(odxlinks)
45
+
46
+ @override
47
+ def _resolve_snrefs(self, diag_layer: "DiagLayer") -> None:
48
+ super()._resolve_snrefs(diag_layer)
49
+
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:
57
+ if not isinstance(physical_value,
58
+ (tuple, list)) or len(physical_value) != self.fixed_number_of_items:
59
+ odxraise(f"Value for static field '{self.short_name}' "
60
+ f"must be a list of size {self.fixed_number_of_items}")
61
+
62
+ result = bytearray()
63
+ for val in physical_value:
64
+ if not isinstance(val, dict):
65
+ odxraise(f"The individual parameter values for static field '{self.short_name}' "
66
+ f"must be dictionaries for structure '{self.structure.short_name}'")
67
+
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:
75
+ # add some padding bytes
76
+ data = data.ljust(self.item_byte_size, b'\x00')
77
+
78
+ result += data
79
+
80
+ return result
81
+
82
+ @override
83
+ def decode_from_pdu(self, decode_state: DecodeState) -> ParameterValue:
84
+
85
+ odxassert(decode_state.cursor_bit_position == 0,
86
+ "No bit position can be specified for static length fields!")
87
+
88
+ result: List[ParameterValue] = []
89
+ for _ in range(self.fixed_number_of_items):
90
+ orig_cursor = decode_state.cursor_byte_position
91
+
92
+ if decode_state.cursor_byte_position - orig_cursor > self.item_byte_size:
93
+ odxraise(f"Insufficient item byte size for static field {self.short_name}: "
94
+ f"Is {self.item_byte_size} bytes, but need at least "
95
+ f"{decode_state.cursor_byte_position - orig_cursor} bytes")
96
+
97
+ result.append(self.structure.decode_from_pdu(decode_state))
98
+
99
+ decode_state.cursor_byte_position = orig_cursor + self.item_byte_size
100
+
101
+ return result
odxtools/table.py CHANGED
@@ -45,7 +45,8 @@ class Table(IdentifiableElement):
45
45
  for sub_elem in et_element:
46
46
  if sub_elem.tag == "TABLE-ROW":
47
47
  table_rows_raw.append(
48
- TableRow.from_et(sub_elem, doc_frags, table_ref=OdxLinkRef.from_id(odx_id)))
48
+ TableRow.tablerow_from_et(
49
+ sub_elem, doc_frags, table_ref=OdxLinkRef.from_id(odx_id)))
49
50
  elif sub_elem.tag == "TABLE-ROW-REF":
50
51
  table_rows_raw.append(OdxLinkRef.from_et(sub_elem, doc_frags))
51
52
 
odxtools/tablerow.py CHANGED
@@ -51,9 +51,13 @@ class TableRow(IdentifiableElement):
51
51
  )
52
52
 
53
53
  @staticmethod
54
- def from_et( # type: ignore[override]
55
- et_element: ElementTree.Element, doc_frags: List[OdxDocFragment], *,
56
- table_ref: OdxLinkRef) -> "TableRow":
54
+ def from_et(et_element: ElementTree.Element, doc_frags: List[OdxDocFragment]) -> Any:
55
+ raise RuntimeError(
56
+ "Calling TableRow.from_et() is not allowed. Use TableRow.tablerow_from_et().")
57
+
58
+ @staticmethod
59
+ def tablerow_from_et(et_element: ElementTree.Element, doc_frags: List[OdxDocFragment], *,
60
+ table_ref: OdxLinkRef) -> "TableRow":
57
61
  """Reads a TABLE-ROW."""
58
62
  kwargs = dataclass_fields_asdict(IdentifiableElement.from_et(et_element, doc_frags))
59
63
  semantic = et_element.get("SEMANTIC")
@@ -95,7 +99,10 @@ class TableRow(IdentifiableElement):
95
99
  if not isinstance(self._dop, (DataObjectProperty, DtcDop)):
96
100
  odxraise("The DOP-REF of TABLE-ROWs must reference a simple DOP!")
97
101
 
98
- self._table = odxlinks.resolve(self.table_ref)
102
+ if TYPE_CHECKING:
103
+ self._table = odxlinks.resolve(self.table_ref, Table)
104
+ else:
105
+ self._table = odxlinks.resolve(self.table_ref)
99
106
 
100
107
  for sdg in self.sdgs:
101
108
  sdg._resolve_odxlinks(odxlinks)
@@ -43,12 +43,24 @@
43
43
  {%- endif %}
44
44
  {%- endmacro -%}
45
45
 
46
- {%- macro printLimitValue(lv) -%}
47
- {%- if hasattr(lv, 'hex') -%}
48
- {#- bytes or bytarray limit #}
49
- {{lv.hex()}}
50
- {%- else -%}
51
- {{lv}}
46
+ {%- macro printLimit(tag_name, limit_obj) -%}
47
+ {%- if limit_obj is not none %}
48
+ <{{tag_name}}
49
+ {%- if limit_obj.interval_type is not none %}
50
+ {{- make_xml_attrib("INTERVAL-TYPE", limit_obj.interval_type.value) }}
51
+ {%- endif %}
52
+ {%- if limit_obj.value_raw is none %}
53
+ {#- #}/>
54
+ {%- else %}
55
+ {#- #}>
56
+ {%- if hasattr(limit_obj._value, 'hex') -%}
57
+ {#- bytes or bytarray limit #}
58
+ {{- limit_obj._value.hex().upper() }}
59
+ {%- else -%}
60
+ {{- limit_obj._value }}
61
+ {%- endif -%}
62
+ </{{tag_name}}>
63
+ {%- endif -%}
52
64
  {%- endif -%}
53
65
  {%- endmacro -%}
54
66
 
@@ -62,8 +74,8 @@
62
74
  {{sc.description}}
63
75
  </DESC>
64
76
  {%- endif %}
65
- <LOWER-LIMIT>{{printLimitValue(sc.lower_limit.value)}}</LOWER-LIMIT>
66
- <UPPER-LIMIT>{{printLimitValue(sc.upper_limit.value)}}</UPPER-LIMIT>
77
+ {{printLimit("LOWER-LIMIT", sc.lower_limit) }}
78
+ {{printLimit("UPPER-LIMIT", sc.upper_limit) }}
67
79
  </SCALE-CONSTR>
68
80
  {%- endmacro -%}
69
81
 
@@ -73,12 +85,8 @@
73
85
  {%- else %}
74
86
  <INTERNAL-CONSTR>
75
87
  {%- endif %}
76
- {%- if ic.lower_limit %}
77
- <LOWER-LIMIT>{{printLimitValue(ic.lower_limit.value)}}</LOWER-LIMIT>
78
- {%- endif %}
79
- {%- if ic.upper_limit %}
80
- <UPPER-LIMIT>{{printLimitValue(ic.upper_limit.value)}}</UPPER-LIMIT>
81
- {%- endif %}
88
+ {{printLimit("LOWER-LIMIT", ic.lower_limit) }}
89
+ {{printLimit("UPPER-LIMIT", ic.upper_limit) }}
82
90
  {%- if ic.scale_constrs %}
83
91
  <SCALE-CONSTRS>
84
92
  {%- for sc in ic.scale_constrs %}
@@ -109,12 +117,8 @@
109
117
  {{cs.description}}
110
118
  </DESC>
111
119
  {%- endif %}
112
- {%- if cs.lower_limit is not none %}
113
- <LOWER-LIMIT>{{printLimitValue(cs.lower_limit.value)}}</LOWER-LIMIT>
114
- {%- endif %}
115
- {%- if cs.upper_limit is not none %}
116
- <UPPER-LIMIT>{{printLimitValue(cs.upper_limit.value)}}</UPPER-LIMIT>
117
- {%- endif %}
120
+ {{printLimit("LOWER-LIMIT", cs.lower_limit) }}
121
+ {{printLimit("UPPER-LIMIT", cs.upper_limit) }}
118
122
  {%- if cs.compu_inverse_value is not none %}
119
123
  <COMPU-INVERSE-VALUE>
120
124
  <V>{{cs.compu_inverse_value}}</V>
@@ -141,12 +145,8 @@
141
145
  <COMPU-INTERNAL-TO-PHYS>
142
146
  <COMPU-SCALES>
143
147
  <COMPU-SCALE>
144
- {%- if cm.internal_lower_limit is not none and cm.internal_lower_limit.interval_type.value != "INFINITE" %}
145
- <LOWER-LIMIT>{{printLimitValue(cm.internal_lower_limit.value)}}</LOWER-LIMIT>
146
- {%- endif %}
147
- {%- if cm.internal_upper_limit is not none and cm.internal_upper_limit.interval_type.value != "INFINITE" %}
148
- <UPPER-LIMIT>{{printLimitValue(cm.internal_upper_limit.value)}}</UPPER-LIMIT>
149
- {%- endif %}
148
+ {{printLimit("LOWER-LIMIT", cm.internal_lower_limit) }}
149
+ {{printLimit("UPPER-LIMIT", cm.internal_upper_limit) }}
150
150
  <COMPU-RATIONAL-COEFFS>
151
151
  <COMPU-NUMERATOR>
152
152
  <V>{{cm.offset}}</V>
@@ -166,12 +166,8 @@
166
166
  <COMPU-SCALES>
167
167
  {%- for lm in cm.linear_methods %}
168
168
  <COMPU-SCALE>
169
- {%- if lm.internal_lower_limit is not none and lm.internal_lower_limit.interval_type.value != "INFINITE" %}
170
- <LOWER-LIMIT>{{printLimitValue(lm.internal_lower_limit.value)}}</LOWER-LIMIT>
171
- {%- endif %}
172
- {%- if lm.internal_upper_limit is not none and lm.internal_upper_limit.interval_type.value != "INFINITE" %}
173
- <UPPER-LIMIT>{{printLimitValue(lm.internal_upper_limit.value)}}</UPPER-LIMIT>
174
- {%- endif %}
169
+ {{printLimit("LOWER-LIMIT", lm.internal_lower_limit) }}
170
+ {{printLimit("UPPER-LIMIT", lm.internal_upper_limit) }}
175
171
  <COMPU-RATIONAL-COEFFS>
176
172
  <COMPU-NUMERATOR>
177
173
  <V>{{lm.offset}}</V>
@@ -191,8 +187,8 @@
191
187
  <COMPU-INTERNAL-TO-PHYS>
192
188
  <COMPU-SCALES>
193
189
  {%- for idx in range( cm.internal_points | length ) %}
194
- <COMPU-SCALE>
195
- <LOWER-LIMIT INTERVAL-TYPE="CLOSED">{{ printLimitValue(cm.internal_points[idx]) }}</LOWER-LIMIT>
190
+ <COMPU-SCALE>
191
+ <LOWER-LIMIT>{{ cm.internal_points[idx] }}</LOWER-LIMIT>
196
192
  <COMPU-CONST>
197
193
  <V>{{ cm.physical_points[idx] }}</V>
198
194
  </COMPU-CONST>
@@ -4,6 +4,7 @@
4
4
  -#}
5
5
 
6
6
  {%- import('macros/printElementId.xml.jinja2') as peid %}
7
+ {%- import('macros/printDOP.xml.jinja2') as pdop %}
7
8
 
8
9
  {%- macro printMux(mux) %}
9
10
  <MUX ID="{{mux.odx_id.local_id}}"
@@ -40,8 +41,8 @@
40
41
  {%- if case.structure_snref is not none %}
41
42
  <STRUCTURE-SNREF SHORT_NAME="{{case.structure_snref}}"/>
42
43
  {%- endif %}
43
- <LOWER-LIMIT>{{case.lower_limit}}</LOWER-LIMIT>
44
- <UPPER-LIMIT>{{case.upper_limit}}</UPPER-LIMIT>
44
+ {{ pdop.printLimit("LOWER-LIMIT", case.lower_limit) }}
45
+ {{ pdop.printLimit("UPPER-LIMIT", case.upper_limit) }}
45
46
  </CASE>
46
47
  {%- endfor %}
47
48
  </CASES>
@@ -8,17 +8,17 @@
8
8
  {%- import('macros/printSpecialData.xml.jinja2') as psd %}
9
9
 
10
10
  {%- macro printParam(param) -%}
11
- {%- if param.semantic is not none %}
12
- {%- set semattrib = " SEMANTIC=\""+param.semantic+"\"" -%}
13
- {%- else %}
14
- {%- set semattrib = "" -%}
15
- {%- endif -%}
16
- {%- if param.parameter_type == "TABLE-KEY" and param.odx_id is not none %}
17
- <PARAM {{semattrib}} ID="{{param.odx_id.local_id}}" xsi:type="{{param.parameter_type}}">
11
+ {%- set semattrib = make_xml_attrib("SEMANTIC", param.semantic) -%}
12
+ {%- if param.parameter_type == "TABLE-KEY" %}
13
+ <PARAM {{ semattrib }}
14
+ {{- make_xml_attrib("ID", param.odx_id and param.odx_id.local_id) }}
15
+ xsi:type="{{param.parameter_type}}">
18
16
  {%- elif param.parameter_type == "SYSTEM" %}
19
- <PARAM SYSPARAM="{{param.sysparam}}" xsi:type="{{param.parameter_type}}">
17
+ <PARAM {{ semattrib }}
18
+ {{- make_xml_attrib("SYSPARAM", param.sysparam) }}
19
+ xsi:type="{{param.parameter_type}}">
20
20
  {%- else %}
21
- <PARAM {{semattrib}} xsi:type="{{param.parameter_type}}">
21
+ <PARAM {{ semattrib }} xsi:type="{{param.parameter_type}}">
22
22
  {%- endif%}
23
23
  {{ peid.printElementIdSubtags(param)|indent(1) }}
24
24
  {{- psd.printSpecialDataGroups(param.sdgs)|indent(1, first=True) }}
@@ -0,0 +1,15 @@
1
+ {#- -*- mode: sgml; tab-width: 1; indent-tabs-mode: nil -*-
2
+ #
3
+ # SPDX-License-Identifier: MIT
4
+ -#}
5
+
6
+ {%- import('macros/printElementId.xml.jinja2') as peid %}
7
+
8
+ {%- macro printStaticField(sf) -%}
9
+ <STATIC-FIELD ID="{{sf.odx_id.local_id}}">
10
+ {{ peid.printElementIdSubtags(sf)|indent(1) }}
11
+ <BASIC-STRUCTURE-REF ID-REF="{{sf.structure_ref.ref_id}}" />
12
+ <FIXED-NUMBER-OF-ITEMS>{{sf.fixed_number_of_items}}</FIXED-NUMBER-OF-ITEMS>
13
+ <ITEM-BYTE-SIZE>{{sf.item_byte_size}}</ITEM-BYTE-SIZE>
14
+ </STATIC-FIELD>
15
+ {%- endmacro -%}
@@ -9,6 +9,7 @@
9
9
  {%- import('macros/printFunctionalClass.xml.jinja2') as pfc %}
10
10
  {%- import('macros/printStructure.xml.jinja2') as pst %}
11
11
  {%- import('macros/printEndOfPdu.xml.jinja2') as peopdu %}
12
+ {%- import('macros/printStaticField.xml.jinja2') as psf %}
12
13
  {%- import('macros/printDynamicLengthField.xml.jinja2') as pdlf %}
13
14
  {%- import('macros/printMux.xml.jinja2') as pm %}
14
15
  {%- import('macros/printEnvData.xml.jinja2') as ped %}
@@ -67,6 +68,13 @@
67
68
  {%- endfor %}
68
69
  </STRUCTURES>
69
70
  {%- endif %}
71
+ {%- if ddds.static_fields %}
72
+ <STATIC-FIELDS>
73
+ {%- for sf in ddds.static_fields %}
74
+ {{ psf.printStaticField(sf)|indent(3) }}
75
+ {%- endfor %}
76
+ </STATIC-FIELDS>
77
+ {%- endif %}
70
78
  {%- if ddds.dynamic_length_fields %}
71
79
  <DYNAMIC-LENGTH-FIELDS>
72
80
  {%- for dlf in ddds.dynamic_length_fields %}
odxtools/uds.py CHANGED
@@ -173,6 +173,6 @@ def is_response_pending(telegram_payload: bytes, request_sid: Optional[int] = No
173
173
 
174
174
 
175
175
  # previous versions of odxtools had a typo here. hit happens!
176
- @deprecated(details="use is_response_pending()")
176
+ @deprecated(details="use is_response_pending()") # type: ignore[misc]
177
177
  def is_reponse_pending(telegram_payload: bytes, request_sid: Optional[int] = None) -> bool:
178
- return is_reponse_pending(telegram_payload, request_sid)
178
+ return is_response_pending(telegram_payload, request_sid)
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.6.1'
16
- __version_tuple__ = version_tuple = (6, 6, 1)
15
+ __version__ = version = '6.7.1'
16
+ __version_tuple__ = version_tuple = (6, 7, 1)
@@ -120,15 +120,15 @@ def write_pdx_file(
120
120
  creation_date = file_cdate.strftime("%Y-%m-%dT%H:%M:%S")
121
121
 
122
122
  mime_type = "text/plain"
123
- if template_file_name.endswith(".odx-cs"):
123
+ if output_file_name.endswith(".odx-cs"):
124
124
  mime_type = "application/x-asam.odx.odx-cs"
125
- elif template_file_name.endswith(".odx-d"):
125
+ elif output_file_name.endswith(".odx-d"):
126
126
  mime_type = "application/x-asam.odx.odx-d"
127
127
 
128
128
  zf_name = os.path.basename(output_file_name)
129
129
  with zf.open(zf_name, "w") as out_file:
130
130
  file_index.append((zf_name, creation_date, mime_type))
131
- out_file.write(data) # type: ignore
131
+ out_file.write(data)
132
132
 
133
133
  jinja_env = jinja2.Environment(loader=jinja2.FileSystemLoader(templates_dir))
134
134
  jinja_env.globals["hasattr"] = hasattr