odxtools 6.6.1__py3-none-any.whl → 9.3.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (222) hide show
  1. odxtools/__init__.py +7 -5
  2. odxtools/additionalaudience.py +3 -5
  3. odxtools/admindata.py +5 -7
  4. odxtools/audience.py +10 -13
  5. odxtools/basecomparam.py +3 -5
  6. odxtools/basicstructure.py +55 -241
  7. odxtools/cli/_parser_utils.py +16 -1
  8. odxtools/cli/_print_utils.py +169 -134
  9. odxtools/cli/browse.py +127 -103
  10. odxtools/cli/compare.py +114 -87
  11. odxtools/cli/decode.py +2 -1
  12. odxtools/cli/dummy_sub_parser.py +3 -1
  13. odxtools/cli/find.py +2 -1
  14. odxtools/cli/list.py +26 -16
  15. odxtools/cli/main.py +1 -0
  16. odxtools/cli/snoop.py +32 -6
  17. odxtools/codec.py +211 -0
  18. odxtools/commrelation.py +122 -0
  19. odxtools/companydata.py +5 -7
  20. odxtools/companydocinfo.py +7 -8
  21. odxtools/companyrevisioninfo.py +3 -5
  22. odxtools/companyspecificinfo.py +8 -9
  23. odxtools/comparam.py +4 -6
  24. odxtools/comparaminstance.py +14 -14
  25. odxtools/comparamspec.py +16 -54
  26. odxtools/comparamsubset.py +22 -62
  27. odxtools/complexcomparam.py +5 -7
  28. odxtools/compumethods/compucodecompumethod.py +63 -0
  29. odxtools/compumethods/compuconst.py +31 -0
  30. odxtools/compumethods/compudefaultvalue.py +27 -0
  31. odxtools/compumethods/compuinternaltophys.py +56 -0
  32. odxtools/compumethods/compuinversevalue.py +7 -0
  33. odxtools/compumethods/compumethod.py +94 -15
  34. odxtools/compumethods/compuphystointernal.py +56 -0
  35. odxtools/compumethods/compurationalcoeffs.py +20 -9
  36. odxtools/compumethods/compuscale.py +67 -32
  37. odxtools/compumethods/createanycompumethod.py +31 -172
  38. odxtools/compumethods/identicalcompumethod.py +31 -6
  39. odxtools/compumethods/limit.py +70 -36
  40. odxtools/compumethods/linearcompumethod.py +70 -181
  41. odxtools/compumethods/linearsegment.py +190 -0
  42. odxtools/compumethods/ratfunccompumethod.py +106 -0
  43. odxtools/compumethods/ratfuncsegment.py +87 -0
  44. odxtools/compumethods/scalelinearcompumethod.py +132 -26
  45. odxtools/compumethods/scaleratfunccompumethod.py +113 -0
  46. odxtools/compumethods/tabintpcompumethod.py +123 -92
  47. odxtools/compumethods/texttablecompumethod.py +117 -57
  48. odxtools/createanydiagcodedtype.py +10 -67
  49. odxtools/database.py +167 -87
  50. odxtools/dataobjectproperty.py +25 -32
  51. odxtools/decodestate.py +14 -17
  52. odxtools/description.py +47 -0
  53. odxtools/determinenumberofitems.py +4 -5
  54. odxtools/diagcodedtype.py +37 -106
  55. odxtools/diagcomm.py +24 -12
  56. odxtools/diagdatadictionaryspec.py +120 -96
  57. odxtools/diaglayercontainer.py +46 -54
  58. odxtools/diaglayers/basevariant.py +128 -0
  59. odxtools/diaglayers/basevariantraw.py +123 -0
  60. odxtools/diaglayers/diaglayer.py +432 -0
  61. odxtools/{diaglayerraw.py → diaglayers/diaglayerraw.py} +105 -120
  62. odxtools/diaglayers/diaglayertype.py +42 -0
  63. odxtools/diaglayers/ecushareddata.py +96 -0
  64. odxtools/diaglayers/ecushareddataraw.py +87 -0
  65. odxtools/diaglayers/ecuvariant.py +124 -0
  66. odxtools/diaglayers/ecuvariantraw.py +129 -0
  67. odxtools/diaglayers/functionalgroup.py +110 -0
  68. odxtools/diaglayers/functionalgroupraw.py +106 -0
  69. odxtools/{diaglayer.py → diaglayers/hierarchyelement.py} +273 -472
  70. odxtools/diaglayers/hierarchyelementraw.py +58 -0
  71. odxtools/diaglayers/protocol.py +64 -0
  72. odxtools/diaglayers/protocolraw.py +91 -0
  73. odxtools/diagnostictroublecode.py +8 -9
  74. odxtools/diagservice.py +57 -44
  75. odxtools/diagvariable.py +113 -0
  76. odxtools/docrevision.py +5 -7
  77. odxtools/dopbase.py +15 -15
  78. odxtools/dtcdop.py +170 -50
  79. odxtools/dynamicendmarkerfield.py +134 -0
  80. odxtools/dynamiclengthfield.py +47 -42
  81. odxtools/dyndefinedspec.py +177 -0
  82. odxtools/dynenddopref.py +38 -0
  83. odxtools/ecuvariantmatcher.py +6 -7
  84. odxtools/element.py +13 -15
  85. odxtools/encodestate.py +199 -22
  86. odxtools/endofpdufield.py +31 -18
  87. odxtools/environmentdata.py +8 -1
  88. odxtools/environmentdatadescription.py +198 -36
  89. odxtools/exceptions.py +11 -2
  90. odxtools/field.py +10 -10
  91. odxtools/functionalclass.py +3 -5
  92. odxtools/inputparam.py +3 -12
  93. odxtools/internalconstr.py +14 -5
  94. odxtools/isotp_state_machine.py +14 -6
  95. odxtools/leadinglengthinfotype.py +37 -18
  96. odxtools/library.py +66 -0
  97. odxtools/loadfile.py +64 -0
  98. odxtools/matchingparameter.py +3 -3
  99. odxtools/message.py +0 -7
  100. odxtools/minmaxlengthtype.py +61 -33
  101. odxtools/modification.py +3 -5
  102. odxtools/multiplexer.py +135 -75
  103. odxtools/multiplexercase.py +39 -18
  104. odxtools/multiplexerdefaultcase.py +15 -12
  105. odxtools/multiplexerswitchkey.py +4 -5
  106. odxtools/nameditemlist.py +33 -8
  107. odxtools/negoutputparam.py +3 -5
  108. odxtools/odxcategory.py +83 -0
  109. odxtools/odxlink.py +62 -53
  110. odxtools/odxtypes.py +93 -8
  111. odxtools/outputparam.py +5 -16
  112. odxtools/parameterinfo.py +219 -61
  113. odxtools/parameters/codedconstparameter.py +45 -32
  114. odxtools/parameters/createanyparameter.py +19 -193
  115. odxtools/parameters/dynamicparameter.py +25 -4
  116. odxtools/parameters/lengthkeyparameter.py +83 -25
  117. odxtools/parameters/matchingrequestparameter.py +48 -18
  118. odxtools/parameters/nrcconstparameter.py +76 -54
  119. odxtools/parameters/parameter.py +97 -73
  120. odxtools/parameters/parameterwithdop.py +41 -38
  121. odxtools/parameters/physicalconstantparameter.py +41 -20
  122. odxtools/parameters/reservedparameter.py +36 -18
  123. odxtools/parameters/systemparameter.py +74 -7
  124. odxtools/parameters/tableentryparameter.py +47 -7
  125. odxtools/parameters/tablekeyparameter.py +142 -55
  126. odxtools/parameters/tablestructparameter.py +79 -58
  127. odxtools/parameters/valueparameter.py +39 -21
  128. odxtools/paramlengthinfotype.py +56 -33
  129. odxtools/parentref.py +20 -3
  130. odxtools/physicaldimension.py +3 -8
  131. odxtools/progcode.py +26 -11
  132. odxtools/protstack.py +3 -5
  133. odxtools/py.typed +0 -0
  134. odxtools/relateddoc.py +7 -9
  135. odxtools/request.py +120 -10
  136. odxtools/response.py +123 -23
  137. odxtools/scaleconstr.py +14 -8
  138. odxtools/servicebinner.py +1 -1
  139. odxtools/singleecujob.py +12 -10
  140. odxtools/snrefcontext.py +29 -0
  141. odxtools/specialdata.py +3 -5
  142. odxtools/specialdatagroup.py +7 -9
  143. odxtools/specialdatagroupcaption.py +3 -6
  144. odxtools/standardlengthtype.py +80 -14
  145. odxtools/state.py +3 -5
  146. odxtools/statechart.py +13 -19
  147. odxtools/statetransition.py +8 -18
  148. odxtools/staticfield.py +107 -0
  149. odxtools/subcomponent.py +288 -0
  150. odxtools/swvariable.py +21 -0
  151. odxtools/table.py +9 -9
  152. odxtools/tablerow.py +30 -15
  153. odxtools/teammember.py +3 -5
  154. odxtools/templates/comparam-spec.odx-c.xml.jinja2 +4 -24
  155. odxtools/templates/comparam-subset.odx-cs.xml.jinja2 +5 -26
  156. odxtools/templates/diag_layer_container.odx-d.xml.jinja2 +15 -31
  157. odxtools/templates/{index.xml.xml.jinja2 → index.xml.jinja2} +1 -1
  158. odxtools/templates/macros/printAudience.xml.jinja2 +1 -1
  159. odxtools/templates/macros/printBaseVariant.xml.jinja2 +53 -0
  160. odxtools/templates/macros/printCompanyData.xml.jinja2 +4 -7
  161. odxtools/templates/macros/printComparam.xml.jinja2 +6 -4
  162. odxtools/templates/macros/printComparamRef.xml.jinja2 +5 -12
  163. odxtools/templates/macros/printCompuMethod.xml.jinja2 +147 -0
  164. odxtools/templates/macros/printDOP.xml.jinja2 +27 -137
  165. odxtools/templates/macros/printDescription.xml.jinja2 +18 -0
  166. odxtools/templates/macros/printDiagComm.xml.jinja2 +1 -1
  167. odxtools/templates/macros/printDiagLayer.xml.jinja2 +222 -0
  168. odxtools/templates/macros/printDiagVariable.xml.jinja2 +66 -0
  169. odxtools/templates/macros/printDynDefinedSpec.xml.jinja2 +48 -0
  170. odxtools/templates/macros/printDynamicEndmarkerField.xml.jinja2 +16 -0
  171. odxtools/templates/macros/printDynamicLengthField.xml.jinja2 +1 -1
  172. odxtools/templates/macros/printEcuSharedData.xml.jinja2 +30 -0
  173. odxtools/templates/macros/printEcuVariant.xml.jinja2 +53 -0
  174. odxtools/templates/macros/printEcuVariantPattern.xml.jinja2 +1 -1
  175. odxtools/templates/macros/printElementId.xml.jinja2 +8 -3
  176. odxtools/templates/macros/printEndOfPdu.xml.jinja2 +1 -1
  177. odxtools/templates/macros/printEnvDataDesc.xml.jinja2 +1 -1
  178. odxtools/templates/macros/printFunctionalClass.xml.jinja2 +1 -1
  179. odxtools/templates/macros/printFunctionalGroup.xml.jinja2 +40 -0
  180. odxtools/templates/macros/printHierarchyElement.xml.jinja2 +24 -0
  181. odxtools/templates/macros/printLibrary.xml.jinja2 +21 -0
  182. odxtools/templates/macros/printMux.xml.jinja2 +5 -3
  183. odxtools/templates/macros/printOdxCategory.xml.jinja2 +28 -0
  184. odxtools/templates/macros/printParam.xml.jinja2 +18 -19
  185. odxtools/templates/macros/printProtStack.xml.jinja2 +1 -1
  186. odxtools/templates/macros/printProtocol.xml.jinja2 +30 -0
  187. odxtools/templates/macros/printRequest.xml.jinja2 +1 -1
  188. odxtools/templates/macros/printResponse.xml.jinja2 +1 -1
  189. odxtools/templates/macros/printService.xml.jinja2 +3 -2
  190. odxtools/templates/macros/printSingleEcuJob.xml.jinja2 +5 -26
  191. odxtools/templates/macros/printSpecialData.xml.jinja2 +1 -1
  192. odxtools/templates/macros/printState.xml.jinja2 +1 -1
  193. odxtools/templates/macros/printStateChart.xml.jinja2 +1 -1
  194. odxtools/templates/macros/printStateTransition.xml.jinja2 +1 -1
  195. odxtools/templates/macros/printStaticField.xml.jinja2 +15 -0
  196. odxtools/templates/macros/printStructure.xml.jinja2 +1 -1
  197. odxtools/templates/macros/printSubComponent.xml.jinja2 +104 -0
  198. odxtools/templates/macros/printTable.xml.jinja2 +4 -5
  199. odxtools/templates/macros/printUnitSpec.xml.jinja2 +3 -5
  200. odxtools/uds.py +2 -10
  201. odxtools/unit.py +4 -8
  202. odxtools/unitgroup.py +3 -5
  203. odxtools/unitspec.py +17 -17
  204. odxtools/utils.py +38 -20
  205. odxtools/variablegroup.py +32 -0
  206. odxtools/version.py +2 -2
  207. odxtools/{write_pdx_file.py → writepdxfile.py} +22 -12
  208. odxtools/xdoc.py +3 -5
  209. {odxtools-6.6.1.dist-info → odxtools-9.3.0.dist-info}/METADATA +44 -33
  210. odxtools-9.3.0.dist-info/RECORD +228 -0
  211. {odxtools-6.6.1.dist-info → odxtools-9.3.0.dist-info}/WHEEL +1 -1
  212. odxtools/createcompanydatas.py +0 -17
  213. odxtools/createsdgs.py +0 -19
  214. odxtools/diaglayertype.py +0 -30
  215. odxtools/load_file.py +0 -13
  216. odxtools/load_odx_d_file.py +0 -6
  217. odxtools/load_pdx_file.py +0 -8
  218. odxtools/templates/macros/printVariant.xml.jinja2 +0 -208
  219. odxtools-6.6.1.dist-info/RECORD +0 -180
  220. {odxtools-6.6.1.dist-info → odxtools-9.3.0.dist-info}/LICENSE +0 -0
  221. {odxtools-6.6.1.dist-info → odxtools-9.3.0.dist-info}/entry_points.txt +0 -0
  222. {odxtools-6.6.1.dist-info → odxtools-9.3.0.dist-info}/top_level.txt +0 -0
