odxtools 6.6.1__py3-none-any.whl → 9.3.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 (222) hide show
  1. odxtools/__init__.py +7 -5
  2. odxtools/additionalaudience.py +3 -5
  3. odxtools/admindata.py +5 -7
  4. odxtools/audience.py +10 -13
  5. odxtools/basecomparam.py +3 -5
  6. odxtools/basicstructure.py +55 -241
  7. odxtools/cli/_parser_utils.py +16 -1
  8. odxtools/cli/_print_utils.py +169 -134
  9. odxtools/cli/browse.py +127 -103
  10. odxtools/cli/compare.py +114 -87
  11. odxtools/cli/decode.py +2 -1
  12. odxtools/cli/dummy_sub_parser.py +3 -1
  13. odxtools/cli/find.py +2 -1
  14. odxtools/cli/list.py +26 -16
  15. odxtools/cli/main.py +1 -0
  16. odxtools/cli/snoop.py +32 -6
  17. odxtools/codec.py +211 -0
  18. odxtools/commrelation.py +122 -0
  19. odxtools/companydata.py +5 -7
  20. odxtools/companydocinfo.py +7 -8
  21. odxtools/companyrevisioninfo.py +3 -5
  22. odxtools/companyspecificinfo.py +8 -9
  23. odxtools/comparam.py +4 -6
  24. odxtools/comparaminstance.py +14 -14
  25. odxtools/comparamspec.py +16 -54
  26. odxtools/comparamsubset.py +22 -62
  27. odxtools/complexcomparam.py +5 -7
  28. odxtools/compumethods/compucodecompumethod.py +63 -0
  29. odxtools/compumethods/compuconst.py +31 -0
  30. odxtools/compumethods/compudefaultvalue.py +27 -0
  31. odxtools/compumethods/compuinternaltophys.py +56 -0
  32. odxtools/compumethods/compuinversevalue.py +7 -0
  33. odxtools/compumethods/compumethod.py +94 -15
  34. odxtools/compumethods/compuphystointernal.py +56 -0
  35. odxtools/compumethods/compurationalcoeffs.py +20 -9
  36. odxtools/compumethods/compuscale.py +67 -32
  37. odxtools/compumethods/createanycompumethod.py +31 -172
  38. odxtools/compumethods/identicalcompumethod.py +31 -6
  39. odxtools/compumethods/limit.py +70 -36
  40. odxtools/compumethods/linearcompumethod.py +70 -181
  41. odxtools/compumethods/linearsegment.py +190 -0
  42. odxtools/compumethods/ratfunccompumethod.py +106 -0
  43. odxtools/compumethods/ratfuncsegment.py +87 -0
  44. odxtools/compumethods/scalelinearcompumethod.py +132 -26
  45. odxtools/compumethods/scaleratfunccompumethod.py +113 -0
  46. odxtools/compumethods/tabintpcompumethod.py +123 -92
  47. odxtools/compumethods/texttablecompumethod.py +117 -57
  48. odxtools/createanydiagcodedtype.py +10 -67
  49. odxtools/database.py +167 -87
  50. odxtools/dataobjectproperty.py +25 -32
  51. odxtools/decodestate.py +14 -17
  52. odxtools/description.py +47 -0
  53. odxtools/determinenumberofitems.py +4 -5
  54. odxtools/diagcodedtype.py +37 -106
  55. odxtools/diagcomm.py +24 -12
  56. odxtools/diagdatadictionaryspec.py +120 -96
  57. odxtools/diaglayercontainer.py +46 -54
  58. odxtools/diaglayers/basevariant.py +128 -0
  59. odxtools/diaglayers/basevariantraw.py +123 -0
  60. odxtools/diaglayers/diaglayer.py +432 -0
  61. odxtools/{diaglayerraw.py → diaglayers/diaglayerraw.py} +105 -120
  62. odxtools/diaglayers/diaglayertype.py +42 -0
  63. odxtools/diaglayers/ecushareddata.py +96 -0
  64. odxtools/diaglayers/ecushareddataraw.py +87 -0
  65. odxtools/diaglayers/ecuvariant.py +124 -0
  66. odxtools/diaglayers/ecuvariantraw.py +129 -0
  67. odxtools/diaglayers/functionalgroup.py +110 -0
  68. odxtools/diaglayers/functionalgroupraw.py +106 -0
  69. odxtools/{diaglayer.py → diaglayers/hierarchyelement.py} +273 -472
  70. odxtools/diaglayers/hierarchyelementraw.py +58 -0
  71. odxtools/diaglayers/protocol.py +64 -0
  72. odxtools/diaglayers/protocolraw.py +91 -0
  73. odxtools/diagnostictroublecode.py +8 -9
  74. odxtools/diagservice.py +57 -44
  75. odxtools/diagvariable.py +113 -0
  76. odxtools/docrevision.py +5 -7
  77. odxtools/dopbase.py +15 -15
  78. odxtools/dtcdop.py +170 -50
  79. odxtools/dynamicendmarkerfield.py +134 -0
  80. odxtools/dynamiclengthfield.py +47 -42
  81. odxtools/dyndefinedspec.py +177 -0
  82. odxtools/dynenddopref.py +38 -0
  83. odxtools/ecuvariantmatcher.py +6 -7
  84. odxtools/element.py +13 -15
  85. odxtools/encodestate.py +199 -22
  86. odxtools/endofpdufield.py +31 -18
  87. odxtools/environmentdata.py +8 -1
  88. odxtools/environmentdatadescription.py +198 -36
  89. odxtools/exceptions.py +11 -2
  90. odxtools/field.py +10 -10
  91. odxtools/functionalclass.py +3 -5
  92. odxtools/inputparam.py +3 -12
  93. odxtools/internalconstr.py +14 -5
  94. odxtools/isotp_state_machine.py +14 -6
  95. odxtools/leadinglengthinfotype.py +37 -18
  96. odxtools/library.py +66 -0
  97. odxtools/loadfile.py +64 -0
  98. odxtools/matchingparameter.py +3 -3
  99. odxtools/message.py +0 -7
  100. odxtools/minmaxlengthtype.py +61 -33
  101. odxtools/modification.py +3 -5
  102. odxtools/multiplexer.py +135 -75
  103. odxtools/multiplexercase.py +39 -18
  104. odxtools/multiplexerdefaultcase.py +15 -12
  105. odxtools/multiplexerswitchkey.py +4 -5
  106. odxtools/nameditemlist.py +33 -8
  107. odxtools/negoutputparam.py +3 -5
  108. odxtools/odxcategory.py +83 -0
  109. odxtools/odxlink.py +62 -53
  110. odxtools/odxtypes.py +93 -8
  111. odxtools/outputparam.py +5 -16
  112. odxtools/parameterinfo.py +219 -61
  113. odxtools/parameters/codedconstparameter.py +45 -32
  114. odxtools/parameters/createanyparameter.py +19 -193
  115. odxtools/parameters/dynamicparameter.py +25 -4
  116. odxtools/parameters/lengthkeyparameter.py +83 -25
  117. odxtools/parameters/matchingrequestparameter.py +48 -18
  118. odxtools/parameters/nrcconstparameter.py +76 -54
  119. odxtools/parameters/parameter.py +97 -73
  120. odxtools/parameters/parameterwithdop.py +41 -38
  121. odxtools/parameters/physicalconstantparameter.py +41 -20
  122. odxtools/parameters/reservedparameter.py +36 -18
  123. odxtools/parameters/systemparameter.py +74 -7
  124. odxtools/parameters/tableentryparameter.py +47 -7
  125. odxtools/parameters/tablekeyparameter.py +142 -55
  126. odxtools/parameters/tablestructparameter.py +79 -58
  127. odxtools/parameters/valueparameter.py +39 -21
  128. odxtools/paramlengthinfotype.py +56 -33
  129. odxtools/parentref.py +20 -3
  130. odxtools/physicaldimension.py +3 -8
  131. odxtools/progcode.py +26 -11
  132. odxtools/protstack.py +3 -5
  133. odxtools/py.typed +0 -0
  134. odxtools/relateddoc.py +7 -9
  135. odxtools/request.py +120 -10
  136. odxtools/response.py +123 -23
  137. odxtools/scaleconstr.py +14 -8
  138. odxtools/servicebinner.py +1 -1
  139. odxtools/singleecujob.py +12 -10
  140. odxtools/snrefcontext.py +29 -0
  141. odxtools/specialdata.py +3 -5
  142. odxtools/specialdatagroup.py +7 -9
  143. odxtools/specialdatagroupcaption.py +3 -6
  144. odxtools/standardlengthtype.py +80 -14
  145. odxtools/state.py +3 -5
  146. odxtools/statechart.py +13 -19
  147. odxtools/statetransition.py +8 -18
  148. odxtools/staticfield.py +107 -0
  149. odxtools/subcomponent.py +288 -0
  150. odxtools/swvariable.py +21 -0
  151. odxtools/table.py +9 -9
  152. odxtools/tablerow.py +30 -15
  153. odxtools/teammember.py +3 -5
  154. odxtools/templates/comparam-spec.odx-c.xml.jinja2 +4 -24
  155. odxtools/templates/comparam-subset.odx-cs.xml.jinja2 +5 -26
  156. odxtools/templates/diag_layer_container.odx-d.xml.jinja2 +15 -31
  157. odxtools/templates/{index.xml.xml.jinja2 → index.xml.jinja2} +1 -1
  158. odxtools/templates/macros/printAudience.xml.jinja2 +1 -1
  159. odxtools/templates/macros/printBaseVariant.xml.jinja2 +53 -0
  160. odxtools/templates/macros/printCompanyData.xml.jinja2 +4 -7
  161. odxtools/templates/macros/printComparam.xml.jinja2 +6 -4
  162. odxtools/templates/macros/printComparamRef.xml.jinja2 +5 -12
  163. odxtools/templates/macros/printCompuMethod.xml.jinja2 +147 -0
  164. odxtools/templates/macros/printDOP.xml.jinja2 +27 -137
  165. odxtools/templates/macros/printDescription.xml.jinja2 +18 -0
  166. odxtools/templates/macros/printDiagComm.xml.jinja2 +1 -1
  167. odxtools/templates/macros/printDiagLayer.xml.jinja2 +222 -0
  168. odxtools/templates/macros/printDiagVariable.xml.jinja2 +66 -0
  169. odxtools/templates/macros/printDynDefinedSpec.xml.jinja2 +48 -0
  170. odxtools/templates/macros/printDynamicEndmarkerField.xml.jinja2 +16 -0
  171. odxtools/templates/macros/printDynamicLengthField.xml.jinja2 +1 -1
  172. odxtools/templates/macros/printEcuSharedData.xml.jinja2 +30 -0
  173. odxtools/templates/macros/printEcuVariant.xml.jinja2 +53 -0
  174. odxtools/templates/macros/printEcuVariantPattern.xml.jinja2 +1 -1
  175. odxtools/templates/macros/printElementId.xml.jinja2 +8 -3
  176. odxtools/templates/macros/printEndOfPdu.xml.jinja2 +1 -1
  177. odxtools/templates/macros/printEnvDataDesc.xml.jinja2 +1 -1
  178. odxtools/templates/macros/printFunctionalClass.xml.jinja2 +1 -1
  179. odxtools/templates/macros/printFunctionalGroup.xml.jinja2 +40 -0
  180. odxtools/templates/macros/printHierarchyElement.xml.jinja2 +24 -0
  181. odxtools/templates/macros/printLibrary.xml.jinja2 +21 -0
  182. odxtools/templates/macros/printMux.xml.jinja2 +5 -3
  183. odxtools/templates/macros/printOdxCategory.xml.jinja2 +28 -0
  184. odxtools/templates/macros/printParam.xml.jinja2 +18 -19
  185. odxtools/templates/macros/printProtStack.xml.jinja2 +1 -1
  186. odxtools/templates/macros/printProtocol.xml.jinja2 +30 -0
  187. odxtools/templates/macros/printRequest.xml.jinja2 +1 -1
  188. odxtools/templates/macros/printResponse.xml.jinja2 +1 -1
  189. odxtools/templates/macros/printService.xml.jinja2 +3 -2
  190. odxtools/templates/macros/printSingleEcuJob.xml.jinja2 +5 -26
  191. odxtools/templates/macros/printSpecialData.xml.jinja2 +1 -1
  192. odxtools/templates/macros/printState.xml.jinja2 +1 -1
  193. odxtools/templates/macros/printStateChart.xml.jinja2 +1 -1
  194. odxtools/templates/macros/printStateTransition.xml.jinja2 +1 -1
  195. odxtools/templates/macros/printStaticField.xml.jinja2 +15 -0
  196. odxtools/templates/macros/printStructure.xml.jinja2 +1 -1
  197. odxtools/templates/macros/printSubComponent.xml.jinja2 +104 -0
  198. odxtools/templates/macros/printTable.xml.jinja2 +4 -5
  199. odxtools/templates/macros/printUnitSpec.xml.jinja2 +3 -5
  200. odxtools/uds.py +2 -10
  201. odxtools/unit.py +4 -8
  202. odxtools/unitgroup.py +3 -5
  203. odxtools/unitspec.py +17 -17
  204. odxtools/utils.py +38 -20
  205. odxtools/variablegroup.py +32 -0
  206. odxtools/version.py +2 -2
  207. odxtools/{write_pdx_file.py → writepdxfile.py} +22 -12
  208. odxtools/xdoc.py +3 -5
  209. {odxtools-6.6.1.dist-info → odxtools-9.3.0.dist-info}/METADATA +44 -33
  210. odxtools-9.3.0.dist-info/RECORD +228 -0
  211. {odxtools-6.6.1.dist-info → odxtools-9.3.0.dist-info}/WHEEL +1 -1
  212. odxtools/createcompanydatas.py +0 -17
  213. odxtools/createsdgs.py +0 -19
  214. odxtools/diaglayertype.py +0 -30
  215. odxtools/load_file.py +0 -13
  216. odxtools/load_odx_d_file.py +0 -6
  217. odxtools/load_pdx_file.py +0 -8
  218. odxtools/templates/macros/printVariant.xml.jinja2 +0 -208
  219. odxtools-6.6.1.dist-info/RECORD +0 -180
  220. {odxtools-6.6.1.dist-info → odxtools-9.3.0.dist-info}/LICENSE +0 -0
  221. {odxtools-6.6.1.dist-info → odxtools-9.3.0.dist-info}/entry_points.txt +0 -0
  222. {odxtools-6.6.1.dist-info → odxtools-9.3.0.dist-info}/top_level.txt +0 -0
@@ -1,10 +1,16 @@
1
1
  # SPDX-License-Identifier: MIT
2
2
  from dataclasses import dataclass
3
- from typing import Optional, cast
3
+ from typing import List, Optional
4
+ from xml.etree import ElementTree
5
+
6
+ from typing_extensions import override
4
7
 
5
8
  from ..decodestate import DecodeState
6
9
  from ..encodestate import EncodeState
7
- from ..odxtypes import ParameterValue
10
+ from ..exceptions import odxrequire
11
+ from ..odxlink import OdxDocFragment
12
+ from ..odxtypes import DataType, ParameterValue
13
+ from ..utils import dataclass_fields_asdict
8
14
  from .parameter import Parameter, ParameterType
9
15
 
10
16
 
@@ -12,35 +18,47 @@ from .parameter import Parameter, ParameterType
12
18
  class ReservedParameter(Parameter):
13
19
  bit_length: int
14
20
 
21
+ @staticmethod
22
+ @override
23
+ def from_et(et_element: ElementTree.Element,
24
+ doc_frags: List[OdxDocFragment]) -> "ReservedParameter":
25
+
26
+ kwargs = dataclass_fields_asdict(Parameter.from_et(et_element, doc_frags))
27
+
28
+ bit_length = int(odxrequire(et_element.findtext("BIT-LENGTH")))
29
+
30
+ return ReservedParameter(bit_length=bit_length, **kwargs)
31
+
15
32
  @property
33
+ @override
16
34
  def parameter_type(self) -> ParameterType:
17
35
  return "RESERVED"
18
36
 
19
37
  @property
38
+ @override
20
39
  def is_required(self) -> bool:
21
40
  return False
22
41
 
23
42
  @property
43
+ @override
24
44
  def is_settable(self) -> bool:
25
45
  return False
26
46
 
47
+ @override
27
48
  def get_static_bit_length(self) -> Optional[int]:
28
49
  return self.bit_length
29
50
 
30
- def get_coded_value_as_bytes(self, encode_state: EncodeState) -> bytes:
31
- bit_position_int = self.bit_position if self.bit_position is not None else 0
32
- return (0).to_bytes((self.bit_length + bit_position_int + 7) // 8, "big")
33
-
34
- def decode_from_pdu(self, decode_state: DecodeState) -> ParameterValue:
35
- # move the cursor
36
- orig_cursor = decode_state.cursor_byte_position
37
- if self.byte_position is not None:
38
- decode_state.cursor_byte_position = decode_state.origin_byte_position + self.byte_position
39
-
40
- decode_state.cursor_byte_position += ((self.bit_position or 0) + self.bit_length + 7) // 8
41
-
42
- decode_state.cursor_byte_position = max(orig_cursor, decode_state.cursor_byte_position)
43
- decode_state.cursor_bit_position = 0
51
+ @override
52
+ def _encode_positioned_into_pdu(self, physical_value: Optional[ParameterValue],
53
+ encode_state: EncodeState) -> None:
54
+ encode_state.cursor_byte_position += (encode_state.cursor_bit_position + self.bit_length +
55
+ 7) // 8
56
+ encode_state.cursor_bit_position = 0
57
+ encode_state.emplace_bytes(b'', self.short_name)
44
58
 
45
- # ignore the value of the parameter data
46
- return cast(int, None)
59
+ @override
60
+ def _decode_positioned_from_pdu(self, decode_state: DecodeState) -> ParameterValue:
61
+ return decode_state.extract_atomic_value(
62
+ bit_length=self.bit_length,
63
+ base_data_type=DataType.A_UINT32,
64
+ is_highlow_byte_order=False)
@@ -1,31 +1,98 @@
1
1
  # SPDX-License-Identifier: MIT
2
+ import getpass
2
3
  from dataclasses import dataclass
4
+ from datetime import datetime
5
+ from typing import List, Optional
6
+ from xml.etree import ElementTree
7
+
8
+ from typing_extensions import override
3
9
 
4
- from ..decodestate import DecodeState
5
10
  from ..encodestate import EncodeState
11
+ from ..exceptions import odxraise, odxrequire
12
+ from ..odxlink import OdxDocFragment
6
13
  from ..odxtypes import ParameterValue
14
+ from ..utils import dataclass_fields_asdict
7
15
  from .parameter import ParameterType
8
16
  from .parameterwithdop import ParameterWithDOP
9
17
 
18
+ # The SYSTEM parameter types mandated by the ODX 2.2 standard. Users
19
+ # are free to specify additional types, but these must be handled
20
+ # (cf. table 5 in section 7.3.5.4 of the ASAM ODX 2.2 specification
21
+ # document.)
22
+ PREDEFINED_SYSPARAM_VALUES = [
23
+ "TIMESTAMP", "SECOND", "MINUTE", "HOUR", "TIMEZONE", "DAY", "WEEK", "MONTH", "YEAR", "CENTURY",
24
+ "TESTERID", "USERID"
25
+ ]
26
+
10
27
 
11
28
  @dataclass
12
29
  class SystemParameter(ParameterWithDOP):
13
30
  sysparam: str
14
31
 
32
+ @staticmethod
33
+ @override
34
+ def from_et(et_element: ElementTree.Element,
35
+ doc_frags: List[OdxDocFragment]) -> "SystemParameter":
36
+
37
+ kwargs = dataclass_fields_asdict(ParameterWithDOP.from_et(et_element, doc_frags))
38
+
39
+ sysparam = odxrequire(et_element.get("SYSPARAM"))
40
+
41
+ return SystemParameter(sysparam=sysparam, **kwargs)
42
+
15
43
  @property
44
+ @override
16
45
  def parameter_type(self) -> ParameterType:
17
46
  return "SYSTEM"
18
47
 
19
48
  @property
49
+ @override
20
50
  def is_required(self) -> bool:
21
- raise NotImplementedError("SystemParameter.is_required is not implemented yet.")
51
+ # if a SYSTEM parameter is not specified explicitly, its value
52
+ # can be determined from the operating system if it is type is
53
+ # predefined
54
+ return self.sysparam not in PREDEFINED_SYSPARAM_VALUES
22
55
 
23
56
  @property
57
+ @override
24
58
  def is_settable(self) -> bool:
25
- raise NotImplementedError("SystemParameter.is_settable is not implemented yet.")
59
+ return True
26
60
 
27
- def get_coded_value_as_bytes(self, encode_state: EncodeState) -> bytes:
28
- raise NotImplementedError("Encoding a SystemParameter is not implemented yet.")
61
+ @override
62
+ def _encode_positioned_into_pdu(self, physical_value: Optional[ParameterValue],
63
+ encode_state: EncodeState) -> None:
64
+ if physical_value is None:
65
+ # determine the value to be encoded automatically
66
+ now = datetime.now()
67
+ if self.sysparam == "TIMESTAMP":
68
+ physical_value = round(now.timestamp() * 1000).to_bytes(8, "big")
69
+ elif self.sysparam == "SECOND":
70
+ physical_value = now.second
71
+ elif self.sysparam == "MINUTE":
72
+ physical_value = now.minute
73
+ elif self.sysparam == "HOUR":
74
+ physical_value = now.hour
75
+ elif self.sysparam == "TIMEZONE":
76
+ if (utc_offset := now.astimezone().utcoffset()) is not None:
77
+ physical_value = utc_offset.seconds // 60
78
+ else:
79
+ physical_value = 0
80
+ elif self.sysparam == "DAY":
81
+ physical_value = now.day
82
+ elif self.sysparam == "WEEK":
83
+ physical_value = now.isocalendar()[1]
84
+ elif self.sysparam == "MONTH":
85
+ physical_value = now.month
86
+ elif self.sysparam == "YEAR":
87
+ physical_value = now.year
88
+ elif self.sysparam == "CENTURY":
89
+ physical_value = now.year // 100
90
+ elif self.sysparam == "TESTERID":
91
+ physical_value = "odxtools".encode("latin1")
92
+ elif self.sysparam == "USERID":
93
+ physical_value = getpass.getuser().encode("latin1")
94
+ else:
95
+ odxraise(f"Unknown system parameter type '{self.sysparam}'")
96
+ physical_value = 0
29
97
 
30
- def decode_from_pdu(self, decode_state: DecodeState) -> ParameterValue:
31
- raise NotImplementedError("Decoding a SystemParameter is not implemented yet.")
98
+ self.dop.encode_into_pdu(physical_value, encode_state=encode_state)
@@ -1,32 +1,72 @@
1
1
  # SPDX-License-Identifier: MIT
2
2
  from dataclasses import dataclass
3
+ from typing import TYPE_CHECKING, List, Optional
4
+ from xml.etree import ElementTree
5
+
6
+ from typing_extensions import override
3
7
 
4
8
  from ..decodestate import DecodeState
5
9
  from ..encodestate import EncodeState
6
- from ..odxlink import OdxLinkRef
10
+ from ..exceptions import odxrequire
11
+ from ..odxlink import OdxDocFragment, OdxLinkDatabase, OdxLinkRef
7
12
  from ..odxtypes import ParameterValue
13
+ from ..utils import dataclass_fields_asdict
8
14
  from .parameter import Parameter, ParameterType
9
15
 
16
+ if TYPE_CHECKING:
17
+ from ..tablerow import TableRow
18
+
10
19
 
11
20
  @dataclass
12
21
  class TableEntryParameter(Parameter):
13
22
  target: str
14
23
  table_row_ref: OdxLinkRef
15
24
 
25
+ @staticmethod
26
+ @override
27
+ def from_et(et_element: ElementTree.Element,
28
+ doc_frags: List[OdxDocFragment]) -> "TableEntryParameter":
29
+
30
+ kwargs = dataclass_fields_asdict(Parameter.from_et(et_element, doc_frags))
31
+
32
+ target = odxrequire(et_element.findtext("TARGET"))
33
+ table_row_ref = odxrequire(OdxLinkRef.from_et(et_element.find("TABLE-ROW-REF"), doc_frags))
34
+
35
+ return TableEntryParameter(target=target, table_row_ref=table_row_ref, **kwargs)
36
+
37
+ @override
38
+ def _resolve_odxlinks(self, odxlinks: OdxLinkDatabase) -> None:
39
+ super()._resolve_odxlinks(odxlinks)
40
+
41
+ if TYPE_CHECKING:
42
+ self._table_row = odxlinks.resolve(self.table_row_ref, TableRow)
43
+ else:
44
+ self._table_row = odxlinks.resolve(self.table_row_ref)
45
+
16
46
  @property
47
+ @override
17
48
  def parameter_type(self) -> ParameterType:
18
49
  return "TABLE-ENTRY"
19
50
 
20
51
  @property
52
+ @override
21
53
  def is_required(self) -> bool:
22
- raise NotImplementedError("TableKeyParameter.is_required is not implemented yet.")
54
+ raise NotImplementedError("TableEntryParameter.is_required is not implemented yet.")
23
55
 
24
56
  @property
57
+ @override
25
58
  def is_settable(self) -> bool:
26
- raise NotImplementedError("TableKeyParameter.is_settable is not implemented yet.")
59
+ raise NotImplementedError("TableEntryParameter.is_settable is not implemented yet.")
27
60
 
28
- def get_coded_value_as_bytes(self, encode_state: EncodeState) -> bytes:
29
- raise NotImplementedError("Encoding a TableKeyParameter is not implemented yet.")
61
+ @override
62
+ def _encode_positioned_into_pdu(self, physical_value: Optional[ParameterValue],
63
+ encode_state: EncodeState) -> None:
64
+ raise NotImplementedError("Encoding a TableEntryParameter is not implemented yet.")
65
+
66
+ @property
67
+ def table_row(self) -> "TableRow":
68
+ return self._table_row
30
69
 
31
- def decode_from_pdu(self, decode_state: DecodeState) -> ParameterValue:
32
- raise NotImplementedError("Decoding a TableKeyParameter is not implemented yet.")
70
+ @override
71
+ def _decode_positioned_from_pdu(self, decode_state: DecodeState) -> ParameterValue:
72
+ raise NotImplementedError("Decoding a TableEntryParameter is not implemented yet.")
@@ -1,16 +1,20 @@
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 final, override
4
7
 
5
8
  from ..decodestate import DecodeState
6
9
  from ..encodestate import EncodeState
7
10
  from ..exceptions import DecodeError, EncodeError, odxraise, odxrequire
8
- from ..odxlink import OdxLinkDatabase, OdxLinkId, OdxLinkRef
11
+ from ..odxlink import OdxDocFragment, OdxLinkDatabase, OdxLinkId, OdxLinkRef, resolve_snref
9
12
  from ..odxtypes import ParameterValue
13
+ from ..snrefcontext import SnRefContext
14
+ from ..utils import dataclass_fields_asdict
10
15
  from .parameter import Parameter, ParameterType
11
16
 
12
17
  if TYPE_CHECKING:
13
- from ..diaglayer import DiagLayer
14
18
  from ..table import Table
15
19
  from ..tablerow import TableRow
16
20
 
@@ -24,17 +28,46 @@ class TableKeyParameter(Parameter):
24
28
  table_row_snref: Optional[str]
25
29
  table_row_ref: Optional[OdxLinkRef]
26
30
 
31
+ @staticmethod
32
+ @override
33
+ def from_et(et_element: ElementTree.Element,
34
+ doc_frags: List[OdxDocFragment]) -> "TableKeyParameter":
35
+
36
+ kwargs = dataclass_fields_asdict(Parameter.from_et(et_element, doc_frags))
37
+
38
+ odx_id = odxrequire(OdxLinkId.from_et(et_element, doc_frags))
39
+
40
+ table_ref = OdxLinkRef.from_et(et_element.find("TABLE-REF"), doc_frags)
41
+ table_snref = None
42
+ if (table_snref_elem := et_element.find("TABLE-SNREF")) is not None:
43
+ table_snref = odxrequire(table_snref_elem.get("SHORT-NAME"))
44
+
45
+ table_row_ref = OdxLinkRef.from_et(et_element.find("TABLE-ROW-REF"), doc_frags)
46
+ table_row_snref = None
47
+ if (table_row_snref_elem := et_element.find("TABLE-ROW-SNREF")) is not None:
48
+ table_row_snref = odxrequire(table_row_snref_elem.get("SHORT-NAME"))
49
+
50
+ return TableKeyParameter(
51
+ odx_id=odx_id,
52
+ table_ref=table_ref,
53
+ table_snref=table_snref,
54
+ table_row_ref=table_row_ref,
55
+ table_row_snref=table_row_snref,
56
+ **kwargs)
57
+
27
58
  def __post_init__(self) -> None:
28
- self._table: "Table"
29
- self._table_row: Optional["TableRow"] = None
59
+ self._table: Table
60
+ self._table_row: Optional[TableRow] = None
30
61
  if self.table_ref is None and self.table_snref is None and \
31
62
  self.table_row_ref is None and self.table_row_snref is None:
32
63
  odxraise("Either a table or a table row must be defined.")
33
64
 
34
65
  @property
66
+ @override
35
67
  def parameter_type(self) -> ParameterType:
36
68
  return "TABLE-KEY"
37
69
 
70
+ @override
38
71
  def _build_odxlinks(self) -> Dict[OdxLinkId, Any]:
39
72
  result = super()._build_odxlinks()
40
73
 
@@ -42,104 +75,161 @@ class TableKeyParameter(Parameter):
42
75
 
43
76
  return result
44
77
 
78
+ @override
45
79
  def _resolve_odxlinks(self, odxlinks: OdxLinkDatabase) -> None:
46
80
  super()._resolve_odxlinks(odxlinks)
47
81
 
48
82
  # Either table_ref or table_row_ref will be defined
49
- if self.table_ref:
83
+ if self.table_ref is not None:
50
84
  if TYPE_CHECKING:
51
85
  self._table = odxlinks.resolve(self.table_ref, Table)
52
86
  else:
53
87
  self._table = odxlinks.resolve(self.table_ref)
54
88
 
55
- if self.table_row_ref:
89
+ if self.table_row_ref is not None:
56
90
  if TYPE_CHECKING:
57
91
  self._table_row = odxlinks.resolve(self.table_row_ref, TableRow)
58
92
  else:
59
93
  self._table_row = odxlinks.resolve(self.table_row_ref)
60
- self._table = self._table_row.table
61
94
 
62
- def _resolve_snrefs(self, diag_layer: "DiagLayer") -> None:
63
- super()._resolve_snrefs(diag_layer)
95
+ if self.table_ref is None and self.table_snref is None:
96
+ if TYPE_CHECKING:
97
+ self._table = odxlinks.resolve(self._table_row.table_ref, Table)
98
+ else:
99
+ self._table = odxlinks.resolve(self._table_row.table_ref)
100
+
101
+ @override
102
+ def _resolve_snrefs(self, context: SnRefContext) -> None:
103
+ super()._resolve_snrefs(context)
64
104
 
65
105
  if self.table_snref is not None:
66
- ddd_spec = diag_layer.diag_data_dictionary_spec
67
- self._table = ddd_spec.tables[self.table_snref]
106
+ tables = odxrequire(context.diag_layer).diag_data_dictionary_spec.tables
107
+ if TYPE_CHECKING:
108
+ self._table = resolve_snref(self.table_snref, tables, Table)
109
+ else:
110
+ self._table = resolve_snref(self.table_snref, tables)
68
111
  if self.table_row_snref is not None:
69
112
  # make sure that we know the table to which the table row
70
113
  # SNREF is relative to.
71
114
  table = odxrequire(
72
- self._table, "If a table-row short name reference is defined, a "
73
- "table must also be specified.")
74
- self._table_row = table.table_rows[self.table_row_snref]
115
+ self._table, "If a table row is referenced via short name, a table must "
116
+ "be referenced as well")
117
+ if TYPE_CHECKING:
118
+ self._table_row = resolve_snref(self.table_row_snref, table.table_rows, TableRow)
119
+ else:
120
+ self._table_row = resolve_snref(self.table_row_snref, table.table_rows)
75
121
 
