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,38 +1,65 @@
1
1
  # SPDX-License-Identifier: MIT
2
- import warnings
3
2
  from dataclasses import dataclass
4
- from typing import TYPE_CHECKING, Any, Dict, List, Optional
3
+ from typing import Any, Dict, List, Optional
4
+ from xml.etree import ElementTree
5
5
 
6
+ from typing_extensions import override
7
+
8
+ from ..createanydiagcodedtype import create_any_diag_coded_type_from_et
6
9
  from ..decodestate import DecodeState
7
10
  from ..diagcodedtype import DiagCodedType
8
11
  from ..encodestate import EncodeState
9
- from ..exceptions import DecodeError, EncodeError
10
- from ..odxlink import OdxLinkDatabase, OdxLinkId
11
- from ..odxtypes import AtomicOdxType, DataType
12
+ from ..exceptions import DecodeMismatch, EncodeError, odxraise, odxrequire
13
+ from ..odxlink import OdxDocFragment, OdxLinkId
14
+ from ..odxtypes import AtomicOdxType, DataType, ParameterValue
15
+ from ..utils import dataclass_fields_asdict
12
16
  from .parameter import Parameter, ParameterType
13
17
 
14
- if TYPE_CHECKING:
15
- from ..diaglayer import DiagLayer
16
-
17
18
 
18
19
  @dataclass
19
20
  class NrcConstParameter(Parameter):
20
- """A param of type NRC-CONST defines a set of values to be matched.
21
-
22
- An NRC-CONST can only be used in a negative response.
23
- Its encoding behaviour is similar to a VALUE parameter with a TEXTTABLE.
24
- However, an NRC-CONST is used for matching a response (similar to a CODED-CONST).
21
+ """A parameter of type NRC-CONST defines a set of values to be
22
+ matched for a negative response object to apply
23
+
24
+ The behaviour of NRC-CONST parameters is similar to CODED-CONST
25
+ parameters in that they allow to specify which coding objects
26
+ apply to a binary string, but in contrast to CODED-CONST
27
+ parameters they allow to specify multiple values. Thus, the value
28
+ of a CODED-CONST parameter is usually set using an overlapping
29
+ VALUE parameter. Since NRC-CONST parameters can only be specified
30
+ for negative responses, they can thus be regarded as a multiplexer
31
+ mechanism that is specific to negative responses.
25
32
 
26
33
  See ASAM MCD-2 D (ODX), p. 77-79.
