odxtools 6.7.0__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 (213) hide show
  1. odxtools/__init__.py +6 -4
  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 -240
  7. odxtools/cli/_parser_utils.py +1 -1
  8. odxtools/cli/_print_utils.py +168 -134
  9. odxtools/cli/browse.py +111 -92
  10. odxtools/cli/compare.py +90 -71
  11. odxtools/cli/list.py +24 -15
  12. odxtools/cli/snoop.py +28 -5
  13. odxtools/codec.py +211 -0
  14. odxtools/commrelation.py +122 -0
  15. odxtools/companydata.py +5 -7
  16. odxtools/companydocinfo.py +7 -8
  17. odxtools/companyrevisioninfo.py +3 -5
  18. odxtools/companyspecificinfo.py +8 -9
  19. odxtools/comparam.py +4 -6
  20. odxtools/comparaminstance.py +7 -9
  21. odxtools/comparamspec.py +16 -54
  22. odxtools/comparamsubset.py +22 -62
  23. odxtools/complexcomparam.py +5 -7
  24. odxtools/compumethods/compucodecompumethod.py +63 -0
  25. odxtools/compumethods/compuconst.py +31 -0
  26. odxtools/compumethods/compudefaultvalue.py +27 -0
  27. odxtools/compumethods/compuinternaltophys.py +56 -0
  28. odxtools/compumethods/compuinversevalue.py +7 -0
  29. odxtools/compumethods/compumethod.py +93 -12
  30. odxtools/compumethods/compuphystointernal.py +56 -0
  31. odxtools/compumethods/compurationalcoeffs.py +20 -9
  32. odxtools/compumethods/compuscale.py +30 -35
  33. odxtools/compumethods/createanycompumethod.py +28 -161
  34. odxtools/compumethods/identicalcompumethod.py +31 -6
  35. odxtools/compumethods/linearcompumethod.py +69 -189
  36. odxtools/compumethods/linearsegment.py +190 -0
  37. odxtools/compumethods/ratfunccompumethod.py +106 -0
  38. odxtools/compumethods/ratfuncsegment.py +87 -0
  39. odxtools/compumethods/scalelinearcompumethod.py +132 -26
  40. odxtools/compumethods/scaleratfunccompumethod.py +113 -0
  41. odxtools/compumethods/tabintpcompumethod.py +119 -99
  42. odxtools/compumethods/texttablecompumethod.py +107 -43
  43. odxtools/createanydiagcodedtype.py +10 -67
  44. odxtools/database.py +167 -87
  45. odxtools/dataobjectproperty.py +15 -25
  46. odxtools/decodestate.py +9 -15
  47. odxtools/description.py +47 -0
  48. odxtools/determinenumberofitems.py +4 -5
  49. odxtools/diagcodedtype.py +36 -106
  50. odxtools/diagcomm.py +24 -12
  51. odxtools/diagdatadictionaryspec.py +33 -34
  52. odxtools/diaglayercontainer.py +46 -54
  53. odxtools/diaglayers/basevariant.py +128 -0
  54. odxtools/diaglayers/basevariantraw.py +123 -0
  55. odxtools/diaglayers/diaglayer.py +432 -0
  56. odxtools/{diaglayerraw.py → diaglayers/diaglayerraw.py} +105 -120
  57. odxtools/diaglayers/ecushareddata.py +96 -0
  58. odxtools/diaglayers/ecushareddataraw.py +87 -0
  59. odxtools/diaglayers/ecuvariant.py +124 -0
  60. odxtools/diaglayers/ecuvariantraw.py +129 -0
  61. odxtools/diaglayers/functionalgroup.py +110 -0
  62. odxtools/diaglayers/functionalgroupraw.py +106 -0
  63. odxtools/{diaglayer.py → diaglayers/hierarchyelement.py} +209 -448
  64. odxtools/diaglayers/hierarchyelementraw.py +58 -0
  65. odxtools/diaglayers/protocol.py +64 -0
  66. odxtools/diaglayers/protocolraw.py +91 -0
  67. odxtools/diagnostictroublecode.py +8 -9
  68. odxtools/diagservice.py +56 -43
  69. odxtools/diagvariable.py +113 -0
  70. odxtools/docrevision.py +5 -7
  71. odxtools/dopbase.py +15 -17
  72. odxtools/dtcdop.py +168 -50
  73. odxtools/dynamicendmarkerfield.py +134 -0
  74. odxtools/dynamiclengthfield.py +41 -37
  75. odxtools/dyndefinedspec.py +177 -0
  76. odxtools/dynenddopref.py +38 -0
  77. odxtools/ecuvariantmatcher.py +6 -7
  78. odxtools/element.py +13 -15
  79. odxtools/encodestate.py +199 -22
  80. odxtools/endofpdufield.py +31 -18
  81. odxtools/environmentdata.py +8 -1
  82. odxtools/environmentdatadescription.py +198 -38
  83. odxtools/exceptions.py +11 -2
  84. odxtools/field.py +10 -10
  85. odxtools/functionalclass.py +3 -5
  86. odxtools/inputparam.py +3 -12
  87. odxtools/leadinglengthinfotype.py +37 -18
  88. odxtools/library.py +66 -0
  89. odxtools/loadfile.py +64 -0
  90. odxtools/matchingparameter.py +3 -3
  91. odxtools/message.py +0 -7
  92. odxtools/minmaxlengthtype.py +61 -33
  93. odxtools/modification.py +3 -5
  94. odxtools/multiplexer.py +128 -73
  95. odxtools/multiplexercase.py +13 -14
  96. odxtools/multiplexerdefaultcase.py +15 -12
  97. odxtools/multiplexerswitchkey.py +4 -5
  98. odxtools/nameditemlist.py +29 -5
  99. odxtools/negoutputparam.py +3 -5
  100. odxtools/odxcategory.py +83 -0
  101. odxtools/odxlink.py +60 -51
  102. odxtools/odxtypes.py +37 -5
  103. odxtools/outputparam.py +4 -15
  104. odxtools/parameterinfo.py +218 -67
  105. odxtools/parameters/codedconstparameter.py +16 -24
  106. odxtools/parameters/dynamicparameter.py +5 -4
  107. odxtools/parameters/lengthkeyparameter.py +60 -26
  108. odxtools/parameters/matchingrequestparameter.py +23 -11
  109. odxtools/parameters/nrcconstparameter.py +45 -46
  110. odxtools/parameters/parameter.py +54 -56
  111. odxtools/parameters/parameterwithdop.py +15 -25
  112. odxtools/parameters/physicalconstantparameter.py +15 -18
  113. odxtools/parameters/reservedparameter.py +6 -2
  114. odxtools/parameters/systemparameter.py +55 -11
  115. odxtools/parameters/tableentryparameter.py +3 -2
  116. odxtools/parameters/tablekeyparameter.py +103 -49
  117. odxtools/parameters/tablestructparameter.py +47 -48
  118. odxtools/parameters/valueparameter.py +16 -20
  119. odxtools/paramlengthinfotype.py +52 -32
  120. odxtools/parentref.py +16 -2
  121. odxtools/physicaldimension.py +3 -8
  122. odxtools/progcode.py +26 -11
  123. odxtools/protstack.py +3 -5
  124. odxtools/py.typed +0 -0
  125. odxtools/relateddoc.py +7 -9
  126. odxtools/request.py +120 -10
  127. odxtools/response.py +123 -23
  128. odxtools/scaleconstr.py +3 -3
  129. odxtools/servicebinner.py +1 -1
  130. odxtools/singleecujob.py +12 -10
  131. odxtools/snrefcontext.py +29 -0
  132. odxtools/specialdata.py +3 -5
  133. odxtools/specialdatagroup.py +7 -9
  134. odxtools/specialdatagroupcaption.py +3 -6
  135. odxtools/standardlengthtype.py +80 -14
  136. odxtools/state.py +3 -5
  137. odxtools/statechart.py +13 -19
  138. odxtools/statetransition.py +7 -17
  139. odxtools/staticfield.py +31 -25
  140. odxtools/subcomponent.py +288 -0
  141. odxtools/swvariable.py +21 -0
  142. odxtools/table.py +7 -8
  143. odxtools/tablerow.py +19 -11
  144. odxtools/teammember.py +3 -5
  145. odxtools/templates/comparam-spec.odx-c.xml.jinja2 +4 -24
  146. odxtools/templates/comparam-subset.odx-cs.xml.jinja2 +5 -26
  147. odxtools/templates/diag_layer_container.odx-d.xml.jinja2 +15 -31
  148. odxtools/templates/{index.xml.xml.jinja2 → index.xml.jinja2} +1 -1
  149. odxtools/templates/macros/printAudience.xml.jinja2 +1 -1
  150. odxtools/templates/macros/printBaseVariant.xml.jinja2 +53 -0
  151. odxtools/templates/macros/printCompanyData.xml.jinja2 +4 -7
  152. odxtools/templates/macros/printComparam.xml.jinja2 +6 -4
  153. odxtools/templates/macros/printComparamRef.xml.jinja2 +5 -12
  154. odxtools/templates/macros/printCompuMethod.xml.jinja2 +147 -0
  155. odxtools/templates/macros/printDOP.xml.jinja2 +27 -133
  156. odxtools/templates/macros/printDescription.xml.jinja2 +18 -0
  157. odxtools/templates/macros/printDiagComm.xml.jinja2 +1 -1
  158. odxtools/templates/macros/printDiagLayer.xml.jinja2 +222 -0
  159. odxtools/templates/macros/printDiagVariable.xml.jinja2 +66 -0
  160. odxtools/templates/macros/printDynDefinedSpec.xml.jinja2 +48 -0
  161. odxtools/templates/macros/printDynamicEndmarkerField.xml.jinja2 +16 -0
  162. odxtools/templates/macros/printDynamicLengthField.xml.jinja2 +1 -1
  163. odxtools/templates/macros/printEcuSharedData.xml.jinja2 +30 -0
  164. odxtools/templates/macros/printEcuVariant.xml.jinja2 +53 -0
  165. odxtools/templates/macros/printEcuVariantPattern.xml.jinja2 +1 -1
  166. odxtools/templates/macros/printElementId.xml.jinja2 +8 -3
  167. odxtools/templates/macros/printEndOfPdu.xml.jinja2 +1 -1
  168. odxtools/templates/macros/printEnvDataDesc.xml.jinja2 +1 -1
  169. odxtools/templates/macros/printFunctionalClass.xml.jinja2 +1 -1
  170. odxtools/templates/macros/printFunctionalGroup.xml.jinja2 +40 -0
  171. odxtools/templates/macros/printHierarchyElement.xml.jinja2 +24 -0
  172. odxtools/templates/macros/printLibrary.xml.jinja2 +21 -0
  173. odxtools/templates/macros/printMux.xml.jinja2 +4 -3
  174. odxtools/templates/macros/printOdxCategory.xml.jinja2 +28 -0
  175. odxtools/templates/macros/printParam.xml.jinja2 +11 -12
  176. odxtools/templates/macros/printProtStack.xml.jinja2 +1 -1
  177. odxtools/templates/macros/printProtocol.xml.jinja2 +30 -0
  178. odxtools/templates/macros/printRequest.xml.jinja2 +1 -1
  179. odxtools/templates/macros/printResponse.xml.jinja2 +1 -1
  180. odxtools/templates/macros/printService.xml.jinja2 +3 -2
  181. odxtools/templates/macros/printSingleEcuJob.xml.jinja2 +5 -26
  182. odxtools/templates/macros/printSpecialData.xml.jinja2 +1 -1
  183. odxtools/templates/macros/printState.xml.jinja2 +1 -1
  184. odxtools/templates/macros/printStateChart.xml.jinja2 +1 -1
  185. odxtools/templates/macros/printStateTransition.xml.jinja2 +1 -1
  186. odxtools/templates/macros/printStaticField.xml.jinja2 +1 -1
  187. odxtools/templates/macros/printStructure.xml.jinja2 +1 -1
  188. odxtools/templates/macros/printSubComponent.xml.jinja2 +104 -0
  189. odxtools/templates/macros/printTable.xml.jinja2 +4 -5
  190. odxtools/templates/macros/printUnitSpec.xml.jinja2 +3 -5
  191. odxtools/uds.py +2 -10
  192. odxtools/unit.py +4 -8
  193. odxtools/unitgroup.py +3 -5
  194. odxtools/unitspec.py +17 -17
  195. odxtools/utils.py +38 -20
  196. odxtools/variablegroup.py +32 -0
  197. odxtools/version.py +2 -2
  198. odxtools/{write_pdx_file.py → writepdxfile.py} +20 -10
  199. odxtools/xdoc.py +3 -5
  200. {odxtools-6.7.0.dist-info → odxtools-9.3.0.dist-info}/METADATA +20 -21
  201. odxtools-9.3.0.dist-info/RECORD +228 -0
  202. {odxtools-6.7.0.dist-info → odxtools-9.3.0.dist-info}/WHEEL +1 -1
  203. odxtools/createcompanydatas.py +0 -17
  204. odxtools/createsdgs.py +0 -19
  205. odxtools/load_file.py +0 -13
  206. odxtools/load_odx_d_file.py +0 -6
  207. odxtools/load_pdx_file.py +0 -8
  208. odxtools/templates/macros/printVariant.xml.jinja2 +0 -216
  209. odxtools-6.7.0.dist-info/RECORD +0 -182
  210. /odxtools/{diaglayertype.py → diaglayers/diaglayertype.py} +0 -0
  211. {odxtools-6.7.0.dist-info → odxtools-9.3.0.dist-info}/LICENSE +0 -0
  212. {odxtools-6.7.0.dist-info → odxtools-9.3.0.dist-info}/entry_points.txt +0 -0
  213. {odxtools-6.7.0.dist-info → odxtools-9.3.0.dist-info}/top_level.txt +0 -0
