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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (222) hide show
  1. odxtools/__init__.py +7 -5
  2. odxtools/additionalaudience.py +3 -5
  3. odxtools/admindata.py +5 -7
  4. odxtools/audience.py +10 -13
  5. odxtools/basecomparam.py +3 -5
  6. odxtools/basicstructure.py +55 -241
  7. odxtools/cli/_parser_utils.py +16 -1
  8. odxtools/cli/_print_utils.py +169 -134
  9. odxtools/cli/browse.py +127 -103
  10. odxtools/cli/compare.py +114 -87
  11. odxtools/cli/decode.py +2 -1
  12. odxtools/cli/dummy_sub_parser.py +3 -1
  13. odxtools/cli/find.py +2 -1
  14. odxtools/cli/list.py +26 -16
  15. odxtools/cli/main.py +1 -0
  16. odxtools/cli/snoop.py +32 -6
  17. odxtools/codec.py +211 -0
  18. odxtools/commrelation.py +122 -0
  19. odxtools/companydata.py +5 -7
  20. odxtools/companydocinfo.py +7 -8
  21. odxtools/companyrevisioninfo.py +3 -5
  22. odxtools/companyspecificinfo.py +8 -9
  23. odxtools/comparam.py +4 -6
  24. odxtools/comparaminstance.py +14 -14
  25. odxtools/comparamspec.py +16 -54
  26. odxtools/comparamsubset.py +22 -62
  27. odxtools/complexcomparam.py +5 -7
  28. odxtools/compumethods/compucodecompumethod.py +63 -0
  29. odxtools/compumethods/compuconst.py +31 -0
  30. odxtools/compumethods/compudefaultvalue.py +27 -0
  31. odxtools/compumethods/compuinternaltophys.py +56 -0
  32. odxtools/compumethods/compuinversevalue.py +7 -0
  33. odxtools/compumethods/compumethod.py +94 -15
  34. odxtools/compumethods/compuphystointernal.py +56 -0
  35. odxtools/compumethods/compurationalcoeffs.py +20 -9
  36. odxtools/compumethods/compuscale.py +67 -32
  37. odxtools/compumethods/createanycompumethod.py +31 -172
  38. odxtools/compumethods/identicalcompumethod.py +31 -6
  39. odxtools/compumethods/limit.py +70 -36
  40. odxtools/compumethods/linearcompumethod.py +70 -181
  41. odxtools/compumethods/linearsegment.py +190 -0
  42. odxtools/compumethods/ratfunccompumethod.py +106 -0
  43. odxtools/compumethods/ratfuncsegment.py +87 -0
  44. odxtools/compumethods/scalelinearcompumethod.py +132 -26
  45. odxtools/compumethods/scaleratfunccompumethod.py +113 -0
  46. odxtools/compumethods/tabintpcompumethod.py +123 -92
  47. odxtools/compumethods/texttablecompumethod.py +117 -57
  48. odxtools/createanydiagcodedtype.py +10 -67
  49. odxtools/database.py +167 -87
  50. odxtools/dataobjectproperty.py +25 -32
  51. odxtools/decodestate.py +14 -17
  52. odxtools/description.py +47 -0
  53. odxtools/determinenumberofitems.py +4 -5
  54. odxtools/diagcodedtype.py +37 -106
  55. odxtools/diagcomm.py +24 -12
  56. odxtools/diagdatadictionaryspec.py +120 -96
  57. odxtools/diaglayercontainer.py +46 -54
  58. odxtools/diaglayers/basevariant.py +128 -0
  59. odxtools/diaglayers/basevariantraw.py +123 -0
  60. odxtools/diaglayers/diaglayer.py +432 -0
  61. odxtools/{diaglayerraw.py → diaglayers/diaglayerraw.py} +105 -120
  62. odxtools/diaglayers/diaglayertype.py +42 -0
  63. odxtools/diaglayers/ecushareddata.py +96 -0
  64. odxtools/diaglayers/ecushareddataraw.py +87 -0
  65. odxtools/diaglayers/ecuvariant.py +124 -0
  66. odxtools/diaglayers/ecuvariantraw.py +129 -0
  67. odxtools/diaglayers/functionalgroup.py +110 -0
  68. odxtools/diaglayers/functionalgroupraw.py +106 -0
  69. odxtools/{diaglayer.py → diaglayers/hierarchyelement.py} +273 -472
  70. odxtools/diaglayers/hierarchyelementraw.py +58 -0
  71. odxtools/diaglayers/protocol.py +64 -0
  72. odxtools/diaglayers/protocolraw.py +91 -0
  73. odxtools/diagnostictroublecode.py +8 -9
  74. odxtools/diagservice.py +57 -44
  75. odxtools/diagvariable.py +113 -0
  76. odxtools/docrevision.py +5 -7
  77. odxtools/dopbase.py +15 -15
  78. odxtools/dtcdop.py +170 -50
  79. odxtools/dynamicendmarkerfield.py +134 -0
  80. odxtools/dynamiclengthfield.py +47 -42
  81. odxtools/dyndefinedspec.py +177 -0
  82. odxtools/dynenddopref.py +38 -0
  83. odxtools/ecuvariantmatcher.py +6 -7
  84. odxtools/element.py +13 -15
  85. odxtools/encodestate.py +199 -22
  86. odxtools/endofpdufield.py +31 -18
  87. odxtools/environmentdata.py +8 -1
  88. odxtools/environmentdatadescription.py +198 -36
  89. odxtools/exceptions.py +11 -2
  90. odxtools/field.py +10 -10
  91. odxtools/functionalclass.py +3 -5
  92. odxtools/inputparam.py +3 -12
  93. odxtools/internalconstr.py +14 -5
  94. odxtools/isotp_state_machine.py +14 -6
  95. odxtools/leadinglengthinfotype.py +37 -18
  96. odxtools/library.py +66 -0
  97. odxtools/loadfile.py +64 -0
  98. odxtools/matchingparameter.py +3 -3
  99. odxtools/message.py +0 -7
  100. odxtools/minmaxlengthtype.py +61 -33
  101. odxtools/modification.py +3 -5
  102. odxtools/multiplexer.py +135 -75
  103. odxtools/multiplexercase.py +39 -18
  104. odxtools/multiplexerdefaultcase.py +15 -12
  105. odxtools/multiplexerswitchkey.py +4 -5
  106. odxtools/nameditemlist.py +33 -8
  107. odxtools/negoutputparam.py +3 -5
  108. odxtools/odxcategory.py +83 -0
  109. odxtools/odxlink.py +62 -53
  110. odxtools/odxtypes.py +93 -8
  111. odxtools/outputparam.py +5 -16
  112. odxtools/parameterinfo.py +219 -61
  113. odxtools/parameters/codedconstparameter.py +45 -32
  114. odxtools/parameters/createanyparameter.py +19 -193
  115. odxtools/parameters/dynamicparameter.py +25 -4
  116. odxtools/parameters/lengthkeyparameter.py +83 -25
  117. odxtools/parameters/matchingrequestparameter.py +48 -18
  118. odxtools/parameters/nrcconstparameter.py +76 -54
  119. odxtools/parameters/parameter.py +97 -73
  120. odxtools/parameters/parameterwithdop.py +41 -38
  121. odxtools/parameters/physicalconstantparameter.py +41 -20
  122. odxtools/parameters/reservedparameter.py +36 -18
  123. odxtools/parameters/systemparameter.py +74 -7
  124. odxtools/parameters/tableentryparameter.py +47 -7
  125. odxtools/parameters/tablekeyparameter.py +142 -55
  126. odxtools/parameters/tablestructparameter.py +79 -58
  127. odxtools/parameters/valueparameter.py +39 -21
  128. odxtools/paramlengthinfotype.py +56 -33
  129. odxtools/parentref.py +20 -3
  130. odxtools/physicaldimension.py +3 -8
  131. odxtools/progcode.py +26 -11
  132. odxtools/protstack.py +3 -5
  133. odxtools/py.typed +0 -0
  134. odxtools/relateddoc.py +7 -9
  135. odxtools/request.py +120 -10
  136. odxtools/response.py +123 -23
  137. odxtools/scaleconstr.py +14 -8
  138. odxtools/servicebinner.py +1 -1
  139. odxtools/singleecujob.py +12 -10
  140. odxtools/snrefcontext.py +29 -0
  141. odxtools/specialdata.py +3 -5
  142. odxtools/specialdatagroup.py +7 -9
  143. odxtools/specialdatagroupcaption.py +3 -6
  144. odxtools/standardlengthtype.py +80 -14
  145. odxtools/state.py +3 -5
  146. odxtools/statechart.py +13 -19
  147. odxtools/statetransition.py +8 -18
  148. odxtools/staticfield.py +107 -0
  149. odxtools/subcomponent.py +288 -0
  150. odxtools/swvariable.py +21 -0
  151. odxtools/table.py +9 -9
  152. odxtools/tablerow.py +30 -15
  153. odxtools/teammember.py +3 -5
  154. odxtools/templates/comparam-spec.odx-c.xml.jinja2 +4 -24
  155. odxtools/templates/comparam-subset.odx-cs.xml.jinja2 +5 -26
  156. odxtools/templates/diag_layer_container.odx-d.xml.jinja2 +15 -31
  157. odxtools/templates/{index.xml.xml.jinja2 → index.xml.jinja2} +1 -1
  158. odxtools/templates/macros/printAudience.xml.jinja2 +1 -1
  159. odxtools/templates/macros/printBaseVariant.xml.jinja2 +53 -0
  160. odxtools/templates/macros/printCompanyData.xml.jinja2 +4 -7
  161. odxtools/templates/macros/printComparam.xml.jinja2 +6 -4
  162. odxtools/templates/macros/printComparamRef.xml.jinja2 +5 -12
  163. odxtools/templates/macros/printCompuMethod.xml.jinja2 +147 -0
  164. odxtools/templates/macros/printDOP.xml.jinja2 +27 -137
  165. odxtools/templates/macros/printDescription.xml.jinja2 +18 -0
  166. odxtools/templates/macros/printDiagComm.xml.jinja2 +1 -1
  167. odxtools/templates/macros/printDiagLayer.xml.jinja2 +222 -0
  168. odxtools/templates/macros/printDiagVariable.xml.jinja2 +66 -0
  169. odxtools/templates/macros/printDynDefinedSpec.xml.jinja2 +48 -0
  170. odxtools/templates/macros/printDynamicEndmarkerField.xml.jinja2 +16 -0
  171. odxtools/templates/macros/printDynamicLengthField.xml.jinja2 +1 -1
  172. odxtools/templates/macros/printEcuSharedData.xml.jinja2 +30 -0
  173. odxtools/templates/macros/printEcuVariant.xml.jinja2 +53 -0
  174. odxtools/templates/macros/printEcuVariantPattern.xml.jinja2 +1 -1
  175. odxtools/templates/macros/printElementId.xml.jinja2 +8 -3
  176. odxtools/templates/macros/printEndOfPdu.xml.jinja2 +1 -1
  177. odxtools/templates/macros/printEnvDataDesc.xml.jinja2 +1 -1
  178. odxtools/templates/macros/printFunctionalClass.xml.jinja2 +1 -1
  179. odxtools/templates/macros/printFunctionalGroup.xml.jinja2 +40 -0
  180. odxtools/templates/macros/printHierarchyElement.xml.jinja2 +24 -0
  181. odxtools/templates/macros/printLibrary.xml.jinja2 +21 -0
  182. odxtools/templates/macros/printMux.xml.jinja2 +5 -3
  183. odxtools/templates/macros/printOdxCategory.xml.jinja2 +28 -0
  184. odxtools/templates/macros/printParam.xml.jinja2 +18 -19
  185. odxtools/templates/macros/printProtStack.xml.jinja2 +1 -1
  186. odxtools/templates/macros/printProtocol.xml.jinja2 +30 -0
  187. odxtools/templates/macros/printRequest.xml.jinja2 +1 -1
  188. odxtools/templates/macros/printResponse.xml.jinja2 +1 -1
  189. odxtools/templates/macros/printService.xml.jinja2 +3 -2
  190. odxtools/templates/macros/printSingleEcuJob.xml.jinja2 +5 -26
  191. odxtools/templates/macros/printSpecialData.xml.jinja2 +1 -1
  192. odxtools/templates/macros/printState.xml.jinja2 +1 -1
  193. odxtools/templates/macros/printStateChart.xml.jinja2 +1 -1
  194. odxtools/templates/macros/printStateTransition.xml.jinja2 +1 -1
  195. odxtools/templates/macros/printStaticField.xml.jinja2 +15 -0
  196. odxtools/templates/macros/printStructure.xml.jinja2 +1 -1
  197. odxtools/templates/macros/printSubComponent.xml.jinja2 +104 -0
  198. odxtools/templates/macros/printTable.xml.jinja2 +4 -5
  199. odxtools/templates/macros/printUnitSpec.xml.jinja2 +3 -5
  200. odxtools/uds.py +2 -10
  201. odxtools/unit.py +4 -8
  202. odxtools/unitgroup.py +3 -5
  203. odxtools/unitspec.py +17 -17
  204. odxtools/utils.py +38 -20
  205. odxtools/variablegroup.py +32 -0
  206. odxtools/version.py +2 -2
  207. odxtools/{write_pdx_file.py → writepdxfile.py} +22 -12
  208. odxtools/xdoc.py +3 -5
  209. {odxtools-6.6.1.dist-info → odxtools-9.3.0.dist-info}/METADATA +44 -33
  210. odxtools-9.3.0.dist-info/RECORD +228 -0
  211. {odxtools-6.6.1.dist-info → odxtools-9.3.0.dist-info}/WHEEL +1 -1
  212. odxtools/createcompanydatas.py +0 -17
  213. odxtools/createsdgs.py +0 -19
  214. odxtools/diaglayertype.py +0 -30
  215. odxtools/load_file.py +0 -13
  216. odxtools/load_odx_d_file.py +0 -6
  217. odxtools/load_pdx_file.py +0 -8
  218. odxtools/templates/macros/printVariant.xml.jinja2 +0 -208
  219. odxtools-6.6.1.dist-info/RECORD +0 -180
  220. {odxtools-6.6.1.dist-info → odxtools-9.3.0.dist-info}/LICENSE +0 -0
  221. {odxtools-6.6.1.dist-info → odxtools-9.3.0.dist-info}/entry_points.txt +0 -0
  222. {odxtools-6.6.1.dist-info → odxtools-9.3.0.dist-info}/top_level.txt +0 -0
odxtools/cli/browse.py CHANGED
@@ -4,14 +4,17 @@ import logging
4
4
  import sys
5
5
  from typing import List, Optional, Union, cast
6
6
 
7
- from InquirerPy import prompt as PI_prompt
8
- from tabulate import tabulate # TODO: switch to rich tables
7
+ import InquirerPy.prompt as IP_prompt
9
8
 
9
+ from ..complexdop import ComplexDop
10
10
  from ..database import Database
11
11
  from ..dataobjectproperty import DataObjectProperty
12
12
  from ..diaglayer import DiagLayer
13
13
  from ..diagservice import DiagService
14
+ from ..dopbase import DopBase
14
15
  from ..exceptions import odxraise, odxrequire
16
+ from ..hierarchyelement import HierarchyElement
17
+ from ..odxlink import resolve_snref
15
18
  from ..odxtypes import AtomicOdxType, DataType, ParameterValueDict
16
19
  from ..parameters.matchingrequestparameter import MatchingRequestParameter
17
20
  from ..parameters.parameter import Parameter
@@ -20,6 +23,7 @@ from ..parameters.valueparameter import ValueParameter
20
23
  from ..request import Request
21
24
  from ..response import Response
22
25
  from . import _parser_utils
26
+ from ._parser_utils import SubparsersList
23
27
  from ._print_utils import extract_parameter_tabulation_data
24
28
 
25
29
  # name of the tool
@@ -88,13 +92,15 @@ def prompt_single_parameter_value(parameter: Parameter) -> Optional[AtomicOdxTyp
88
92
  # else _convert_string_to_odx_type(x, p.physical_type.base_data_type, param=p) # This does not work because the next parameter to be promted is used (for some reason?)
89
93
  }]
90
94
 
91
- if hasattr(parameter, "dop") and hasattr(parameter.dop, "compu_method") \
92
- and hasattr(parameter.dop.compu_method, "get_scales"):
93
- scales = parameter.dop.compu_method.get_scales()
95
+ if (dop := getattr(parameter, "dop", None)) and \
96
+ (compu_method := getattr(dop, "compu_method", None)):
97
+ scales = compu_method.internal_to_phys
94
98
  choices = [scale.compu_const for scale in scales if scale is not None]
99
+ if (cdv := compu_method.compu_default_value) is not None:
100
+ choices.append(cdv.compu_const)
95
101
  param_prompt[0]["choices"] = choices
96
102
 
97
- answer = PI_prompt(param_prompt)
103
+ answer = IP_prompt(param_prompt)
98
104
  if answer.get(parameter.short_name) == "" and not parameter.is_required:
99
105
  return None
100
106
  elif parameter.physical_type.base_data_type is not None:
@@ -104,26 +110,47 @@ def prompt_single_parameter_value(parameter: Parameter) -> Optional[AtomicOdxTyp
104
110
  logging.warning(
105
111
  f"Parameter {parameter.short_name} does not have a physical data type. Param details: {parameter}"
106
112
  )
107
- return answer.get(parameter.short_name)
113
+ return cast(str, answer.get(parameter.short_name))
108
114
 
109
115
 
110
- def encode_message_interactively(sub_service: Union[Request, Response],
116
+ def encode_message_interactively(codec: Union[Request, Response],
111
117
  ask_user_confirmation: bool = False) -> None:
112
- if not sys.__stdin__.isatty() or not sys.__stdout__.isatty():
118
+ if sys.__stdin__ is None or sys.__stdout__ is None or not sys.__stdin__.isatty(
119
+ ) or not sys.__stdout__.isatty():
113
120
  raise SystemError("This command can only be used in an interactive shell!")
114
- param_dict = sub_service.parameter_dict()
115
121
 
116
- exists_definable_param = False
117
- for param_or_dict in param_dict.values():
118
- if isinstance(param_or_dict, dict):
119
- for param in param_or_dict.values():
120
- if not isinstance(param_or_dict, dict) and param.is_settable:
121
- exists_definable_param = True
122
- elif param_or_dict.is_settable:
123
- exists_definable_param = True
122
+ answered_request = b''
123
+ if isinstance(codec, Response):
124
+ answered_request_prompt = [{
125
+ "type":
126
+ "input",
127
+ "name":
128
+ "request",
129
+ "message":
130
+ f"What is the request you want to answer? (Enter the coded request as integer in hexadecimal format (e.g. 12 3B 5)",
131
+ "filter":
132
+ lambda input: _convert_string_to_bytes(input),
133
+ }]
134
+ answer = IP_prompt(answered_request_prompt)
135
+ answered_request = cast(bytes, answer.get("request"))
136
+ print(f"Input interpretation as list: {list(answered_request)}")
137
+
138
+ has_settable_param = False
139
+ for param in codec.parameters:
140
+ if not param.is_settable:
141
+ continue
142
+
143
+ # TODO: Specifying complex parameters with nesting depth > 1
144
+ # is not possible yet
145
+ if (inner_params := getattr(getattr(param, "dop", None), "parameters", None)) is not None:
146
+ for inner_param in inner_params:
147
+ if inner_param.is_settable:
148
+ has_settable_param = True
149
+ elif param.is_settable:
150
+ has_settable_param = True
124
151
 
125
152
  param_values: ParameterValueDict = {}
126
- if exists_definable_param > 0:
153
+ if has_settable_param:
127
154
  # Ask whether user wants to encode a message
128
155
  if ask_user_confirmation:
129
156
  encode_message_prompt = [{
@@ -132,51 +159,44 @@ def encode_message_interactively(sub_service: Union[Request, Response],
132
159
  "message": f"Do you want to encode a message? [y/n]",
133
160
  "choices": ["yes", "no"],
134
161
  }]
135
- answer = PI_prompt(encode_message_prompt)
162
+ answer = IP_prompt(encode_message_prompt)
136
163
  if answer.get("yes_no_prompt") == "no":
137
164
  return
138
165
 
139
- if isinstance(sub_service, Response):
140
- answered_request_prompt = [{
141
- "type":
142
- "input",
143
- "name":
144
- "request",
145
- "message":
146
- f"What is the request you want to answer? (Enter the coded request as integer in hexadecimal format (e.g. 12 3B 5)",
147
- "filter":
148
- lambda input: _convert_string_to_bytes(input),
149
- }]
150
- answer = PI_prompt(answered_request_prompt)
151
- answered_request = cast(bytes, answer.get("request"))
152
- print(f"Input interpretation as list: {list(answered_request)}")
153
-
154
- # Request values for parameters
155
- for key, param_or_structure in param_dict.items():
156
- if isinstance(param_or_structure, dict):
157
- # param_or_structure refers to a structure (represented as dict of params)
166
+ # Query user for the values of all settable parameters
167
+ for param in codec.parameters:
168
+ if (inner_params := getattr(dop := getattr(param, "dop", None), "parameters",
169
+ None)) is not None:
170
+ assert isinstance(dop, DopBase)
171
+ inner_params = cast(List[Parameter], inner_params)
172
+ # param refers to a complex DOP, i.e., the required
173
+ # value is a key-value dict
158
174
  print(
159
- f"The next {len(param_or_structure)} parameters belong to the structure '{key}'"
175
+ f"The next {len(inner_params)} parameters belong to the structure '{dop.short_name}'"
160
176
  )
161
177
  structure_param_values: ParameterValueDict = {}
162
- for param_sn, param in param_or_structure.items():
163
- if not isinstance(param, dict) and param.is_settable:
164
- val = prompt_single_parameter_value(param)
178
+ for inner_param in inner_params:
179
+ if inner_param.is_settable:
180
+ val = prompt_single_parameter_value(inner_param)
165
181
  if val is not None:
166
- structure_param_values[param_sn] = val
167
- param_values[key] = structure_param_values
168
- elif param_or_structure.is_settable:
169
- # param_or_structure is a parameter
170
- val = prompt_single_parameter_value(param_or_structure)
182
+ structure_param_values[inner_param.short_name] = val
183
+ param_values[param.short_name] = structure_param_values
184
+ elif param.is_settable:
185
+ val = prompt_single_parameter_value(param)
171
186
  if val is not None:
172
- param_values[key] = val
173
- if isinstance(sub_service, Response):
174
- payload = sub_service.encode(coded_request=answered_request, **param_values)
187
+ param_values[param.short_name] = val
188
+
189
+ if isinstance(codec, Response):
190
+ payload = codec.encode(coded_request=answered_request, **param_values)
175
191
  else:
176
- payload = sub_service.encode(coded_request=b'', **param_values)
192
+ payload = codec.encode(coded_request=b'', **param_values)
177
193
  else:
178
- # There are no optional parameters that need to be defined by the user -> Just print message
179
- payload = sub_service.encode()
194
+ # There are no settable parameters -> Just print message
195
+ if isinstance(codec, Response):
196
+ payload = codec.encode(coded_request=answered_request)
197
+ else:
198
+ payload = codec.encode()
199
+
180
200
  print(f"Message payload: 0x{bytes(payload).hex()}")
181
201
 
182
202
 
@@ -187,56 +207,58 @@ def encode_message_from_string_values(
187
207
  if parameter_values is None:
188
208
  parameter_values = {}
189
209
  parameter_values = parameter_values.copy()
190
- param_dict = sub_service.parameter_dict()
191
210
 
192
- # Check if all needed parameters are given
211
+ # Check if all needed parameters have been specified
193
212
  missing_parameter_names = []
194
- for param_sn, param in param_dict.items():
195
- if isinstance(param, dict):
196
- # param_value refers to a structure (represented as dict of params)
197
- for simple_param_sn, simple_param in param.items():
198
- structured_value = parameter_values.get(param_sn)
199
- if not isinstance(simple_param, Parameter):
200
- continue
201
- if simple_param.is_required and (not isinstance(structured_value, dict) or
202
- structured_value.get(simple_param_sn) is None):
203
- missing_parameter_names.append(f"{param_sn} :: {simple_param_sn}")
213
+ for param in sub_service.parameters:
214
+ if (inner_params := getattr(dop := getattr(param, "dop", None), "parameters",
215
+ None)) is not None:
216
+ inner_param_values = parameter_values.get(param.short_name, {})
217
+ if not isinstance(inner_param_values, dict):
218
+ print(f"Value for composite parameter {param.short_name} must be "
219
+ f"a dictionary, got {type(inner_param_values).__name__}")
220
+ continue
221
+ for inner_param in inner_params:
222
+ if inner_param.is_required and inner_param.short_name not in inner_param_values:
223
+ missing_parameter_names.append(f"{param.short_name}.{inner_param.short_name}")
204
224
  else:
205
- if param.is_required and parameter_values.get(param_sn) is None:
206
- missing_parameter_names.append(param_sn)
225
+ if param.is_required and parameter_values.get(param.short_name) is None:
226
+ missing_parameter_names.append(param.short_name)
207
227
 
208
228
  if len(missing_parameter_names) > 0:
209
- print("The following parameters are required but missing!")
210
- print(" - " + "\n - ".join(missing_parameter_names))
229
+ print("The following parameters are required but missing:")
230
+ print(" - " + "\n - ".join(sorted(missing_parameter_names)))
211
231
  return
212
232
 
213
233
  # Request values for parameters
214
234
  for parameter_sn, parameter_value in parameter_values.items():
215
- parameter = param_dict.get(parameter_sn)
235
+ parameter = resolve_snref(parameter_sn, sub_service.parameters, Parameter)
216
236
  if parameter is None:
217
237
  print(f"I don't know the parameter {parameter_sn}")
218
238
  continue
219
239
 
220
240
  if isinstance(parameter_value, dict):
221
241
  # parameter_value refers to a structure (represented as dict of params)
242
+ dop = getattr(parameter, "dop", None)
243
+ inner_params = getattr(dop, "parameters", None)
244
+ assert isinstance(dop, ComplexDop)
245
+ assert isinstance(inner_params, list)
246
+ inner_params = cast(List[Parameter], inner_params)
247
+
222
248
  typed_dict = parameter_value.copy()
223
- for simple_param_sn, simple_param_val in parameter_value.items():
224
- simple_parameter = param_dict.get(simple_param_sn)
225
- if simple_parameter is None:
226
- print(f"Unknown sub-parameter {simple_param_sn}")
249
+ for inner_param_sn, inner_param_value in parameter_value.items():
250
+ inner_param = resolve_snref(inner_param_sn, inner_params, Parameter)
251
+ if inner_param is None:
252
+ print(f"Unknown sub-parameter {inner_param_sn}")
227
253
  continue
228
- if not isinstance(simple_param_val, str):
229
- print(f"The value specified for parameter {simple_param_sn} is not a string")
254
+ if not isinstance(inner_param_value, str):
255
+ print(f"The value specified for parameter {inner_param_sn} is not a string")
230
256
  continue
231
257
 
232
- typed_dict[simple_param_sn] = _convert_string_to_odx_type(
233
- simple_param_val,
234
- simple_parameter.physical_type.base_data_type # type: ignore[union-attr]
235
- )
236
- parameter_values[parameter_sn] = typed_dict
258
+ typed_dict[inner_param_sn] = _convert_string_to_odx_type(
259
+ inner_param_value, inner_param.physical_type.base_data_type)
260
+ parameter_values[parameter.short_name] = typed_dict
237
261
  else:
238
- assert isinstance(parameter, Parameter)
239
-
240
262
  if not isinstance(parameter_value, str):
241
263
  print(f"Value for parameter {parameter_sn} is not a string")
242
264
  continue
@@ -250,12 +272,14 @@ def encode_message_from_string_values(
250
272
  parameter_values[parameter_sn] = _convert_string_to_odx_type(
251
273
  parameter_value, DataType.A_BYTEFIELD)
252
274
 
253
- payload = sub_service.encode(**parameter_values) # type: ignore
275
+ payload = sub_service.encode(coded_request=b'\xff' * 100, **parameter_values)
276
+
254
277
  print(f"Message payload: 0x{bytes(payload).hex()}")
255
278
 
256
279
 
257
280
  def browse(odxdb: Database) -> None:
258
- if not sys.__stdin__.isatty() or not sys.__stdout__.isatty():
281
+ if sys.__stdin__ is None or sys.__stdout__ is None or not sys.__stdin__.isatty(
282
+ ) or not sys.__stdout__.isatty():
259
283
  raise SystemError("This command can only be used in an interactive shell!")
260
284
  dl_names = [dl.short_name for dl in odxdb.diag_layers]
261
285
  while True:
@@ -266,7 +290,7 @@ def browse(odxdb: Database) -> None:
266
290
  "message": "Select a Variant.",
267
291
  "choices": list(dl_names) + ["[exit]"],
268
292
  }]
269
- answer = PI_prompt(selection)
293
+ answer = IP_prompt(selection)
270
294
  if answer.get("variant") == "[exit]":
271
295
  return
272
296
 
@@ -276,19 +300,20 @@ def browse(odxdb: Database) -> None:
276
300
  print(f"{type(answer.get('variant'))=}")
277
301
  assert isinstance(variant, DiagLayer)
278
302
 
279
- if (rx_id := variant.get_receive_id()) is not None:
280
- recv_id = hex(rx_id)
281
- else:
282
- recv_id = "None"
303
+ if isinstance(variant, HierarchyElement):
304
+ if (rx_id := variant.get_receive_id()) is not None:
305
+ recv_id = hex(rx_id)
306
+ else:
307
+ recv_id = "None"
283
308
 
284
- if (tx_id := variant.get_send_id()) is not None:
285
- send_id = hex(tx_id)
286
- else:
287
- send_id = "None"
309
+ if (tx_id := variant.get_send_id()) is not None:
310
+ send_id = hex(tx_id)
311
+ else:
312
+ send_id = "None"
288
313
 
289
- print(
290
- f"{variant.variant_type.value} '{variant.short_name}' (Receive ID: {recv_id}, Send ID: {send_id})"
291
- )
314
+ print(
315
+ f"{variant.variant_type.value} '{variant.short_name}' (Receive ID: {recv_id}, Send ID: {send_id})"
316
+ )
292
317
 
293
318
  while True:
294
319
  services: List[DiagService] = [
@@ -304,7 +329,7 @@ def browse(odxdb: Database) -> None:
304
329
  f"The variant {variant.short_name} offers the following services. Select one!",
305
330
  "choices": [s.short_name for s in services] + ["[back]"],
306
331
  }]
307
- answer = PI_prompt(selection)
332
+ answer = IP_prompt(selection)
308
333
  if answer.get("service") == "[back]":
309
334
  break
310
335
 
@@ -339,7 +364,7 @@ def browse(odxdb: Database) -> None:
339
364
  "short": f"Negative response: {nr.short_name}",
340
365
  } for nr in service.negative_responses] + ["[back]"], # type: ignore
341
366
  }]
342
- answer = PI_prompt(selection)
367
+ answer = IP_prompt(selection)
343
368
  if answer.get("message_type") == "[back]":
344
369
  continue
345
370
 
@@ -347,13 +372,12 @@ def browse(odxdb: Database) -> None:
347
372
  if codec is not None:
348
373
  assert isinstance(codec, (Request, Response))
349
374
  table = extract_parameter_tabulation_data(codec.parameters)
350
- table_str = tabulate(table, headers='keys', tablefmt='presto')
351
- print(table_str)
375
+ print(table)
352
376
 
353
377
  encode_message_interactively(codec, ask_user_confirmation=True)
354
378
 
355
379
 
356
- def add_subparser(subparsers: "argparse._SubParsersAction") -> None:
380
+ def add_subparser(subparsers: SubparsersList) -> None:
357
381
  # Browse interactively to avoid spamming the console.
358
382
  parser = subparsers.add_parser(
359
383
  "browse",