odxtools/__init__.py CHANGED
@@ -62,11 +62,13 @@ References
62
62
  - _`[ISO22901]` The ISO 22901 Standard: https://www.iso.org/standard/41207.html
63
63
 
64
64
  """
65
- from .load_file import load_file
66
- from .load_odx_d_file import load_odx_d_file
67
- from .load_pdx_file import load_pdx_file
68
- from .version import __version__
69
- from .write_pdx_file import write_pdx_file
65
+ from .loadfile import load_directory as load_directory # noqa: F401
66
+ from .loadfile import load_file as load_file # noqa: F401
67
+ from .loadfile import load_files # noqa: F401
68
+ from .loadfile import load_odx_d_file as load_odx_d_file # noqa: F401
69
+ from .loadfile import load_pdx_file as load_pdx_file # noqa: F401
70
+ from .version import __version__ as __version__ # noqa: F401
71
+ from .writepdxfile import write_pdx_file as write_pdx_file # noqa: F401
70
72
 
71
73
  __author__ = "Katrin Bauer"
72
74
 
@@ -1,15 +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
6
  from .element import IdentifiableElement
7
7
  from .odxlink import OdxDocFragment, OdxLinkDatabase, OdxLinkId
8
+ from .snrefcontext import SnRefContext
8
9
  from .utils import dataclass_fields_asdict
9
10
 
10
- if TYPE_CHECKING:
11
- from .diaglayer import DiagLayer
12
-
13
11
 
14
12
  @dataclass
15
13
  class AdditionalAudience(IdentifiableElement):
@@ -30,5 +28,5 @@ class AdditionalAudience(IdentifiableElement):
30
28
  def _resolve_odxlinks(self, odxlinks: OdxLinkDatabase) -> None:
31
29
  pass
32
30
 
33
- def _resolve_snrefs(self, diag_layer: "DiagLayer") -> None:
31
+ def _resolve_snrefs(self, context: SnRefContext) -> None:
34
32
  pass
odxtools/admindata.py CHANGED
@@ -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 .companydocinfo import CompanyDocInfo
7
7
  from .docrevision import DocRevision
8
8
  from .odxlink import OdxDocFragment, OdxLinkDatabase, OdxLinkId
9
-
10
- if TYPE_CHECKING:
11
- from .diaglayer import DiagLayer
9
+ from .snrefcontext import SnRefContext
12
10
 
13
11
 
14
12
  @dataclass
@@ -54,9 +52,9 @@ class AdminData:
54
52
  for dr in self.doc_revisions:
55
53
  dr._resolve_odxlinks(odxlinks)
56
54
 
57
- def _resolve_snrefs(self, diag_layer: "DiagLayer") -> None:
55
+ def _resolve_snrefs(self, context: SnRefContext) -> None:
58
56
  for cdi in self.company_doc_infos:
59
- cdi._resolve_snrefs(diag_layer)
57
+ cdi._resolve_snrefs(context)
60
58
 
61
59
  for dr in self.doc_revisions:
62
- dr._resolve_snrefs(diag_layer)
60
+ dr._resolve_snrefs(context)
odxtools/audience.py CHANGED
@@ -1,14 +1,13 @@
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 .additionalaudience import AdditionalAudience
7
+ from .nameditemlist import NamedItemList
7
8
  from .odxlink import OdxDocFragment, OdxLinkDatabase, OdxLinkId, OdxLinkRef
8
9
  from .odxtypes import odxstr_to_bool
9
-
10
- if TYPE_CHECKING:
11
- from .diaglayer import DiagLayer
10
+ from .snrefcontext import SnRefContext
12
11
 
13
12
 
14
13
  @dataclass
@@ -47,11 +46,11 @@ class Audience:
47
46
  return self.is_aftermarket_raw in [None, True]
48
47
 
49
48
  @property
50
- def enabled_audiences(self) -> List[AdditionalAudience]:
49
+ def enabled_audiences(self) -> NamedItemList[AdditionalAudience]:
51
50
  return self._enabled_audiences
52
51
 
53
52
  @property
54
- def disabled_audiences(self) -> List[AdditionalAudience]:
53
+ def disabled_audiences(self) -> NamedItemList[AdditionalAudience]:
55
54
  return self._disabled_audiences
56
55
 
57
56
  @staticmethod
@@ -87,12 +86,10 @@ class Audience:
87
86
  return {}
88
87
 
89
88
  def _resolve_odxlinks(self, odxlinks: OdxLinkDatabase) -> None:
90
- self._enabled_audiences = [
91
- odxlinks.resolve(ref, AdditionalAudience) for ref in self.enabled_audience_refs
92
- ]
93
- self._disabled_audiences = [
94
- odxlinks.resolve(ref, AdditionalAudience) for ref in self.disabled_audience_refs
95
- ]
89
+ self._enabled_audiences = NamedItemList(
90
+ [odxlinks.resolve(ref, AdditionalAudience) for ref in self.enabled_audience_refs])
91
+ self._disabled_audiences = NamedItemList(
92
+ [odxlinks.resolve(ref, AdditionalAudience) for ref in self.disabled_audience_refs])
96
93
 
97
- def _resolve_snrefs(self, diag_layer: "DiagLayer") -> None:
94
+ def _resolve_snrefs(self, context: SnRefContext) -> None:
98
95
  pass
odxtools/basecomparam.py CHANGED
@@ -1,17 +1,15 @@
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, cast
4
+ from typing import Any, Dict, List, Optional, cast
5
5
  from xml.etree import ElementTree
6
6
 
7
7
  from .element import IdentifiableElement
8
8
  from .exceptions import odxraise, odxrequire
9
9
  from .odxlink import OdxDocFragment, OdxLinkDatabase, OdxLinkId
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
  class StandardizationLevel(Enum):
17
15
  STANDARD = "STANDARD"
@@ -74,5 +72,5 @@ class BaseComparam(IdentifiableElement):
74
72
  def _resolve_odxlinks(self, odxlinks: OdxLinkDatabase) -> None:
75
73
  pass
76
74
 
77
- def _resolve_snrefs(self, diag_layer: "DiagLayer") -> None:
75
+ def _resolve_snrefs(self, context: SnRefContext) -> None:
78
76
  pass
@@ -1,34 +1,34 @@
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
5
4
  from xml.etree import ElementTree
6
5
 
6
+ from typing_extensions import override
7
+
8
+ from .codec import (composite_codec_decode_from_pdu, composite_codec_encode_into_pdu,
9
+ composite_codec_get_free_parameters, composite_codec_get_required_parameters,
10
+ composite_codec_get_static_bit_length)
7
11
  from .complexdop import ComplexDop
8
- from .dataobjectproperty import DataObjectProperty
9
12
  from .decodestate import DecodeState
10
13
  from .encodestate import EncodeState
11
- from .exceptions import DecodeError, EncodeError, OdxWarning, odxassert, odxraise, strict_mode
14
+ from .exceptions import DecodeError, odxraise
12
15
  from .nameditemlist import NamedItemList
13
16
  from .odxlink import OdxDocFragment, OdxLinkDatabase, OdxLinkId
14
- from .odxtypes import ParameterDict, ParameterValue, ParameterValueDict
15
- from .parameters.codedconstparameter import CodedConstParameter
17
+ from .odxtypes import ParameterValue
16
18
  from .parameters.createanyparameter import create_any_parameter_from_et
17
- from .parameters.lengthkeyparameter import LengthKeyParameter
18
- from .parameters.matchingrequestparameter import MatchingRequestParameter
19
- from .parameters.nrcconstparameter import NrcConstParameter
20
19
  from .parameters.parameter import Parameter
21
- from .parameters.parameterwithdop import ParameterWithDOP
22
- from .parameters.physicalconstantparameter import PhysicalConstantParameter
23
- from .parameters.tablekeyparameter import TableKeyParameter
20
+ from .snrefcontext import SnRefContext
24
21
  from .utils import dataclass_fields_asdict
25
22
 
26
- if TYPE_CHECKING:
27
- from .diaglayer import DiagLayer
28
-
29
23
 
30
24
  @dataclass
31
25
  class BasicStructure(ComplexDop):
26
+ """Base class for structure-like objects
27
+
28
+ "Structure-like" objects are structures as well as environment
29
+ data objects. All structure-like objects adhere to the
30
+ `CompositeCodec` type protocol.
31
+ """
32
32
  parameters: NamedItemList[Parameter]
33
33
  byte_size: Optional[int]
34
34
 
@@ -49,63 +49,20 @@ class BasicStructure(ComplexDop):
49
49
  return BasicStructure(parameters=parameters, byte_size=byte_size, **kwargs)
50
50
 
51
51
  def get_static_bit_length(self) -> Optional[int]:
52
- # Explicit size was specified
53
- if self.byte_size:
52
+ # Explicit size was specified, so we do not need to look at
53
+ # the list of parameters
54
+ if self.byte_size is not None:
54
55
  return 8 * self.byte_size
55
56
 
56
- cursor = 0
57
- length = 0
58
- for param in self.parameters:
59
- param_bit_length = param.get_static_bit_length()
60
- if param_bit_length is None:
61
- # We were not able to calculate a static bit length
62
- return None
63
- elif param.byte_position is not None:
64
- bit_pos = param.bit_position or 0
65
- byte_pos = param.byte_position or 0
66
- cursor = byte_pos * 8 + bit_pos
67
-
68
- cursor += param_bit_length
69
- length = max(length, cursor)
70
-
71
- # Round up to account for padding bits
72
- return ((length + 7) // 8) * 8
73
-
74
- def coded_const_prefix(self, request_prefix: bytes = b'') -> bytes:
75
- prefix = b''
76
- encode_state = EncodeState(
77
- bytearray(prefix), parameter_values={}, triggering_request=request_prefix)
78
- for param in self.parameters:
79
- if isinstance(param, (CodedConstParameter, NrcConstParameter, MatchingRequestParameter,
80
- PhysicalConstantParameter)):
81
- encode_state.coded_message = bytearray(param.encode_into_pdu(encode_state))
82
- else:
83
- break
84
- return encode_state.coded_message
57
+ return composite_codec_get_static_bit_length(self)
85
58
 
86
59
  @property
87
60
  def required_parameters(self) -> List[Parameter]:
88
- """Return the list of parameters which are required for
89
- encoding the structure."""
90
- return [p for p in self.parameters if p.is_required]
61
+ return composite_codec_get_required_parameters(self)
91
62
 
92
63
  @property
93
64
  def free_parameters(self) -> List[Parameter]:
94
- """Return the list of parameters which can be freely specified by
95
- the user when encoding the structure.
96
-
97
- This means all required parameters plus the parameters that
98
- can be omitted minus those which are implicitly specified by
99
- the corresponding request (in the case of responses).
100
-
101
- """
102
- result: List[Parameter] = []
103
- for param in self.parameters:
104
- if not param.is_settable:
105
- continue
106
- result.append(param)
107
-
108
- return result
65
+ return composite_codec_get_free_parameters(self)
109
66
 
110
67
  def print_free_parameters_info(self) -> None:
111
68
  """Return a human readable description of the structure's