@@ -3,18 +3,18 @@ from dataclasses import dataclass
3
3
  from typing import TYPE_CHECKING, Any, Dict, List, Optional
4
4
  from xml.etree import ElementTree
5
5
 
6
- from typing_extensions import override
6
+ from typing_extensions import final, override
7
7
 
8
8
  from ..decodestate import DecodeState
9
9
  from ..encodestate import EncodeState
10
10
  from ..exceptions import DecodeError, EncodeError, odxraise, odxrequire
11
- from ..odxlink import OdxDocFragment, OdxLinkDatabase, OdxLinkId, OdxLinkRef
11
+ from ..odxlink import OdxDocFragment, OdxLinkDatabase, OdxLinkId, OdxLinkRef, resolve_snref
12
12
  from ..odxtypes import ParameterValue
13
+ from ..snrefcontext import SnRefContext
13
14
  from ..utils import dataclass_fields_asdict
14
15
  from .parameter import Parameter, ParameterType
15
16
 
16
17
  if TYPE_CHECKING:
17
- from ..diaglayer import DiagLayer
18
18
  from ..table import Table
19
19
  from ..tablerow import TableRow
20
20
 
@@ -56,8 +56,8 @@ class TableKeyParameter(Parameter):
56
56
  **kwargs)
57
57
 
58
58
  def __post_init__(self) -> None:
59
- self._table: "Table"
60
- self._table_row: Optional["TableRow"] = None
59
+ self._table: Table
60
+ self._table_row: Optional[TableRow] = None
61
61
  if self.table_ref is None and self.table_snref is None and \
62
62
  self.table_row_ref is None and self.table_row_snref is None:
63
63
  odxraise("Either a table or a table row must be defined.")
@@ -80,41 +80,48 @@ class TableKeyParameter(Parameter):
80
80
  super()._resolve_odxlinks(odxlinks)
81
81
 
82
82
  # Either table_ref or table_row_ref will be defined
83
- if self.table_ref:
83
+ if self.table_ref is not None:
84
84
  if TYPE_CHECKING:
85
85
  self._table = odxlinks.resolve(self.table_ref, Table)
86
86
  else:
87
87
  self._table = odxlinks.resolve(self.table_ref)
88
88
 
89
- if self.table_row_ref:
89
+ if self.table_row_ref is not None:
90
90
  if TYPE_CHECKING:
91
91
  self._table_row = odxlinks.resolve(self.table_row_ref, TableRow)
92
92
  else:
93
93
  self._table_row = odxlinks.resolve(self.table_row_ref)
94
- self._table = self._table_row.table
94
+
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)
95
100
 
96
101
  @override
97
- def _resolve_snrefs(self, diag_layer: "DiagLayer") -> None:
98
- super()._resolve_snrefs(diag_layer)
102
+ def _resolve_snrefs(self, context: SnRefContext) -> None:
103
+ super()._resolve_snrefs(context)
99
104
 
100
105
  if self.table_snref is not None:
101
- ddd_spec = diag_layer.diag_data_dictionary_spec
102
- 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)
103
111
  if self.table_row_snref is not None:
104
112
  # make sure that we know the table to which the table row
105
113
  # SNREF is relative to.
106
114
  table = odxrequire(
107
- self._table, "If a table-row short name reference is defined, a "
108
- "table must also be specified.")
109
- 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)
110
121
 
111
122
  @property
112
123
  def table(self) -> "Table":
113
- if self._table is not None:
114
- return self._table
115
- if self._table_row is not None:
116
- return self._table_row.table
117
- odxraise(f'Could not resolve the table of {self.short_name}')
124
+ return self._table
118
125
 
119
126
  @property
120
127
  def table_row(self) -> Optional["TableRow"]:
@@ -133,46 +140,93 @@ class TableKeyParameter(Parameter):
133
140
  return True
134
141
 
135
142
  @override
136
- def get_coded_value_as_bytes(self, encode_state: EncodeState) -> bytes:
137
- tr_short_name = encode_state.parameter_values.get(self.short_name)
138
-
139
- if tr_short_name is None:
140
- # the table key has not been defined explicitly yet, but
141
- # it is most likely implicitly defined by the associated
142
- # TABLE-STRUCT parameters. Use all-zeros as a standin for
143
- # the real data...
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.")
151
+
152
+ def encode_placeholder_into_pdu(self, physical_value: Optional[ParameterValue],
153
+ encode_state: EncodeState) -> None:
154
+
155
+ if physical_value is not None:
144
156
  key_dop = self.table.key_dop
145
157
  if key_dop is None:
146
- raise EncodeError(f"Table '{self.table.short_name}' does not define "
147
- f"a KEY-DOP, but is used in TABLE-KEY parameter "
148
- 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
174
+
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
149
192
 
150
- byte_len = (odxrequire(key_dop.get_static_bit_length()) + 7) // 8
151
- if self.bit_position is not None and self.bit_position > 0:
152
- byte_len += 1
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)
153
197
 
154
- return bytes([0] * byte_len)
198
+ encode_state.cursor_bit_position = 0
155
199
 
156
- # the table key is known. We need to encode the associated DOP
157
- # into the PDU.
200
+ def encode_value_into_pdu(self, encode_state: EncodeState) -> None:
201
+
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]
216
+
217
+ # We need to encode the table key using the associated DOP into the PDU.
158
218
  tr_candidates = [x for x in self.table.table_rows if x.short_name == tr_short_name]
159
219
  if len(tr_candidates) == 0:
160
- 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
161
222
  elif len(tr_candidates) > 1:
162
- raise EncodeError(f"Multiple rows exhibiting short name '{tr_short_name}'")
223
+ odxraise(f"Multiple rows exhibiting short name '{tr_short_name}'", EncodeError)
163
224
  tr = tr_candidates[0]
164
225
 