76
122
  @property
77
123
  def table(self) -> "Table":
78
- if self._table is not None:
79
- return self._table
80
- if self._table_row is not None:
81
- return self._table_row.table
82
- odxraise(f'Could not resolve the table of {self.short_name}')
124
+ return self._table
83
125
 
84
126
  @property
85
127
  def table_row(self) -> Optional["TableRow"]:
86
128
  return self._table_row
87
129
 
88
130
  @property
131
+ @override
89
132
  def is_required(self) -> bool:
90
133
  # TABLE-KEY parameters can be implicitly determined from the
91
134
  # corresponding TABLE-STRUCT
92
135
  return False
93
136
 
94
137
  @property
138
+ @override
95
139
  def is_settable(self) -> bool:
96
140
  return True
97
141
 
98
- def get_coded_value_as_bytes(self, encode_state: EncodeState) -> bytes:
99
- tr_short_name = encode_state.parameter_values.get(self.short_name)
142
+ @override
143
+ @final
144
+ def _encode_positioned_into_pdu(self, physical_value: Optional[ParameterValue],
145
+ encode_state: EncodeState) -> None:
146
+ # if you get this exception, you ought to use
147
+ # `.encode_placeholder_into_pdu()` followed by (after the
148
+ # value of the table key has been determined)
149
+ # `.encode_value_into_pdu()`.
150
+ raise RuntimeError("_encode_positioned_into_pdu() cannot be called for table keys.")
100
151
 
101
- if tr_short_name is None:
102
- # the table key has not been defined explicitly yet, but
103
- # it is most likely implicitly defined by the associated
104
- # TABLE-STRUCT parameters. Use all-zeros as a standin for
105
- # the real data...
152
+ def encode_placeholder_into_pdu(self, physical_value: Optional[ParameterValue],
153
+ encode_state: EncodeState) -> None:
154
+
155
+ if physical_value is not None:
106
156
  key_dop = self.table.key_dop
107
157
  if key_dop is None:
108
- raise EncodeError(f"Table '{self.table.short_name}' does not define "
109
- f"a KEY-DOP, but is used in TABLE-KEY parameter "
110
- f"'{self.short_name}'")
158
+ odxraise(
159
+ f"Table '{self.table.short_name}' does not define "
160
+ f"a KEY-DOP, but is used by TABLE-KEY parameter "
161
+ f"'{self.short_name}'", EncodeError)
162
+ return
163
+
164
+ if not isinstance(physical_value, str):
165
+ odxraise(f"Invalid type for for table key '{self.short_name}' specified. "
166
+ f"(expect name of table row.)")
167
+
168
+ tkv = encode_state.table_keys.get(self.short_name)
169
+ if tkv is not None and tkv != physical_value:
170
+ odxraise(f"Got conflicting values for table key {self.short_name}: "
171
+ f"{tkv} and {physical_value!r}")
172
+
173
+ encode_state.table_keys[self.short_name] = physical_value
111
174
 