@@ -115,188 +72,44 @@ class BasicStructure(ComplexDop):
115
72
 
116
73
  print(parameter_info(self.free_parameters), end="")
117
74
 
118
- def convert_physical_to_internal(self,
119
- param_value: ParameterValue,
120
- triggering_coded_request: Optional[bytes],
121
- is_end_of_pdu: bool = True) -> bytes:
122
-
123
- if not isinstance(param_value, dict):
124
- raise EncodeError(
125
- f"Expected a dictionary for the values of structure {self.short_name}, "
126
- f"got {type(param_value)}")
127
-
128
- # in strict mode, ensure that no values for unknown parameters are specified.
129
- if strict_mode:
130
- param_names = [param.short_name for param in self.parameters]
131
- for param_key in param_value:
132
- if param_key not in param_names:
133
- odxraise(f"Value for unknown parameter '{param_key}' specified")
134
-
135
- encode_state = EncodeState(
136
- bytearray(),
137
- dict(param_value),
138
- triggering_request=triggering_coded_request,
139
- is_end_of_pdu=False,
140
- )
141
-
142
- for param in self.parameters:
143
- if param == self.parameters[-1]:
144
- # The last parameter is at the end of the PDU if the
145
- # structure itself is at the end of the PDU. TODO:
146
- # This assumes that the last parameter specified in
147
- # the ODX is located last in the PDU...
148
- encode_state.is_end_of_pdu = is_end_of_pdu
149
-
150
- if isinstance(
151
- param,
152
- (LengthKeyParameter, TableKeyParameter)) and param.short_name in param_value:
153
- # This is a hack to always encode a dummy value for
154
- # length- and table keys. since these can be specified
155
- # implicitly (i.e., by means of parameters that use
156
- # these keys), to avoid getting an "overlapping
157
- # parameter" warning, we must encode a value of zero
158
- # into the PDU here and add the real value of the
159
- # parameter in a post processing step.
160
- tmp = encode_state.parameter_values.pop(param.short_name)
161
- encode_state.coded_message = bytearray(param.encode_into_pdu(encode_state))
162
- encode_state.parameter_values[param.short_name] = tmp
163
- continue
164
-
165
- encode_state.coded_message = bytearray(param.encode_into_pdu(encode_state))
166
-
167
- if self.byte_size is not None and len(encode_state.coded_message) < self.byte_size:
168
- # Padding bytes needed
169
- encode_state.coded_message = encode_state.coded_message.ljust(self.byte_size, b"\0")
170
-
171
- # encode the length- and table keys. This cannot be done above
172
- # because we allow these to be defined implicitly (i.e. they
173
- # are defined by their respective users)
174
- for param in self.parameters:
175
- if not isinstance(param, (LengthKeyParameter, TableKeyParameter)):
176
- # the current parameter is neither a length- nor a table key
177
- continue
178
-
179
- # Encode the key parameter into the message
180
- encode_state.coded_message = bytearray(param.encode_into_pdu(encode_state))
181
-
182
- # Assert that length is as expected
183
- self._validate_coded_message(encode_state.coded_message)
75
+ @override
76
+ def encode_into_pdu(self, physical_value: Optional[ParameterValue],
77
+ encode_state: EncodeState) -> None:
78
+ orig_pos = encode_state.cursor_byte_position
184
79
 
