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
@@ -0,0 +1,58 @@
1
+ # SPDX-License-Identifier: MIT
2
+ from dataclasses import dataclass
3
+ from typing import Any, Dict, List
4
+ from xml.etree import ElementTree
5
+
6
+ from ..comparaminstance import ComparamInstance
7
+ from ..odxlink import OdxDocFragment, OdxLinkDatabase, OdxLinkId
8
+ from ..snrefcontext import SnRefContext
9
+ from ..utils import dataclass_fields_asdict
10
+ from .diaglayerraw import DiagLayerRaw
11
+
12
+
13
+ @dataclass
14
+ class HierarchyElementRaw(DiagLayerRaw):
15
+ """This is the base class for diagnostic layers that may be involved in value inheritance
16
+
17
+ This class represents the data present in the XML, not the "logical" view.
18
+ """
19
+
20
+ comparam_refs: List[ComparamInstance]
21
+
22
+ @staticmethod
23
+ def from_et(et_element: ElementTree.Element,
24
+ doc_frags: List[OdxDocFragment]) -> "HierarchyElementRaw":
25
+ # objects contained by diagnostic layers exibit an additional
26
+ # document fragment for the diag layer, so we use the document
27
+ # fragments of the odx id of the diag layer for IDs of
28
+ # contained objects.
29
+ dlr = DiagLayerRaw.from_et(et_element, doc_frags)
30
+ kwargs = dataclass_fields_asdict(dlr)
31
+ doc_frags = dlr.odx_id.doc_fragments
32
+
33
+ comparam_refs = [
34
+ ComparamInstance.from_et(el, doc_frags)
35
+ for el in et_element.iterfind("COMPARAM-REFS/COMPARAM-REF")
36
+ ]
37
+
38
+ return HierarchyElementRaw(comparam_refs=comparam_refs, **kwargs)
39
+
40
+ def _build_odxlinks(self) -> Dict[OdxLinkId, Any]:
41
+ result = super()._build_odxlinks()
42
+
43
+ for comparam_ref in self.comparam_refs:
44
+ result.update(comparam_ref._build_odxlinks())
45
+
46
+ return result
47
+
48
+ def _resolve_odxlinks(self, odxlinks: OdxLinkDatabase) -> None:
49
+ super()._resolve_odxlinks(odxlinks)
50
+
51
+ for comparam_ref in self.comparam_refs:
52
+ comparam_ref._resolve_odxlinks(odxlinks)
53
+
54
+ def _resolve_snrefs(self, context: SnRefContext) -> None:
55
+ super()._resolve_snrefs(context)
56
+
57
+ for comparam_ref in self.comparam_refs:
58
+ comparam_ref._resolve_snrefs(context)
@@ -0,0 +1,64 @@
1
+ # SPDX-License-Identifier: MIT
2
+ from copy import deepcopy
3
+ from dataclasses import dataclass
4
+ from typing import Any, Dict, List, Optional, cast
5
+ from xml.etree import ElementTree
6
+
7
+ from ..comparamspec import ComparamSpec
8
+ from ..exceptions import odxassert
9
+ from ..odxlink import OdxDocFragment
10
+ from ..protstack import ProtStack
11
+ from .hierarchyelement import HierarchyElement
12
+ from .protocolraw import ProtocolRaw
13
+
14
+
15
+ @dataclass
16
+ class Protocol(HierarchyElement):
17
+ """This is the class for primitives that are common for a given communication protocol
18
+
19
+ Most importantly this diagnostic layer is responsible for defining
20
+ the communication parameters that ought to be used.
21
+ """
22
+
23
+ @property
24
+ def protocol_raw(self) -> ProtocolRaw:
25
+ return cast(ProtocolRaw, self.diag_layer_raw)
26
+
27
+ @property
28
+ def comparam_spec(self) -> ComparamSpec:
29
+ return self.protocol_raw.comparam_spec
30
+
31
+ @property
32
+ def prot_stack(self) -> Optional[ProtStack]:
33
+ return self.protocol_raw.prot_stack
34
+
35
+ @staticmethod
36
+ def from_et(et_element: ElementTree.Element, doc_frags: List[OdxDocFragment]) -> "Protocol":
37
+ protocol_raw = ProtocolRaw.from_et(et_element, doc_frags)
38
+
39
+ return Protocol(diag_layer_raw=protocol_raw)
40
+
41
+ def __post_init__(self) -> None:
42
+ super().__post_init__()
43
+
44
+ odxassert(
45
+ isinstance(self.diag_layer_raw, ProtocolRaw),
46
+ "The raw diagnostic layer passed to Protocol "
47
+ "must be a ProtocolRaw")
48
+
49
+ def __deepcopy__(self, memo: Dict[int, Any]) -> Any:
50
+ """Create a deep copy of the protocol layer
51
+
52
+ Note that the copied diagnostic layer is not fully
53
+ initialized, so `_finalize_init()` should to be called on it
54
+ before it can be used normally.
55
+ """
56
+
57
+ result = super().__deepcopy__(memo)
58
+
59
+ # note that the self.protocol_raw object is *not* copied at
60
+ # this place because the attribute points to the same object
61
+ # as self.diag_layer_raw.
62
+ result.protocol_raw = deepcopy(self.protocol_raw, memo)
63
+
64
+ return result
@@ -0,0 +1,91 @@
1
+ # SPDX-License-Identifier: MIT
2
+ from dataclasses import dataclass
3
+ from typing import Any, Dict, List, Optional
4
+ from xml.etree import ElementTree
5
+
6
+ from ..comparamspec import ComparamSpec
7
+ from ..exceptions import odxrequire
8
+ from ..odxlink import OdxDocFragment, OdxLinkDatabase, OdxLinkId, OdxLinkRef, resolve_snref
9
+ from ..parentref import ParentRef
10
+ from ..protstack import ProtStack
11
+ from ..snrefcontext import SnRefContext
12
+ from ..utils import dataclass_fields_asdict
13
+ #from .comparaminstance import ComparamInstance
14
+ from .hierarchyelementraw import HierarchyElementRaw
15
+
16
+
17
+ @dataclass
18
+ class ProtocolRaw(HierarchyElementRaw):
19
+ """This is the base class for diagnostic layers that describe a
20
+ protocol which can be used to communicate with an ECU
21
+
22
+ This class represents the data present in the XML, not the "logical" view.
23
+
24
+ """
25
+
26
+ comparam_spec_ref: OdxLinkRef
27
+ prot_stack_snref: Optional[str]
28
+ parent_refs: List[ParentRef]
29
+
30
+ @property
31
+ def comparam_spec(self) -> ComparamSpec:
32
+ return self._comparam_spec
33
+
34
+ @property
35
+ def prot_stack(self) -> Optional[ProtStack]:
36
+ return self._prot_stack
37
+
38
+ @staticmethod
39
+ def from_et(et_element: ElementTree.Element, doc_frags: List[OdxDocFragment]) -> "ProtocolRaw":
40
+ # objects contained by diagnostic layers exibit an additional
41
+ # document fragment for the diag layer, so we use the document
42
+ # fragments of the odx id of the diag layer for IDs of
43
+ # contained objects.
44
+ her = HierarchyElementRaw.from_et(et_element, doc_frags)
45
+ kwargs = dataclass_fields_asdict(her)
46
+ doc_frags = her.odx_id.doc_fragments
47
+
48
+ comparam_spec_ref = OdxLinkRef.from_et(
49
+ odxrequire(et_element.find("COMPARAM-SPEC-REF")), doc_frags)
50
+
51
+ prot_stack_snref = None
52
+ if (prot_stack_snref_elem := et_element.find("PROT-STACK-SNREF")) is not None:
53
+ prot_stack_snref = odxrequire(prot_stack_snref_elem.get("SHORT-NAME"))
54
+
55
+ parent_refs = [
56
+ ParentRef.from_et(pr_el, doc_frags)
57
+ for pr_el in et_element.iterfind("PARENT-REFS/PARENT-REF")
58
+ ]
59
+
60
+ return ProtocolRaw(
61
+ comparam_spec_ref=comparam_spec_ref,
62
+ prot_stack_snref=prot_stack_snref,
63
+ parent_refs=parent_refs,
64
+ **kwargs)
65
+
66
+ def _build_odxlinks(self) -> Dict[OdxLinkId, Any]:
67
+ result = super()._build_odxlinks()
68
+
69
+ for parent_ref in self.parent_refs:
70
+ result.update(parent_ref._build_odxlinks())
71
+
72
+ return result
73
+
74
+ def _resolve_odxlinks(self, odxlinks: OdxLinkDatabase) -> None:
75
+ super()._resolve_odxlinks(odxlinks)
76
+
77
+ for parent_ref in self.parent_refs:
78
+ parent_ref._resolve_odxlinks(odxlinks)
79
+
80
+ self._comparam_spec = odxlinks.resolve(self.comparam_spec_ref, ComparamSpec)
81
+
82
+ def _resolve_snrefs(self, context: SnRefContext) -> None:
83
+ super()._resolve_snrefs(context)
84
+
85
+ self._prot_stack = None
86
+ if self.prot_stack_snref is not None:
87
+ self._prot_stack = resolve_snref(self.prot_stack_snref, self._comparam_spec.prot_stacks,
88
+ ProtStack)
89
+
90
+ for parent_ref in self.parent_refs:
91
+ parent_ref._resolve_snrefs(context)
@@ -1,26 +1,23 @@
1
1
  # SPDX-License-Identifier: MIT
2
2
  from dataclasses import dataclass
3
- from typing import TYPE_CHECKING, Any, Dict, List, Optional, Union
3
+ from typing import Any, Dict, List, Optional
4
4
  from xml.etree import ElementTree
5
5
 
6
- from .createsdgs import create_sdgs_from_et
7
6
  from .element import IdentifiableElement
8
7
  from .exceptions import odxrequire
9
8
  from .odxlink import OdxDocFragment, OdxLinkDatabase, OdxLinkId
10
9
  from .odxtypes import odxstr_to_bool
10
+ from .snrefcontext import SnRefContext
11
11
  from .specialdatagroup import SpecialDataGroup
12
12
  from .utils import dataclass_fields_asdict
13
13
 
14
- if TYPE_CHECKING:
15
- from .diaglayer import DiagLayer
16
-
17
14
 
18
15
  @dataclass
19
16
  class DiagnosticTroubleCode(IdentifiableElement):
20
17
  trouble_code: int
21
18
  text: Optional[str]
22
19
  display_trouble_code: Optional[str]
23
- level: Union[int, None]
20
+ level: Optional[int]
24
21
  is_temporary_raw: Optional[bool]
25
22
  sdgs: List[SpecialDataGroup]
26
23
 
@@ -43,7 +40,9 @@ class DiagnosticTroubleCode(IdentifiableElement):
43
40
  level = None