112
- byte_len = (odxrequire(key_dop.get_static_bit_length()) + 7) // 8
113
- if self.bit_position is not None and self.bit_position > 0:
114
- byte_len += 1
175
+ pos = encode_state.cursor_byte_position
176
+ if self.byte_position is not None:
177
+ pos = encode_state.origin_byte_position + self.byte_position
178
+ encode_state.key_pos[self.short_name] = pos
179
+ encode_state.cursor_byte_position = pos
180
+ encode_state.cursor_bit_position = self.bit_position or 0
181
+
182
+ key_dop = self.table.key_dop
183
+ if key_dop is None:
184
+ odxraise(f"No KEY-DOP specified for table {self.table.short_name}")
185
+ return
186
+
187
+ sz = key_dop.get_static_bit_length()
188
+ if sz is None:
189
+ odxraise("The DOP of table key {self.short_name} must exhibit a fixed size.",
190
+ EncodeError)
191
+ return
192
+
193
+ # emplace a value of zero into the encode state, but pretend the bits not to be used
194
+ n = sz + encode_state.cursor_bit_position
195
+ tmp_val = b'\x00' * ((n + 7) // 8)
196
+ encode_state.emplace_bytes(tmp_val, obj_used_mask=tmp_val)
197
+
198
+ encode_state.cursor_bit_position = 0
199
+
200
+ def encode_value_into_pdu(self, encode_state: EncodeState) -> None:
115
201
 
116
- return bytes([0] * byte_len)
202
+ key_dop = self.table.key_dop
203
+ if key_dop is None:
204
+ odxraise(
205
+ f"Table '{self.table.short_name}' does not define "
206
+ f"a KEY-DOP, but is used by TABLE-KEY parameter "
207
+ f"'{self.short_name}'", EncodeError)
208
+ return
209
+
210
+ if self.short_name not in encode_state.table_keys:
211
+ odxraise(f"Table key {self.short_name} has not been defined before "
212
+ f"it is required.", EncodeError)
213
+ return
214
+ else:
215
+ tr_short_name = encode_state.table_keys[self.short_name]
117
216
 
118
- # the table key is known. We need to encode the associated DOP
119
- # into the PDU.
217
+ # We need to encode the table key using the associated DOP into the PDU.
120
218
  tr_candidates = [x for x in self.table.table_rows if x.short_name == tr_short_name]
121
219
  if len(tr_candidates) == 0:
122
- raise EncodeError(f"No table row with short name '{tr_short_name}' found")
220
+ odxraise(f"No table row with short name '{tr_short_name}' found", EncodeError)
221
+ return
123
222
  elif len(tr_candidates) > 1:
124
- raise EncodeError(f"Multiple rows exhibiting short name '{tr_short_name}'")
223
+ odxraise(f"Multiple rows exhibiting short name '{tr_short_name}'", EncodeError)
125
224
  tr = tr_candidates[0]
126
225
 
127
- key_dop = self.table.key_dop
128
- if key_dop is None:
129
- raise EncodeError(f"Table '{self.table.short_name}' does not define "
130
- f"a KEY-DOP, but is used in TABLE-KEY parameter "
131
- f"'{self.short_name}'")
132
- bit_position = 0 if self.bit_position is None else self.bit_position
133
- return key_dop.convert_physical_to_bytes(tr.key, encode_state, bit_position=bit_position)
134
-
135
- def encode_into_pdu(self, encode_state: EncodeState) -> bytes:
136
- return super().encode_into_pdu(encode_state)
226
+ encode_state.cursor_byte_position = encode_state.key_pos[self.short_name]
227
+ encode_state.cursor_bit_position = self.bit_position or 0
137
228
 
138
- def decode_from_pdu(self, decode_state: DecodeState) -> ParameterValue:
139
- orig_cursor = decode_state.cursor_byte_position
140
- if self.byte_position is not None:
141
- decode_state.cursor_byte_position = decode_state.origin_byte_position + self.byte_position
229
+ key_dop.encode_into_pdu(encode_state=encode_state, physical_value=odxrequire(tr.key))
142
230
 
231
+ @override
232
+ def _decode_positioned_from_pdu(self, decode_state: DecodeState) -> ParameterValue:
143
233
  if self.table_row is not None:
144
234
  # the table row to be used is statically specified -> no
145
235
  # need to decode anything!
@@ -147,7 +237,6 @@ class TableKeyParameter(Parameter):
147
237
  else:
148
238
  # Use DOP to decode
149
239
  key_dop = odxrequire(self.table.key_dop)
150
- decode_state.cursor_bit_position = self.bit_position or 0
151
240
  key_dop_val = key_dop.decode_from_pdu(decode_state)
152
241
 
153
242
  table_row_candidates = [x for x in self.table.table_rows if x.key == key_dop_val]
@@ -162,6 +251,4 @@ class TableKeyParameter(Parameter):
162
251
  # update the decode_state's table key
163
252
  decode_state.table_keys[self.short_name] = table_row
164
253
 
165
- decode_state.cursor_byte_position = max(decode_state.cursor_byte_position, orig_cursor)
166
-
167
254
  return phys_val