34
+
27
35
  """
28
36
 
29
37
  diag_coded_type: DiagCodedType
30
38
  coded_values: List[AtomicOdxType]
31
39
 
40
+ @staticmethod
41
+ @override
42
+ def from_et(et_element: ElementTree.Element,
43
+ doc_frags: List[OdxDocFragment]) -> "NrcConstParameter":
44
+
45
+ kwargs = dataclass_fields_asdict(Parameter.from_et(et_element, doc_frags))
46
+
47
+ dct_elem = odxrequire(et_element.find("DIAG-CODED-TYPE"))
48
+ diag_coded_type = create_any_diag_coded_type_from_et(dct_elem, doc_frags)
49
+ coded_values = [
50
+ diag_coded_type.base_data_type.from_string(odxrequire(val.text))
51
+ for val in et_element.iterfind("CODED-VALUES/CODED-VALUE")
52
+ ]
53
+
54
+ return NrcConstParameter(
55
+ diag_coded_type=diag_coded_type, coded_values=coded_values, **kwargs)
56
+
32
57
  @property
58
+ @override
33
59
  def parameter_type(self) -> ParameterType:
34
60
  return "NRC-CONST"
35
61
 
62
+ @override
36
63
  def _build_odxlinks(self) -> Dict[OdxLinkId, Any]:
37
64
  result = super()._build_odxlinks()
38
65
 
@@ -40,12 +67,7 @@ class NrcConstParameter(Parameter):
40
67
 
41
68
  return result
42
69
 
43
- def _resolve_odxlinks(self, odxlinks: OdxLinkDatabase) -> None:
44
- super()._resolve_odxlinks(odxlinks)
45
-
46
- def _resolve_snrefs(self, diag_layer: "DiagLayer") -> None:
47
- super()._resolve_snrefs(diag_layer)
48
-
70
+ @override
49
71
  def get_static_bit_length(self) -> Optional[int]:
50
72
  return self.diag_coded_type.get_static_bit_length()
51
73
 
@@ -54,52 +76,52 @@ class NrcConstParameter(Parameter):
54
76
  return self.diag_coded_type.base_data_type
55
77
 
56
78
  @property
79
+ @override
57
80
  def is_required(self) -> bool:
58
81
  return False
59
82
 
60
83
  @property
84
+ @override
61
85
  def is_settable(self) -> bool:
62
86
  return False
63
87
 
64
- def get_coded_value_as_bytes(self, encode_state: EncodeState) -> bytes:
65
- if self.short_name in encode_state.parameter_values:
66
- if encode_state.parameter_values[self.short_name] not in self.coded_values:
67
- raise EncodeError(f"The parameter '{self.short_name}' must have"
68
- f" one of the constant values {self.coded_values}")
69
- else:
70
- coded_value = encode_state.parameter_values[self.short_name]
71
- else:
72
- # If the user does not select one, just select any.
73
- # I think it does not matter ...
74
- coded_value = self.coded_values[0]
75
-
76
- bit_position_int = self.bit_position if self.bit_position is not None else 0
77
- return self.diag_coded_type.convert_internal_to_bytes(
78
- coded_value, encode_state, bit_position=bit_position_int)
79
-
80
- def decode_from_pdu(self, decode_state: DecodeState) -> AtomicOdxType:
81
- orig_cursor = decode_state.cursor_byte_position
82
- if self.byte_position is not None:
83
- # Update cursor position
84
- decode_state.cursor_byte_position = decode_state.origin_byte_position + self.byte_position
85
-
86
- # Extract coded values
87
- decode_state.cursor_bit_position = self.bit_position or 0
88
+ @override
89
+ def _encode_positioned_into_pdu(self, physical_value: Optional[ParameterValue],
90
+ encode_state: EncodeState) -> None:
91
+ # NRC-CONST parameters are not encoding any value on its
92
+ # own. instead, it is supposed to overlap with a value
93
+ # parameter.
94
+ if physical_value is not None:
95
+ odxraise("The value of NRC-CONST parameters cannot be set directly!", EncodeError)
96
+
97
+ # TODO (?): extract the parameter and check if it is one of
98
+ # the values of self.coded_values. if not, throw an
99
+ # EncodeMismatch exception! This is probably a bad idea
100
+ # because the parameter which determines the value of the
101
+ # NRC-CONST might possibly be specified after the NRC-CONST.
102
+
103
+ # move the cursor forward by the size of the parameter
104
+ bit_pos = encode_state.cursor_bit_position
105
+ bit_len = self.diag_coded_type.get_static_bit_length()
106
+
107
+ if bit_len is None:
108
+ odxraise("The diag coded type of NRC-CONST parameters must "
109
+ "exhibit a static size")
110
+ return
111
+
112
+ encode_state.cursor_byte_position += (bit_pos + bit_len + 7) // 8
113
+ encode_state.cursor_bit_position = 0
114
+
115
+ encode_state.emplace_bytes(b'', self.short_name)
116
+
117
+ @override
118
+ def _decode_positioned_from_pdu(self, decode_state: DecodeState) -> AtomicOdxType:
119
+ # Extract coded value
88
120
  coded_value = self.diag_coded_type.decode_from_pdu(decode_state)
89
121
 
90
122
  # Check if the coded value in the message is correct.
91
123
  if coded_value not in self.coded_values:
92
- warnings.warn(
93
- f"Coded constant parameter does not match! "
94
- f"The parameter {self.short_name} expected a coded "
95
- f"value in {str(self.coded_values)} but got {str(coded_value)} "
96
- f"at byte position {decode_state.cursor_byte_position} "
97
- f"in coded message {decode_state.coded_message.hex()}.",
98
- DecodeError,
99
- stacklevel=1,
100
- )
101
-
102
- decode_state.cursor_byte_position = max(decode_state.cursor_byte_position, orig_cursor)
124
+ raise DecodeMismatch(f"NRC-CONST parameter {self.short_name} does not apply")
103
125
 
104
126
  return coded_value
105
127
 
@@ -1,17 +1,18 @@
1
1
  # SPDX-License-Identifier: MIT
2
- import abc
3
2
  from dataclasses import dataclass
4
- from typing import TYPE_CHECKING, Any, Dict, List, Literal, Optional
3
+ from typing import Any, Dict, List, Literal, Optional
4
+ from xml.etree import ElementTree
5
+
6
+ from typing_extensions import final, override
5
7
 
6
8
  from ..decodestate import DecodeState
7
9
  from ..element import NamedElement
8
10
  from ..encodestate import EncodeState
9
- from ..odxlink import OdxLinkDatabase, OdxLinkId
11
+ from ..odxlink import OdxDocFragment, OdxLinkDatabase, OdxLinkId
10
12
  from ..odxtypes import ParameterValue
13
+ from ..snrefcontext import SnRefContext
11
14
  from ..specialdatagroup import SpecialDataGroup
12
-
13
- if TYPE_CHECKING:
14
- from ..diaglayer import DiagLayer
15
+ from ..utils import dataclass_fields_asdict
15
16
 
16
17
  ParameterType = Literal[
17
18
  "CODED-CONST",
@@ -30,12 +31,49 @@ ParameterType = Literal[
30
31
 
31
32
 
32
33
  @dataclass
33
- class Parameter(NamedElement, abc.ABC):
34
+ class Parameter(NamedElement):
35
+ """This class corresponds to POSITIONABLE-PARAM in the ODX
36
+ specification
37
+
38
+ All parameter classes must adhere to the `Codec` type protocol, so
39
+ `isinstance(param, Codec)` ought to be true. Be aware that, even
40
+ though the ODX specification seems to make the distinction of
41
+ "positionable" and "normal" parameters, it does not define any
42
+ non-positionable parameter types.
43
+
44
+ """
45
+ oid: Optional[str]
34
46
  byte_position: Optional[int]
35
47
  bit_position: Optional[int]
36
48
  semantic: Optional[str]
37
49
  sdgs: List[SpecialDataGroup]
38
50
 
51
+ @staticmethod
52
+ @override
53
+ def from_et(et_element: ElementTree.Element, doc_frags: List[OdxDocFragment]) -> "Parameter":
54
+
55
+ kwargs = dataclass_fields_asdict(NamedElement.from_et(et_element, doc_frags))
56
+
57
+ oid = et_element.get("OID")
58
+ semantic = et_element.get("SEMANTIC")
59
+ sdgs = [
60
+ SpecialDataGroup.from_et(sdge, doc_frags) for sdge in et_element.iterfind("SDGS/SDG")
61
+ ]
62
+
63
+ byte_position_str = et_element.findtext("BYTE-POSITION")
64
+ bit_position_str = et_element.findtext("BIT-POSITION")
65
+
66
+ byte_position = int(byte_position_str) if byte_position_str is not None else None
67
+ bit_position = int(bit_position_str) if bit_position_str is not None else None
68
+
69
+ return Parameter(
70
+ oid=oid,
71
+ byte_position=byte_position,
72
+ bit_position=bit_position,
73
+ semantic=semantic,
74
+ sdgs=sdgs,
75
+ **kwargs)
76
+
39
77
  def _build_odxlinks(self) -> Dict[OdxLinkId, Any]:
40
78
  result = {}
41
79
 
@@ -48,14 +86,14 @@ class Parameter(NamedElement, abc.ABC):
48
86
  for sdg in self.sdgs:
49
87
  sdg._resolve_odxlinks(odxlinks)
50
88
 
51
- def _resolve_snrefs(self, diag_layer: "DiagLayer") -> None:
89
+ def _resolve_snrefs(self, context: SnRefContext) -> None:
52
90
  for sdg in self.sdgs:
53
- sdg._resolve_snrefs(diag_layer)
91
+ sdg._resolve_snrefs(context)
54
92
 
55
93
  @property
56
- @abc.abstractmethod
57
94
  def parameter_type(self) -> ParameterType:
58
- pass
95
+ raise NotImplementedError(
96
+ ".parameter_type is not implemented by the concrete parameter class")
59
97
 
60
98
  def get_static_bit_length(self) -> Optional[int]:
61
99
  return None
@@ -70,7 +108,7 @@ class Parameter(NamedElement, abc.ABC):
70
108
  specified.
71
109
 
72
110
  """
