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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (213) hide show
  1. odxtools/__init__.py +6 -4
  2. odxtools/additionalaudience.py +3 -5
  3. odxtools/admindata.py +5 -7
  4. odxtools/audience.py +10 -13
  5. odxtools/basecomparam.py +3 -5
  6. odxtools/basicstructure.py +55 -240
  7. odxtools/cli/_parser_utils.py +1 -1
  8. odxtools/cli/_print_utils.py +168 -134
  9. odxtools/cli/browse.py +111 -92
  10. odxtools/cli/compare.py +90 -71
  11. odxtools/cli/list.py +24 -15
  12. odxtools/cli/snoop.py +28 -5
  13. odxtools/codec.py +211 -0
  14. odxtools/commrelation.py +122 -0
  15. odxtools/companydata.py +5 -7
  16. odxtools/companydocinfo.py +7 -8
  17. odxtools/companyrevisioninfo.py +3 -5
  18. odxtools/companyspecificinfo.py +8 -9
  19. odxtools/comparam.py +4 -6
  20. odxtools/comparaminstance.py +7 -9
  21. odxtools/comparamspec.py +16 -54
  22. odxtools/comparamsubset.py +22 -62
  23. odxtools/complexcomparam.py +5 -7
  24. odxtools/compumethods/compucodecompumethod.py +63 -0
  25. odxtools/compumethods/compuconst.py +31 -0
  26. odxtools/compumethods/compudefaultvalue.py +27 -0
  27. odxtools/compumethods/compuinternaltophys.py +56 -0
  28. odxtools/compumethods/compuinversevalue.py +7 -0
  29. odxtools/compumethods/compumethod.py +93 -12
  30. odxtools/compumethods/compuphystointernal.py +56 -0
  31. odxtools/compumethods/compurationalcoeffs.py +20 -9
  32. odxtools/compumethods/compuscale.py +30 -35
  33. odxtools/compumethods/createanycompumethod.py +28 -161
  34. odxtools/compumethods/identicalcompumethod.py +31 -6
  35. odxtools/compumethods/linearcompumethod.py +69 -189
  36. odxtools/compumethods/linearsegment.py +190 -0
  37. odxtools/compumethods/ratfunccompumethod.py +106 -0
  38. odxtools/compumethods/ratfuncsegment.py +87 -0
  39. odxtools/compumethods/scalelinearcompumethod.py +132 -26
  40. odxtools/compumethods/scaleratfunccompumethod.py +113 -0
  41. odxtools/compumethods/tabintpcompumethod.py +119 -99
  42. odxtools/compumethods/texttablecompumethod.py +107 -43
  43. odxtools/createanydiagcodedtype.py +10 -67
  44. odxtools/database.py +167 -87
  45. odxtools/dataobjectproperty.py +15 -25
  46. odxtools/decodestate.py +9 -15
  47. odxtools/description.py +47 -0
  48. odxtools/determinenumberofitems.py +4 -5
  49. odxtools/diagcodedtype.py +36 -106
  50. odxtools/diagcomm.py +24 -12
  51. odxtools/diagdatadictionaryspec.py +33 -34
  52. odxtools/diaglayercontainer.py +46 -54
  53. odxtools/diaglayers/basevariant.py +128 -0
  54. odxtools/diaglayers/basevariantraw.py +123 -0
  55. odxtools/diaglayers/diaglayer.py +432 -0
  56. odxtools/{diaglayerraw.py → diaglayers/diaglayerraw.py} +105 -120
  57. odxtools/diaglayers/ecushareddata.py +96 -0
  58. odxtools/diaglayers/ecushareddataraw.py +87 -0
  59. odxtools/diaglayers/ecuvariant.py +124 -0
  60. odxtools/diaglayers/ecuvariantraw.py +129 -0
  61. odxtools/diaglayers/functionalgroup.py +110 -0
  62. odxtools/diaglayers/functionalgroupraw.py +106 -0
  63. odxtools/{diaglayer.py → diaglayers/hierarchyelement.py} +209 -448
  64. odxtools/diaglayers/hierarchyelementraw.py +58 -0
  65. odxtools/diaglayers/protocol.py +64 -0
  66. odxtools/diaglayers/protocolraw.py +91 -0
  67. odxtools/diagnostictroublecode.py +8 -9
  68. odxtools/diagservice.py +56 -43
  69. odxtools/diagvariable.py +113 -0
  70. odxtools/docrevision.py +5 -7
  71. odxtools/dopbase.py +15 -17
  72. odxtools/dtcdop.py +168 -50
  73. odxtools/dynamicendmarkerfield.py +134 -0
  74. odxtools/dynamiclengthfield.py +41 -37
  75. odxtools/dyndefinedspec.py +177 -0
  76. odxtools/dynenddopref.py +38 -0
  77. odxtools/ecuvariantmatcher.py +6 -7
  78. odxtools/element.py +13 -15
  79. odxtools/encodestate.py +199 -22
  80. odxtools/endofpdufield.py +31 -18
  81. odxtools/environmentdata.py +8 -1
  82. odxtools/environmentdatadescription.py +198 -38
  83. odxtools/exceptions.py +11 -2
  84. odxtools/field.py +10 -10
  85. odxtools/functionalclass.py +3 -5
  86. odxtools/inputparam.py +3 -12
  87. odxtools/leadinglengthinfotype.py +37 -18
  88. odxtools/library.py +66 -0
  89. odxtools/loadfile.py +64 -0
  90. odxtools/matchingparameter.py +3 -3
  91. odxtools/message.py +0 -7
  92. odxtools/minmaxlengthtype.py +61 -33
  93. odxtools/modification.py +3 -5
  94. odxtools/multiplexer.py +128 -73
  95. odxtools/multiplexercase.py +13 -14
  96. odxtools/multiplexerdefaultcase.py +15 -12
  97. odxtools/multiplexerswitchkey.py +4 -5
  98. odxtools/nameditemlist.py +29 -5
  99. odxtools/negoutputparam.py +3 -5
  100. odxtools/odxcategory.py +83 -0
  101. odxtools/odxlink.py +60 -51
  102. odxtools/odxtypes.py +37 -5
  103. odxtools/outputparam.py +4 -15
  104. odxtools/parameterinfo.py +218 -67
  105. odxtools/parameters/codedconstparameter.py +16 -24
  106. odxtools/parameters/dynamicparameter.py +5 -4
  107. odxtools/parameters/lengthkeyparameter.py +60 -26
  108. odxtools/parameters/matchingrequestparameter.py +23 -11
  109. odxtools/parameters/nrcconstparameter.py +45 -46
  110. odxtools/parameters/parameter.py +54 -56
  111. odxtools/parameters/parameterwithdop.py +15 -25
  112. odxtools/parameters/physicalconstantparameter.py +15 -18
  113. odxtools/parameters/reservedparameter.py +6 -2
  114. odxtools/parameters/systemparameter.py +55 -11
  115. odxtools/parameters/tableentryparameter.py +3 -2
  116. odxtools/parameters/tablekeyparameter.py +103 -49
  117. odxtools/parameters/tablestructparameter.py +47 -48
  118. odxtools/parameters/valueparameter.py +16 -20
  119. odxtools/paramlengthinfotype.py +52 -32
  120. odxtools/parentref.py +16 -2
  121. odxtools/physicaldimension.py +3 -8
  122. odxtools/progcode.py +26 -11
  123. odxtools/protstack.py +3 -5
  124. odxtools/py.typed +0 -0
  125. odxtools/relateddoc.py +7 -9
  126. odxtools/request.py +120 -10
  127. odxtools/response.py +123 -23
  128. odxtools/scaleconstr.py +3 -3
  129. odxtools/servicebinner.py +1 -1
  130. odxtools/singleecujob.py +12 -10
  131. odxtools/snrefcontext.py +29 -0
  132. odxtools/specialdata.py +3 -5
  133. odxtools/specialdatagroup.py +7 -9
  134. odxtools/specialdatagroupcaption.py +3 -6
  135. odxtools/standardlengthtype.py +80 -14
  136. odxtools/state.py +3 -5
  137. odxtools/statechart.py +13 -19
  138. odxtools/statetransition.py +7 -17
  139. odxtools/staticfield.py +31 -25
  140. odxtools/subcomponent.py +288 -0
  141. odxtools/swvariable.py +21 -0
  142. odxtools/table.py +7 -8
  143. odxtools/tablerow.py +19 -11
  144. odxtools/teammember.py +3 -5
  145. odxtools/templates/comparam-spec.odx-c.xml.jinja2 +4 -24
  146. odxtools/templates/comparam-subset.odx-cs.xml.jinja2 +5 -26
  147. odxtools/templates/diag_layer_container.odx-d.xml.jinja2 +15 -31
  148. odxtools/templates/{index.xml.xml.jinja2 → index.xml.jinja2} +1 -1
  149. odxtools/templates/macros/printAudience.xml.jinja2 +1 -1
  150. odxtools/templates/macros/printBaseVariant.xml.jinja2 +53 -0
  151. odxtools/templates/macros/printCompanyData.xml.jinja2 +4 -7
  152. odxtools/templates/macros/printComparam.xml.jinja2 +6 -4
  153. odxtools/templates/macros/printComparamRef.xml.jinja2 +5 -12
  154. odxtools/templates/macros/printCompuMethod.xml.jinja2 +147 -0
  155. odxtools/templates/macros/printDOP.xml.jinja2 +27 -133
  156. odxtools/templates/macros/printDescription.xml.jinja2 +18 -0
  157. odxtools/templates/macros/printDiagComm.xml.jinja2 +1 -1
  158. odxtools/templates/macros/printDiagLayer.xml.jinja2 +222 -0
  159. odxtools/templates/macros/printDiagVariable.xml.jinja2 +66 -0
  160. odxtools/templates/macros/printDynDefinedSpec.xml.jinja2 +48 -0
  161. odxtools/templates/macros/printDynamicEndmarkerField.xml.jinja2 +16 -0
  162. odxtools/templates/macros/printDynamicLengthField.xml.jinja2 +1 -1
  163. odxtools/templates/macros/printEcuSharedData.xml.jinja2 +30 -0
  164. odxtools/templates/macros/printEcuVariant.xml.jinja2 +53 -0
  165. odxtools/templates/macros/printEcuVariantPattern.xml.jinja2 +1 -1
  166. odxtools/templates/macros/printElementId.xml.jinja2 +8 -3
  167. odxtools/templates/macros/printEndOfPdu.xml.jinja2 +1 -1
  168. odxtools/templates/macros/printEnvDataDesc.xml.jinja2 +1 -1
  169. odxtools/templates/macros/printFunctionalClass.xml.jinja2 +1 -1
  170. odxtools/templates/macros/printFunctionalGroup.xml.jinja2 +40 -0
  171. odxtools/templates/macros/printHierarchyElement.xml.jinja2 +24 -0
  172. odxtools/templates/macros/printLibrary.xml.jinja2 +21 -0
  173. odxtools/templates/macros/printMux.xml.jinja2 +4 -3
  174. odxtools/templates/macros/printOdxCategory.xml.jinja2 +28 -0
  175. odxtools/templates/macros/printParam.xml.jinja2 +11 -12
  176. odxtools/templates/macros/printProtStack.xml.jinja2 +1 -1
  177. odxtools/templates/macros/printProtocol.xml.jinja2 +30 -0
  178. odxtools/templates/macros/printRequest.xml.jinja2 +1 -1
  179. odxtools/templates/macros/printResponse.xml.jinja2 +1 -1
  180. odxtools/templates/macros/printService.xml.jinja2 +3 -2
  181. odxtools/templates/macros/printSingleEcuJob.xml.jinja2 +5 -26
  182. odxtools/templates/macros/printSpecialData.xml.jinja2 +1 -1
  183. odxtools/templates/macros/printState.xml.jinja2 +1 -1
  184. odxtools/templates/macros/printStateChart.xml.jinja2 +1 -1
  185. odxtools/templates/macros/printStateTransition.xml.jinja2 +1 -1
  186. odxtools/templates/macros/printStaticField.xml.jinja2 +1 -1
  187. odxtools/templates/macros/printStructure.xml.jinja2 +1 -1
  188. odxtools/templates/macros/printSubComponent.xml.jinja2 +104 -0
  189. odxtools/templates/macros/printTable.xml.jinja2 +4 -5
  190. odxtools/templates/macros/printUnitSpec.xml.jinja2 +3 -5
  191. odxtools/uds.py +2 -10
  192. odxtools/unit.py +4 -8
  193. odxtools/unitgroup.py +3 -5
  194. odxtools/unitspec.py +17 -17
  195. odxtools/utils.py +38 -20
  196. odxtools/variablegroup.py +32 -0
  197. odxtools/version.py +2 -2
  198. odxtools/{write_pdx_file.py → writepdxfile.py} +20 -10
  199. odxtools/xdoc.py +3 -5
  200. {odxtools-6.7.0.dist-info → odxtools-9.3.0.dist-info}/METADATA +20 -21
  201. odxtools-9.3.0.dist-info/RECORD +228 -0
  202. {odxtools-6.7.0.dist-info → odxtools-9.3.0.dist-info}/WHEEL +1 -1
  203. odxtools/createcompanydatas.py +0 -17
  204. odxtools/createsdgs.py +0 -19
  205. odxtools/load_file.py +0 -13
  206. odxtools/load_odx_d_file.py +0 -6
  207. odxtools/load_pdx_file.py +0 -8
  208. odxtools/templates/macros/printVariant.xml.jinja2 +0 -216
  209. odxtools-6.7.0.dist-info/RECORD +0 -182
  210. /odxtools/{diaglayertype.py → diaglayers/diaglayertype.py} +0 -0
  211. {odxtools-6.7.0.dist-info → odxtools-9.3.0.dist-info}/LICENSE +0 -0
  212. {odxtools-6.7.0.dist-info → odxtools-9.3.0.dist-info}/entry_points.txt +0 -0
  213. {odxtools-6.7.0.dist-info → odxtools-9.3.0.dist-info}/top_level.txt +0 -0
odxtools/parameterinfo.py CHANGED
@@ -1,105 +1,256 @@
1
1
  # SPDX-License-Identifier: MIT
2
- import re
3
- from typing import Iterable, Union
2
+ import textwrap
3
+ from io import StringIO
4
+ from typing import Iterable
4
5
 
6
+ from .compumethods.compucodecompumethod import CompuCodeCompuMethod
5
7
  from .compumethods.identicalcompumethod import IdenticalCompuMethod
6
8
  from .compumethods.limit import IntervalType
7
9
  from .compumethods.linearcompumethod import LinearCompuMethod
10
+ from .compumethods.linearsegment import LinearSegment
11
+ from .compumethods.ratfunccompumethod import RatFuncCompuMethod
12
+ from .compumethods.ratfuncsegment import RatFuncSegment
13
+ from .compumethods.scalelinearcompumethod import ScaleLinearCompuMethod
14
+ from .compumethods.scaleratfunccompumethod import ScaleRatFuncCompuMethod
8
15
  from .compumethods.texttablecompumethod import TexttableCompuMethod
9
16
  from .dataobjectproperty import DataObjectProperty
17
+ from .dtcdop import DtcDop
18
+ from .dynamiclengthfield import DynamicLengthField
10
19
  from .endofpdufield import EndOfPduField
11
- from .odxtypes import DataType
20
+ from .exceptions import odxrequire
21
+ from .multiplexer import Multiplexer
12
22
  from .parameters.codedconstparameter import CodedConstParameter
13
23
  from .parameters.matchingrequestparameter import MatchingRequestParameter
24
+ from .parameters.nrcconstparameter import NrcConstParameter
14
25
  from .parameters.parameter import Parameter
15
26
  from .parameters.parameterwithdop import ParameterWithDOP
16
27
  from .parameters.reservedparameter import ReservedParameter
28
+ from .parameters.systemparameter import SystemParameter
29
+ from .parameters.tablekeyparameter import TableKeyParameter
30
+ from .parameters.tablestructparameter import TableStructParameter
31
+ from .paramlengthinfotype import ParamLengthInfoType
32
+ from .staticfield import StaticField
17
33
 
18
34
 
19
- def parameter_info(param_list: Iterable[Union[Parameter, EndOfPduField]]) -> str:
20
- result = ""
35
+ def _get_linear_segment_info(segment: LinearSegment) -> str:
36
+ ll = segment.physical_lower_limit
37
+ if ll is None or ll.interval_type == IntervalType.INFINITE:
38
+ ll_str = "(-inf"
39
+ else:
40
+ ll_delim = '(' if ll.interval_type == IntervalType.OPEN else '['
41
+ ll_str = f"{ll_delim}{ll._value!r}"
42
+
43
+ ul = segment.physical_upper_limit
44
+ if ul is None or ul.interval_type == IntervalType.INFINITE:
45
+ ul_str = "inf)"
46
+ else:
47
+ ul_delim = ')' if ul.interval_type == IntervalType.OPEN else ']'
48
+ ul_str = f"{ul._value!r}{ul_delim}"
49
+
50
+ return f"{ll_str}, {ul_str}"
51
+
52
+
53
+ def _get_rat_func_segment_info(segment: RatFuncSegment) -> str:
54
+ ll = segment.lower_limit
55
+ if ll is None or ll.interval_type == IntervalType.INFINITE:
56
+ ll_str = "(-inf"
57
+ else:
58
+ ll_delim = '(' if ll.interval_type == IntervalType.OPEN else '['
59
+ ll_str = f"{ll_delim}{ll._value!r}"
60
+
61
+ ul = segment.upper_limit
62
+ if ul is None or ul.interval_type == IntervalType.INFINITE:
63
+ ul_str = "inf)"
64
+ else:
65
+ ul_delim = ')' if ul.interval_type == IntervalType.OPEN else ']'
66
+ ul_str = f"{ul._value!r}{ul_delim}"
67
+
68
+ return f"{ll_str}, {ul_str}"
69
+
70
+
71
+ def parameter_info(param_list: Iterable[Parameter], quoted_names: bool = False) -> str:
72
+ q = "'" if quoted_names else ""
73
+ of = StringIO()
21
74
  for param in param_list:
22
75
  if isinstance(param, CodedConstParameter):
23
- result += f"{param.short_name} : const = {param._coded_value_str}\n"
76
+ of.write(f"{q}{param.short_name}{q}: const = {param._coded_value_str}\n")
24
77
  continue
25
78
  elif isinstance(param, MatchingRequestParameter):
26
- result += f"{param.short_name} : <matches request>\n"
79
+ of.write(f"{q}{param.short_name}{q}: <matches request>\n")
80
+ continue
81
+ elif isinstance(param, NrcConstParameter):
82
+ of.write(f"{q}{param.short_name}{q}: const; choices = {param.coded_values}\n")
27
83
  continue
28
84
  elif isinstance(param, ReservedParameter):
29
- result += f"{param.short_name} : <reserved>\n"
85
+ of.write(f"{q}{param.short_name}{q}: <reserved>\n")
30
86
  continue
31
- elif not isinstance(param, ParameterWithDOP):
32
- result += f"{param.short_name} : <unhandled parameter type>\n"
87
+ elif isinstance(param, SystemParameter):
88
+ of.write(
89
+ f"{q}{param.short_name}{q}: <system; kind = \"{param.sysparam}\">; required = {param.is_required}\n"
90
+ )
33
91
  continue
92
+ elif isinstance(param, TableKeyParameter):
93
+ of.write(
94
+ f"{q}{param.short_name}{q}: <optional> table key; table = '{param.table.short_name}'; choices:\n"
95
+ )
96
+ for tr in param.table.table_rows:
97
+ of.write(f" '{tr.short_name}',\n")
34
98
 
35
- dop = param.dop
36
-
37
- if isinstance(dop, EndOfPduField):
38
- result += f"{param.short_name} : <optional> list({{\n"
39
- tmp = parameter_info(dop.structure.parameters).strip()
40
- tmp = re.sub("^", " ", tmp)
41
- result += tmp + "\n"
42
- result += f"}})\n"
43
99
  continue
100
+ elif isinstance(param, TableStructParameter):
101
+ of.write(
102
+ f"{q}{param.short_name}{q}: table struct; key = '{param.table_key.short_name}'; choices:\n"
103
+ )
104
+ for tr in param.table_key.table.table_rows:
105
+ of.write(f" ('{tr.short_name}',\n")
106
+ of.write(f" {{\n")
107
+ of.write(
108
+ textwrap.indent(
109
+ parameter_info(odxrequire(tr.structure).parameters, True), " "))
110
+ of.write(f" }}),\n")
44
111
 
45
- result += f"{param.short_name}"
112
+ continue
113
+ elif not isinstance(param, ParameterWithDOP):
114
+ of.write(
115
+ f"{q}{param.short_name}{q}: <unhandled parameter type '{type(param).__name__}'>\n")
116
+ continue
46
117
 
118
+ dop = param.dop
47
119
  if dop is None:
48
- result += ": <no DOP>\n"
120
+ of.write("{q}{param.short_name}{q}: <no DOP>\n")
49
121
  continue
50
- elif not isinstance(dop, DataObjectProperty):
51
- result += ": <unhandled DOP>\n"
122
+ elif isinstance(dop, EndOfPduField):
123
+ of.write(f"{q}{param.short_name}{q}: list({{\n")
124
+ of.write(textwrap.indent(parameter_info(dop.structure.parameters, True), " "))
125
+ of.write(f"}})\n")
52
126
  continue
53
-
54
- if (cm := dop.compu_method) is None:
55
- result += ": <no compu method>\n"
127
+ elif isinstance(dop, StaticField):
128
+ of.write(f"{q}{param.short_name}{q}: length={dop.fixed_number_of_items}; list({{\n")
129
+ of.write(textwrap.indent(parameter_info(dop.structure.parameters, True), " "))
130
+ of.write(f"}})\n")
131
+ continue
132
+ elif isinstance(dop, DynamicLengthField):
133
+ of.write(f"{q}{param.short_name}{q}: list({{\n")
134
+ of.write(textwrap.indent(parameter_info(dop.structure.parameters, True), " "))
135
+ of.write(f"}})\n")
136
+ continue
137
+ elif isinstance(dop, ParamLengthInfoType):
138
+ of.write(f"{q}{param.short_name}{q}: ")
139
+ of.write("<optional> ")
140
+ of.write(f"int; length_key='{dop.length_key.short_name}'\n")
56
141
  continue
142
+ elif isinstance(dop, DtcDop):
143
+ of.write(f"{q}{param.short_name}{q}: ")
144
+ of.write(f"DTC; choices:\n")
145
+ for dtc in dop.dtcs:
146
+ if dtc.display_trouble_code is not None:
147
+ dtc_desc = dtc.text and f"; \"{dtc.text}\""
148
+ of.write(
149
+ f" '{dtc.display_trouble_code}' (0x{dtc.trouble_code:06x}{dtc_desc})\n")
150
+ else:
151
+ dtc_desc = dtc.text and f" (\"{dtc.text}\")"
152
+ of.write(f" 0x{dtc.trouble_code:06x}{dtc_desc}\n")
153
+ continue
154
+ elif isinstance(dop, Multiplexer):
155
+ of.write(f"{q}{param.short_name}{q}: ")
156
+ if dop.default_case is not None:
157
+ of.write(f"<optional>")
158
+ of.write(f"multiplexer; choices:\n")
159
+ for mux_case in dop.cases:
160
+ of.write(f" ({repr(mux_case.short_name)}, {{\n")
161
+ if (struc := mux_case.structure) is not None:
162
+ of.write(textwrap.indent(parameter_info(struc.parameters, True), " "))
163
+ of.write(f" }})\n")
164
+ continue
165
+ elif isinstance(dop, DataObjectProperty):
166
+ # a "simple" DOP
167
+ if (cm := dop.compu_method) is None:
168
+ of.write(f"{q}{param.short_name}{q}: <no compu method>\n")
169
+ continue
57
170
 
58
- if isinstance(cm, TexttableCompuMethod):
59
- result += f": enum; choices:\n"
60
- for scale in cm.internal_to_phys:
61
- result += f" '{str(scale.compu_const)}'\n"
62
-
63
- elif isinstance(cm, IdenticalCompuMethod):
64
- bdt = dop.physical_type.base_data_type
65
- if bdt in (DataType.A_UTF8STRING, DataType.A_UNICODE2STRING, DataType.A_ASCIISTRING):
66
- result += f": str"
67
- elif bdt in (DataType.A_BYTEFIELD,):
68
- result += f": bytes"
69
- elif bdt.name.startswith("A_FLOAT"):
70
- result += f": float"
71
- elif bdt.name.startswith("A_UINT"):
72
- result += f": uint"
73
- elif bdt.name.startswith("A_INT"):
74
- result += f": int"
75
- else:
76
- result += f": <unknown type>"
171
+ if isinstance(cm, TexttableCompuMethod):
172
+ of.write(f"{q}{param.short_name}{q}: enum; choices:\n")
173
+ for scale in odxrequire(cm.compu_internal_to_phys).compu_scales:
174
+ val_str = ""
175
+ if scale.lower_limit is not None:
176
+ val_str = f"({repr(scale.lower_limit.value)})"
77
177
 
78
- if (bl := dop.get_static_bit_length()) is not None:
79
- result += f"{bl}"
178
+ if scale.compu_const is None:
179
+ of.write(f" <ERROR in ODX data: no value specified>\n")
180
+ else:
181
+ vt = scale.compu_const.vt
182
+ v = scale.compu_const.v
183
+ if vt is not None:
184
+ of.write(f" \"{vt}\" {val_str}\n")
185
+ else:
186
+ of.write(f" {v}\n")
80
187
 
81
- result += "\n"
188
+ elif isinstance(cm, IdenticalCompuMethod):
189
+ of.write(f"{q}{param.short_name}{q}: {dop.physical_type.base_data_type}\n")
82
190
 
83
- elif isinstance(cm, LinearCompuMethod):
84
- result += f": float\n"
85
- ll = cm.physical_lower_limit
86
- ul = cm.physical_upper_limit
87
- if ll is None:
88
- ll_str = "(inf"
89
- else:
90
- ll_delim = '[' if ll.interval_type == IntervalType.CLOSED else '('
91
- ll_str = f"{ll_delim}{ll._value!r}"
191
+ elif isinstance(cm, ScaleLinearCompuMethod):
192
+ of.write(f"{q}{param.short_name}{q}: {dop.physical_type.base_data_type}")
193
+ seg_list = [_get_linear_segment_info(x) for x in cm.segments]
194
+ of.write(f"; ranges = {{ {', '.join(seg_list)} }}")
92
195
 
93
- if ul is None:
94
- ul_str = "inf)"
95
- else:
96
- ul_delim = ']' if ul.interval_type == IntervalType.CLOSED else ')'
97
- ul_str = f"{ul._value!r}{ul_delim}"
98
- result += f" range: {ll_str}, {ul_str}\n"
196
+ unit = dop.unit
197
+ unit_str = unit.display_name if unit is not None else None
198
+ if unit_str is not None:
199
+ of.write(f"; unit: {unit_str}")
200
+
201
+ of.write("\n")
202
+
203
+ elif isinstance(cm, LinearCompuMethod):
204
+ of.write(f"{q}{param.short_name}{q}: {dop.physical_type.base_data_type}")
205
+ of.write(f"; range: {_get_linear_segment_info(cm.segment)}")
206
+
207
+ unit = dop.unit
208
+ unit_str = unit.display_name if unit is not None else None
209
+ if unit_str is not None:
210
+ of.write(f"; unit: {unit_str}")
211
+
212
+ of.write("\n")
99
213
 
100
- unit = dop.unit
101
- unit_str = unit.display_name if unit is not None else None
102
- if unit_str is not None:
103
- result += f" unit: {unit_str}\n"
214
+ elif isinstance(cm, ScaleRatFuncCompuMethod):
215
+ of.write(f"{q}{param.short_name}{q}: {dop.physical_type.base_data_type}")
216
+ if cm._phys_to_int_segments is None:
217
+ of.write("<NOT ENCODABLE>")
218
+ else:
219
+ seg_list = [_get_rat_func_segment_info(x) for x in cm._phys_to_int_segments]
220
+ of.write(f"; ranges = {{ {', '.join(seg_list)} }}")
221
+
222
+ unit = dop.unit
223
+ unit_str = unit.display_name if unit is not None else None
224
+ if unit_str is not None:
225
+ of.write(f"; unit: {unit_str}")
226
+
227
+ of.write("\n")
228
+
229
+ elif isinstance(cm, RatFuncCompuMethod):
230
+ of.write(f"{q}{param.short_name}{q}: {dop.physical_type.base_data_type}")
231
+ if cm._phys_to_int_segment is None:
232
+ of.write("<NOT ENCODABLE>")
233
+ else:
234
+ of.write(f"; range: {_get_rat_func_segment_info(cm._phys_to_int_segment)}")
235
+
236
+ unit = dop.unit
237
+ unit_str = unit.display_name if unit is not None else None
238
+ if unit_str is not None:
239
+ of.write(f"; unit: {unit_str}")
240
+
241
+ of.write("\n")
242
+
243
+ elif isinstance(cm, CompuCodeCompuMethod):
244
+ of.write(f"{q}{param.short_name}{q}: {dop.physical_type.base_data_type}")
245
+ of.write(f"; <programmatic translation>")
246
+
247
+ of.write("\n")
248
+
249
+ else:
250
+ of.write(
251
+ f"{q}{param.short_name}{q}: unknown compu method {type(dop.compu_method).__name__}\n"
252
+ )
253
+ else:
254
+ of.write(f"{q}{param.short_name}{q}: <unhandled DOP '{type(dop).__name__}'>\n")
104
255
 
105
- return result
256
+ return of.getvalue()
@@ -1,7 +1,7 @@
1
1
  # SPDX-License-Identifier: MIT
2
2
  import warnings
3
3
  from dataclasses import dataclass
4
- from typing import TYPE_CHECKING, Any, Dict, List, Optional
4
+ from typing import Any, Dict, List, Optional
5
5
  from xml.etree import ElementTree
6
6
 
7
7
  from typing_extensions import override
@@ -10,15 +10,12 @@ from ..createanydiagcodedtype import create_any_diag_coded_type_from_et
10
10
  from ..decodestate import DecodeState
11
11
  from ..diagcodedtype import DiagCodedType
12
12
  from ..encodestate import EncodeState
13
- from ..exceptions import DecodeError, odxrequire
14
- from ..odxlink import OdxDocFragment, OdxLinkDatabase, OdxLinkId
15
- from ..odxtypes import AtomicOdxType, DataType
13
+ from ..exceptions import DecodeError, EncodeError, odxraise, odxrequire
14
+ from ..odxlink import OdxDocFragment, OdxLinkId
15
+ from ..odxtypes import AtomicOdxType, DataType, ParameterValue
16
16
  from ..utils import dataclass_fields_asdict
17
17
  from .parameter import Parameter, ParameterType
18
18
 
19
- if TYPE_CHECKING:
20
- from ..diaglayer import DiagLayer
21
-
22
19
 
23
20
  @dataclass
24
21
  class CodedConstParameter(Parameter):
@@ -54,14 +51,6 @@ class CodedConstParameter(Parameter):
54
51
 
55
52
  return result
56
53
 
57
- @override
58
- def _resolve_odxlinks(self, odxlinks: OdxLinkDatabase) -> None:
59
- super()._resolve_odxlinks(odxlinks)
60
-
61
- @override
62
- def _resolve_snrefs(self, diag_layer: "DiagLayer") -> None:
63
- super()._resolve_snrefs(diag_layer)
64
-
65
54
  @override
66
55
  def get_static_bit_length(self) -> Optional[int]:
67
56
  return self.diag_coded_type.get_static_bit_length()
@@ -81,20 +70,23 @@ class CodedConstParameter(Parameter):
81
70
  return False
82
71
 
83
72
  @override
84
- def get_coded_value_as_bytes(self, encode_state: EncodeState) -> bytes:
85
- if (self.short_name in encode_state.parameter_values and
86
- encode_state.parameter_values[self.short_name] != self.coded_value):
87
- raise TypeError(f"The parameter '{self.short_name}' is constant {self._coded_value_str}"
88
- " and thus can not be changed.")
89
- bit_position_int = self.bit_position if self.bit_position is not None else 0
90
- return self.diag_coded_type.convert_internal_to_bytes(
91
- self.coded_value, encode_state=encode_state, bit_position=bit_position_int)
73
+ def _encode_positioned_into_pdu(self, physical_value: Optional[ParameterValue],
74
+ encode_state: EncodeState) -> None:
75
+ if physical_value is not None and physical_value != self.coded_value:
76
+ odxraise(
77
+ f"Value for constant parameter `{self.short_name}` name can "
78
+ f"only be specified as {self.coded_value!r} (is: {physical_value!r})", EncodeError)
79
+
80
+ internal_value = self.coded_value
81
+
82
+ self.diag_coded_type.encode_into_pdu(
83
+ internal_value=internal_value, encode_state=encode_state)
92
84
 
93
85
  @override
94
86
  def _decode_positioned_from_pdu(self, decode_state: DecodeState) -> AtomicOdxType:
95
87
  coded_val = self.diag_coded_type.decode_from_pdu(decode_state)
96
88
 
97
- # Check if the coded value in the message is correct.
89
+ # Check if the coded value contained by the message is correct.
98
90
  if self.coded_value != coded_val:
99
91
  warnings.warn(
100
92
  f"Coded constant parameter does not match! "
@@ -1,6 +1,6 @@
1
1
  # SPDX-License-Identifier: MIT
2
2
  from dataclasses import dataclass
3
- from typing import List
3
+ from typing import List, Optional
4
4
  from xml.etree import ElementTree
5
5
 
6
6
  from typing_extensions import override
@@ -41,9 +41,10 @@ class DynamicParameter(Parameter):
41
41
  raise NotImplementedError(".is_settable for a DynamicParameter")
42
42
 
43
43
  @override
44
- def get_coded_value_as_bytes(self, encode_state: EncodeState) -> bytes:
45
- raise NotImplementedError("Encoding a DynamicParameter is not implemented yet.")
44
+ def _encode_positioned_into_pdu(self, physical_value: Optional[ParameterValue],
45
+ encode_state: EncodeState) -> None:
46
+ raise NotImplementedError("Encoding DynamicParameter is not implemented yet.")
46
47
 
47
48
  @override
48
49
  def _decode_positioned_from_pdu(self, decode_state: DecodeState) -> ParameterValue:
49
- raise NotImplementedError("Decoding a DynamicParameter is not implemented yet.")
50
+ raise NotImplementedError("Decoding DynamicParameter is not implemented yet.")
@@ -1,22 +1,19 @@
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, Optional
4
4
  from xml.etree import ElementTree
5
5
 
6
- from typing_extensions import override
6
+ from typing_extensions import final, override
7
7
 
8
8
  from ..decodestate import DecodeState
9
9
  from ..encodestate import EncodeState
10
- from ..exceptions import odxraise, odxrequire
11
- from ..odxlink import OdxDocFragment, OdxLinkDatabase, OdxLinkId
10
+ from ..exceptions import EncodeError, odxraise, odxrequire
11
+ from ..odxlink import OdxDocFragment, OdxLinkId
12
12
  from ..odxtypes import ParameterValue
13
13
  from ..utils import dataclass_fields_asdict
14
14
  from .parameter import ParameterType
15
15
  from .parameterwithdop import ParameterWithDOP
16
16
 
17
- if TYPE_CHECKING:
18
- from ..diaglayer import DiagLayer
19
-
20
17
 
21
18
  @dataclass
22
19
  class LengthKeyParameter(ParameterWithDOP):
@@ -55,14 +52,6 @@ class LengthKeyParameter(ParameterWithDOP):
55
52
 
56
53
  return result
57
54
 
58
- @override
59
- def _resolve_odxlinks(self, odxlinks: OdxLinkDatabase) -> None:
60
- super()._resolve_odxlinks(odxlinks)
61
-
62
- @override
63
- def _resolve_snrefs(self, diag_layer: "DiagLayer") -> None:
64
- super()._resolve_snrefs(diag_layer)
65
-
66
55
  @property
67
56
  @override
68
57
  def is_required(self) -> bool:
@@ -77,17 +66,62 @@ class LengthKeyParameter(ParameterWithDOP):
77
66
  return True
78
67
 
79
68
  @override
80
- def get_coded_value_as_bytes(self, encode_state: EncodeState) -> bytes:
81
- physical_value = encode_state.parameter_values.get(self.short_name, 0)
82
-
83
- bit_pos = self.bit_position or 0
84
- dop = odxrequire(super().dop,
85
- f"A DOP is required for length key parameter {self.short_name}")
86
- return dop.convert_physical_to_bytes(physical_value, encode_state, bit_position=bit_pos)
87
-
88
- @override
89
- def encode_into_pdu(self, encode_state: EncodeState) -> bytes:
90
- return super().encode_into_pdu(encode_state)
69
+ @final
70
+ def _encode_positioned_into_pdu(self, physical_value: Optional[ParameterValue],
71
+ encode_state: EncodeState) -> None:
72
+ # if you get this exception, you ought to use
73
+ # `.encode_placeholder_into_pdu()` followed by (after the
74
+ # value of the length key has been determined)
75
+ # `.encode_value_into_pdu()`.
76
+ raise RuntimeError("_encode_positioned_into_pdu() cannot be called for length keys.")
77
+
78
+ def encode_placeholder_into_pdu(self, physical_value: Optional[ParameterValue],
79
+ encode_state: EncodeState) -> None:
80
+
81
+ if physical_value is not None:
82
+ if not self.dop.is_valid_physical_value(physical_value):
83
+ odxraise(f"Invalid explicitly specified physical value '{physical_value!r}' "
84
+ f"for length key '{self.short_name}'.")
85
+
86
+ lkv = encode_state.length_keys.get(self.short_name)
87
+ if lkv is not None and lkv != physical_value:
88
+ odxraise(f"Got conflicting values for length key {self.short_name}: "
89
+ f"{lkv} and {physical_value!r}")
90
+
91
+ if not isinstance(physical_value, int):
92
+ odxraise(
93
+ f"Value of length key {self.short_name} is of type {type(physical_value).__name__} "
94
+ f"instead of int")
95
+
96
+ encode_state.length_keys[self.short_name] = physical_value
97
+
98
+ pos = encode_state.cursor_byte_position
99
+ if self.byte_position is not None:
100
+ pos = encode_state.origin_byte_position + self.byte_position
101
+ encode_state.key_pos[self.short_name] = pos
102
+ encode_state.cursor_byte_position = pos
103
+ encode_state.cursor_bit_position = self.bit_position or 0
104
+
105
+ # emplace a value of zero into the encode state, but pretend the bits not to be used
106
+ n = odxrequire(self.dop.get_static_bit_length()) + encode_state.cursor_bit_position
107
+ tmp_val = b'\x00' * ((n + 7) // 8)
108
+ encode_state.cursor_bit_position = 0
109
+ encode_state.emplace_bytes(tmp_val, obj_used_mask=tmp_val)
110
+
111
+ def encode_value_into_pdu(self, encode_state: EncodeState) -> None:
112
+
113
+ if self.short_name not in encode_state.length_keys:
114
+ odxraise(
115
+ f"Length key {self.short_name} has not been defined before "
116
+ f"it is required.", EncodeError)
117
+ return
118
+ else:
119
+ physical_value = encode_state.length_keys[self.short_name]
120
+
121
+ encode_state.cursor_byte_position = encode_state.key_pos[self.short_name]
122
+ encode_state.cursor_bit_position = self.bit_position or 0
123
+
124
+ self.dop.encode_into_pdu(encode_state=encode_state, physical_value=physical_value)
91
125
 
92
126
  @override
93
127
  def _decode_positioned_from_pdu(self, decode_state: DecodeState) -> ParameterValue:
@@ -7,7 +7,7 @@ from typing_extensions import override
7
7
 
8
8
  from ..decodestate import DecodeState
9
9
  from ..encodestate import EncodeState
10
- from ..exceptions import EncodeError, odxrequire
10
+ from ..exceptions import EncodeError, odxraise, odxrequire
11
11
  from ..odxlink import OdxDocFragment
12
12
  from ..odxtypes import DataType, ParameterValue
13
13
  from ..utils import dataclass_fields_asdict
@@ -52,19 +52,31 @@ class MatchingRequestParameter(Parameter):
52
52
  return False
53
53
 
54
54
  @override
55
- def get_coded_value_as_bytes(self, encode_state: EncodeState) -> bytes:
56
- if not encode_state.triggering_request:
57
- raise EncodeError(f"Parameter '{self.short_name}' is of matching request type,"
58
- " but no original request has been specified.")
59
- return encode_state.triggering_request[self
60
- .request_byte_position:self.request_byte_position +
61
- self.byte_length]
55
+ def _encode_positioned_into_pdu(self, physical_value: Optional[ParameterValue],
56
+ encode_state: EncodeState) -> None:
57
+ if encode_state.triggering_request is None:
58
+ odxraise(
59
+ f"Parameter '{self.short_name}' is of matching request type,"
60
+ f" but no original request has been specified.", EncodeError)
61
+ return
62
+
63
+ rq_pos = self.request_byte_position
64
+ rq_len = self.byte_length
65
+
66
+ if len(encode_state.triggering_request) < rq_pos + rq_len:
67
+ odxraise(
68
+ f"Specified triggering request 0x{encode_state.triggering_request.hex()} "
69
+ f"is not long enough to encode matching request parameter "
70
+ f"'{self.short_name}': Have {len(encode_state.triggering_request)} "
71
+ f"bytes, need at least {rq_pos + rq_len} bytes", EncodeError)
72
+ return
73
+
74
+ encode_state.emplace_bytes(encode_state.triggering_request[rq_pos:rq_pos + rq_len],
75
+ self.short_name)
62
76
 
63
77
  @override
64
78
  def _decode_positioned_from_pdu(self, decode_state: DecodeState) -> ParameterValue:
65
- result = decode_state.extract_atomic_value(
79
+ return decode_state.extract_atomic_value(
66
80
  bit_length=self.byte_length * 8,
67
81
  base_data_type=DataType.A_UINT32,
68
82
  is_highlow_byte_order=False)
69
-
70
- return result