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
@@ -0,0 +1,56 @@
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 ..odxlink import OdxDocFragment, OdxLinkDatabase, OdxLinkId
7
+ from ..odxtypes import DataType
8
+ from ..progcode import ProgCode
9
+ from ..snrefcontext import SnRefContext
10
+ from .compudefaultvalue import CompuDefaultValue
11
+ from .compuscale import CompuScale
12
+
13
+
14
+ @dataclass
15
+ class CompuPhysToInternal:
16
+ compu_scales: List[CompuScale]
17
+ prog_code: Optional[ProgCode]
18
+ compu_default_value: Optional[CompuDefaultValue]
19
+
20
+ @staticmethod
21
+ def compu_phys_to_internal_from_et(et_element: ElementTree.Element,
22
+ doc_frags: List[OdxDocFragment], *, internal_type: DataType,
23
+ physical_type: DataType) -> "CompuPhysToInternal":
24
+ compu_scales = [
25
+ CompuScale.compuscale_from_et(
26
+ cse, doc_frags, domain_type=physical_type, range_type=internal_type)
27
+ for cse in et_element.iterfind("COMPU-SCALES/COMPU-SCALE")
28
+ ]
29
+
30
+ prog_code = None
31
+ if (pce := et_element.find("PROG-CODE")) is not None:
32
+ prog_code = ProgCode.from_et(pce, doc_frags)
33
+
34
+ compu_default_value = None
35
+ if (cdve := et_element.find("COMPU-DEFAULT-VALUE")) is not None:
36
+ compu_default_value = CompuDefaultValue.compuvalue_from_et(
37
+ cdve, data_type=internal_type)
38
+
39
+ return CompuPhysToInternal(
40
+ compu_scales=compu_scales, prog_code=prog_code, compu_default_value=compu_default_value)
41
+
42
+ def _build_odxlinks(self) -> Dict[OdxLinkId, Any]:
43
+ result = {}
44
+
45
+ if self.prog_code is not None:
46
+ result.update(self.prog_code._build_odxlinks())
47
+
48
+ return result
49
+
50
+ def _resolve_odxlinks(self, odxlinks: OdxLinkDatabase) -> None:
51
+ if self.prog_code is not None:
52
+ self.prog_code._resolve_odxlinks(odxlinks)
53
+
54
+ def _resolve_snrefs(self, context: SnRefContext) -> None:
55
+ if self.prog_code is not None:
56
+ self.prog_code._resolve_snrefs(context)
@@ -1,25 +1,36 @@
1
1
  # SPDX-License-Identifier: MIT
2
2
  from dataclasses import dataclass
3
- from typing import List
3
+ from typing import List, Union, cast
4
4
  from xml.etree import ElementTree
5
5
 
6
- from ..exceptions import odxrequire
6
+ from ..exceptions import odxassert, odxrequire
7
7
  from ..odxlink import OdxDocFragment
8
+ from ..odxtypes import DataType
8
9
 
9
10
 
10
11
  @dataclass
11
12
  class CompuRationalCoeffs:
12
- numerators: List[float]
13
- denominators: List[float]
13
+ value_type: DataType
14
+
15
+ numerators: List[Union[int, float]]
16
+ denominators: List[Union[int, float]]
14
17
 
15
18
  @staticmethod
16
- def from_et(et_element: ElementTree.Element,
17
- doc_frags: List[OdxDocFragment]) -> "CompuRationalCoeffs":
19
+ def coeffs_from_et(et_element: ElementTree.Element, doc_frags: List[OdxDocFragment], *,
20
+ value_type: DataType) -> "CompuRationalCoeffs":
21
+ odxassert(
22
+ value_type
23
+ in (DataType.A_UINT32, DataType.A_INT32, DataType.A_FLOAT32, DataType.A_FLOAT64),
24
+ "Rational coefficients must be of numeric type.")
25
+
18
26
  numerators = [
19
- float(odxrequire(elem.text)) for elem in et_element.iterfind("COMPU-NUMERATOR/V")
27
+ cast(float, value_type.from_string(odxrequire(elem.text)))
28
+ for elem in et_element.iterfind("COMPU-NUMERATOR/V")
20
29
  ]
21
30
  denominators = [
22
- float(odxrequire(elem.text)) for elem in et_element.iterfind("COMPU-DENOMINATOR/V")
31
+ cast(float, value_type.from_string(odxrequire(elem.text)))
32
+ for elem in et_element.iterfind("COMPU-DENOMINATOR/V")
23
33
  ]
24
34
 
25
- return CompuRationalCoeffs(numerators=numerators, denominators=denominators)
35
+ return CompuRationalCoeffs(
36
+ value_type=value_type, numerators=numerators, denominators=denominators)
@@ -3,9 +3,11 @@ from dataclasses import dataclass
3
3
  from typing import List, Optional
4
4
  from xml.etree import ElementTree
5
5
 
6
+ from ..description import Description
6
7
  from ..odxlink import OdxDocFragment
7
8
  from ..odxtypes import AtomicOdxType, DataType
8
- from ..utils import create_description_from_et
9
+ from .compuconst import CompuConst
10
+ from .compuinversevalue import CompuInverseValue
9
11
  from .compurationalcoeffs import CompuRationalCoeffs
10
12
  from .limit import Limit
11
13
 
@@ -13,58 +15,51 @@ from .limit import Limit
13
15
  @dataclass
14
16
  class CompuScale:
15
17
  """A COMPU-SCALE represents one value range of a COMPU-METHOD.
16
-
17
- Example:
18
-
19
- For a TEXTTABLE compu method a compu scale within COMPU-INTERNAL-TO-PHYS
20
- can be defined with
21
- ```
22
- scale = CompuScale(
23
- short_label="example_label", # optional: provide a label
24
- description="<p>fancy description</p>", # optional: provide a description
25
- lower_limit=Limit(0), # required: lower limit
26
- upper_limit=Limit(3), # required: upper limit
27
- compu_inverse_value=2, # required if lower_limit != upper_limit
28
- compu_const="true", # required: physical value to be shown to the user
29
- )
30
- ```
31
-
32
- Almost all attributes are optional but there are compu-method-specific restrictions.
33
- E.g., lower_limit must always be defined unless the COMPU-METHOD is of CATEGORY LINEAR or RAT-FUNC.
34
- Either `compu_const` or `compu_rational_coeffs` must be defined but never both.
35
18
  """
36
19
 
37
20
  short_label: Optional[str]
38
- description: Optional[str]
21
+ description: Optional[Description]
39
22
  lower_limit: Optional[Limit]
40
23
  upper_limit: Optional[Limit]
41
- compu_inverse_value: Optional[AtomicOdxType]
42
- compu_const: Optional[AtomicOdxType]
24
+ compu_inverse_value: Optional[CompuInverseValue]
25
+ compu_const: Optional[CompuConst]
43
26
  compu_rational_coeffs: Optional[CompuRationalCoeffs]
44
27
 
45
28
  # the following two attributes are not specified for COMPU-SCALE
46
29
  # tags in the XML, but they are required to do anything useful
47
- # with it.
48
- internal_type: DataType
49
- physical_type: DataType
30
+ # with compu scales: The domain type is the input set of the
31
+ # function associated with the compu scale object, whilst the
32
+ # range type represents the output set. IOW, for scales contained
33
+ # by the internal-to-physical mapping function, the domain type is
34
+ # the internal and the range type is the physical type of the
35
+ # compu method. (Vice versa for scales specified by the
36
+ # physical-to-internal mapping function.)
37
+ domain_type: DataType
38
+ range_type: DataType
50
39
 
51
40
  @staticmethod
52
41
  def compuscale_from_et(et_element: ElementTree.Element, doc_frags: List[OdxDocFragment], *,
53
- internal_type: DataType, physical_type: DataType) -> "CompuScale":
42
+ domain_type: DataType, range_type: DataType) -> "CompuScale":
54
43
  short_label = et_element.findtext("SHORT-LABEL")
55
- description = create_description_from_et(et_element.find("DESC"))
44
+ description = Description.from_et(et_element.find("DESC"), doc_frags)
56
45
 
57
46
  lower_limit = Limit.limit_from_et(
58
- et_element.find("LOWER-LIMIT"), doc_frags, value_type=internal_type)
47
+ et_element.find("LOWER-LIMIT"), doc_frags, value_type=domain_type)
59
48
  upper_limit = Limit.limit_from_et(
60
- et_element.find("UPPER-LIMIT"), doc_frags, value_type=internal_type)
49
+ et_element.find("UPPER-LIMIT"), doc_frags, value_type=domain_type)
50
+
51
+ compu_inverse_value = None
52
+ if (cive := et_element.find("COMPU-INVERSE-VALUE")) is not None:
53
+ compu_inverse_value = CompuInverseValue.compuvalue_from_et(cive, data_type=domain_type)
61
54
 
62
- compu_inverse_value = internal_type.create_from_et(et_element.find("COMPU-INVERSE-VALUE"))
63
- compu_const = physical_type.create_from_et(et_element.find("COMPU-CONST"))
55
+ compu_const = None
56
+ if (cce := et_element.find("COMPU-CONST")) is not None:
57
+ compu_const = CompuConst.compuvalue_from_et(cce, data_type=range_type)
64
58
 
65
59
  compu_rational_coeffs: Optional[CompuRationalCoeffs] = None
66
60
  if (crc_elem := et_element.find("COMPU-RATIONAL-COEFFS")) is not None:
67
- compu_rational_coeffs = CompuRationalCoeffs.from_et(crc_elem, doc_frags)
61
+ compu_rational_coeffs = CompuRationalCoeffs.coeffs_from_et(
62
+ crc_elem, doc_frags, value_type=range_type)
68
63
 
69
64
  return CompuScale(
70
65
  short_label=short_label,
@@ -74,8 +69,8 @@ class CompuScale:
74
69
  compu_inverse_value=compu_inverse_value,
75
70
  compu_const=compu_const,
76
71
  compu_rational_coeffs=compu_rational_coeffs,
77
- internal_type=internal_type,
78
- physical_type=physical_type)
72
+ domain_type=domain_type,
73
+ range_type=range_type)
79
74
 
80
75
  def applies(self, internal_value: AtomicOdxType) -> bool:
81
76
 
@@ -1,186 +1,53 @@
1
1
  # SPDX-License-Identifier: MIT
2
- from typing import Any, Dict, List, Optional
2
+ from typing import List
3
3
  from xml.etree import ElementTree
4
4
 
5
- from ..exceptions import odxassert, odxraise, odxrequire
5
+ from ..exceptions import odxraise, odxrequire
6
6
  from ..odxlink import OdxDocFragment
7
7
  from ..odxtypes import DataType
8
+ from .compucodecompumethod import CompuCodeCompuMethod
8
9
  from .compumethod import CompuMethod
9
- from .compuscale import CompuScale
10
10
  from .identicalcompumethod import IdenticalCompuMethod
11
- from .limit import Limit
12
11
  from .linearcompumethod import LinearCompuMethod
12
+ from .ratfunccompumethod import RatFuncCompuMethod
13
13
  from .scalelinearcompumethod import ScaleLinearCompuMethod
14
+ from .scaleratfunccompumethod import ScaleRatFuncCompuMethod
14
15
  from .tabintpcompumethod import TabIntpCompuMethod
15
16
  from .texttablecompumethod import TexttableCompuMethod
16
17
 
17
18
 
18
- def _parse_compu_scale_to_linear_compu_method(
19
- et_element: ElementTree.Element,
20
- doc_frags: List[OdxDocFragment],
21
- *,
22
- internal_type: DataType,
23
- physical_type: DataType,
24
- is_scale_linear: bool = False,
25
- **kwargs: Any,
26
- ) -> LinearCompuMethod:
27
- odxassert(physical_type in [
28
- DataType.A_FLOAT32,
29
- DataType.A_FLOAT64,
30
- DataType.A_INT32,
31
- DataType.A_UINT32,
32
- ])
33
- odxassert(internal_type in [
34
- DataType.A_FLOAT32,
35
- DataType.A_FLOAT64,
36
- DataType.A_INT32,
37
- DataType.A_UINT32,
38
- ])
39
-
40
- if physical_type.python_type == float:
41
- computation_python_type = physical_type.from_string
42
- else:
43
- computation_python_type = internal_type.from_string
44
-
45
- kwargs = kwargs.copy()
46
- kwargs["internal_type"] = internal_type
47
- kwargs["physical_type"] = physical_type
48
-
49
- coeffs = odxrequire(et_element.find("COMPU-RATIONAL-COEFFS"))
50
- nums = coeffs.iterfind("COMPU-NUMERATOR/V")
51
-
52
- offset = computation_python_type(odxrequire(next(nums).text))
53
- factor_el = next(nums, None)
54
- factor = computation_python_type(odxrequire(factor_el.text) if factor_el is not None else "0")
55
- denominator = 1.0
56
- if (string := coeffs.findtext("COMPU-DENOMINATOR/V")) is not None:
57
- denominator = float(string)
58
- if denominator == 0:
59
- odxraise("CompuMethod: A denominator of zero will lead to divisions by zero.")
60
-
61
- # Read lower limit
62
- internal_lower_limit = Limit.limit_from_et(
63
- et_element.find("LOWER-LIMIT"),
64
- doc_frags,
65
- value_type=internal_type,
66
- )
67
-
68
- kwargs["internal_lower_limit"] = internal_lower_limit
69
-
70
- # Read upper limit
71
- internal_upper_limit = Limit.limit_from_et(
72
- et_element.find("UPPER-LIMIT"),
73
- doc_frags,
74
- value_type=internal_type,
75
- )
76
-
77
- kwargs["internal_upper_limit"] = internal_upper_limit
78
- kwargs["denominator"] = denominator
79
- kwargs["factor"] = factor
80
- kwargs["offset"] = offset
81
-
82
- return LinearCompuMethod(**kwargs)
83
-
84
-
85
- def create_compu_default_value(et_element: Optional[ElementTree.Element],
86
- doc_frags: List[OdxDocFragment], internal_type: DataType, *,
87
- physical_type: DataType) -> Optional[CompuScale]:
88
- if et_element is None:
89
- return None
90
- compu_const = physical_type.create_from_et(et_element)
91
- scale = CompuScale.compuscale_from_et(
92
- et_element, doc_frags, internal_type=internal_type, physical_type=physical_type)
93
- scale.compu_const = compu_const
94
- return scale
95
-
96
-
97
19
  def create_any_compu_method_from_et(et_element: ElementTree.Element,
98
20
  doc_frags: List[OdxDocFragment], *, internal_type: DataType,
99
21
  physical_type: DataType) -> CompuMethod:
100
- compu_category = et_element.findtext("CATEGORY")
101
- odxassert(compu_category in [
102
- "IDENTICAL",
103
- "LINEAR",
104
- "SCALE-LINEAR",
105
- "TEXTTABLE",
106
- "COMPUCODE",
107
- "TAB-INTP",
108
- "RAT-FUNC",
109
- "SCALE-RAT-FUNC",
110
- ])
111
-
112
- if et_element.find("COMPU-PHYS-TO-INTERNAL") is not None: # TODO: Is this never used?
113
- raise NotImplementedError(f"Found COMPU-PHYS-TO-INTERNAL for category {compu_category}")
114
-
115
- kwargs: Dict[str, Any] = {
116
- "physical_type": physical_type,
117
- "internal_type": internal_type,
118
- }
22
+ compu_category = odxrequire(et_element.findtext("CATEGORY"))
119
23
 
120
24
  if compu_category == "IDENTICAL":
121
- odxassert(
122
- internal_type == physical_type or
123
- (internal_type in [DataType.A_ASCIISTRING, DataType.A_UTF8STRING] and
124
- physical_type == DataType.A_UNICODE2STRING),
125
- f"Internal type '{internal_type}' and physical type '{physical_type}'"
126
- f" must be the same for compu methods of category '{compu_category}'")
127
- return IdenticalCompuMethod(internal_type=internal_type, physical_type=physical_type)
128
-
129
- if compu_category == "TEXTTABLE":
130
- odxassert(physical_type == DataType.A_UNICODE2STRING)
131
- compu_internal_to_phys = odxrequire(et_element.find("COMPU-INTERNAL-TO-PHYS"))
132
-
133
- internal_to_phys: List[CompuScale] = []
134
- for scale_elem in compu_internal_to_phys.iterfind("COMPU-SCALES/COMPU-SCALE"):
135
- internal_to_phys.append(
136
- CompuScale.compuscale_from_et(
137
- scale_elem, doc_frags, internal_type=internal_type,
138
- physical_type=physical_type))
139
- compu_default_value = create_compu_default_value(
140
- et_element.find("COMPU-DEFAULT-VALUE"), doc_frags, **kwargs)
141
-
142
- return TexttableCompuMethod(
143
- internal_to_phys=internal_to_phys,
144
- compu_default_value=compu_default_value,
145
- **kwargs,
146
- )
147
-
25
+ return IdenticalCompuMethod.compu_method_from_et(
26
+ et_element, doc_frags, internal_type=internal_type, physical_type=physical_type)
148
27
  elif compu_category == "LINEAR":
149
- # Compu method can be described by the function f(x) = (offset + factor * x) / denominator
150
-
151
- scale_elem = odxrequire(et_element.find("COMPU-INTERNAL-TO-PHYS/COMPU-SCALES/COMPU-SCALE"))
152
- return _parse_compu_scale_to_linear_compu_method(scale_elem, doc_frags, **kwargs)
153
-
28
+ return LinearCompuMethod.compu_method_from_et(
29
+ et_element, doc_frags, internal_type=internal_type, physical_type=physical_type)
154
30
  elif compu_category == "SCALE-LINEAR":
155
-
156
- scale_elems = et_element.iterfind("COMPU-INTERNAL-TO-PHYS/COMPU-SCALES/COMPU-SCALE")
157
- linear_methods = [
158
- _parse_compu_scale_to_linear_compu_method(scale_elem, doc_frags, **kwargs)
159
- for scale_elem in scale_elems
160
- ]
161
- return ScaleLinearCompuMethod(linear_methods=linear_methods, **kwargs)
162
-
31
+ return ScaleLinearCompuMethod.compu_method_from_et(
32
+ et_element, doc_frags, internal_type=internal_type, physical_type=physical_type)
33
+ elif compu_category == "RAT-FUNC":
34
+ return RatFuncCompuMethod.compu_method_from_et(
35
+ et_element, doc_frags, internal_type=internal_type, physical_type=physical_type)
36
+ elif compu_category == "SCALE-RAT-FUNC":
37
+ return ScaleRatFuncCompuMethod.compu_method_from_et(
38
+ et_element, doc_frags, internal_type=internal_type, physical_type=physical_type)
39
+ elif compu_category == "TEXTTABLE":
40
+ return TexttableCompuMethod.compu_method_from_et(
41
+ et_element, doc_frags, internal_type=internal_type, physical_type=physical_type)
42
+ elif compu_category == "COMPUCODE":
43
+ return CompuCodeCompuMethod.compu_method_from_et(
44
+ et_element, doc_frags, internal_type=internal_type, physical_type=physical_type)
163
45
  elif compu_category == "TAB-INTP":
164
- internal_points = []
165
- physical_points = []
166
- for scale_elem in et_element.iterfind("COMPU-INTERNAL-TO-PHYS/COMPU-SCALES/COMPU-SCALE"):
167
- internal_point = internal_type.from_string(
168
- odxrequire(scale_elem.findtext("LOWER-LIMIT")))
169
- physical_point = physical_type.create_from_et(
170
- odxrequire(scale_elem.find("COMPU-CONST")))
171
-
172
- if not isinstance(internal_point, (float, int)):
173
- odxraise()
174
- if not isinstance(physical_point, (float, int)):
175
- odxraise()
176
-
177
- internal_points.append(internal_point)
178
- physical_points.append(physical_point)
179
-
180
- return TabIntpCompuMethod(
181
- internal_points=internal_points, physical_points=physical_points, **kwargs)
46
+ return TabIntpCompuMethod.compu_method_from_et(
47
+ et_element, doc_frags, internal_type=internal_type, physical_type=physical_type)
182
48
 
183
49
  # TODO: Implement all categories (never instantiate the CompuMethod base class!)
184
50
  odxraise(f"Warning: Computation category {compu_category} is not implemented!")
185
51
 
186
- return IdenticalCompuMethod(internal_type=DataType.A_UINT32, physical_type=DataType.A_UINT32)
52
+ return IdenticalCompuMethod.compu_method_from_et(
53
+ et_element, doc_frags, internal_type=internal_type, physical_type=physical_type)
@@ -1,16 +1,41 @@
1
1
  # SPDX-License-Identifier: MIT
2
2
  from dataclasses import dataclass
3
+ from typing import List
4
+ from xml.etree import ElementTree
3
5
 
4
- from ..odxtypes import AtomicOdxType
5
- from .compumethod import CompuMethod, CompuMethodCategory
6
+ from ..exceptions import odxassert
7
+ from ..odxlink import OdxDocFragment
8
+ from ..odxtypes import AtomicOdxType, DataType
9
+ from ..utils import dataclass_fields_asdict
10
+ from .compumethod import CompuMethod
6
11
 
7
12
 
8
13
  @dataclass
9
14
  class IdenticalCompuMethod(CompuMethod):
10
-
11
- @property
12
- def category(self) -> CompuMethodCategory:
13
- return "IDENTICAL"
15
+ """Identical compu methods just pass through the internal value.
16
+
17
+ For details, refer to ASAM specification MCD-2 D (ODX), section 7.3.6.6.2.
18
+ """
19
+
20
+ @staticmethod
21
+ def compu_method_from_et(et_element: ElementTree.Element, doc_frags: List[OdxDocFragment], *,
22
+ internal_type: DataType,
23
+ physical_type: DataType) -> "IdenticalCompuMethod":
24
+ cm = CompuMethod.compu_method_from_et(
25
+ et_element, doc_frags, internal_type=internal_type, physical_type=physical_type)
26
+ kwargs = dataclass_fields_asdict(cm)
27
+
28
+ odxassert(
29
+ internal_type == physical_type or
30
+ (internal_type
31
+ in [DataType.A_ASCIISTRING, DataType.A_UTF8STRING, DataType.A_UNICODE2STRING] and
32
+ physical_type
33
+ in [DataType.A_ASCIISTRING, DataType.A_UTF8STRING, DataType.A_UNICODE2STRING]),
34
+ f"Internal type and physical type must be the same for compu methods of category "
35
+ f"'{cm.category}' (internal type: '{internal_type.value}', physical type: "
36
+ f"'{physical_type.value}')")
37
+
38
+ return IdenticalCompuMethod(**kwargs)
14
39
 
15
40
  def convert_physical_to_internal(self, physical_value: AtomicOdxType) -> AtomicOdxType:
16
41
  return physical_value