165
- key_dop = self.table.key_dop
166
- if key_dop is None:
167
- raise EncodeError(f"Table '{self.table.short_name}' does not define "
168
- f"a KEY-DOP, but is used in TABLE-KEY parameter "
169
- f"'{self.short_name}'")
170
- bit_position = 0 if self.bit_position is None else self.bit_position
171
- return key_dop.convert_physical_to_bytes(tr.key, encode_state, bit_position=bit_position)
226
+ encode_state.cursor_byte_position = encode_state.key_pos[self.short_name]
227
+ encode_state.cursor_bit_position = self.bit_position or 0
172
228
 
173
- @override
174
- def encode_into_pdu(self, encode_state: EncodeState) -> bytes:
175
- return super().encode_into_pdu(encode_state)
229
+ key_dop.encode_into_pdu(encode_state=encode_state, physical_value=odxrequire(tr.key))
176
230
 
177
231
  @override
178
232
  def _decode_positioned_from_pdu(self, decode_state: DecodeState) -> ParameterValue:
@@ -1,23 +1,20 @@
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, cast
3
+ from typing import Any, Dict, List, Optional, cast
5
4
  from xml.etree import ElementTree
6
5
 
7
6
  from typing_extensions import override
8
7
 
9
8
  from ..decodestate import DecodeState
10
9
  from ..encodestate import EncodeState
11
- from ..exceptions import DecodeError, EncodeError, OdxWarning, odxraise, odxrequire
12
- from ..odxlink import OdxDocFragment, OdxLinkDatabase, OdxLinkId, OdxLinkRef
10
+ from ..exceptions import DecodeError, EncodeError, odxraise, odxrequire
11
+ from ..odxlink import OdxDocFragment, OdxLinkDatabase, OdxLinkId, OdxLinkRef, resolve_snref
13
12
  from ..odxtypes import ParameterValue
13
+ from ..snrefcontext import SnRefContext
14
14
  from ..utils import dataclass_fields_asdict
15
15
  from .parameter import Parameter, ParameterType
16
16
  from .tablekeyparameter import TableKeyParameter
17
17
 
18
- if TYPE_CHECKING:
19
- from ..diaglayer import DiagLayer
20
-
21
18
 
22
19
  @dataclass
23
20
  class TableStructParameter(Parameter):
@@ -61,15 +58,12 @@ class TableStructParameter(Parameter):
61
58
  self._table_key = odxlinks.resolve(self.table_key_ref, TableKeyParameter)
62
59
 
63
60
  @override
64
- def _resolve_snrefs(self, diag_layer: "DiagLayer") -> None:
65
- super()._resolve_snrefs(diag_layer)
61
+ def _resolve_snrefs(self, context: SnRefContext) -> None:
62
+ super()._resolve_snrefs(context)
66
63
 
67
64
  if self.table_key_snref is not None:
68
- warnings.warn(
69
- "Table keys cannot yet be defined using SNREFs"
70
- " in TableStructParameters.",
71
- OdxWarning,
72
- stacklevel=1)
65
+ self._table_key = resolve_snref(self.table_key_snref, odxrequire(context.parameters),
66
+ TableKeyParameter)
73
67
 
74
68
  @property
75
69
  def table_key(self) -> TableKeyParameter:
@@ -86,71 +80,76 @@ class TableStructParameter(Parameter):
86
80
  return True
87
81
 
88
82
  @override
89
- def get_coded_value_as_bytes(self, encode_state: EncodeState) -> bytes:
90
- physical_value = encode_state.parameter_values.get(self.short_name)
83
+ def _encode_positioned_into_pdu(self, physical_value: Optional[ParameterValue],
84
+ encode_state: EncodeState) -> None:
91
85
 
92
- if not isinstance(physical_value, tuple) or \
86
+ if not isinstance(physical_value, (tuple, list)) or \
93
87
  len(physical_value) != 2 or \
94
88
  not isinstance(physical_value[0], str):
95
- raise EncodeError(f"The physical value of TableStructParameter 'self.short_name' "
96
- f"must be a tuple with the short name of the selected table "
97
- f"row as the first element and the physical value for the "
98
- f"row's structure or DOP as the second.")
89
+ odxraise(
90
+ f"The physical value of TableStructParameter 'self.short_name' "
91
+ f"must be a tuple containing the short name of the selected table "
92
+ f"row as the first element and the physical value for the "
93
+ f"row's structure or DOP as the second.", EncodeError)
99
94
 
100
95
  tr_short_name = physical_value[0]
101
96
 
102
97
  # make sure that the same table row is selected for all
103
98
  # TABLE-STRUCT parameters that are using the same key
104
99
  tk_short_name = self.table_key.short_name
105
- tk_value = encode_state.parameter_values.get(tk_short_name)
100
+ tk_value = encode_state.table_keys.get(tk_short_name)
106
101
  if tk_value is None:
107
102
  # no value for the key has been set yet. Set it to the
108
103
  # value which we are using right now
109
- encode_state.parameter_values[tk_short_name] = tr_short_name
104
+ encode_state.table_keys[tk_short_name] = tr_short_name
110
105
  elif tk_value != tr_short_name:
111
- raise EncodeError(f"Cannot determine a unique value for table key '{tk_short_name}': "
112
- f"Requested are '{tk_value}' and '{tr_short_name}'")
106
+ odxraise(
107
+ f"Cannot determine a unique value for table key '{tk_short_name}': "
108
+ f"Requested are '{tk_value}' and '{tr_short_name}'", EncodeError)
109
+ return
113
110
 
114
111
  # deal with the static case (i.e., the table row is selected
115
112
  # by the table key object itself)
116
- if self.table_key.table_row is not None and \
117
- self.table_key.table_row.short_name != tr_short_name:
118
- raise EncodeError(f"The selected table row for the {self.short_name} "
119
- f"parameter must be '{self.table_key.table_row.short_name}' "
120
- f"(is: '{tr_short_name}')")
113
+ if self.table_key.table_row is not None:
114
+ if tr_short_name is not None and self.table_key.table_row.short_name != tr_short_name:
115
+ odxraise(
116
+ f"The selected table row for the {self.short_name} "
117
+ f"parameter must be '{self.table_key.table_row.short_name}' "
118
+ f"instead of '{tr_short_name}'", EncodeError)
119
+ return
120
+
121
+ tr_short_name = self.table_key.table_row.short_name
121
122
 
122
123
  # encode the user specified value using the structure (or DOP)
123
124
  # of the selected table row
124
125
  table = self.table_key.table
125
126
  candidate_trs = [tr for tr in table.table_rows if tr.short_name == tr_short_name]
126
- if len(candidate_trs) != 1:
127
- raise EncodeError(f"Could not uniquely resolve a table row named "
128
- f"'{tr_short_name}' in table '{table.short_name}' ")
127
+ if len(candidate_trs) == 0:
128
+ odxraise(
129
+ f"Could not find a table row named "
130
+ f"'{tr_short_name}' in table '{table.short_name}'", EncodeError)
131
+ return
132
+ elif len(candidate_trs) > 1:
133
+ odxraise(
134
+ f"Found multiple table rows named "
135
+ f"'{tr_short_name}' in table '{table.short_name}'", EncodeError)
136
+
129
137
  tr = candidate_trs[0]
130
138
  tr_value = physical_value[1]
131
139
 
132
- bit_position = self.bit_position or 0
133
140
  if tr.structure is not None:
134
141
  # the selected table row references a structure
135
- inner_encode_state = EncodeState(
136
- coded_message=bytearray(b''),
137
- parameter_values=tr_value,
138
- triggering_request=encode_state.triggering_request)
139
-
140
- return tr.structure.convert_physical_to_bytes(
141
- tr_value, inner_encode_state, bit_position=bit_position)
142
+ tr.structure.encode_into_pdu(tr_value, encode_state)
143
+ return
142
144
 
143
145
  # if the table row does not reference a structure, it must
144
146
  # point to a DOP!
145
147
  if tr.dop is None:
146
- odxraise()
148
+ odxraise(f"Neither a structure nor a DOP has been defined for table row"
149
+ f"'{tr.short_name}'")
150
+ return
147
151
 
148
- return tr.dop.convert_physical_to_bytes(
149
- tr_value, encode_state=encode_state, bit_position=bit_position)
150
-
151
- @override
152
- def encode_into_pdu(self, encode_state: EncodeState) -> bytes:
153
- return super().encode_into_pdu(encode_state)
152
+ tr.dop.encode_into_pdu(tr_value, encode_state)
154
153
 
155
154
  @override
156
155
  def _decode_positioned_from_pdu(self, decode_state: DecodeState) -> ParameterValue:
@@ -1,22 +1,20 @@
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
6
  from typing_extensions import override
7
7
 
8
8
  from ..dataobjectproperty import DataObjectProperty
9
9
  from ..encodestate import EncodeState
10
- from ..exceptions import odxraise, odxrequire
10
+ from ..exceptions import EncodeError, odxraise, odxrequire
11
11
  from ..odxlink import OdxDocFragment, OdxLinkDatabase, OdxLinkId
12
- from ..odxtypes import AtomicOdxType
12
+ from ..odxtypes import AtomicOdxType, ParameterValue
13
+ from ..snrefcontext import SnRefContext
13
14
  from ..utils import dataclass_fields_asdict
14
15
  from .parameter import ParameterType
15
16
  from .parameterwithdop import ParameterWithDOP
16
17
 
17
- if TYPE_CHECKING:
18
- from ..diaglayer import DiagLayer
19
-
20
18
 
21
19
  @dataclass
22
20
  class ValueParameter(ParameterWithDOP):
@@ -50,8 +48,8 @@ class ValueParameter(ParameterWithDOP):
50
48
  super()._resolve_odxlinks(odxlinks)
51
49
 
52
50
  @override
53
- def _resolve_snrefs(self, diag_layer: "DiagLayer") -> None:
54
- super()._resolve_snrefs(diag_layer)
51
+ def _resolve_snrefs(self, context: SnRefContext) -> None:
52
+ super()._resolve_snrefs(context)
55
53
 
56
54
  if self.physical_default_value_raw is not None:
57
55
  dop = odxrequire(self.dop)
@@ -77,16 +75,14 @@ class ValueParameter(ParameterWithDOP):
77
75
  return True
78
76
 
79
77
  @override
80
- def get_coded_value_as_bytes(self, encode_state: EncodeState) -> bytes:
81
- physical_value = encode_state.parameter_values.get(self.short_name,
82
- self.physical_default_value)
78
+ def _encode_positioned_into_pdu(self, physical_value: Optional[ParameterValue],
79
+ encode_state: EncodeState) -> None:
80
+
83
81
  if physical_value is None:
84
- raise TypeError(f"A value for parameter '{self.short_name}' must be specified"
85
- f" as the parameter does not exhibit a default.")
86
- dop = odxrequire(
87
- self.dop,
88
- f"Param {self.short_name} does not have a DOP. Maybe resolving references failed?")
89
-
90
- bit_position_int = self.bit_position if self.bit_position is not None else 0
91
- return dop.convert_physical_to_bytes(
92
- physical_value, encode_state=encode_state, bit_position=bit_position_int)
82
+ physical_value = self._physical_default_value
83
+ if physical_value is None:
84
+ odxraise(
85
+ f"A value for parameter '{self.short_name}' must be specified"
86
+ f" because the parameter does not exhibit a default.", EncodeError)
87
+
88
+ self.dop.encode_into_pdu(physical_value, encode_state=encode_state)
@@ -1,28 +1,46 @@
1
1
  # SPDX-License-Identifier: MIT
2
2
  from dataclasses import dataclass
3
- from typing import TYPE_CHECKING, Any, Dict, cast
3
+ from typing import TYPE_CHECKING, Any, Dict, List, cast
4
+ from xml.etree import ElementTree
5
+
6
+ from typing_extensions import override
4
7
 
5
8
  from .decodestate import DecodeState
6
9
  from .diagcodedtype import DctType, DiagCodedType
7
10
  from .encodestate import EncodeState
8
- from .exceptions import odxraise
9
- from .odxlink import OdxLinkDatabase, OdxLinkId, OdxLinkRef
11
+ from .exceptions import EncodeError, odxraise, odxrequire
12
+ from .odxlink import OdxDocFragment, OdxLinkDatabase, OdxLinkId, OdxLinkRef
10
13
  from .odxtypes import AtomicOdxType, DataType
14
+ from .snrefcontext import SnRefContext
15
+ from .utils import dataclass_fields_asdict
11
16
 
12
17
  if TYPE_CHECKING:
13
- from .diaglayer import DiagLayer
14
18
  from .parameters.lengthkeyparameter import LengthKeyParameter
15
19
 
16
20
 
17
21
  @dataclass
18
22
  class ParamLengthInfoType(DiagCodedType):
19
-
20
23
  length_key_ref: OdxLinkRef
21
24
 
22
25
  @property
23
26
  def dct_type(self) -> DctType:
24
27
  return "PARAM-LENGTH-INFO-TYPE"
25
28
 
29
+ @property
30
+ def length_key(self) -> "LengthKeyParameter":
31
+ return self._length_key
32
+
33
+ @staticmethod
34
+ @override
35
+ def from_et(et_element: ElementTree.Element,
36
+ doc_frags: List[OdxDocFragment]) -> "ParamLengthInfoType":
37
+ kwargs = dataclass_fields_asdict(DiagCodedType.from_et(et_element, doc_frags))
38
+
39
+ length_key_ref = odxrequire(
40
+ OdxLinkRef.from_et(et_element.find("LENGTH-KEY-REF"), doc_frags))
41
+
42
+ return ParamLengthInfoType(length_key_ref=length_key_ref, **kwargs)
43
+
26
44
  def _build_odxlinks(self) -> Dict[OdxLinkId, Any]:
27
45
  return super()._build_odxlinks()
28
46
 
@@ -35,43 +53,47 @@ class ParamLengthInfoType(DiagCodedType):
35
53
  else:
36
54
  self._length_key = odxlinks.resolve(self.length_key_ref)
37
55
 
38
- def _resolve_snrefs(self, diag_layer: "DiagLayer") -> None:
56
+ def _resolve_snrefs(self, context: SnRefContext) -> None:
39
57
  """Recursively resolve any short-name references"""
40
- super()._resolve_snrefs(diag_layer)
58
+ super()._resolve_snrefs(context)
41
59
 
42
- @property
43
- def length_key(self) -> "LengthKeyParameter":
44
- return self._length_key
45
-
46
- def convert_internal_to_bytes(self, internal_value: AtomicOdxType, encode_state: EncodeState,
47
- bit_position: int) -> bytes:
48
- bit_length = encode_state.parameter_values.get(self.length_key.short_name, None)
60
+ @override
61
+ def encode_into_pdu(self, internal_value: AtomicOdxType, encode_state: EncodeState) -> None:
62
+ bit_length = encode_state.length_keys.get(self.length_key.short_name)
49
63
 
50
64
  if bit_length is None:
65
+ # the length key is implicit, i.e., we need to set the
66
+ # value for the length key in the encode_state based on
67
+ # the value passed here.
51
68
  if self.base_data_type in [
52
69
  DataType.A_BYTEFIELD,
53
70
  DataType.A_ASCIISTRING,
54
71
  DataType.A_UTF8STRING,
55
72
  ]:
56
- bit_length = 8 * len(internal_value) # type: ignore[arg-type]
57
- if self.base_data_type in [DataType.A_UNICODE2STRING]:
58
- bit_length = 16 * len(internal_value) # type: ignore[arg-type]
59
-
60
- if self.base_data_type in [DataType.A_INT32, DataType.A_UINT32]:
73
+ bit_length = 8 * len(cast(str, internal_value))
74
+ elif self.base_data_type in [DataType.A_UNICODE2STRING]:
75
+ bit_length = 16 * len(cast(str, internal_value))
76
+ elif self.base_data_type in [DataType.A_INT32, DataType.A_UINT32]:
61
77
  bit_length = int(internal_value).bit_length()
62
78
  if self.base_data_type == DataType.A_INT32:
63
79
  bit_length += 1
64
80
  # Round up
65
81
  bit_length = ((bit_length + 7) // 8) * 8
66
-
67
- encode_state.parameter_values[self.length_key.short_name] = bit_length
68
-
69
- if bit_length is None:
70
- odxraise()
71
-
72
- return self._encode_internal_value(
73
- internal_value,
74
- bit_position=bit_position,
82
+ elif self.base_data_type == DataType.A_FLOAT32:
83
+ bit_length = 32
84
+ elif self.base_data_type == DataType.A_FLOAT64:
85
+ bit_length = 64
86
+ else:
87
+ odxraise(
88
+ f"Cannot determine size of an object of type "
89
+ f"{self.base_data_type.value}", EncodeError)
90
+ return
91
+
92
+ encode_state.length_keys[self.length_key.short_name] = bit_length
93
+
94
+ encode_state.emplace_atomic_value(
95
+ internal_value=internal_value,
96
+ used_mask=None,
75
97
  bit_length=bit_length,
76
98
  base_data_type=self.base_data_type,
77
99
  is_highlow_byte_order=self.is_highlow_byte_order,
@@ -82,7 +104,7 @@ class ParamLengthInfoType(DiagCodedType):
82
104
  if self.length_key.short_name not in decode_state.length_keys:
83
105
  odxraise(f"Unspecified mandatory length key parameter "
84
106
  f"{self.length_key.short_name}")
85
- decode_state.cursor_bit_position = None
107
+ decode_state.cursor_bit_position = 0
86
108
  return cast(None, AtomicOdxType)
87
109
 
88
110
  bit_length = decode_state.length_keys[self.length_key.short_name]
@@ -91,10 +113,8 @@ class ParamLengthInfoType(DiagCodedType):
91
113
  bit_length = 0
92
114
 
93
115
  # Extract the internal value and return.
94
- value = decode_state.extract_atomic_value(
116
+ return decode_state.extract_atomic_value(
95
117
  bit_length,
96
118
  self.base_data_type,
97
119
  self.is_highlow_byte_order,
98
120
  )
99
-
100
- return value
odxtools/parentref.py CHANGED
@@ -1,13 +1,16 @@
1
1
  # SPDX-License-Identifier: MIT
2
+ from copy import deepcopy
2
3
  from dataclasses import dataclass
3
4
  from typing import TYPE_CHECKING, Any, Dict, List
4
5
  from xml.etree import ElementTree
5
6
 
6
7
  from .exceptions import odxrequire
7
8
  from .odxlink import OdxDocFragment, OdxLinkDatabase, OdxLinkId, OdxLinkRef
9
+ from .snrefcontext import SnRefContext
10
+ from .utils import dataclass_fields_asdict
8
11
 
9
12
  if TYPE_CHECKING:
10
- from .diaglayer import DiagLayer
13
+ from .diaglayers.diaglayer import DiagLayer
11
14
 
12
15
 
13
16
  @dataclass
@@ -76,5 +79,16 @@ class ParentRef:
76
79
  else:
77
80
  self._layer = odxlinks.resolve(self.layer_ref)
78
81
 
79
- def _resolve_snrefs(self, diag_layer: "DiagLayer") -> None:
82
+ def _resolve_snrefs(self, context: SnRefContext) -> None:
80
83
  pass
84
+
85
+ def __deepcopy__(self, memo: Dict[int, Any]) -> Any:
86
+ cls = self.__class__
87
+ result = cls.__new__(cls)
88
+ memo[id(self)] = result
89
+
90
+ fields = dataclass_fields_asdict(self)
91
+ for name, value in fields.items():
92
+ setattr(result, name, deepcopy(value))
93
+
94
+ return result