44
41
 
45
42
  is_temporary_raw = odxstr_to_bool(et_element.get("IS-TEMPORARY"))
46
- sdgs = create_sdgs_from_et(et_element.find("SDGS"), doc_frags)
43
+ sdgs = [
44
+ SpecialDataGroup.from_et(sdge, doc_frags) for sdge in et_element.iterfind("SDGS/SDG")
45
+ ]
47
46
 
48
47
  return DiagnosticTroubleCode(
49
48
  trouble_code=int(odxrequire(et_element.findtext("TROUBLE-CODE"))),
@@ -69,6 +68,6 @@ class DiagnosticTroubleCode(IdentifiableElement):
69
68
  for sdg in self.sdgs:
70
69
  sdg._resolve_odxlinks(odxlinks)
71
70
 
72
- def _resolve_snrefs(self, diag_layer: "DiagLayer") -> None:
71
+ def _resolve_snrefs(self, context: SnRefContext) -> None:
73
72
  for sdg in self.sdgs:
74
- sdg._resolve_snrefs(diag_layer)
73
+ sdg._resolve_snrefs(context)
odxtools/diagservice.py CHANGED
@@ -1,12 +1,12 @@
1
1
  # SPDX-License-Identifier: MIT
2
2
  from dataclasses import dataclass
3
3
  from enum import Enum
4
- from typing import TYPE_CHECKING, Any, Dict, List, Optional, Union, cast
4
+ from typing import Any, Dict, List, Optional, Union, cast
5
5
  from xml.etree import ElementTree
6
6
 
7
7
  from .comparaminstance import ComparamInstance
8
8
  from .diagcomm import DiagComm
9
- from .exceptions import DecodeError, odxassert, odxraise, odxrequire
9
+ from .exceptions import DecodeError, DecodeMismatch, odxassert, odxraise, odxrequire
10
10
  from .message import Message
11
11
  from .nameditemlist import NamedItemList
12
12
  from .odxlink import OdxDocFragment, OdxLinkDatabase, OdxLinkId, OdxLinkRef
@@ -14,11 +14,9 @@ from .odxtypes import ParameterValue, odxstr_to_bool
14
14
  from .parameters.parameter import Parameter
15
15
  from .request import Request
16
16
  from .response import Response
17
+ from .snrefcontext import SnRefContext
17
18
  from .utils import dataclass_fields_asdict
18
19
 
19
- if TYPE_CHECKING:
20
- from .diaglayer import DiagLayer
21
-
22
20
 
23
21
  class Addressing(Enum):
24
22
  FUNCTIONAL = "FUNCTIONAL"
@@ -44,7 +42,9 @@ class DiagService(DiagComm):
44
42
  pos_response_refs: List[OdxLinkRef]
45
43
  neg_response_refs: List[OdxLinkRef]
46
44
 
47
- # TODO: pos_response_suppressable: Optional[PosResponseSuppressable]
45
+ # note that the spec has a typo here: it calls the corresponding
46
+ # XML tag POS-RESPONSE-SUPPRESSABLE...
47
+ # TODO: pos_response_suppressible: Optional[PosResponseSuppressible]
48
48
 
49
49
  is_cyclic_raw: Optional[bool]
50
50
  is_multiple_raw: Optional[bool]
@@ -167,7 +167,7 @@ class DiagService(DiagComm):
167
167
  for cpr in self.comparam_refs:
168
168
  cpr._resolve_odxlinks(odxlinks)
169
169
 
170
- self._request = odxlinks.resolve(self.request_ref)
170
+ self._request = odxlinks.resolve(self.request_ref, Request)
171
171
 
172
172
  self._positive_responses = NamedItemList[Response](
173
173
  [odxlinks.resolve(x, Response) for x in self.pos_response_refs])
@@ -175,16 +175,21 @@ class DiagService(DiagComm):
175
175
  self._negative_responses = NamedItemList[Response](
176
176
  [odxlinks.resolve(x, Response) for x in self.neg_response_refs])
177
177
 
178
- def _resolve_snrefs(self, diag_layer: "DiagLayer") -> None:
179
- super()._resolve_snrefs(diag_layer)
178
+ def _resolve_snrefs(self, context: SnRefContext) -> None:
179
+ context.diag_service = self
180
+
181
+ super()._resolve_snrefs(context)
180
182
 
181
183
  for cpr in self.comparam_refs:
182
- cpr._resolve_snrefs(diag_layer)
184
+ cpr._resolve_snrefs(context)
183
185
 
184
- # comparams named list is lazy loaded
185
- # since ComparamInstance short_name is only valid after resolution
186
+ # The named item list of communication parameters is created
187
+ # here because ComparamInstance.short_name is only valid after
188
+ # reference resolution
186
189
  self._comparams = NamedItemList(self.comparam_refs)
187
190
 
191
+ context.diag_service = None
192
+
188
193
  def decode_message(self, raw_message: bytes) -> Message:
189
194
  request_prefix = b''
190
195
  candidate_coding_objects: List[Union[Request, Response]] = [
@@ -200,28 +205,36 @@ class DiagService(DiagComm):
200
205
  if len(raw_message) >= len(prefix) and prefix == raw_message[:len(prefix)]:
201
206
  coding_objects.append(candidate_coding_object)
202
207
 
203
- if len(coding_objects) != 1:
204
- raise DecodeError(
205
- f"The service {self.short_name} cannot decode the message {raw_message.hex()}")
206
- coding_object = coding_objects[0]
207
- param_dict = coding_object.decode(raw_message)
208
- if not isinstance(param_dict, dict):
209
- # if this happens, this is probably due to a bug in
210
- # coding_object.decode()
211
- raise RuntimeError(f"Expected a set of decoded parameters, got {type(param_dict)}")
212
- return Message(
213
- coded_message=raw_message,
214
- service=self,
215
- coding_object=coding_object,
216
- param_dict=param_dict)
217
-
218
- def encode_request(self, **params: ParameterValue) -> bytes:
219
- """
220
- Composes an UDS request as list of bytes for this service.
221
- Parameters:
222
- ----------
223
- params: dict
224
- Parameters of the RPC as mapping from SHORT-NAME of the parameter to the physical value
208
+ result_list: List[Message] = []
209
+ for coding_object in coding_objects:
210
+ try:
211
+ result_list.append(
212
+ Message(
213
+ coded_message=raw_message,
214
+ service=self,
215
+ coding_object=coding_object,
216
+ param_dict=coding_object.decode(raw_message)))
217
+ except DecodeMismatch:
218
+ # An NRC-CONST or environment data parameter
219
+ # encountered a non-matching value -> coding object
220
+ # does not apply
221
+ pass
222
+
223
+ if len(result_list) < 1:
224
+ odxraise(f"The service {self.short_name} cannot decode the message {raw_message.hex()}",
225
+ DecodeError)
226
+ return Message(
227
+ coded_message=raw_message, service=self, coding_object=None, param_dict={})
228
+ elif len(result_list) > 1:
229
+ odxraise(
230
+ f"The service {self.short_name} cannot uniquely decode the message {raw_message.hex()}",
231
+ DecodeError)
232
+
233
+ return result_list[0]
234
+
235
+ def encode_request(self, **kwargs: ParameterValue) -> bytes:
236
+ """Prepare an array of bytes ready to be send over the wire
237
+ for the request of this service.
225
238
  """
226
239
  # make sure that all parameters which are required for
227
240
  # encoding are specified (parameters which have a default are
@@ -230,31 +243,31 @@ class DiagService(DiagComm):
230
243
  return b''
231
244
 
232
245
  missing_params = {x.short_name
233
- for x in self.request.required_parameters}.difference(params.keys())
246
+ for x in self.request.required_parameters}.difference(kwargs.keys())
234
247
  odxassert(
235
248
  len(missing_params) == 0, f"The parameters {missing_params} are required but missing!")
236
249
 
237
250
  # make sure that no unknown parameters are specified
238
251
  rq_all_param_names = {x.short_name for x in self.request.parameters}
239
252
  odxassert(
240
- set(params.keys()).issubset(rq_all_param_names),
241
- f"Unknown parameters specified for encoding: {params.keys()}, "
253
+ set(kwargs.keys()).issubset(rq_all_param_names),
254
+ f"Unknown parameters specified for encoding: {kwargs.keys()}, "
242
255
  f"known parameters are: {rq_all_param_names}")
243
- return self.request.encode(coded_request=None, **params)
256
+ return self.request.encode(**kwargs)
244
257
 
245
258
  def encode_positive_response(self,
246
259
  coded_request: bytes,
247
260
  response_index: int = 0,
248
- **params: ParameterValue) -> bytes:
261
+ **kwargs: ParameterValue) -> bytes:
249
262
  # TODO: Should the user decide the positive response or what are the differences?
250
- return self.positive_responses[response_index].encode(coded_request, **params)
263
+ return self.positive_responses[response_index].encode(coded_request, **kwargs)
251
264
 
252
265
  def encode_negative_response(self,
253
266
  coded_request: bytes,
254
267
  response_index: int = 0,
255
- **params: ParameterValue) -> bytes:
256
- return self.negative_responses[response_index].encode(coded_request, **params)
268
+ **kwargs: ParameterValue) -> bytes:
269
+ return self.negative_responses[response_index].encode(coded_request, **kwargs)
257
270
 
258
- def __call__(self, **params: ParameterValue) -> bytes:
271
+ def __call__(self, **kwargs: ParameterValue) -> bytes:
259
272
  """Encode a request."""
260
- return self.encode_request(**params)
273
+ return self.encode_request(**kwargs)
@@ -0,0 +1,113 @@
1
+ # SPDX-License-Identifier: MIT
2
+ import typing
3
+ from dataclasses import dataclass
4
+ from typing import Any, Dict, List, Optional, runtime_checkable
5
+ from xml.etree import ElementTree
6
+
7
+ from .admindata import AdminData
8
+ from .commrelation import CommRelation
9
+ from .element import IdentifiableElement
10
+ from .exceptions import odxrequire
11
+ from .nameditemlist import NamedItemList
12
+ from .odxlink import OdxDocFragment, OdxLinkDatabase, OdxLinkId, OdxLinkRef
13
+ from .odxtypes import odxstr_to_bool
14
+ from .snrefcontext import SnRefContext
15
+ from .specialdatagroup import SpecialDataGroup
16
+ from .swvariable import SwVariable
17
+ from .utils import dataclass_fields_asdict
18
+ from .variablegroup import VariableGroup
19
+
20
+
21
+ @runtime_checkable
22
+ class HasDiagVariables(typing.Protocol):
23
+
24
+ @property
25
+ def diag_variables(self) -> "NamedItemList[DiagVariable]":
26
+ ...
27
+
28
+
29
+ @dataclass
30
+ class DiagVariable(IdentifiableElement):
31
+ """Representation of a diagnostic variable
32
+ """
33
+
34
+ admin_data: Optional[AdminData]
35
+ variable_group_ref: OdxLinkRef
36
+ sw_variables: List[SwVariable]
37
+ comm_relations: List[CommRelation]
38
+ #snref_to_tablerow: Optional[SnrefToTableRow] # TODO
39
+ sdgs: List[SpecialDataGroup]
40
+ is_read_before_write_raw: Optional[bool]
41
+
42
+ @property
43
+ def variable_group(self) -> VariableGroup:
44
+ return self._variable_group
45
+
46
+ @property
47
+ def is_read_before_write(self) -> bool:
48
+ return self.is_read_before_write_raw is True
49
+
50
+ @staticmethod
51
+ def from_et(et_element: ElementTree.Element, doc_frags: List[OdxDocFragment]) -> "DiagVariable":
52
+ kwargs = dataclass_fields_asdict(IdentifiableElement.from_et(et_element, doc_frags))
53
+
54
+ admin_data = AdminData.from_et(et_element.find("ADMIN-DATA"), doc_frags)
55
+ variable_group_ref = odxrequire(
56
+ OdxLinkRef.from_et(et_element.find("VARIABLE-GROUP-REF"), doc_frags))
57
+ sw_variables = NamedItemList([
58
+ SwVariable.from_et(swv_elem, doc_frags)
59
+ for swv_elem in et_element.iterfind("SW-VARIABLES/SW-VARIABLE")
60
+ ])
61
+ comm_relations = [
62
+ CommRelation.from_et(cr_elem, doc_frags)
63
+ for cr_elem in et_element.iterfind("COMM-RELATIONS/COMM-RELATION")
64
+ ]
65
+ sdgs = [
66
+ SpecialDataGroup.from_et(sdge, doc_frags) for sdge in et_element.iterfind("SDGS/SDG")
67
+ ]
68
+ is_read_before_write_raw = odxstr_to_bool(et_element.get("IS-READ-BEFORE-WRITE"))
69
+
70
+ return DiagVariable(
71
+ admin_data=admin_data,
72
+ variable_group_ref=variable_group_ref,
73
+ sw_variables=sw_variables,
74
+ comm_relations=comm_relations,
75
+ sdgs=sdgs,
76
+ is_read_before_write_raw=is_read_before_write_raw,
77
+ **kwargs)
78
+
79
+ def _build_odxlinks(self) -> Dict[OdxLinkId, Any]:
80
+ result = {self.odx_id: self}
81
+
82
+ if self.admin_data is not None:
83
+ result.update(self.admin_data._build_odxlinks())
84
+
85
+ for sdg in self.sdgs:
86
+ result.update(sdg._build_odxlinks())
87
+
88
+ for cr in self.comm_relations:
89
+ result.update(cr._build_odxlinks())
90
+
91
+ return result
92
+
93
+ def _resolve_odxlinks(self, odxlinks: OdxLinkDatabase) -> None:
94
+ self._variable_group = odxlinks.resolve(self.variable_group_ref, VariableGroup)
95
+
96
+ if self.admin_data is not None:
97
+ self.admin_data._resolve_odxlinks(odxlinks)
98
+
99
+ for sdg in self.sdgs:
100
+ sdg._resolve_odxlinks(odxlinks)
101
+
102
+ for cr in self.comm_relations:
103
+ cr._resolve_odxlinks(odxlinks)
104
+
105
+ def _resolve_snrefs(self, context: SnRefContext) -> None:
106
+ if self.admin_data is not None:
107
+ self.admin_data._resolve_snrefs(context)
108
+
109
+ for sdg in self.sdgs:
110
+ sdg._resolve_snrefs(context)
111
+
112
+ for cr in self.comm_relations:
113
+ cr._resolve_snrefs(context)
odxtools/docrevision.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, Optional
3
+ from typing import Any, Dict, List, Optional
4
4
  from xml.etree import ElementTree
5
5
 
6
6
  from .companyrevisioninfo import CompanyRevisionInfo
7
7
  from .exceptions import odxrequire
8
8
  from .modification import Modification
9
9
  from .odxlink import OdxDocFragment, OdxLinkDatabase, OdxLinkId, OdxLinkRef
10
+ from .snrefcontext import SnRefContext
10
11
  from .teammember import TeamMember
11
12
 
12
- if TYPE_CHECKING:
13
- from .diaglayer import DiagLayer
14
-
15
13
 
16
14
  @dataclass
17
15
  class DocRevision:
@@ -75,9 +73,9 @@ class DocRevision:
75
73
  for mod in self.modifications:
76
74
  mod._resolve_odxlinks(odxlinks)
77
75
 
78
- def _resolve_snrefs(self, diag_layer: "DiagLayer") -> None:
76
+ def _resolve_snrefs(self, context: SnRefContext) -> None:
79
77
  for cri in self.company_revision_infos:
80
- cri._resolve_snrefs(diag_layer)
78
+ cri._resolve_snrefs(context)
81
79
 
82
80
  for mod in self.modifications:
83
- mod._resolve_snrefs(diag_layer)
81
+ mod._resolve_snrefs(context)
odxtools/dopbase.py CHANGED
@@ -1,27 +1,27 @@
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 .admindata import AdminData
7
- from .createsdgs import create_sdgs_from_et
8
7
  from .decodestate import DecodeState
9
8
  from .element import IdentifiableElement
10
9
  from .encodestate import EncodeState
11
10
  from .odxlink import OdxDocFragment, OdxLinkDatabase, OdxLinkId
12
11
  from .odxtypes import ParameterValue
12
+ from .snrefcontext import SnRefContext
13
13
  from .specialdatagroup import SpecialDataGroup
14
14
  from .utils import dataclass_fields_asdict
15
15
 
16
- if TYPE_CHECKING:
17
- from .diaglayer import DiagLayer
18
-
19
16
 
20
17
  @dataclass
21
18
  class DopBase(IdentifiableElement):
22
- """Base class for all DOPs.
19
+ """Base class for all (simple and complex) data object properties.
20
+
21
+ Any class that a parameter can reference via a DOP-REF (Simple
22
+ DOPs, structures, ...) inherits from this class. All DOPs objects
23
+ implement the `Codec` type protocol.
23
24
 
24
- Any class that a parameter can reference via a DOP-REF should inherit from this class.
25
25
  """
26
26
 
27
27
  admin_data: Optional[AdminData]
@@ -36,7 +36,9 @@ class DopBase(IdentifiableElement):
36
36
  if (admin_data_elem := et_element.find("ADMIN-DATA")) is not None:
37
37
  admin_data = AdminData.from_et(admin_data_elem, doc_frags)
38
38
 
39
- sdgs = create_sdgs_from_et(et_element.find("SDGS"), doc_frags)
39
+ sdgs = [
40
+ SpecialDataGroup.from_et(sdge, doc_frags) for sdge in et_element.iterfind("SDGS/SDG")
41
+ ]
40
42
 
41
43
  return DopBase(admin_data=admin_data, sdgs=sdgs, **kwargs)
42
44
 
@@ -52,21 +54,19 @@ class DopBase(IdentifiableElement):
52
54
  for sdg in self.sdgs:
53
55
  sdg._resolve_odxlinks(odxlinks)
54
56
 
55
- def _resolve_snrefs(self, diag_layer: "DiagLayer") -> None:
57
+ def _resolve_snrefs(self, context: SnRefContext) -> None:
56
58
  for sdg in self.sdgs:
57
- sdg._resolve_snrefs(diag_layer)
59
+ sdg._resolve_snrefs(context)
58
60
 
59
61
  def get_static_bit_length(self) -> Optional[int]:
60
62
  return None
61
63
 
62
64
  def is_valid_physical_value(self, physical_value: ParameterValue) -> bool:
63
- """Determine if a phyical value can be handled by the DOP
64
- """
65
+ """Determine if a phyical value can be handled by the DOP"""
65
66
  raise NotImplementedError
66
67
 
67
- def convert_physical_to_bytes(self, physical_value: ParameterValue, encode_state: EncodeState,
68
- bit_position: int) -> bytes:
69
- """Convert the physical value into bytes."""
68
+ def encode_into_pdu(self, physical_value: ParameterValue, encode_state: EncodeState) -> None:
69
+ """Convert the physical value to bytes and emplace them into a PDU."""
70
70
  raise NotImplementedError
71
71
 
72
72
  def decode_from_pdu(self, decode_state: DecodeState) -> ParameterValue: