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
@@ -1,36 +1,60 @@
1
1
  # SPDX-License-Identifier: MIT
2
2
  from dataclasses import dataclass
3
- from typing import TYPE_CHECKING, Any, Dict, List, Optional, Tuple
3
+ from typing import Any, Dict, List, Optional, cast
4
4
  from xml.etree import ElementTree
5
5
 
6
+ from typing_extensions import override
7
+
6
8
  from .complexdop import ComplexDop
9
+ from .dataobjectproperty import DataObjectProperty
7
10
  from .decodestate import DecodeState
11
+ from .dtcdop import DtcDop
8
12
  from .encodestate import EncodeState
9
13
  from .environmentdata import EnvironmentData
10
- from .exceptions import DecodeError, EncodeError, odxrequire
14
+ from .exceptions import odxraise, odxrequire
15
+ from .nameditemlist import NamedItemList
11
16
  from .odxlink import OdxDocFragment, OdxLinkDatabase, OdxLinkId, OdxLinkRef
12
- from .odxtypes import ParameterValue
17
+ from .odxtypes import DataType, ParameterValue, ParameterValueDict
18
+ from .parameters.codedconstparameter import CodedConstParameter
19
+ from .parameters.parameter import Parameter
20
+ from .parameters.parameterwithdop import ParameterWithDOP
21
+ from .parameters.physicalconstantparameter import PhysicalConstantParameter
22
+ from .parameters.valueparameter import ValueParameter
23
+ from .snrefcontext import SnRefContext
13
24
  from .utils import dataclass_fields_asdict
14
25
 
15
- if TYPE_CHECKING:
16
- from .diaglayer import DiagLayer
17
-
18
26
 
19
27
  @dataclass
20
28
  class EnvironmentDataDescription(ComplexDop):
21
- """This class represents Environment Data Description, which is a complex DOP
22
- that is used to define the interpretation of environment data."""
29
+ """This class represents environment data descriptions
30
+
31
+ An environment data description provides a list of all environment
32
+ data objects that are potentially applicable to decode a given
33
+ response. (If a given environment data object is applicable
34
+ depends on the value of the DtcDOP that is associated with it.)
35
+
36
+ """
37
+
38
+ param_snref: Optional[str]
39
+ param_snpathref: Optional[str]
23
40
 
24
41
  # in ODX 2.0.0, ENV-DATAS seems to be a mandatory
25
42
  # sub-element of ENV-DATA-DESC, in ODX 2.2 it is not
26
43
  # present
27
- env_datas: List[EnvironmentData]
44
+ env_datas: NamedItemList[EnvironmentData]
28
45
  env_data_refs: List[OdxLinkRef]
29
- param_snref: Optional[str]
30
- param_snpathref: Optional[str]
31
46
 
32
- def __post_init__(self) -> None:
33
- self.bit_length = None
47
+ @property
48
+ def param(self) -> Parameter:
49
+ # the parameter referenced via SNREF cannot be resolved here
50
+ # because the relevant list of parameters depends on the
51
+ # concrete codec object processed, whilst an environment data
52
+ # description object can be featured in an arbitrary number of
53
+ # responses. Instead, lookup of the appropriate parameter is
54
+ # done within the encode and decode methods.
55
+ odxraise("The parameter of ENV-DATA-DESC objects cannot be resolved "
56
+ "because it depends on the context")
57
+ return cast(None, Parameter)
34
58
 
35
59
  @staticmethod
36
60
  def from_et(et_element: ElementTree.Element,
@@ -44,16 +68,20 @@ class EnvironmentDataDescription(ComplexDop):
44
68
  param_snpathref = None
45
69
  if (param_snpathref_elem := et_element.find("PARAM-SNPATHREF")) is not None:
46
70
  param_snpathref = odxrequire(param_snpathref_elem.get("SHORT-NAME-PATH"))
71
+
72
+ # ODX 2.0 mandates ENV-DATA-DESC to contain a list of
73
+ # ENV-DATAS and no ENV-DATA-REFS while for ODX 2.2 the
74
+ # situation is reversed. This means that we will create one
75
+ # empty and one non-empty list here. (Which is which depends
76
+ # on the version of the standard used by the file.)
47
77
  env_data_refs = [
48
78
  odxrequire(OdxLinkRef.from_et(env_data_ref, doc_frags))
49
79
  for env_data_ref in et_element.iterfind("ENV-DATA-REFS/ENV-DATA-REF")
50
80
  ]
51
-
52
- # ODX 2.0.0 says ENV-DATA-DESC could contain a list of ENV-DATAS
53
- env_datas = [
81
+ env_datas = NamedItemList([
54
82
  EnvironmentData.from_et(env_data_elem, doc_frags)
55
83
  for env_data_elem in et_element.iterfind("ENV-DATAS/ENV-DATA")
56
- ]
84
+ ])
57
85
 
58
86
  return EnvironmentDataDescription(
59
87
  param_snref=param_snref,
@@ -65,8 +93,9 @@ class EnvironmentDataDescription(ComplexDop):
65
93
  def _build_odxlinks(self) -> Dict[OdxLinkId, Any]:
66
94
  odxlinks = {self.odx_id: self}
67
95
 
68
- for ed in self.env_datas:
69
- odxlinks.update(ed._build_odxlinks())
96
+ if not self.env_data_refs:
97
+ for ed in self.env_datas:
98
+ odxlinks.update(ed._build_odxlinks())
70
99
 
71
100
  return odxlinks
72
101
 
@@ -74,35 +103,166 @@ class EnvironmentDataDescription(ComplexDop):
74
103
  # ODX 2.0 specifies environment data objects here, ODX 2.2
75
104
  # uses references
76
105
  if self.env_data_refs:
77
- self.env_datas = [odxlinks.resolve(x) for x in self.env_data_refs]
106
+ self.env_datas = NamedItemList([odxlinks.resolve(x) for x in self.env_data_refs])
78
107
  else:
79
108
  for ed in self.env_datas:
80
109
  ed._resolve_odxlinks(odxlinks)
81
110
 
82
- def _resolve_snrefs(self, diag_layer: "DiagLayer") -> None:
111
+ def _resolve_snrefs(self, context: SnRefContext) -> None:
83
112
  # ODX 2.0 specifies environment data objects here, ODX 2.2
84
113
  # uses references
85
114
  if self.env_data_refs:
86
115
  for ed in self.env_datas:
87
- ed._resolve_snrefs(diag_layer)
88
-
89
- def convert_physical_to_bytes(self,
90
- physical_value: ParameterValue,
91
- encode_state: EncodeState,
92
- bit_position: int = 0) -> bytes:
93
- """Convert the physical value into bytes.
116
+ ed._resolve_snrefs(context)
94
117
 
95
- Since environmental data is supposed to never appear on the
96
- wire, this method just raises an EncodeError exception.
118
+ @override
119
+ def encode_into_pdu(self, physical_value: Optional[ParameterValue],
120
+ encode_state: EncodeState) -> None:
121
+ """Convert a physical value into bytes and emplace them into a PDU.
97
122
  """
98
- raise EncodeError("EnvironmentDataDescription DOPs cannot be encoded or decoded")
99
123
 
100
- def convert_bytes_to_physical(self,
101
- decode_state: DecodeState,
102
- bit_position: int = 0) -> Tuple[ParameterValue, int]:
103
- """Extract the bytes from the PDU and convert them to the physical value.
124
+ # retrieve the DTC as a numerical value from the referenced
125
+ # parameter (which must be located somewhere before the
126
+ # parameter using the environment data description)
127
+ if self.param_snref is None:
128
+ odxraise("Specifying the DTC parameter for environment data "
129
+ "descriptions via SNPATHREF is not supported yet")
130
+ return None
131
+
132
+ numerical_dtc_value: Optional[ParameterValue] = None
133
+ for prev_param, prev_param_value in reversed(encode_state.journal):
134
+ if prev_param.short_name == self.param_snref:
135
+ numerical_dtc_value = self._get_numerical_dtc_from_parameter(
136
+ prev_param, prev_param_value)
137
+ break
138
+
139
+ if numerical_dtc_value is None:
140
+ odxraise("Environment data description parameters are only allowed following "
141
+ "the referenced parameter.")
142
+ return
143
+
144
+ # deal with the "all value" environment data. This holds
145
+ # parameters that are common to all DTCs. Be aware that the
146
+ # specification mandates that there is at most one such
147
+ # environment data object
148
+ for env_data in self.env_datas:
149
+ if env_data.all_value:
150
+ tmp = encode_state.allow_unknown_parameters
151
+ encode_state.allow_unknown_parameters = True
152
+ env_data.encode_into_pdu(physical_value, encode_state)
153
+ encode_state.allow_unknown_parameters = tmp
154
+ break
155
+
156
+ # find the environment data corresponding to the given trouble
157
+ # code
158
+ for env_data in self.env_datas:
159
+ if numerical_dtc_value in env_data.dtc_values:
160
+ tmp = encode_state.allow_unknown_parameters
161
+ encode_state.allow_unknown_parameters = True
162
+ env_data.encode_into_pdu(physical_value, encode_state)
163
+ encode_state.allow_unknown_parameters = tmp
164
+ break
104
165
 
105
- Since environmental data is supposed to never appear on the
106
- wire, this method just raises an DecodeError exception.
166
+ @override
167
+ def decode_from_pdu(self, decode_state: DecodeState) -> ParameterValue:
168
+ """Extract the bytes from a PDU and convert them to a physical value.
107
169
  """
108
- raise DecodeError("EnvironmentDataDescription DOPs cannot be encoded or decoded")
170
+
171
+ # retrieve the DTC as a numerical value from the referenced
172
+ # parameter (which must be located somewhere before the
173
+ # parameter using the environment data description)
174
+ if self.param_snref is None:
175
+ odxraise("Specifying the DTC parameter for environment data "
176
+ "descriptions via SNPATHREF is not supported yet")
177
+ return None
178
+
179
+ numerical_dtc_value: Optional[ParameterValue] = None
180
+ for prev_param, prev_param_value in reversed(decode_state.journal):
181
+ if prev_param.short_name == self.param_snref:
182
+ numerical_dtc_value = self._get_numerical_dtc_from_parameter(
183
+ prev_param, prev_param_value)
184
+ break
185
+
186
+ if numerical_dtc_value is None:
187
+ odxraise("Environment data description parameters are only allowed following "
188
+ "the referenced parameter.")
189
+ return
190
+
191
+ result: ParameterValueDict = {}
192
+
193
+ # deal with the "all value" environment data. This holds
194
+ # parameters that are common to all DTCs. Be aware that the
195
+ # specification mandates that there is at most one such
196
+ # environment data object
197
+ for env_data in self.env_datas:
198
+ if env_data.all_value:
199
+ tmp = env_data.decode_from_pdu(decode_state)
200
+ if not isinstance(tmp, dict):
201
+ odxraise()
202
+ result.update(tmp)
203
+ break
204
+
205
+ # find the environment data corresponding to the given trouble
206
+ # code
207
+ for env_data in self.env_datas:
208
+ if numerical_dtc_value in env_data.dtc_values:
209
+ tmp = env_data.decode_from_pdu(decode_state)
210
+ if not isinstance(tmp, dict):
211
+ odxraise()
212
+ result.update(tmp)
213
+ break
214
+
215
+ return result
216
+
217
+ def _get_numerical_dtc_from_parameter(self, param: Parameter,
218
+ param_value: Optional[ParameterValue]) -> int:
219
+ if isinstance(param, ParameterWithDOP):
220
+ dop = param.dop
221
+ if not isinstance(dop, (DataObjectProperty, DtcDop)):
222
+ odxraise(f"The DOP of the parameter referenced by environment data descriptions "
223
+ f"must use either be DataObjectProperty or a DtcDop (encountered "
224
+ f"{type(param).__name__} for parameter '{self.param.short_name}' "
225
+ f"of ENV-DATA-DESC '{self.short_name}')")
226
+ return 0
227
+
228
+ if dop.diag_coded_type.base_data_type != DataType.A_UINT32:
229
+ odxraise(f"The data type used by the DOP of the parameter referenced "
230
+ f"by environment data descriptions must be A_UINT32 "
231
+ f"(encountered '{dop.diag_coded_type.base_data_type.value}')")
232
+
233
+ if param_value is None:
234
+ if isinstance(param, ValueParameter):
235
+ param_value = param.physical_default_value
236
+ elif isinstance(param, PhysicalConstantParameter):
237
+ param_value = param.physical_constant_value
238
+ else:
239
+ odxraise() # make mypy happy...
240
+ return
241
+
242
+ if isinstance(dop, DtcDop):
243
+ return dop.convert_to_numerical_trouble_code(odxrequire(param_value))
244
+ elif isinstance(dop, DataObjectProperty):
245
+ return int(dop.compu_method.convert_physical_to_internal(
246
+ param_value)) # type: ignore[arg-type]
247
+
248
+ odxraise() # not reachable...
249
+
250
+ elif isinstance(param, CodedConstParameter):
251
+ if param.diag_coded_type.base_data_type != DataType.A_UINT32:
252
+ odxraise(f"The data type used by the parameter referenced "
253
+ f"by environment data descriptions must be A_UINT32 "
254
+ f"(encountered '{param.diag_coded_type.base_data_type.value}')")
255
+
256
+ return param.coded_value
257
+
258
+ if not isinstance(param.coded_value, int):
259
+ odxraise()
260
+
261
+ return param.coded_value
262
+
263
+ else:
264
+ odxraise(f"The parameter referenced by environment data descriptions "
265
+ f"must be a parameter that either specifies a DOP or a constant "
266
+ f"(encountered {type(param).__name__} for reference '{self.param_snref}' of "
267
+ f"ENV-DATA-DESC '{self.short_name}')")
268
+ return 0
odxtools/exceptions.py CHANGED
@@ -9,13 +9,22 @@ class OdxError(Exception):
9
9
 
10
10
 
11
11
  class EncodeError(Warning, OdxError):
12
- """Encoding of a message to raw data failed."""
12
+ """Encoding of a message to raw data failed"""
13
13
 
14
14
 
15
15
  class DecodeError(Warning, OdxError):
16
16
  """Decoding raw data failed."""
17
17
 
18
18
 
19
+ class DecodeMismatch(DecodeError):
20
+ """Decoding failed because some parameters exhibit an incorrect value
21
+
22
+ This is can happen if NRC-CONST or environment data descriptions
23
+ are present.
24
+
25
+ """
26
+
27
+
19
28
  class OdxWarning(Warning):
20
29
  """Any warning that happens during interacting with diagnostic objects."""
21
30
 
@@ -40,7 +49,7 @@ def odxraise(message: Optional[str] = None, error_type: Type[Exception] = OdxErr
40
49
  else:
41
50
  raise error_type(message)
42
51
  elif message is not None:
43
- logger.warn(message)
52
+ logger.warning(message)
44
53
 
45
54
 
46
55
  def odxassert(condition: bool,
odxtools/field.py CHANGED
@@ -1,18 +1,17 @@
1
+ # SPDX-License-Identifier: MIT
1
2
  from dataclasses import dataclass
2
- from typing import TYPE_CHECKING, List, Optional
3
+ from typing import List, Optional
3
4
  from xml.etree import ElementTree
4
5
 
5
6
  from .basicstructure import BasicStructure
6
7
  from .complexdop import ComplexDop
7
8
  from .environmentdatadescription import EnvironmentDataDescription
8
9
  from .exceptions import odxassert, odxrequire
9
- from .odxlink import OdxDocFragment, OdxLinkDatabase, OdxLinkRef
10
+ from .odxlink import OdxDocFragment, OdxLinkDatabase, OdxLinkRef, resolve_snref
10
11
  from .odxtypes import odxstr_to_bool
12
+ from .snrefcontext import SnRefContext
11
13
  from .utils import dataclass_fields_asdict
12
14
 
13
- if TYPE_CHECKING:
14
- from .diaglayer import DiagLayer
15
-
16
15
 
17
16
  @dataclass
18
17
  class Field(ComplexDop):
@@ -80,12 +79,13 @@ class Field(ComplexDop):
80
79
  self._env_data_desc = odxlinks.resolve(self.env_data_desc_ref,
81
80
  EnvironmentDataDescription)
82
81
 
83
- def _resolve_snrefs(self, diag_layer: "DiagLayer") -> None:
82
+ def _resolve_snrefs(self, context: SnRefContext) -> None:
84
83
  """Recursively resolve any short-name references"""
84
+ ddds = odxrequire(context.diag_layer).diag_data_dictionary_spec
85
+
85
86
  if self.structure_snref is not None:
86
- structures = diag_layer.diag_data_dictionary_spec.structures
87
- self._structure = odxrequire(structures.get(self.structure_snref))
87
+ self._structure = resolve_snref(self.structure_snref, ddds.structures, BasicStructure)
88
88
 
89
89
  if self.env_data_desc_snref is not None:
90
- env_data_descs = diag_layer.diag_data_dictionary_spec.env_data_descs
91
- self._env_data_desc = odxrequire(env_data_descs.get(self.env_data_desc_snref))
90
+ self._env_data_desc = resolve_snref(self.env_data_desc_snref, ddds.env_data_descs,
91
+ EnvironmentDataDescription)
@@ -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 FunctionalClass(IdentifiableElement):
@@ -31,5 +29,5 @@ class FunctionalClass(IdentifiableElement):
31
29
  def _resolve_odxlinks(self, odxlinks: OdxLinkDatabase) -> None:
32
30
  pass
33
31
 
34
- def _resolve_snrefs(self, diag_layer: "DiagLayer") -> None:
32
+ def _resolve_snrefs(self, context: SnRefContext) -> None:
35
33
  pass
odxtools/inputparam.py CHANGED
@@ -1,19 +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
- from deprecation import deprecated
7
-
8
6
  from .dopbase import DopBase
9
7
  from .element import NamedElement
10
8
  from .exceptions import odxrequire
11
9
  from .odxlink import OdxDocFragment, OdxLinkDatabase, OdxLinkId, OdxLinkRef
10
+ from .snrefcontext import SnRefContext
12
11
  from .utils import dataclass_fields_asdict
13
12
 
14
- if TYPE_CHECKING:
15
- from .diaglayer import DiagLayer
16
-
17
13
 
18
14
  @dataclass
19
15
  class InputParam(NamedElement):
@@ -44,15 +40,10 @@ class InputParam(NamedElement):
44
40
  def _resolve_odxlinks(self, odxlinks: OdxLinkDatabase) -> None:
45
41
  self._dop_base = odxlinks.resolve(self.dop_base_ref, DopBase)
46
42
 
47
- def _resolve_snrefs(self, diag_layer: "DiagLayer") -> None:
43
+ def _resolve_snrefs(self, context: SnRefContext) -> None:
48
44
  pass
49
45
 
50
46
  @property
51
47
  def dop_base(self) -> DopBase:
52
48
  """The data object property describing this parameter."""
53
49
  return self._dop_base
54
-
55
- @property
56
- @deprecated(details="use .dop_base") # type: ignore[misc]
57
- def dop(self) -> DopBase:
58
- return self._dop_base
@@ -1,12 +1,17 @@
1
1
  # SPDX-License-Identifier: MIT
2
2
  from dataclasses import dataclass
3
- from typing import Any, Optional
3
+ from typing import List
4
+ from xml.etree import ElementTree
5
+
6
+ from typing_extensions import override
4
7
 
5
8
  from .decodestate import DecodeState
6
9
  from .diagcodedtype import DctType, DiagCodedType
7
10
  from .encodestate import EncodeState
8
- from .exceptions import odxassert, odxraise
11
+ from .exceptions import EncodeError, odxassert, odxraise, odxrequire
12
+ from .odxlink import OdxDocFragment
9
13
  from .odxtypes import AtomicOdxType, DataType
14
+ from .utils import dataclass_fields_asdict
10
15
 
11
16
 
12
17
  @dataclass
@@ -18,6 +23,16 @@ class LeadingLengthInfoType(DiagCodedType):
18
23
  #: object.
19
24
  bit_length: int
20
25
 
26
+ @staticmethod
27
+ @override
28
+ def from_et(et_element: ElementTree.Element,
29
+ doc_frags: List[OdxDocFragment]) -> "LeadingLengthInfoType":
30
+ kwargs = dataclass_fields_asdict(DiagCodedType.from_et(et_element, doc_frags))
31
+
32
+ bit_length = int(odxrequire(et_element.findtext("BIT-LENGTH")))
33
+
34
+ return LeadingLengthInfoType(bit_length=bit_length, **kwargs)
35
+
21
36
  def __post_init__(self) -> None:
22
37
  odxassert(self.bit_length > 0,
23
38
  "A Leading length info type with bit length == 0 does not make sense.")
@@ -35,36 +50,40 @@ class LeadingLengthInfoType(DiagCodedType):
35
50
  def dct_type(self) -> DctType:
36
51
  return "LEADING-LENGTH-INFO-TYPE"
37
52
 
38
- def get_static_bit_length(self) -> Optional[int]:
39
- # note that self.bit_length is just the length of the length
40
- # specifier field. This is then followed by the same number of
41
- # bytes as the value of this field, i.e., the length of this
42
- # DCT is dynamic!
43
- return None
53
+ @override
54
+ def encode_into_pdu(self, internal_value: AtomicOdxType, encode_state: EncodeState) -> None:
44
55
 
45
- def convert_internal_to_bytes(self, internal_value: Any, encode_state: EncodeState,
46
- bit_position: int) -> bytes:
56
+ if not isinstance(internal_value, (str, bytes)):
57
+ odxraise(
58
+ f"LEADING-LENGTH-INFO types can only be used for strings and byte fields, "
59
+ f"not {type(internal_value).__name__}", EncodeError)
60
+ return
47
61
 
48
62
  byte_length = self._minimal_byte_length_of(internal_value)
49
63
 
50
- length_bytes = self._encode_internal_value(
51
- byte_length,
52
- bit_position=bit_position,
64
+ used_mask = None
65
+ bit_pos = encode_state.cursor_bit_position
66
+ if encode_state.cursor_bit_position != 0 or (bit_pos + self.bit_length) % 8 != 0:
67
+ used_mask = (1 << self.bit_length) - 1
68
+ used_mask <<= bit_pos
69
+
70
+ encode_state.emplace_atomic_value(
71
+ internal_value=byte_length,
72
+ used_mask=None,
53
73
  bit_length=self.bit_length,
54
74
  base_data_type=DataType.A_UINT32,
55
75
  is_highlow_byte_order=self.is_highlow_byte_order,
56
76
  )
57
77
 
58
- value_bytes = self._encode_internal_value(
59
- internal_value,
60
- bit_position=0,
78
+ encode_state.emplace_atomic_value(
79
+ internal_value=internal_value,
80
+ used_mask=None,
61
81
  bit_length=8 * byte_length,
62
82
  base_data_type=self.base_data_type,
63
83
  is_highlow_byte_order=self.is_highlow_byte_order,
64
84
  )
65
85
 
66
- return length_bytes + value_bytes
67
-
86
+ @override
68
87
  def decode_from_pdu(self, decode_state: DecodeState) -> AtomicOdxType:
69
88
 
70
89
  # Extract length of the parameter value
odxtools/library.py ADDED
@@ -0,0 +1,66 @@
1
+ # SPDX-License-Identifier: MIT
2
+ from dataclasses import dataclass
3
+ from typing import Any, Dict, List, Optional, cast
4
+ from xml.etree import ElementTree
5
+
6
+ from .element import IdentifiableElement
7
+ from .exceptions import odxraise, odxrequire
8
+ from .odxlink import OdxDocFragment, OdxLinkDatabase, OdxLinkId
9
+ from .snrefcontext import SnRefContext
10
+ from .utils import dataclass_fields_asdict
11
+
12
+
13
+ @dataclass
14
+ class Library(IdentifiableElement):
15
+ """
16
+ A library defines a shared library used for single ECU jobs etc.
17
+
18
+ It this is basically equivalent to ProgCode.
19
+ """
20
+
21
+ code_file: str
22
+ encryption: Optional[str]
23
+ syntax: str
24
+ revision: str
25
+ entrypoint: Optional[str]
26
+
27
+ @property
28
+ def code(self) -> bytes:
29
+ return self._code
30
+
31
+ @staticmethod
32
+ def from_et(et_element: ElementTree.Element, doc_frags: List[OdxDocFragment]) -> "Library":
33
+
34
+ kwargs = dataclass_fields_asdict(IdentifiableElement.from_et(et_element, doc_frags))
35
+
36
+ code_file = odxrequire(et_element.findtext("CODE-FILE"))
37
+ encryption = et_element.findtext("ENCRYPTION")
38
+ syntax = odxrequire(et_element.findtext("SYNTAX"))
39
+ revision = odxrequire(et_element.findtext("REVISION"))
40
+ entrypoint = et_element.findtext("ENTRYPOINT")
41
+
42
+ return Library(
43
+ code_file=code_file,
44
+ encryption=encryption,
45
+ syntax=syntax,
46
+ revision=revision,
47
+ entrypoint=entrypoint,
48
+ **kwargs)
49
+
50
+ def _build_odxlinks(self) -> Dict[OdxLinkId, Any]:
51
+ return {self.odx_id: self}
52
+
53
+ def _resolve_odxlinks(self, odxlinks: OdxLinkDatabase) -> None:
54
+ pass
55
+
56
+ def _resolve_snrefs(self, context: SnRefContext) -> None:
57
+ aux_file = odxrequire(context.database).auxiliary_files.get(self.code_file)
58
+
59
+ if aux_file is None:
60
+ odxraise(f"Reference to auxiliary file '{self.code_file}' "
61
+ f"could not be resolved")
62
+ self._code: bytes = cast(bytes, None)
63
+ return
64
+
65
+ self._code = aux_file.read()
66
+ aux_file.seek(0)
odxtools/loadfile.py ADDED
@@ -0,0 +1,64 @@
1
+ # SPDX-License-Identifier: MIT
2
+ import os
3
+ from pathlib import Path
4
+ from typing import Union
5
+
6
+ from .database import Database
7
+
8
+
9
+ def load_pdx_file(pdx_file: Union[str, Path]) -> Database:
10
+ db = Database()
11
+ db.add_pdx_file(str(pdx_file))
12
+ db.refresh()
13
+ return db
14
+
15
+
16
+ def load_odx_d_file(odx_d_file_name: Union[str, Path]) -> Database:
17
+ db = Database()
18
+ db.add_odx_file(str(odx_d_file_name))
19
+ db.refresh()
20
+
21
+ return db
22
+
23
+
24
+ def load_file(file_name: Union[str, Path]) -> Database:
25
+ if str(file_name).lower().endswith(".pdx"):
26
+ return load_pdx_file(str(file_name))
27
+ elif str(file_name).lower().endswith(".odx-d"):
28
+ return load_odx_d_file(str(file_name))
29
+ else:
30
+ raise RuntimeError(f"Could not guess the file format of file '{file_name}'!")
31
+
32
+
33
+ def load_files(*file_names: Union[str, Path]) -> Database:
34
+ db = Database()
35
+ for file_name in file_names:
36
+ p = Path(file_name)
37
+ if p.suffix.lower() == ".pdx":
38
+ db.add_pdx_file(str(file_name))
39
+ elif p.suffix.lower().startswith(".odx"):
40
+ db.add_odx_file(str(file_name))
41
+ elif p.name.lower() != "index.xml":
42
+ db.add_auxiliary_file(str(file_name))
43
+
44
+ db.refresh()
45
+ return db
46
+
47
+
48
+ def load_directory(dir_name: Union[str, Path]) -> Database:
49
+ db = Database()
50
+ for file_name in os.listdir(str(dir_name)):
51
+ p = Path(dir_name) / file_name
52
+
53
+ if not p.is_file():
54
+ continue
55
+
56
+ if p.suffix.lower() == ".pdx":
57
+ db.add_pdx_file(str(p))
58
+ elif p.suffix.lower().startswith(".odx"):
59
+ db.add_odx_file(str(p))
60
+ elif p.name.lower() != "index.xml":
61
+ db.add_auxiliary_file(p.name, open(str(p), "rb"))
62
+
63
+ db.refresh()
64
+ return db