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
odxtools/cli/list.py CHANGED
@@ -4,9 +4,14 @@ from typing import Callable, List, Optional
4
4
 
5
5
  import rich
6
6
 
7
+ from ..comparaminstance import ComparamInstance
7
8
  from ..database import Database
9
+ from ..dataobjectproperty import DataObjectProperty
8
10
  from ..diagcomm import DiagComm
9
- from ..diaglayer import DiagLayer
11
+ from ..diaglayers.basevariant import BaseVariant
12
+ from ..diaglayers.diaglayer import DiagLayer
13
+ from ..diaglayers.ecuvariant import EcuVariant
14
+ from ..diaglayers.hierarchyelement import HierarchyElement
10
15
  from ..diagservice import DiagService
11
16
  from ..singleecujob import SingleEcuJob
12
17
  from . import _parser_utils
@@ -47,7 +52,7 @@ def print_summary(odxdb: Database,
47
52
  if diag_layers:
48
53
  rich.print("\n")
49
54
  rich.print(f"Overview of diagnostic layers: ")
50
- print_dl_metrics(diag_layers, print_fn=rich.print)
55
+ print_dl_metrics(diag_layers)
51
56
 
52
57
  for dl in diag_layers:
53
58
  rich.print("\n")
@@ -56,15 +61,14 @@ def print_summary(odxdb: Database,
56
61
 
57
62
  all_services: List[DiagComm] = sorted(dl.services, key=lambda x: x.short_name)
58
63
 
59
- data_object_properties = dl.diag_data_dictionary_spec.data_object_props
60
- comparams = dl.comparams
64
+ if isinstance(dl, (BaseVariant, EcuVariant)):
65
+ for proto in dl.protocols:
66
+ if (can_rx_id := dl.get_can_receive_id(proto.short_name)) is not None:
67
+ rich.print(
68
+ f" CAN receive ID for protocol '{proto.short_name}': 0x{can_rx_id:x}")
61
69
 
62
- for proto in dl.protocols:
63
- if (can_rx_id := dl.get_can_receive_id(proto.short_name)) is not None:
64
- rich.print(f" CAN receive ID for protocol '{proto.short_name}': 0x{can_rx_id:x}")
65
-
66
- if (can_tx_id := dl.get_can_send_id(proto.short_name)) is not None:
67
- rich.print(f" CAN send ID for protocol '{proto.short_name}': 0x{can_tx_id:x}")
70
+ if (can_tx_id := dl.get_can_send_id(proto.short_name)) is not None:
71
+ rich.print(f" CAN send ID for protocol '{proto.short_name}': 0x{can_tx_id:x}")
68
72
 
69
73
  if dl.description:
70
74
  desc = format_desc(dl.description, indent=2)
@@ -89,13 +93,14 @@ def print_summary(odxdb: Database,
89
93
  print_pre_condition_states=print_pre_condition_states,
90
94
  print_state_transitions=print_state_transitions,
91
95
  print_audiences=print_audiences,
92
- allow_unknown_bit_lengths=allow_unknown_bit_lengths,
93
- print_fn=rich.print)
96
+ allow_unknown_bit_lengths=allow_unknown_bit_lengths)
94
97
  elif isinstance(service, SingleEcuJob):
95
98
  rich.print(f" Single ECU job: {service.odx_id}")
96
99
  else:
97
100
  rich.print(f" Unidentifiable service: {service}")
98
-
101
+ ddd_spec = dl.diag_data_dictionary_spec
102
+ data_object_properties: List[
103
+ DataObjectProperty] = [] if ddd_spec is None else ddd_spec.data_object_props
99
104
  if print_dops and len(data_object_properties) > 0:
100
105
  rich.print("\n")
101
106
  rich.print(f"The DOPs of the {dl.variant_type.value} '{dl.short_name}' are: ")
@@ -103,12 +108,16 @@ def print_summary(odxdb: Database,
103
108
  data_object_properties, key=lambda x: (type(x).__name__, x.short_name)):
104
109
  rich.print(" " + str(dop.short_name).replace("\n", "\n "))
105
110
 
106
- if print_comparams and len(comparams) > 0:
111
+ comparam_refs: List[ComparamInstance] = []
112
+ if isinstance(dl, HierarchyElement):
113
+ comparam_refs = dl.comparam_refs
114
+
115
+ if print_comparams and len(comparam_refs) > 0:
107
116
  rich.print("\n")
108
117
  rich.print(
109
118
  f"The communication parameters of the {dl.variant_type.value} '{dl.short_name}' are: "
110
119
  )
111
- for com_param in comparams:
120
+ for com_param in comparam_refs:
112
121
  rich.print(f" {com_param.short_name}: {com_param.value}")
113
122
 
114
123
 
odxtools/cli/snoop.py CHANGED
@@ -4,12 +4,13 @@
4
4
  import argparse
5
5
  import asyncio
6
6
  import sys
7
- from typing import Any, Type
7
+ from typing import Any, List, Optional, Type
8
8
 
9
9
  import can
10
10
 
11
11
  import odxtools.isotp_state_machine as ism
12
12
  import odxtools.uds as uds
13
+ from odxtools.diaglayers.protocol import Protocol
13
14
  from odxtools.exceptions import DecodeError
14
15
  from odxtools.isotp_state_machine import IsoTpStateMachine
15
16
  from odxtools.response import Response, ResponseType
@@ -245,21 +246,43 @@ def run(args: argparse.Namespace) -> None:
245
246
 
246
247
  protocol_name = args.protocol
247
248
  if odx_diag_layer is not None and protocol_name is not None:
248
- protocol_names = [x.short_name for x in odx_diag_layer.protocols]
249
+ protocols: Optional[List[Protocol]] = getattr(odx_diag_layer, "protocols", None)
250
+
251
+ if protocols is None:
252
+ print(f"ECU variant {odx_diag_layer.short_name} is of type "
253
+ f"{odx_diag_layer.variant_type.value} and thus does not "
254
+ f"feature any protocols")
255
+ sys.exit(1)
256
+
257
+ protocol_names = [x.short_name for x in protocols]
249
258
  if protocol_name not in protocol_names:
250
259
  print(f"ECU variant {odx_diag_layer.short_name} does not support "
251
260
  f"a protocol named '{protocol_name}'. Supported protocols are:")
252
- for x in odx_diag_layer.protocols:
261
+ for x in protocols:
253
262
  desc = "" if x.description is None else f": {x.description}"
254
263
  print(f" {x.short_name}{desc}")
255
264
  sys.exit(1)
256
265
 
257
266
  # if no can IDs have been explicitly specified, take them from the DL
258
267
  if args.rx is None and odx_diag_layer:
259
- args.rx = str(odx_diag_layer.get_can_receive_id(protocol=protocol_name))
268
+ get_can_rx_id = getattr(odx_diag_layer, "get_can_receive_id", None)
269
+ if get_can_rx_id is None:
270
+ print(f"ECU variant {odx_diag_layer.short_name} is of type "
271
+ f"{odx_diag_layer.variant_type.value} and thus does not "
272
+ f"provide any communication parameters")
273
+ sys.exit(1)
274
+
275
+ args.rx = str(get_can_rx_id(protocol=protocol_name))
260
276
 
261
277
  if args.tx is None and odx_diag_layer:
262
- args.tx = str(odx_diag_layer.get_can_send_id(protocol=protocol_name))
278
+ get_can_tx_id = getattr(odx_diag_layer, "get_can_send_id", None)
279
+ if get_can_tx_id is None:
280
+ print(f"ECU variant {odx_diag_layer.short_name} is of type "
281
+ f"{odx_diag_layer.variant_type.value} and thus does not "
282
+ f"provide any communication parameters")
283
+ sys.exit(1)
284
+
285
+ args.tx = str(get_can_tx_id(protocol=protocol_name))
263
286
 
264
287
  if args.rx is None:
265
288
  print(f"Could not determine a CAN receive ID.")
odxtools/codec.py ADDED
@@ -0,0 +1,211 @@
1
+ # SPDX-License-Identifier: MIT
2
+ import typing
3
+ from typing import List, Optional, runtime_checkable
4
+
5
+ from .decodestate import DecodeState
6
+ from .encodestate import EncodeState
7
+ from .exceptions import EncodeError, odxraise
8
+ from .odxtypes import ParameterValue
9
+ from .parameters.codedconstparameter import CodedConstParameter
10
+ from .parameters.matchingrequestparameter import MatchingRequestParameter
11
+ from .parameters.parameter import Parameter
12
+ from .parameters.physicalconstantparameter import PhysicalConstantParameter
13
+
14
+
15
+ @runtime_checkable
16
+ class Codec(typing.Protocol):
17
+ """Any object which can be en- or decoded to be transferred over
18
+ the wire implements this API.
19
+ """
20
+
21
+ @property
22
+ def short_name(self) -> str:
23
+ return ""
24
+
25
+ def encode_into_pdu(self, physical_value: Optional[ParameterValue],
26
+ encode_state: EncodeState) -> None:
27
+ ...
28
+
29
+ def decode_from_pdu(self, decode_state: DecodeState) -> ParameterValue:
30
+ ...
31
+
32
+ def get_static_bit_length(self) -> Optional[int]:
33
+ ...
34
+
35
+
36
+ @runtime_checkable
37
+ class CompositeCodec(Codec, typing.Protocol):
38
+ """Any object which can be en- or decoded to be transferred over
39
+ the wire which is composed of multiple parameter implements this
40
+ API.
41
+
42
+ """
43
+
44
+ @property
45
+ def parameters(self) -> List[Parameter]:
46
+ return []
47
+
48
+ @property
49
+ def required_parameters(self) -> List[Parameter]:
50
+ return []
51
+
52
+ @property
53
+ def free_parameters(self) -> List[Parameter]:
54
+ return []
55
+
56
+
57
+ # some helper functions useful for composite codec objects
58
+ def composite_codec_get_static_bit_length(codec: CompositeCodec) -> Optional[int]:
59
+ """Compute the length of a composite codec object in bits
60
+
61
+ This is basically the sum of the lengths of all parameters. If the
62
+ length of any parameter can only determined at runtime, `None` is
63
+ returned.
64
+ """
65
+
66
+ cursor = 0
67
+ byte_length = 0
68
+ for param in codec.parameters:
69
+ param_bit_length = param.get_static_bit_length()
70
+ if param_bit_length is None:
71
+ # We were not able to calculate a static bit length
72
+ return None
73
+ elif param.byte_position is not None:
74
+ cursor = param.byte_position
75
+
76
+ cursor += ((param.bit_position or 0) + param_bit_length + 7) // 8
77
+ byte_length = max(byte_length, cursor)
78
+
79
+ return byte_length * 8
80
+
81
+
82
+ def composite_codec_get_required_parameters(codec: CompositeCodec) -> List[Parameter]:
83
+ """Return the list of parameters which are required to be
84
+ specified for encoding the composite codec object
85
+
86
+ I.e., all free parameters that do not exhibit a default value.
87
+ """
88
+ return [p for p in codec.parameters if p.is_required]
89
+
90
+
91
+ def composite_codec_get_free_parameters(codec: CompositeCodec) -> List[Parameter]:
92
+ """Return the list of parameters which can be freely specified by
93
+ the user when encoding the composite codec object
94
+
95
+ This means all required parameters plus parameters that can be
96
+ omitted because they specify a default.
97
+ """
98
+ return [p for p in codec.parameters if p.is_settable]
99
+
100
+
101
+ def composite_codec_get_coded_const_prefix(codec: CompositeCodec,
102
+ request_prefix: bytes = b'') -> bytes:
103
+ encode_state = EncodeState(coded_message=bytearray(), triggering_request=request_prefix)
104
+
105
+ for param in codec.parameters:
106
+ if (isinstance(param, MatchingRequestParameter) and param.request_byte_position < len(request_prefix)) or \
107
+ isinstance(param, (CodedConstParameter, PhysicalConstantParameter)):
108
+ param.encode_into_pdu(physical_value=None, encode_state=encode_state)
109
+ else:
110
+ break
111
+
112
+ return encode_state.coded_message
113
+
114
+
115
+ def composite_codec_encode_into_pdu(codec: CompositeCodec, physical_value: Optional[ParameterValue],
116
+ encode_state: EncodeState) -> None:
117
+ from .parameters.lengthkeyparameter import LengthKeyParameter
118
+ from .parameters.tablekeyparameter import TableKeyParameter
119
+
120
+ if not isinstance(physical_value, dict):
121
+ odxraise(
122
+ f"Expected a dictionary for the values of {codec.short_name}, "
123
+ f"got {type(physical_value).__name__}", EncodeError)
124
+ elif encode_state.cursor_bit_position != 0:
125
+ odxraise(
126
+ f"Compositional codec objecs must be byte aligned, but "
127
+ f"{codec.short_name} requested to be at bit position "
128
+ f"{encode_state.cursor_bit_position}", EncodeError)
129
+ encode_state.bit_position = 0
130
+
131
+ orig_origin = encode_state.origin_byte_position
132
+ encode_state.origin_byte_position = encode_state.cursor_byte_position
133
+
134
+ orig_is_end_of_pdu = encode_state.is_end_of_pdu
135
+ encode_state.is_end_of_pdu = False
136
+
137
+ # ensure that no values for unknown parameters are specified.
138
+ if not encode_state.allow_unknown_parameters:
139
+ param_names = {param.short_name for param in codec.parameters}
140
+ for param_value_name in physical_value:
141
+ if param_value_name not in param_names:
142
+ odxraise(f"Value for unknown parameter '{param_value_name}' specified "
143
+ f"for composite codec object {codec.short_name}")
144
+
145
+ for param in codec.parameters:
146
+ if id(param) == id(codec.parameters[-1]):
147
+ # The last parameter of the composite codec object is at
148
+ # the end of the PDU if the codec object itself is at the
149
+ # end of the PDU.
150
+ #
151
+ # TODO: This assumes that the last parameter specified in
152
+ # the ODX is located last in the PDU...
153
+ encode_state.is_end_of_pdu = orig_is_end_of_pdu
154
+
155
+ if isinstance(param, (LengthKeyParameter, TableKeyParameter)):
156
+ # At this point, we encode a placeholder value for length-
157
+ # and table keys, since these can be specified
158
+ # implicitly (i.e., by means of parameters that use
159
+ # these keys). To avoid getting an "overlapping
160
+ # parameter" warning, we must encode a value of zero
161
+ # into the PDU here and add the real value of the
162
+ # parameter in a post-processing step.
163
+ param.encode_placeholder_into_pdu(
164
+ physical_value=physical_value.get(param.short_name), encode_state=encode_state)
165
+
166
+ continue
167
+
168
+ if param.is_required and param.short_name not in physical_value:
169
+ odxraise(f"No value for required parameter {param.short_name} specified", EncodeError)
170
+
171
+ param_phys_value = physical_value.get(param.short_name)
172
+ param.encode_into_pdu(physical_value=param_phys_value, encode_state=encode_state)
173
+
174
+ encode_state.journal.append((param, param_phys_value))
175
+
176
+ encode_state.is_end_of_pdu = False
177
+
178
+ # encode the length- and table keys. This cannot be done above
179
+ # because we allow these to be defined implicitly (i.e. they
180
+ # are defined by their respective users)
181
+ for param in codec.parameters:
182
+ if not isinstance(param, (LengthKeyParameter, TableKeyParameter)):
183
+ # the current parameter is neither a length- nor a table key
184
+ continue
185
+
186
+ # Encode the value of the key parameter into the message
187
+ param.encode_value_into_pdu(encode_state=encode_state)
188
+
189
+ encode_state.origin_byte_position = orig_origin
190
+
191
+
192
+ def composite_codec_decode_from_pdu(codec: CompositeCodec,
193
+ decode_state: DecodeState) -> ParameterValue:
194
+ # move the origin since positions specified by sub-parameters of
195
+ # composite codec objects are relative to the beginning of the
196
+ # object.
197
+ orig_origin = decode_state.origin_byte_position
198
+ decode_state.origin_byte_position = decode_state.cursor_byte_position
199
+
200
+ result = {}
201
+ for param in codec.parameters:
202
+ value = param.decode_from_pdu(decode_state)
203
+
204
+ decode_state.journal.append((param, value))
205
+ result[param.short_name] = value
206
+
207
+ # decoding of the composite codec object finished. go back the
208
+ # original origin.
209
+ decode_state.origin_byte_position = orig_origin
210
+
211
+ return result
@@ -0,0 +1,122 @@
1
+ # SPDX-License-Identifier: MIT
2
+ import warnings
3
+ from dataclasses import dataclass
4
+ from enum import Enum
5
+ from typing import Any, Dict, List, Optional
6
+ from xml.etree import ElementTree
7
+
8
+ from .description import Description
9
+ from .diagcomm import DiagComm
10
+ from .diagservice import DiagService
11
+ from .exceptions import OdxWarning, odxraise, odxrequire
12
+ from .odxlink import OdxDocFragment, OdxLinkDatabase, OdxLinkId, OdxLinkRef, resolve_snref
13
+ from .parameters.parameter import Parameter
14
+ from .snrefcontext import SnRefContext
15
+
16
+
17
+ class CommRelationValueType(Enum):
18
+ CURRENT = "CURRENT"
19
+ STORED = "STORED"
20
+ STATIC = "STATIC"
21
+ SUBSTITUTED = "SUBSTITUTED"
22
+
23
+
24
+ @dataclass
25
+ class CommRelation:
26
+ description: Optional[Description]
27
+ relation_type: str
28
+ diag_comm_ref: Optional[OdxLinkRef]
29
+ diag_comm_snref: Optional[str]
30
+ in_param_if_snref: Optional[str]
31
+ #in_param_if_snpathref: Optional[str] # TODO
32
+ out_param_if_snref: Optional[str]
33
+ #out_param_if_snpathref: Optional[str] # TODO
34
+ value_type_raw: Optional[CommRelationValueType]
35
+
36
+ @property
37
+ def diag_comm(self) -> DiagComm:
38
+ return self._diag_comm
39
+
40
+ @property
41
+ def in_param_if(self) -> Optional[Parameter]:
42
+ return self._in_param_if
43
+
44
+ @property
45
+ def out_param_if(self) -> Optional[Parameter]:
46
+ return self._out_param_if
47
+
48
+ @property
49
+ def value_type(self) -> CommRelationValueType:
50
+ if self.value_type_raw is None:
51
+ return CommRelationValueType.CURRENT
52
+
53
+ return self.value_type_raw
54
+
55
+ @staticmethod
56
+ def from_et(et_element: ElementTree.Element, doc_frags: List[OdxDocFragment]) -> "CommRelation":
57
+ description = Description.from_et(et_element.find("DESC"), doc_frags)
58
+ relation_type = odxrequire(et_element.findtext("RELATION-TYPE"))
59
+
60
+ diag_comm_ref = OdxLinkRef.from_et(et_element.find("DIAG-COMM-REF"), doc_frags)
61
+ diag_comm_snref = None
62
+ if (diag_comm_snref_elem := et_element.find("DIAG-COMM-SNREF")) is not None:
63
+ diag_comm_snref = odxrequire(diag_comm_snref_elem.get("SHORT-NAME"))
64
+
65
+ in_param_if_snref = None
66
+ if (in_param_if_snref_elem := et_element.find("IN-PARAM-IF-SNREF")) is not None:
67
+ in_param_if_snref = odxrequire(in_param_if_snref_elem.get("SHORT-NAME"))
68
+
69
+ if et_element.find("IN-PARAM-IF-SNPATHREF") is not None:
70
+ warnings.warn("SNPATHREFs are not supported by odxtools yet", OdxWarning, stacklevel=1)
71
+
72
+ out_param_if_snref = None
73
+ if (out_param_if_snref_elem := et_element.find("OUT-PARAM-IF-SNREF")) is not None:
74
+ out_param_if_snref = odxrequire(out_param_if_snref_elem.get("SHORT-NAME"))
75
+
76
+ if et_element.find("OUT-PARAM-IF-SNPATHREF") is not None:
77
+ warnings.warn("SNPATHREFs are not supported by odxtools yet", OdxWarning, stacklevel=1)
78
+
79
+ value_type_raw = None
80
+ if (value_type_str := et_element.get("VALUE-TYPE")) is not None:
81
+ try:
82
+ value_type_raw = CommRelationValueType(value_type_str)
83
+ except ValueError:
84
+ odxraise(f"Encountered unknown comm relation value type '{value_type_str}'")
85
+
86
+ return CommRelation(
87
+ description=description,
88
+ relation_type=relation_type,
89
+ diag_comm_ref=diag_comm_ref,
90
+ diag_comm_snref=diag_comm_snref,
91
+ in_param_if_snref=in_param_if_snref,
92
+ out_param_if_snref=out_param_if_snref,
93
+ value_type_raw=value_type_raw)
94
+
95
+ def _build_odxlinks(self) -> Dict[OdxLinkId, Any]:
96
+ return {}
97
+
98
+ def _resolve_odxlinks(self, odxlinks: OdxLinkDatabase) -> None:
99
+ if self.diag_comm_ref is not None:
100
+ self._diag_comm = odxlinks.resolve(self.diag_comm_ref, DiagComm)
101
+
102
+ def _resolve_snrefs(self, context: SnRefContext) -> None:
103
+ diag_layer = odxrequire(context.diag_layer)
104
+
105
+ if self.diag_comm_snref is not None:
106
+ self._diag_comm = resolve_snref(self.diag_comm_snref, diag_layer.diag_comms, DiagComm)
107
+
108
+ service = self.diag_comm
109
+ if not isinstance(service, DiagService):
110
+ odxraise(f"DIAG-VARIABLE references non-service {type(service).__name__} "
111
+ f"diagnostic communication")
112
+
113
+ self._in_param_if = None
114
+ if self.in_param_if_snref is not None:
115
+ self._in_param_if = resolve_snref(self.in_param_if_snref,
116
+ odxrequire(service.request).parameters, Parameter)
117
+
118
+ self._out_param_if = None
119
+ if self.out_param_if_snref is not None:
120
+ self._out_param_if = resolve_snref(self.out_param_if_snref,
121
+ odxrequire(service.positive_responses[0]).parameters,
122
+ Parameter)
odxtools/companydata.py CHANGED
@@ -1,6 +1,6 @@
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 .companyspecificinfo import CompanySpecificInfo
@@ -8,12 +8,10 @@ from .element import IdentifiableElement
8
8
  from .exceptions import odxrequire
9
9
  from .nameditemlist import NamedItemList
10
10
  from .odxlink import OdxDocFragment, OdxLinkDatabase, OdxLinkId
11
+ from .snrefcontext import SnRefContext
11
12
  from .teammember import TeamMember
12
13
  from .utils import dataclass_fields_asdict
13
14
 
14
- if TYPE_CHECKING:
15
- from .diaglayer import DiagLayer
16
-
17
15
 
18
16
  @dataclass
19
17
  class CompanyData(IdentifiableElement):
@@ -64,9 +62,9 @@ class CompanyData(IdentifiableElement):
64
62
  if self.company_specific_info:
65
63
  self.company_specific_info._resolve_odxlinks(odxlinks)
66
64
 
67
- def _resolve_snrefs(self, diag_layer: "DiagLayer") -> None:
65
+ def _resolve_snrefs(self, context: SnRefContext) -> None:
68
66
  for tm in self.team_members:
69
- tm._resolve_snrefs(diag_layer)
67
+ tm._resolve_snrefs(context)
70
68
 
71
69
  if self.company_specific_info:
72
- self.company_specific_info._resolve_snrefs(diag_layer)
70
+ self.company_specific_info._resolve_snrefs(context)
@@ -1,18 +1,15 @@
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 .companydata import CompanyData
7
- from .createsdgs import create_sdgs_from_et
8
7
  from .exceptions import odxrequire
9
8
  from .odxlink import OdxDocFragment, OdxLinkDatabase, OdxLinkId, OdxLinkRef
9
+ from .snrefcontext import SnRefContext
10
10
  from .specialdatagroup import SpecialDataGroup
11
11
  from .teammember import TeamMember
12
12
 
13
- if TYPE_CHECKING:
14
- from .diaglayer import DiagLayer
15
-
16
13
 
17
14
  @dataclass
18
15
  class CompanyDocInfo:
@@ -37,7 +34,9 @@ class CompanyDocInfo:
37
34
  OdxLinkRef.from_et(et_element.find("COMPANY-DATA-REF"), doc_frags))
38
35
  team_member_ref = OdxLinkRef.from_et(et_element.find("TEAM-MEMBER-REF"), doc_frags)
39
36
  doc_label = et_element.findtext("DOC-LABEL")
40
- sdgs = create_sdgs_from_et(et_element.find("SDGS"), doc_frags)
37
+ sdgs = [
38
+ SpecialDataGroup.from_et(sdge, doc_frags) for sdge in et_element.iterfind("SDGS/SDG")
39
+ ]
41
40
 
42
41
  return CompanyDocInfo(
43
42
  company_data_ref=company_data_ref,
@@ -64,6 +63,6 @@ class CompanyDocInfo:
64
63
  for sdg in self.sdgs:
65
64
  sdg._resolve_odxlinks(odxlinks)
66
65
 
67
- def _resolve_snrefs(self, diag_layer: "DiagLayer") -> None:
66
+ def _resolve_snrefs(self, context: SnRefContext) -> None:
68
67
  for sdg in self.sdgs:
69
- sdg._resolve_snrefs(diag_layer)
68
+ sdg._resolve_snrefs(context)
@@ -1,14 +1,12 @@
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 .companydata import CompanyData
7
7
  from .exceptions import odxrequire
8
8
  from .odxlink import OdxDocFragment, OdxLinkDatabase, OdxLinkId, OdxLinkRef
9
-
10
- if TYPE_CHECKING:
11
- from .diaglayer import DiagLayer
9
+ from .snrefcontext import SnRefContext
12
10
 
13
11
 
14
12
  @dataclass
@@ -39,5 +37,5 @@ class CompanyRevisionInfo:
39
37
  def _resolve_odxlinks(self, odxlinks: OdxLinkDatabase) -> None:
40
38
  self._company_data = odxlinks.resolve(self.company_data_ref, CompanyData)
41
39
 
42
- def _resolve_snrefs(self, diag_layer: "DiagLayer") -> None:
40
+ def _resolve_snrefs(self, context: SnRefContext) -> None:
43
41
  pass
@@ -1,16 +1,13 @@
1
1
  # SPDX-License-Identifier: MIT
2
2
  from dataclasses import dataclass
3
- from typing import TYPE_CHECKING, Any, Dict, List
3
+ from typing import Any, Dict, List
4
4
  from xml.etree import ElementTree
5
5
 
6
- from .createsdgs import create_sdgs_from_et
7
6
  from .odxlink import OdxDocFragment, OdxLinkDatabase, OdxLinkId
8
7
  from .relateddoc import RelatedDoc
8
+ from .snrefcontext import SnRefContext
9
9
  from .specialdatagroup import SpecialDataGroup
10
10
 
11
- if TYPE_CHECKING:
12
- from .diaglayer import DiagLayer
13
-
14
11
 
15
12
  @dataclass
16
13
  class CompanySpecificInfo:
@@ -25,7 +22,9 @@ class CompanySpecificInfo:
25
22
  for rd in et_element.iterfind("RELATED-DOCS/RELATED-DOC")
26
23
  ]
27
24
 
28
- sdgs = create_sdgs_from_et(et_element.find("SDGS"), doc_frags)
25
+ sdgs = [
26
+ SpecialDataGroup.from_et(sdge, doc_frags) for sdge in et_element.iterfind("SDGS/SDG")
27
+ ]
29
28
 
30
29
  return CompanySpecificInfo(related_docs=related_docs, sdgs=sdgs)
31
30
 
@@ -44,9 +43,9 @@ class CompanySpecificInfo:
44
43
  for sdg in self.sdgs:
45
44
  sdg._resolve_odxlinks(odxlinks)
46
45
 
47
- def _resolve_snrefs(self, diag_layer: "DiagLayer") -> None:
46
+ def _resolve_snrefs(self, context: SnRefContext) -> None:
48
47
  for rd in self.related_docs:
49
- rd._resolve_snrefs(diag_layer)
48
+ rd._resolve_snrefs(context)
50
49
 
51
50
  for sdg in self.sdgs:
52
- sdg._resolve_snrefs(diag_layer)
51
+ sdg._resolve_snrefs(context)
odxtools/comparam.py CHANGED
@@ -1,17 +1,15 @@
1
1
  # SPDX-License-Identifier: MIT
2
2
  from dataclasses import dataclass
3
- from typing import TYPE_CHECKING, Any, Dict, List
3
+ from typing import Any, Dict, List
4
4
  from xml.etree import ElementTree
5
5
 
6
6
  from .basecomparam import BaseComparam
7
7
  from .dataobjectproperty import DataObjectProperty
8
8
  from .exceptions import odxrequire
9
9
  from .odxlink import OdxDocFragment, OdxLinkDatabase, OdxLinkId, OdxLinkRef
10
+ from .snrefcontext import SnRefContext
10
11
  from .utils import dataclass_fields_asdict
11
12
 
12
- if TYPE_CHECKING:
13
- from .diaglayer import DiagLayer
14
-
15
13
 
16
14
  @dataclass
17
15
  class Comparam(BaseComparam):
@@ -41,5 +39,5 @@ class Comparam(BaseComparam):
41
39
 
42
40
  self._dop = odxlinks.resolve(self.dop_ref, DataObjectProperty)
43
41
 
44
- def _resolve_snrefs(self, diag_layer: "DiagLayer") -> None:
45
- super()._resolve_snrefs(diag_layer)
42
+ def _resolve_snrefs(self, context: SnRefContext) -> None:
43
+ super()._resolve_snrefs(context)