73
- raise NotImplementedError
111
+ raise NotImplementedError(".is_required is not implemented by the concrete parameter class")
74
112
 
75
113
  @property
76
114
  def is_settable(self) -> bool:
@@ -81,78 +119,64 @@ class Parameter(NamedElement, abc.ABC):
81
119
  have a default value are settable but not required to be
82
120
  specified.
83
121
  """
84
- raise NotImplementedError
122
+ raise NotImplementedError(".is_settable is not implemented by the concrete parameter class")
85
123
 
86
- @abc.abstractmethod
87
- def get_coded_value_as_bytes(self, encode_state: EncodeState) -> bytes:
88
- """Get the coded value of the parameter given the encode state.
89
- Note that this method is called by `encode_into_pdu`.
90
- """
91
- pass
124
+ @final
125
+ def encode_into_pdu(self, physical_value: Optional[ParameterValue],
126
+ encode_state: EncodeState) -> None:
127
+ """Convert a physical value into its encoded form and place it
128
+ into the PDU
129
+
130
+ Also, adapt the `encode_state` so that it points to where the
131
+ next parameter is located (if the next parameter does not
132
+ explicitly specify a position)
92
133
 
93
- @abc.abstractmethod
94
- def decode_from_pdu(self, decode_state: DecodeState) -> ParameterValue:
95
- """Decode the parameter value from the coded message.
96
-
97
- If the parameter does have a byte position property, the coded bytes the parameter covers are extracted
98
- at this byte position and the function parameter `default_byte_position` is ignored.
99
-
100
- If the parameter does not have a byte position and a byte position is passed,
101
- the bytes are extracted at the byte position given by the argument `default_byte_position`.
102
-
103
- If the parameter does not have a byte position and the argument `default_byte_position` is None,
104
- this function throws a `DecodeError`.
105
-
106
- Parameters
107
- ----------
108
- decode_state : DecodeState
109
- The decoding state containing
110
- * the byte message to be decoded
111
- * the parameter values that are already decoded
112
- * the next byte position that is used iff the parameter does not specify a byte position
113
-
114
- Returns
115
- -------
116
- ParameterValuePair | List[ParameterValuePair]
117
- the decoded parameter value (the type is defined by the DOP)
118
- int
119
- the next byte position after the extracted parameter
120
134
  """
121
- pass
122
135
 
123
- def encode_into_pdu(self, encode_state: EncodeState) -> bytes:
124
- """Encode the value of a parameter into a binary blob and return it
136
+ if self.byte_position is not None:
137
+ encode_state.cursor_byte_position = encode_state.origin_byte_position + self.byte_position
138
+
139
+ encode_state.cursor_bit_position = self.bit_position or 0
140
+
141
+ self._encode_positioned_into_pdu(physical_value, encode_state)
125
142
 
126
- If the byte position of the parameter is not defined,
127
- the byte code is appended to the blob.
143
+ encode_state.cursor_bit_position = 0
128
144
 
129
- Technical note for subclasses: The default implementation
130
- tries to compute the coded value via
131
- `self.get_coded_value_as_bytes(encoded_state)` and inserts it
132
- into the PDU. Thus it usually suffices to overwrite
133
- `get_coded_value_as_bytes()` instead of `encode_into_pdu()`.
145
+ def _encode_positioned_into_pdu(self, physical_value: Optional[ParameterValue],
146
+ encode_state: EncodeState) -> None:
147
+ """Method which actually encodes the parameter
134
148
 
135
- Parameters:
136
- ----------
137
- encode_state: EncodeState, i.e. a named tuple with attributes
138
- * coded_message: bytes, the message encoded so far
139
- * parameter_values: List[ParameterValuePairs]
140
- * triggering_coded_request: bytes
149
+ Its location is managed by `Parameter`."""
150
+ raise NotImplementedError(
151
+ f"Required method '_encode_positioned_into_pdu()' not implemented by "
152
+ f"child class {type(self).__name__}")
153
+
154
+ @final
155
+ def decode_from_pdu(self, decode_state: DecodeState) -> ParameterValue:
156
+ """Retrieve the raw data for the parameter from the PDU and
157
+ convert it to its physical interpretation
141
158
 
142
- Returns:
143
- -------
144
- bytes
145
- the message's blob after adding the encoded parameter into it
159
+ Also, adapt the `encode_state` so that it points to where the
160
+ next parameter is located (if the next parameter does not
161
+ explicitly specify a position)
146
162
 
147
163
  """
148
- msg_blob = encode_state.coded_message
149
- param_blob = self.get_coded_value_as_bytes(encode_state)
150
164
 
151
165
  if self.byte_position is not None:
152
- byte_position = self.byte_position
153
- else:
154
- byte_position = len(msg_blob)
166
+ decode_state.cursor_byte_position = decode_state.origin_byte_position + self.byte_position
167
+
168
+ decode_state.cursor_bit_position = self.bit_position or 0
169
+
170
+ result = self._decode_positioned_from_pdu(decode_state)
171
+
172
+ decode_state.cursor_bit_position = 0
173
+
174
+ return result
155
175
 
156
- encode_state.emplace_atomic_value(param_blob, self.short_name, byte_position)
176
+ def _decode_positioned_from_pdu(self, decode_state: DecodeState) -> ParameterValue:
177
+ """Method which actually decodes the parameter
157
178
 
158
- return encode_state.coded_message
179
+ Its location is managed by `Parameter`."""
180
+ raise NotImplementedError(
181
+ f"Required method '_decode_positioned_from_pdu()' not implemented by "
182
+ f"child class {type(self).__name__}")
@@ -1,6 +1,9 @@
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 Any, Dict, List, Optional, cast
4
+ from xml.etree import ElementTree
5
+
6
+ from typing_extensions import override
4
7
 
5
8
  from ..dataobjectproperty import DataObjectProperty
6
9
  from ..decodestate import DecodeState
@@ -8,53 +11,65 @@ from ..dopbase import DopBase
8
11
  from ..dtcdop import DtcDop
9
12
  from ..encodestate import EncodeState
10
13
  from ..exceptions import odxassert, odxrequire
11
- from ..odxlink import OdxLinkDatabase, OdxLinkId, OdxLinkRef
12
- from ..odxtypes import ParameterValue
14
+ from ..odxlink import OdxDocFragment, OdxLinkDatabase, OdxLinkId, OdxLinkRef, resolve_snref
15
+ from ..odxtypes import AtomicOdxType, ParameterValue
13
16
  from ..physicaltype import PhysicalType
17
+ from ..snrefcontext import SnRefContext
18
+ from ..utils import dataclass_fields_asdict
14
19
  from .parameter import Parameter
15
20
 
16
- if TYPE_CHECKING:
17
- from ..diaglayer import DiagLayer
18
-
19
21
 
20
22
  @dataclass
21
23
  class ParameterWithDOP(Parameter):
22
24
  dop_ref: Optional[OdxLinkRef]
23
25
  dop_snref: Optional[str]
24
26
 
27
+ @staticmethod
28
+ @override
29
+ def from_et(et_element: ElementTree.Element,
30
+ doc_frags: List[OdxDocFragment]) -> "ParameterWithDOP":
31
+
32
+ kwargs = dataclass_fields_asdict(Parameter.from_et(et_element, doc_frags))
33
+
34
+ dop_ref = OdxLinkRef.from_et(et_element.find("DOP-REF"), doc_frags)
35
+ dop_snref = None
36
+ if (dop_snref_elem := et_element.find("DOP-SNREF")) is not None:
37
+ dop_snref = odxrequire(dop_snref_elem.get("SHORT-NAME"))
38
+
39
+ return ParameterWithDOP(dop_ref=dop_ref, dop_snref=dop_snref, **kwargs)
40
+
25
41
  def __post_init__(self) -> None:
26
42
  odxassert(self.dop_snref is not None or self.dop_ref is not None,
27
43
  f"Param {self.short_name} without a DOP-(SN)REF should not exist!")
28
- self._dop: Optional[DopBase] = None
44
+ self._dop: DopBase
29
45
 
46
+ @override
30
47
  def _build_odxlinks(self) -> Dict[OdxLinkId, Any]:
31
48
  return super()._build_odxlinks()
32
49
 
50
+ @override
33
51
  def _resolve_odxlinks(self, odxlinks: OdxLinkDatabase) -> None:
34
52
  super()._resolve_odxlinks(odxlinks)
35
53
 
36
54
  if self.dop_ref is not None:
37
55
  odxassert(self.dop_snref is None)
38
- # TODO: do not do lenient resolves here. The problem is
39
- # that currently not all kinds of DOPs are internalized
40
- # (e.g., static and dynamic fields)
41
- self._dop = odxlinks.resolve_lenient(self.dop_ref)
56
+ self._dop = odxlinks.resolve(self.dop_ref)
42
57
 
43
- def _resolve_snrefs(self, diag_layer: "DiagLayer") -> None:
44
- super()._resolve_snrefs(diag_layer)
58
+ @override
59
+ def _resolve_snrefs(self, context: SnRefContext) -> None:
60
+ super()._resolve_snrefs(context)
45
61
 
46
62
  if self.dop_snref:
47
- ddds = diag_layer.diag_data_dictionary_spec
48
- self._dop = odxrequire(ddds.all_data_object_properties.get(self.dop_snref))
63
+ ddds = odxrequire(context.diag_layer).diag_data_dictionary_spec
64
+ self._dop = resolve_snref(self.dop_snref, ddds.all_data_object_properties, DopBase)
49
65
 
50
66
  @property
51
67
  def dop(self) -> DopBase:
52
- """may be a DataObjectProperty, a Structure or None"""
68
+ """This is usually a DataObjectProperty or a Structure object"""
53
69
 
54
- return odxrequire(
55
- self._dop, "Specifying a data object property is mandatory but it "
56
- "could not be resolved")
70
+ return self._dop
57
71
 
72
+ @override
58
73
  def get_static_bit_length(self) -> Optional[int]:
59
74
  if self._dop is not None:
60
75
  return self._dop.get_static_bit_length()
@@ -68,23 +83,11 @@ class ParameterWithDOP(Parameter):
68
83
  else:
69
84
  return None
70
85
 
71
- def get_coded_value_as_bytes(self, encode_state: EncodeState) -> bytes:
72
- dop = odxrequire(self.dop, "Reference to DOP is not resolved")
73
- physical_value = encode_state.parameter_values[self.short_name]
74
- bit_position_int = self.bit_position if self.bit_position is not None else 0
75
- return dop.convert_physical_to_bytes(
76
- physical_value, encode_state, bit_position=bit_position_int)
77
-
78
- def decode_from_pdu(self, decode_state: DecodeState) -> ParameterValue:
79
- orig_cursor = decode_state.cursor_byte_position
80
- if self.byte_position is not None:
81
- decode_state.cursor_byte_position = decode_state.origin_byte_position + self.byte_position
82
-
83
- decode_state.cursor_bit_position = self.bit_position or 0
84
-
85
- # Use DOP to decode
86
- phys_val = self.dop.decode_from_pdu(decode_state)
87
-
88
- decode_state.cursor_byte_position = max(orig_cursor, decode_state.cursor_byte_position)
86
+ @override
87
+ def _encode_positioned_into_pdu(self, physical_value: Optional[ParameterValue],
88
+ encode_state: EncodeState) -> None:
89
+ self.dop.encode_into_pdu(cast(AtomicOdxType, physical_value), encode_state)
89
90
 
90
- return phys_val
91
+ @override
92
+ def _decode_positioned_from_pdu(self, decode_state: DecodeState) -> ParameterValue:
93
+ return self.dop.decode_from_pdu(decode_state)
@@ -1,37 +1,55 @@
1
1
  # SPDX-License-Identifier: MIT
2
2
  from dataclasses import dataclass
3
- from typing import TYPE_CHECKING, Any, Dict
3
+ from typing import 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 ..decodestate import DecodeState
7
10
  from ..encodestate import EncodeState
8
- from ..exceptions import DecodeError, odxraise, odxrequire
9
- from ..odxlink import OdxLinkDatabase, OdxLinkId
11
+ from ..exceptions import DecodeError, EncodeError, odxraise, odxrequire
12
+ from ..odxlink import OdxDocFragment, OdxLinkDatabase, OdxLinkId
10
13
  from ..odxtypes import ParameterValue
14
+ from ..snrefcontext import SnRefContext
15
+ from ..utils import dataclass_fields_asdict
11
16
  from .parameter import ParameterType
12
17
  from .parameterwithdop import ParameterWithDOP
13
18
 
14
- if TYPE_CHECKING:
15
- from ..diaglayer import DiagLayer
16
-
17
19
 
18
20
  @dataclass
19
21
  class PhysicalConstantParameter(ParameterWithDOP):
20
22
 
21
23
  physical_constant_value_raw: str
22
24
 
25
+ @staticmethod
26
+ @override
27
+ def from_et(et_element: ElementTree.Element,
28
+ doc_frags: List[OdxDocFragment]) -> "PhysicalConstantParameter":
29
+
30
+ kwargs = dataclass_fields_asdict(ParameterWithDOP.from_et(et_element, doc_frags))
31
+
32
+ physical_constant_value_raw = odxrequire(et_element.findtext("PHYS-CONSTANT-VALUE"))
33
+
34
+ return PhysicalConstantParameter(
35
+ physical_constant_value_raw=physical_constant_value_raw, **kwargs)
36
+
23
37
  @property
38
+ @override
24
39
  def parameter_type(self) -> ParameterType:
25
40
  return "PHYS-CONST"
26
41
 
42
+ @override
27
43
  def _build_odxlinks(self) -> Dict[OdxLinkId, Any]:
28
44
  return super()._build_odxlinks()
29
45
 
46
+ @override
30
47
  def _resolve_odxlinks(self, odxlinks: OdxLinkDatabase) -> None:
31
48
  super()._resolve_odxlinks(odxlinks)
32
49
 
33
- def _resolve_snrefs(self, diag_layer: "DiagLayer") -> None:
34
- super()._resolve_snrefs(diag_layer)
50
+ @override
51
+ def _resolve_snrefs(self, context: SnRefContext) -> None:
52
+ super()._resolve_snrefs(context)
35
53
 
36
54
  dop = odxrequire(self.dop)
37
55
  if not isinstance(dop, DataObjectProperty):
@@ -44,28 +62,30 @@ class PhysicalConstantParameter(ParameterWithDOP):
44
62
  return self._physical_constant_value
45
63
 
46
64
  @property
65
+ @override
47
66
  def is_required(self) -> bool:
48
67
  return False
49
68
 
50
69
  @property
70
+ @override
51
71
  def is_settable(self) -> bool:
52
72
  return False
53
73
 
54
- def get_coded_value_as_bytes(self, encode_state: EncodeState) -> bytes:
55
- dop = odxrequire(self.dop, "Reference to DOP is not resolved")
56
- if (self.short_name in encode_state.parameter_values and
57
- encode_state.parameter_values[self.short_name] != self.physical_constant_value):
58
- raise TypeError(
59
- f"The parameter '{self.short_name}' is constant {self.physical_constant_value!r}"
60
- f" and thus can not be changed.")
74
+ @override
75
+ def _encode_positioned_into_pdu(self, physical_value: Optional[ParameterValue],
76
+ encode_state: EncodeState) -> None:
77
+ if physical_value is not None and physical_value != self.physical_constant_value:
78
+ odxraise(
79
+ f"Value for constant parameter `{self.short_name}` name can "
80
+ f"only be specified as {self.physical_constant_value!r} (is: {physical_value!r})",
81
+ EncodeError)
61
82
 
62
- bit_position_int = self.bit_position if self.bit_position is not None else 0
63
- return dop.convert_physical_to_bytes(
64
- self.physical_constant_value, encode_state, bit_position=bit_position_int)
83
+ self.dop.encode_into_pdu(self.physical_constant_value, encode_state)
65
84
 
66
- def decode_from_pdu(self, decode_state: DecodeState) -> ParameterValue:
85
+ @override
86
+ def _decode_positioned_from_pdu(self, decode_state: DecodeState) -> ParameterValue:
67
87
  # Decode value
68
- phys_val = super().decode_from_pdu(decode_state)
88
+ phys_val = super()._decode_positioned_from_pdu(decode_state)
69
89
 
70
90
  # Check if decoded value matches expected value
71
91
  if phys_val != self.physical_constant_value:
@@ -75,4 +95,5 @@ class PhysicalConstantParameter(ParameterWithDOP):
75
95
  f"{self.physical_constant_value!r} but got {phys_val!r} "
76
96
  f"at byte position {decode_state.cursor_byte_position} "
77
97
  f"in coded message {decode_state.coded_message.hex()}.", DecodeError)
98
+
78
99
  return phys_val