185
- return bytearray(encode_state.coded_message)
186
-
187
- def _validate_coded_message(self, coded_message: bytes) -> None:
80
+ composite_codec_encode_into_pdu(self, physical_value, encode_state)
188
81
 
189
82
  if self.byte_size is not None:
190
- # We definitely broke something if we didn't respect the explicit byte_size
191
- odxassert(
192
- len(coded_message) == self.byte_size,
193
- "Verification of coded message {coded_message.hex()} failed: Incorrect size.")
194
- # No need to check further
195
- return
196
-
197
- bit_length = self.get_static_bit_length()
198
-
199
- if bit_length is None:
200
- # Nothing to check
201
- return
202
-
203
- if len(coded_message) * 8 != bit_length:
204
- # We may have broke something
205
- # but it could be that bit_length was mis calculated and not the actual bytes are wrong
206
- # Could happen with overlapping parameters and parameters with gaps
207
- warnings.warn(
208
- f"Verification of coded message '{coded_message.hex()}' possibly failed: Size may be incorrect.",
209
- OdxWarning,
210
- stacklevel=1)
211
-
212
- def convert_physical_to_bytes(self,
213
- param_values: ParameterValue,
214
- encode_state: EncodeState,
215
- bit_position: int = 0) -> bytes:
216
- if not isinstance(param_values, dict):
217
- raise EncodeError(
218
- f"Expected a dictionary for the values of structure {self.short_name}, "
219
- f"got {type(param_values)}")
220
- if bit_position != 0:
221
- raise EncodeError("Structures must be aligned, i.e. bit_position=0, but "
222
- f"{self.short_name} was passed the bit position {bit_position}")
223
- return self.convert_physical_to_internal(
224
- param_values,
225
- triggering_coded_request=encode_state.triggering_request,
226
- is_end_of_pdu=encode_state.is_end_of_pdu,
227
- )
228
-
83
+ actual_len = encode_state.cursor_byte_position - orig_pos
84
+
85
+ if actual_len < self.byte_size:
86
+ # Padding bytes are needed. We add an empty object at
87
+ # the position directly after the structure and let
88
+ # EncodeState add the padding as needed.
89
+ encode_state.cursor_byte_position = encode_state.origin_byte_position + self.byte_size
90
+ # Padding bytes needed. these count as "used".
91
+ encode_state.coded_message += b"\x00" * (self.byte_size - actual_len)
92
+ encode_state.used_mask += b"\xff" * (self.byte_size - actual_len)
93
+
94
+ @override
229
95
  def decode_from_pdu(self, decode_state: DecodeState) -> ParameterValue:
230
- # move the origin since positions specified by sub-parameters of
231
- # structures are relative to the beginning of the structure object.
232
- orig_origin = decode_state.origin_byte_position
233
- decode_state.origin_byte_position = decode_state.cursor_byte_position
96
+ orig_pos = decode_state.cursor_byte_position
234
97
 
235
- result = {}
236
- for param in self.parameters:
237
- value = param.decode_from_pdu(decode_state)
98
+ result = composite_codec_decode_from_pdu(self, decode_state)
238
99
 
239
- result[param.short_name] = value
100
+ if self.byte_size is not None:
101
+ n = decode_state.cursor_byte_position - orig_pos
102
+ if n > self.byte_size:
103
+ odxraise(
104
+ f"Attempted to decode too large instance of structure "
105
+ f"{self.short_name} ({n} instead at most "
106
+ f"{self.byte_size} bytes)", DecodeError)
107
+ return result
240
108
 
241
- # decoding of the structure finished. go back the original origin.
242
- decode_state.origin_byte_position = orig_origin
109
+ decode_state.cursor_byte_position = orig_pos + self.byte_size
243
110
 
244
111
  return result
245
112
 
246
- def encode(self, coded_request: Optional[bytes] = None, **params: ParameterValue) -> bytes:
247
- """
248
- Composes an UDS message as bytes for this service.
249
- Parameters:
250
- ----------
251
- coded_request: bytes
252
- coded request (only needed when encoding a response)
253
- params: dict
254
- Parameters of the RPC as mapping from SHORT-NAME of the parameter to the value
255
- """
256
- return self.convert_physical_to_internal(
257
- params, # type: ignore[arg-type]
258
- triggering_coded_request=coded_request,
259
- is_end_of_pdu=True)
260
-
261
- def decode(self, message: bytes) -> ParameterValueDict:
262
- decode_state = DecodeState(coded_message=message)
263
- param_values = self.decode_from_pdu(decode_state)
264
-
265
- if len(message) != decode_state.cursor_byte_position:
266
- odxraise(
267
- f"The message {message.hex()} probably could not be completely parsed:"
268
- f" Expected length of {decode_state.cursor_byte_position} but got {len(message)}.",
269
- DecodeError)
270
- return {}
271
-
272
- if not isinstance(param_values, dict):
273
- odxraise("Decoding structures must result in a dictionary")
274
-
275
- return cast(ParameterValueDict, param_values)
276
-
277
- def parameter_dict(self) -> ParameterDict:
278
- """
279
- Returns a dictionary with all parameter short names as keys.
280
-
281
- The values are parameters for simple types or a nested dict for structures.
282
- """
283
- from .structure import Structure
284
- odxassert(
285
- all(not isinstance(p, ParameterWithDOP) or isinstance(p.dop, DataObjectProperty) or
286
- isinstance(p.dop, Structure) for p in self.parameters))
287
- param_dict: ParameterDict = {
288
- p.short_name: p
289
- for p in self.parameters
290
- if not isinstance(p, ParameterWithDOP) or not isinstance(p.dop, Structure)
291
- }
292
- param_dict.update({
293
- struct_param.short_name: struct_param.dop.parameter_dict()
294
- for struct_param in self.parameters
295
- if isinstance(struct_param, ParameterWithDOP) and
296
- isinstance(struct_param.dop, BasicStructure)
297
- })
298
- return param_dict
299
-
300
113
  def _build_odxlinks(self) -> Dict[OdxLinkId, Any]:
301
114
  result = super()._build_odxlinks()
302
115
 
@@ -306,15 +119,16 @@ class BasicStructure(ComplexDop):
306
119
  return result
307
120
 
308
121
  def _resolve_odxlinks(self, odxlinks: OdxLinkDatabase) -> None:
309
- """Recursively resolve any references (odxlinks or sn-refs)"""
310
122
  super()._resolve_odxlinks(odxlinks)
311
123
 
312
124
  for param in self.parameters:
313
125
  param._resolve_odxlinks(odxlinks)
314
126
 
315
- def _resolve_snrefs(self, diag_layer: "DiagLayer") -> None:
316
- """Recursively resolve any references (odxlinks or sn-refs)"""
317
- super()._resolve_snrefs(diag_layer)
127
+ def _resolve_snrefs(self, context: SnRefContext) -> None:
128
+ context.parameters = self.parameters
318
129
 
130
+ super()._resolve_snrefs(context)
319
131
  for param in self.parameters:
320
- param._resolve_snrefs(diag_layer)
132
+ param._resolve_snrefs(context)
133
+
134
+ context.parameters = None
@@ -2,9 +2,24 @@
2
2
  #
3
3
  # SPDX-License-Identifier: MIT
4
4
  import argparse
5
+ from typing import Any, Protocol, runtime_checkable
5
6
 
6
7
  from ..database import Database
7
- from ..load_file import load_file as _load_file
8
+ from ..loadfile import load_file as _load_file
9
+
10
+
11
+ @runtime_checkable
12
+ class SubparsersList(Protocol):
13
+ """This protocol reproduces the parts of `argparse._SubparsersAction` which are needed by odxtools
14
+
15
+ Unfortunately this is necessary because
16
+ `argparse._SubparsersAction` is a private class of the `argparse`
17
+ module that is used by some of its public API and thus cannot be
18
+ used outside of the `argparse` module.
19
+ """
20
+
21
+ def add_parser(self, name: str, **kwargs: Any) -> "argparse.ArgumentParser":
22
+ ...
8
23
 
9
24
 
10
25
  def add_pdx_argument(parser: argparse.ArgumentParser, is_optional: bool = False) -> None: