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
odxtools/odxtypes.py CHANGED
@@ -1,6 +1,7 @@
1
1
  # SPDX-License-Identifier: MIT
2
2
  from enum import Enum
3
- from typing import TYPE_CHECKING, Any, Callable, Dict, List, Optional, Tuple, Type, Union, overload
3
+ from typing import (TYPE_CHECKING, Any, Callable, Dict, Iterable, Optional, Tuple, Type, Union,
4
+ overload)
4
5
  from xml.etree import ElementTree
5
6
 
6
7
  from .exceptions import odxassert, odxraise, odxrequire
@@ -28,7 +29,7 @@ ParameterDict = Dict[str, Union["Parameter", "ParameterDict"]]
28
29
  # multiple items, so this can be a list of objects.
29
30
  TableStructParameterValue = Tuple[str, "ParameterValue"]
30
31
  ParameterValue = Union[AtomicOdxType, "ParameterValueDict", TableStructParameterValue,
31
- List["ParameterValue"], "DiagnosticTroubleCode"]
32
+ Iterable["ParameterValue"], "DiagnosticTroubleCode"]
32
33
  ParameterValueDict = Dict[str, ParameterValue]
33
34
 
34
35
 
@@ -63,8 +64,8 @@ def bool_to_odxstr(bool_val: bool) -> str:
63
64
 
64
65
  def parse_int(value: str) -> int:
65
66
  try:
66
- return int(value)
67
- except ValueError:
67
+ return int(value, 0)
68
+ except (ValueError, TypeError):
68
69
  try:
69
70
  v = float(value)
70
71
  except Exception as e:
@@ -102,6 +103,71 @@ _ODX_TYPE_TO_PYTHON_TYPE: Dict[str, Type[AtomicOdxType]] = {
102
103
  }
103
104
 
104
105
 
106
+ def compare_odx_values(a: AtomicOdxType, b: AtomicOdxType) -> int:
107
+ # this function implements the comparison according to the ODX
108
+ # specification. (cf section 7.3.6.5)
109
+
110
+ # numeric values are compared numerically (duh!)
111
+ if isinstance(a, (int, float)):
112
+ if not isinstance(b, (int, float)):
113
+ odxraise()
114
+
115
+ tmp = a - b
116
+ if tmp < 0:
117
+ return -1
118
+ elif tmp > 0:
119
+ return 1
120
+ return 0
121
+
122
+ # strings are compared lexicographically. (the spec only allows
123
+ # equals, but this cannot easily implemented using a single
124
+ # comparison function.
125
+ if isinstance(a, str):
126
+ if not isinstance(b, str):
127
+ odxraise()
128
+
129
+ if a < b:
130
+ return -1
131
+ elif b < a:
132
+ return 1
133
+ else:
134
+ return 0
135
+
136
+ # bytefields are treated like long integers: to pad the shorter
137
+ # object with zeros and treat the results like strings.
138
+ if isinstance(a, (bytes, bytearray)):
139
+ if not isinstance(b, (bytes, bytearray)):
140
+ odxraise()
141
+
142
+ obj_len = max(len(a), len(b))
143
+
144
+ tmp_a = a.ljust(obj_len, b'\x00')
145
+ tmp_b = b.ljust(obj_len, b'\x00')
146
+
147
+ if tmp_a > tmp_b:
148
+ return 1
149
+ elif tmp_a < tmp_b:
150
+ return -1
151
+ else:
152
+ return 0
153
+
154
+ odxraise(f"Unhandled comparsion between objects of type {type(a).__name__} "
155
+ f"and {type(b).__name__}")
156
+
157
+
158
+ # format specifiers for the data type using the bitstruct module
159
+ _BITSTRUCT_FORMAT_LETTER_MAP__ = {
160
+ "A_INT32": "s",
161
+ "A_UINT32": "u",
162
+ "A_FLOAT32": "f",
163
+ "A_FLOAT64": "f",
164
+ "A_BYTEFIELD": "r",
165
+ "A_UNICODE2STRING": "r", # UTF-16 strings must be converted explicitly
166
+ "A_ASCIISTRING": "r",
167
+ "A_UTF8STRING": "r",
168
+ }
169
+
170
+
105
171
  class DataType(Enum):
106
172
  """Types for the physical and internal value.
107
173
 
@@ -125,9 +191,14 @@ class DataType(Enum):
125
191
  A_ASCIISTRING = "A_ASCIISTRING"
126
192
  A_UTF8STRING = "A_UTF8STRING"
127
193
 
128
- def as_python_type(self) -> type:
194
+ @property
195
+ def python_type(self) -> Type[AtomicOdxType]:
129
196
  return _ODX_TYPE_TO_PYTHON_TYPE[self.value]
130
197
 
198
+ @property
199
+ def bitstruct_format_letter(self) -> str:
200
+ return _BITSTRUCT_FORMAT_LETTER_MAP__[self.value]
201
+
131
202
  def from_string(self, value: str) -> AtomicOdxType:
132
203
  return _PARSE_ODX_TYPE[self.value](value)
133
204
 
@@ -158,15 +229,29 @@ class DataType(Enum):
158
229
  return self.from_string(value)
159
230
  else:
160
231
  # regular type cast of python objects
161
- return self.as_python_type()(value)
232
+ return self.python_type(value)
162
233
 
163
234
  def isinstance(self, value: Any) -> bool:
164
- expected_type = self.as_python_type()
235
+ expected_type = self.python_type
165
236
  if isinstance(value, expected_type):
166
237
  return True
167
- elif expected_type == float and isinstance(value, (int, float)):
238
+ elif expected_type is float and isinstance(value, (int, float)):
168
239
  return True
169
240
  elif self == DataType.A_BYTEFIELD and isinstance(value, (bytearray, bytes)):
170
241
  return True
171
242
  else:
172
243
  return False
244
+
245
+ def __str__(self) -> str:
246
+ if self == DataType.A_INT32:
247
+ return "int"
248
+ elif self == DataType.A_UINT32:
249
+ return "uint"
250
+ elif self in (DataType.A_FLOAT32, DataType.A_FLOAT64):
251
+ return "float"
252
+ elif self == DataType.A_BYTEFIELD:
253
+ return "bytefield"
254
+ elif self in (DataType.A_UNICODE2STRING, DataType.A_ASCIISTRING, DataType.A_UTF8STRING):
255
+ return "string"
256
+ else:
257
+ return f"<unknown type '{self.value}'>"
odxtools/outputparam.py CHANGED
@@ -1,24 +1,19 @@
1
1
  # SPDX-License-Identifier: MIT
2
2
  from dataclasses import dataclass
3
- from typing import TYPE_CHECKING, Any, Dict, List, Optional
3
+ from typing import Any, Dict, List, Optional
4
4
  from xml.etree import ElementTree
5
5
 
6
- from deprecation import deprecated
7
-
8
6
  from .dopbase import DopBase
9
7
  from .element import IdentifiableElement
10
8
  from .exceptions import odxrequire
11
9
  from .odxlink import OdxDocFragment, OdxLinkDatabase, OdxLinkId, OdxLinkRef
10
+ from .snrefcontext import SnRefContext
12
11
  from .utils import dataclass_fields_asdict
13
12
 
14
- if TYPE_CHECKING:
15
- from .diaglayer import DiagLayer
16
-
17
13
 
18
14
  @dataclass
19
15
  class OutputParam(IdentifiableElement):
20
16
  dop_base_ref: OdxLinkRef
21
- oid: Optional[str]
22
17
  semantic: Optional[str]
23
18
 
24
19
  @staticmethod
@@ -27,25 +22,19 @@ class OutputParam(IdentifiableElement):
27
22
  kwargs = dataclass_fields_asdict(IdentifiableElement.from_et(et_element, doc_frags))
28
23
  dop_base_ref = odxrequire(OdxLinkRef.from_et(et_element.find("DOP-BASE-REF"), doc_frags))
29
24
  semantic = et_element.get("SEMANTIC")
30
- oid = et_element.get("OID")
31
25
 
32
- return OutputParam(dop_base_ref=dop_base_ref, semantic=semantic, oid=oid, **kwargs)
26
+ return OutputParam(dop_base_ref=dop_base_ref, semantic=semantic, **kwargs)
33
27
 
34
28
  def _build_odxlinks(self) -> Dict[OdxLinkId, Any]:
35
29
  return {}
36
30
 
37
31
  def _resolve_odxlinks(self, odxlinks: OdxLinkDatabase) -> None:
38
- self._dop_base = odxlinks.resolve(self.dop_base_ref)
32
+ self._dop_base = odxlinks.resolve(self.dop_base_ref, DopBase)
39
33
 
40
- def _resolve_snrefs(self, diag_layer: "DiagLayer") -> None:
34
+ def _resolve_snrefs(self, context: SnRefContext) -> None:
41
35
  pass
42
36
 
43
37
  @property
44
38
  def dop_base(self) -> DopBase:
45
39
  """The data object property describing this parameter."""
46
40
  return self._dop_base
47
-
48
- @property
49
- @deprecated(details="use .dop_base")
50
- def dop(self) -> DopBase:
51
- return self._dop_base
odxtools/parameterinfo.py CHANGED
@@ -1,98 +1,256 @@
1
1
  # SPDX-License-Identifier: MIT
2
- import re
3
- from typing import Iterable, Union
2
+ import textwrap
3
+ from io import StringIO
4
+ from typing import Iterable
4
5
 
6
+ from .compumethods.compucodecompumethod import CompuCodeCompuMethod
5
7
  from .compumethods.identicalcompumethod import IdenticalCompuMethod
6
8
  from .compumethods.limit import IntervalType
7
9
  from .compumethods.linearcompumethod import LinearCompuMethod
10
+ from .compumethods.linearsegment import LinearSegment
11
+ from .compumethods.ratfunccompumethod import RatFuncCompuMethod
12
+ from .compumethods.ratfuncsegment import RatFuncSegment
13
+ from .compumethods.scalelinearcompumethod import ScaleLinearCompuMethod
14
+ from .compumethods.scaleratfunccompumethod import ScaleRatFuncCompuMethod
8
15
  from .compumethods.texttablecompumethod import TexttableCompuMethod
9
16
  from .dataobjectproperty import DataObjectProperty
17
+ from .dtcdop import DtcDop
18
+ from .dynamiclengthfield import DynamicLengthField
10
19
  from .endofpdufield import EndOfPduField
11
- from .odxtypes import DataType
20
+ from .exceptions import odxrequire
21
+ from .multiplexer import Multiplexer
12
22
  from .parameters.codedconstparameter import CodedConstParameter
13
23
  from .parameters.matchingrequestparameter import MatchingRequestParameter
24
+ from .parameters.nrcconstparameter import NrcConstParameter
14
25
  from .parameters.parameter import Parameter
15
26
  from .parameters.parameterwithdop import ParameterWithDOP
16
27
  from .parameters.reservedparameter import ReservedParameter
28
+ from .parameters.systemparameter import SystemParameter
29
+ from .parameters.tablekeyparameter import TableKeyParameter
30
+ from .parameters.tablestructparameter import TableStructParameter
31
+ from .paramlengthinfotype import ParamLengthInfoType
32
+ from .staticfield import StaticField
17
33
 
18
34
 
19
- def parameter_info(param_list: Iterable[Union[Parameter, EndOfPduField]]) -> str:
20
- result = ""
35
+ def _get_linear_segment_info(segment: LinearSegment) -> str:
36
+ ll = segment.physical_lower_limit
37
+ if ll is None or ll.interval_type == IntervalType.INFINITE:
38
+ ll_str = "(-inf"
39
+ else:
40
+ ll_delim = '(' if ll.interval_type == IntervalType.OPEN else '['
41
+ ll_str = f"{ll_delim}{ll._value!r}"
42
+
43
+ ul = segment.physical_upper_limit
44
+ if ul is None or ul.interval_type == IntervalType.INFINITE:
45
+ ul_str = "inf)"
46
+ else:
47
+ ul_delim = ')' if ul.interval_type == IntervalType.OPEN else ']'
48
+ ul_str = f"{ul._value!r}{ul_delim}"
49
+
50
+ return f"{ll_str}, {ul_str}"
51
+
52
+
53
+ def _get_rat_func_segment_info(segment: RatFuncSegment) -> str:
54
+ ll = segment.lower_limit
55
+ if ll is None or ll.interval_type == IntervalType.INFINITE:
56
+ ll_str = "(-inf"
57
+ else:
58
+ ll_delim = '(' if ll.interval_type == IntervalType.OPEN else '['
59
+ ll_str = f"{ll_delim}{ll._value!r}"
60
+
61
+ ul = segment.upper_limit
62
+ if ul is None or ul.interval_type == IntervalType.INFINITE:
63
+ ul_str = "inf)"
64
+ else:
65
+ ul_delim = ')' if ul.interval_type == IntervalType.OPEN else ']'
66
+ ul_str = f"{ul._value!r}{ul_delim}"
67
+
68
+ return f"{ll_str}, {ul_str}"
69
+
70
+
71
+ def parameter_info(param_list: Iterable[Parameter], quoted_names: bool = False) -> str:
72
+ q = "'" if quoted_names else ""
73
+ of = StringIO()
21
74
  for param in param_list:
22
75
  if isinstance(param, CodedConstParameter):
23
- result += f"{param.short_name} : const = {param._coded_value_str}\n"
76
+ of.write(f"{q}{param.short_name}{q}: const = {param._coded_value_str}\n")
24
77
  continue
25
78
  elif isinstance(param, MatchingRequestParameter):
26
- result += f"{param.short_name} : <matches request>\n"
79
+ of.write(f"{q}{param.short_name}{q}: <matches request>\n")
80
+ continue
81
+ elif isinstance(param, NrcConstParameter):
82
+ of.write(f"{q}{param.short_name}{q}: const; choices = {param.coded_values}\n")
27
83
  continue
28
84
  elif isinstance(param, ReservedParameter):
29
- result += f"{param.short_name} : <reserved>\n"
85
+ of.write(f"{q}{param.short_name}{q}: <reserved>\n")
30
86
  continue
31
- elif not isinstance(param, ParameterWithDOP):
32
- result += f"{param.short_name} : <unhandled parameter type>\n"
87
+ elif isinstance(param, SystemParameter):
88
+ of.write(
89
+ f"{q}{param.short_name}{q}: <system; kind = \"{param.sysparam}\">; required = {param.is_required}\n"
90
+ )
33
91
  continue
92
+ elif isinstance(param, TableKeyParameter):
93
+ of.write(
94
+ f"{q}{param.short_name}{q}: <optional> table key; table = '{param.table.short_name}'; choices:\n"
95
+ )
96
+ for tr in param.table.table_rows:
97
+ of.write(f" '{tr.short_name}',\n")
34
98
 
35
- dop = param.dop
36
-
37
- if isinstance(dop, EndOfPduField):
38
- result += f"{param.short_name} : <optional> list({{\n"
39
- tmp = parameter_info(dop.structure.parameters).strip()
40
- tmp = re.sub("^", " ", tmp)
41
- result += tmp + "\n"
42
- result += f"}})\n"
43
99
  continue
100
+ elif isinstance(param, TableStructParameter):
101
+ of.write(
102
+ f"{q}{param.short_name}{q}: table struct; key = '{param.table_key.short_name}'; choices:\n"
103
+ )
104
+ for tr in param.table_key.table.table_rows:
105
+ of.write(f" ('{tr.short_name}',\n")
106
+ of.write(f" {{\n")
107
+ of.write(
108
+ textwrap.indent(
109
+ parameter_info(odxrequire(tr.structure).parameters, True), " "))
110
+ of.write(f" }}),\n")
44
111
 
45
- result += f"{param.short_name}"
112
+ continue
113
+ elif not isinstance(param, ParameterWithDOP):
114
+ of.write(
115
+ f"{q}{param.short_name}{q}: <unhandled parameter type '{type(param).__name__}'>\n")
116
+ continue
46
117
 
118
+ dop = param.dop
47
119
  if dop is None:
48
- result += ": <no DOP>\n"
120
+ of.write("{q}{param.short_name}{q}: <no DOP>\n")
49
121
  continue
50
- elif not isinstance(dop, DataObjectProperty):
51
- result += ": <unhandled DOP>\n"
122
+ elif isinstance(dop, EndOfPduField):
123
+ of.write(f"{q}{param.short_name}{q}: list({{\n")
124
+ of.write(textwrap.indent(parameter_info(dop.structure.parameters, True), " "))
125
+ of.write(f"}})\n")
52
126
  continue
53
-
54
- if (cm := dop.compu_method) is None:
55
- result += ": <no compu method>\n"
127
+ elif isinstance(dop, StaticField):
128
+ of.write(f"{q}{param.short_name}{q}: length={dop.fixed_number_of_items}; list({{\n")
129
+ of.write(textwrap.indent(parameter_info(dop.structure.parameters, True), " "))
130
+ of.write(f"}})\n")
131
+ continue
132
+ elif isinstance(dop, DynamicLengthField):
133
+ of.write(f"{q}{param.short_name}{q}: list({{\n")
134
+ of.write(textwrap.indent(parameter_info(dop.structure.parameters, True), " "))
135
+ of.write(f"}})\n")
56
136
  continue
137
+ elif isinstance(dop, ParamLengthInfoType):
138
+ of.write(f"{q}{param.short_name}{q}: ")
139
+ of.write("<optional> ")
140
+ of.write(f"int; length_key='{dop.length_key.short_name}'\n")
141
+ continue
142
+ elif isinstance(dop, DtcDop):
143
+ of.write(f"{q}{param.short_name}{q}: ")
144
+ of.write(f"DTC; choices:\n")
145
+ for dtc in dop.dtcs:
146
+ if dtc.display_trouble_code is not None:
147
+ dtc_desc = dtc.text and f"; \"{dtc.text}\""
148
+ of.write(
149
+ f" '{dtc.display_trouble_code}' (0x{dtc.trouble_code:06x}{dtc_desc})\n")
150
+ else:
151
+ dtc_desc = dtc.text and f" (\"{dtc.text}\")"
152
+ of.write(f" 0x{dtc.trouble_code:06x}{dtc_desc}\n")
153
+ continue
154
+ elif isinstance(dop, Multiplexer):
155
+ of.write(f"{q}{param.short_name}{q}: ")
156
+ if dop.default_case is not None:
157
+ of.write(f"<optional>")
158
+ of.write(f"multiplexer; choices:\n")
159
+ for mux_case in dop.cases:
160
+ of.write(f" ({repr(mux_case.short_name)}, {{\n")
161
+ if (struc := mux_case.structure) is not None:
162
+ of.write(textwrap.indent(parameter_info(struc.parameters, True), " "))
163
+ of.write(f" }})\n")
164
+ continue
165
+ elif isinstance(dop, DataObjectProperty):
166
+ # a "simple" DOP
167
+ if (cm := dop.compu_method) is None:
168
+ of.write(f"{q}{param.short_name}{q}: <no compu method>\n")
169
+ continue
57
170
 
58
- if isinstance(cm, TexttableCompuMethod):
59
- result += f": enum; choices:\n"
60
- for scale in cm.internal_to_phys:
61
- result += f" '{str(scale.compu_const)}'\n"
62
-
63
- elif isinstance(cm, IdenticalCompuMethod):
64
- bdt = dop.physical_type.base_data_type
65
- if bdt in (DataType.A_UTF8STRING, DataType.A_UNICODE2STRING, DataType.A_ASCIISTRING):
66
- result += f": str"
67
- elif bdt in (DataType.A_BYTEFIELD,):
68
- result += f": bytes"
69
- elif bdt.name.startswith("A_FLOAT"):
70
- result += f": float"
71
- elif bdt.name.startswith("A_UINT"):
72
- result += f": uint"
73
- elif bdt.name.startswith("A_INT"):
74
- result += f": int"
75
- else:
76
- result += f": <unknown type>"
171
+ if isinstance(cm, TexttableCompuMethod):
172
+ of.write(f"{q}{param.short_name}{q}: enum; choices:\n")
173
+ for scale in odxrequire(cm.compu_internal_to_phys).compu_scales:
174
+ val_str = ""
175
+ if scale.lower_limit is not None:
176
+ val_str = f"({repr(scale.lower_limit.value)})"
177
+
178
+ if scale.compu_const is None:
179
+ of.write(f" <ERROR in ODX data: no value specified>\n")
180
+ else:
181
+ vt = scale.compu_const.vt
182
+ v = scale.compu_const.v
183
+ if vt is not None:
184
+ of.write(f" \"{vt}\" {val_str}\n")
185
+ else:
186
+ of.write(f" {v}\n")
187
+
188
+ elif isinstance(cm, IdenticalCompuMethod):
189
+ of.write(f"{q}{param.short_name}{q}: {dop.physical_type.base_data_type}\n")
190
+
191
+ elif isinstance(cm, ScaleLinearCompuMethod):
192
+ of.write(f"{q}{param.short_name}{q}: {dop.physical_type.base_data_type}")
193
+ seg_list = [_get_linear_segment_info(x) for x in cm.segments]
194
+ of.write(f"; ranges = {{ {', '.join(seg_list)} }}")
195
+
196
+ unit = dop.unit
197
+ unit_str = unit.display_name if unit is not None else None
198
+ if unit_str is not None:
199
+ of.write(f"; unit: {unit_str}")
200
+
201
+ of.write("\n")
77
202
 
78
- if (bl := dop.get_static_bit_length()) is not None:
79
- result += f"{bl}"
203
+ elif isinstance(cm, LinearCompuMethod):
204
+ of.write(f"{q}{param.short_name}{q}: {dop.physical_type.base_data_type}")
205
+ of.write(f"; range: {_get_linear_segment_info(cm.segment)}")
80
206
 
81
- result += "\n"
207
+ unit = dop.unit
208
+ unit_str = unit.display_name if unit is not None else None
209
+ if unit_str is not None:
210
+ of.write(f"; unit: {unit_str}")
82
211
 
83
- elif isinstance(cm, LinearCompuMethod):
84
- result += f": float\n"
85
- ll = cm.physical_lower_limit
86
- ul = cm.physical_upper_limit
87
- result += (f" range: "
88
- f"{'[' if ll.interval_type == IntervalType.CLOSED else '('}"
89
- f"{ll.value!r}, "
90
- f"{ul.value!r}"
91
- f"{']' if ul.interval_type == IntervalType.CLOSED else ')'}\n")
212
+ of.write("\n")
92
213
 
93
- unit = dop.unit
94
- unit_str = unit.display_name if unit is not None else None
95
- if unit_str is not None:
96
- result += f" unit: {unit_str}\n"
214
+ elif isinstance(cm, ScaleRatFuncCompuMethod):
215
+ of.write(f"{q}{param.short_name}{q}: {dop.physical_type.base_data_type}")
216
+ if cm._phys_to_int_segments is None:
217
+ of.write("<NOT ENCODABLE>")
218
+ else:
219
+ seg_list = [_get_rat_func_segment_info(x) for x in cm._phys_to_int_segments]
220
+ of.write(f"; ranges = {{ {', '.join(seg_list)} }}")
221
+
222
+ unit = dop.unit
223
+ unit_str = unit.display_name if unit is not None else None
224
+ if unit_str is not None:
225
+ of.write(f"; unit: {unit_str}")
226
+
227
+ of.write("\n")
228
+
229
+ elif isinstance(cm, RatFuncCompuMethod):
230
+ of.write(f"{q}{param.short_name}{q}: {dop.physical_type.base_data_type}")
231
+ if cm._phys_to_int_segment is None:
232
+ of.write("<NOT ENCODABLE>")
233
+ else:
234
+ of.write(f"; range: {_get_rat_func_segment_info(cm._phys_to_int_segment)}")
235
+
236
+ unit = dop.unit
237
+ unit_str = unit.display_name if unit is not None else None
238
+ if unit_str is not None:
239
+ of.write(f"; unit: {unit_str}")
240
+
241
+ of.write("\n")
242
+
243
+ elif isinstance(cm, CompuCodeCompuMethod):
244
+ of.write(f"{q}{param.short_name}{q}: {dop.physical_type.base_data_type}")
245
+ of.write(f"; <programmatic translation>")
246
+
247
+ of.write("\n")
248
+
249
+ else:
250
+ of.write(
251
+ f"{q}{param.short_name}{q}: unknown compu method {type(dop.compu_method).__name__}\n"
252
+ )
253
+ else:
254
+ of.write(f"{q}{param.short_name}{q}: <unhandled DOP '{type(dop).__name__}'>\n")
97
255
 
98
- return result
256
+ return of.getvalue()
@@ -1,19 +1,21 @@
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
4
+ from typing import Any, Dict, List, Optional
5
+ from xml.etree import ElementTree
5
6
 
7
+ from typing_extensions import override
8
+
9
+ from ..createanydiagcodedtype import create_any_diag_coded_type_from_et
6
10
  from ..decodestate import DecodeState
7
11
  from ..diagcodedtype import DiagCodedType
8
12
  from ..encodestate import EncodeState
9
- from ..exceptions import DecodeError
10
- from ..odxlink import OdxLinkDatabase, OdxLinkId
11
- from ..odxtypes import AtomicOdxType, DataType
13
+ from ..exceptions import DecodeError, EncodeError, odxraise, odxrequire
14
+ from ..odxlink import OdxDocFragment, OdxLinkId
15
+ from ..odxtypes import AtomicOdxType, DataType, ParameterValue
16
+ from ..utils import dataclass_fields_asdict
12
17
  from .parameter import Parameter, ParameterType
13
18
 
14
- if TYPE_CHECKING:
15
- from ..diaglayer import DiagLayer
16
-
17
19
 
18
20
  @dataclass
19
21
  class CodedConstParameter(Parameter):
@@ -21,10 +23,27 @@ class CodedConstParameter(Parameter):
21
23
  diag_coded_type: DiagCodedType
22
24
  coded_value: AtomicOdxType
23
25
 
26
+ @staticmethod
27
+ @override
28
+ def from_et(et_element: ElementTree.Element,
29
+ doc_frags: List[OdxDocFragment]) -> "CodedConstParameter":
30
+
31
+ kwargs = dataclass_fields_asdict(Parameter.from_et(et_element, doc_frags))
32
+
33
+ dct_elem = odxrequire(et_element.find("DIAG-CODED-TYPE"))
34
+ diag_coded_type = create_any_diag_coded_type_from_et(dct_elem, doc_frags)
35
+ coded_value = diag_coded_type.base_data_type.from_string(
36
+ odxrequire(et_element.findtext("CODED-VALUE")))
37
+
38
+ return CodedConstParameter(
39
+ diag_coded_type=diag_coded_type, coded_value=coded_value, **kwargs)
40
+
24
41
  @property
42
+ @override
25
43
  def parameter_type(self) -> ParameterType:
26
44
  return "CODED-CONST"
27
45
 
46
+ @override
28
47
  def _build_odxlinks(self) -> Dict[OdxLinkId, Any]:
29
48
  result = super()._build_odxlinks()
30
49
 
@@ -32,12 +51,7 @@ class CodedConstParameter(Parameter):
32
51
 
33
52
  return result
34
53
 
35
- def _resolve_odxlinks(self, odxlinks: OdxLinkDatabase) -> None:
36
- super()._resolve_odxlinks(odxlinks)
37
-
38
- def _resolve_snrefs(self, diag_layer: "DiagLayer") -> None:
39
- super()._resolve_snrefs(diag_layer)
40
-
54
+ @override
41
55
  def get_static_bit_length(self) -> Optional[int]:
42
56
  return self.diag_coded_type.get_static_bit_length()
43
57
 
@@ -46,32 +60,33 @@ class CodedConstParameter(Parameter):
46
60
  return self.diag_coded_type.base_data_type
47
61
 
48
62
  @property
63
+ @override
49
64
  def is_required(self) -> bool:
50
65
  return False
51
66
 
52
67
  @property
68
+ @override
53
69
  def is_settable(self) -> bool:
54
70
  return False
55
71
 
56
- def get_coded_value_as_bytes(self, encode_state: EncodeState) -> bytes:
57
- if (self.short_name in encode_state.parameter_values and
58
- encode_state.parameter_values[self.short_name] != self.coded_value):
59
- raise TypeError(f"The parameter '{self.short_name}' is constant {self._coded_value_str}"
60
- " and thus can not be changed.")
61
- bit_position_int = self.bit_position if self.bit_position is not None else 0
62
- return self.diag_coded_type.convert_internal_to_bytes(
63
- self.coded_value, encode_state=encode_state, bit_position=bit_position_int)
64
-
65
- def decode_from_pdu(self, decode_state: DecodeState) -> AtomicOdxType:
66
- # Extract coded values
67
- orig_cursor_pos = decode_state.cursor_byte_position
68
- if self.byte_position is not None:
69
- decode_state.cursor_byte_position = decode_state.origin_byte_position + self.byte_position
70
-
71
- decode_state.cursor_bit_position = self.bit_position or 0
72
+ @override
73
+ def _encode_positioned_into_pdu(self, physical_value: Optional[ParameterValue],
74
+ encode_state: EncodeState) -> None:
75
+ if physical_value is not None and physical_value != self.coded_value:
76
+ odxraise(
77
+ f"Value for constant parameter `{self.short_name}` name can "
78
+ f"only be specified as {self.coded_value!r} (is: {physical_value!r})", EncodeError)
79
+
80
+ internal_value = self.coded_value
81
+
82
+ self.diag_coded_type.encode_into_pdu(
83
+ internal_value=internal_value, encode_state=encode_state)
84
+
85
+ @override
86
+ def _decode_positioned_from_pdu(self, decode_state: DecodeState) -> AtomicOdxType:
72
87
  coded_val = self.diag_coded_type.decode_from_pdu(decode_state)
73
88
 
74
- # Check if the coded value in the message is correct.
89
+ # Check if the coded value contained by the message is correct.
75
90
  if self.coded_value != coded_val:
76
91
  warnings.warn(
77
92
  f"Coded constant parameter does not match! "
@@ -83,8 +98,6 @@ class CodedConstParameter(Parameter):
83
98
  stacklevel=1,
84
99
  )
85
100
 
86
- decode_state.cursor_byte_position = max(orig_cursor_pos, decode_state.cursor_byte_position)
87
-
88
101
  return coded_val
89
102
 
90
103
  @property