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/compare.py CHANGED
@@ -3,15 +3,16 @@
3
3
 
4
4
  import argparse
5
5
  import os
6
- from typing import Any, Dict, List, Optional, Set, Union
6
+ from typing import Any, Dict, List, Optional, Set, Union, cast
7
7
 
8
- from rich import print
9
- from tabulate import tabulate # TODO: switch to rich tables
8
+ from rich import print as rich_print
9
+ from rich.padding import Padding as RichPadding
10
+ from rich.table import Table as RichTable
10
11
 
11
12
  from ..database import Database
12
- from ..diaglayer import DiagLayer
13
+ from ..diaglayers.diaglayer import DiagLayer
13
14
  from ..diagservice import DiagService
14
- from ..load_file import load_file
15
+ from ..loadfile import load_file
15
16
  from ..odxtypes import AtomicOdxType
16
17
  from ..parameters.codedconstparameter import CodedConstParameter
17
18
  from ..parameters.nrcconstparameter import NrcConstParameter
@@ -19,6 +20,7 @@ from ..parameters.parameter import Parameter
19
20
  from ..parameters.physicalconstantparameter import PhysicalConstantParameter
20
21
  from ..parameters.valueparameter import ValueParameter
21
22
  from . import _parser_utils
23
+ from ._parser_utils import SubparsersList
22
24
  from ._print_utils import (extract_service_tabulation_data, print_dl_metrics,
23
25
  print_service_parameters)
24
26
 
@@ -45,11 +47,14 @@ class Display:
45
47
  # class with variables and functions to display the result of the comparison
46
48
 
47
49
  # TODO
48
- # Idea: results as json export
49
- # - write results of comparison in json structure
50
- # - use odxlinks to refer to dignostic services / objects if changes have already been detected (e.g. in another ecu variant / diagnostic layer)
51
-
52
- # print all information about parameter properties (request, pos. response & neg. response parameters) for changed diagnostic services
50
+ # - Idea: results as json export
51
+ # - write results of comparison in json structure
52
+ # - use odxlinks to refer to dignostic services / objects if
53
+ # changes have already been detected (e.g. in another ecu
54
+ # variant / diagnostic layer)
55
+ # - print all information about parameter properties (request,
56
+ # pos. response & neg. response parameters) for changed diagnostic
57
+ # services
53
58
  param_detailed: bool
54
59
  obj_detailed: bool
55
60
 
@@ -61,56 +66,69 @@ class Display:
61
66
  if service_dict["new_services"] or service_dict["deleted_services"] or service_dict[
62
67
  "changed_name_of_service"][0] or service_dict["changed_parameters_of_service"][0]:
63
68
  assert isinstance(service_dict["diag_layer"], str)
64
- print()
65
- print(
69
+ rich_print()
70
+ rich_print(
66
71
  f"Changed diagnostic services for diagnostic layer '{service_dict['diag_layer']}' ({service_dict['diag_layer_type']}):"
67
72
  )
68
73
  if service_dict["new_services"]:
69
74
  assert isinstance(service_dict["new_services"], List)
70
- print()
71
- print(" [blue]New services[/blue]")
72
- table = extract_service_tabulation_data(
73
- service_dict["new_services"]) # type: ignore[arg-type]
74
- print(tabulate(table, headers="keys", tablefmt="presto"))
75
+ rich_print()
76
+ rich_print(" [blue]New services[/blue]")
77
+ rich_print(extract_service_tabulation_data(
78
+ service_dict["new_services"])) # type: ignore[arg-type]
75
79
  if service_dict["deleted_services"]:
76
80
  assert isinstance(service_dict["deleted_services"], List)
77
- print()
78
- print(" [blue]Deleted services[/blue]")
79
- table = extract_service_tabulation_data(
80
- service_dict["deleted_services"]) # type: ignore[arg-type]
81
- print(tabulate(table, headers="keys", tablefmt="presto"))
81
+ rich_print()
82
+ rich_print(" [blue]Deleted services[/blue]")
83
+ rich_print(extract_service_tabulation_data(
84
+ service_dict["deleted_services"])) # type: ignore[arg-type]
82
85
  if service_dict["changed_name_of_service"][0]:
83
- print()
84
- print(" [blue]Renamed services[/blue]")
85
- table = extract_service_tabulation_data(
86
- service_dict["changed_name_of_service"][0]) # type: ignore[arg-type]
87
- table["Old service name"] = service_dict["changed_name_of_service"][1]
88
- print(tabulate(table, headers="keys", tablefmt="presto"))
86
+ rich_print()
87
+ rich_print(" [blue]Renamed services[/blue]")
88
+ rich_print(extract_service_tabulation_data(
89
+ service_dict["changed_name_of_service"][0])) # type: ignore[arg-type]
89
90
  if service_dict["changed_parameters_of_service"][0]:
90
- print()
91
- print(" [blue]Services with parameter changes[/blue]")
91
+ rich_print()
92
+ rich_print(" [blue]Services with parameter changes[/blue]")
92
93
  # create table with information about services with parameter changes
94
+ changed_param_column = [
95
+ str(x) for x in service_dict["changed_parameters_of_service"][
96
+ 1] # type: ignore[union-attr]
97
+ ]
93
98
  table = extract_service_tabulation_data(
94
- service_dict["changed_parameters_of_service"][0]) # type: ignore[arg-type]
95
- # add information about changed parameters
96
- table["Changed parameters"] = service_dict["changed_parameters_of_service"][1]
97
- print(tabulate(table, headers="keys", tablefmt="presto"))
99
+ service_dict["changed_parameters_of_service"][0], # type: ignore[arg-type]
100
+ additional_columns=[("Changed Parameters", changed_param_column)])
101
+ rich_print(table)
98
102
 
99
103
  for service_idx, service in enumerate(
100
104
  service_dict["changed_parameters_of_service"][0]): # type: ignore[arg-type]
101
105
  assert isinstance(service, DiagService)
102
- print()
103
- print(
106
+ rich_print()
107
+ rich_print(
104
108
  f" Detailed changes of diagnostic service [u cyan]{service.short_name}[/u cyan]"
105
109
  )
106
110
  # detailed_info in [infotext1, dict1, infotext2, dict2, ...]
107
- for detailed_info in service_dict["changed_parameters_of_service"][2][
108
- service_idx]: # type: ignore[arg-type, index, union-attr]
111
+ info_list = cast(
112
+ list, # type: ignore[type-arg]
113
+ service_dict["changed_parameters_of_service"])[2][service_idx]
114
+ for detailed_info in info_list:
109
115
  if isinstance(detailed_info, str):
110
- print()
111
- print(detailed_info)
116
+ rich_print()
117
+ rich_print(detailed_info)
112
118
  elif isinstance(detailed_info, dict):
113
- print(tabulate(detailed_info, headers="keys", tablefmt="presto"))
119
+ table = RichTable(
120
+ show_header=True,
121
+ header_style="bold cyan",
122
+ border_style="blue",
123
+ show_lines=True)
124
+ for header in detailed_info:
125
+ table.add_column(header)
126
+ rows = zip(*detailed_info.values())
127
+ for row in rows:
128
+ table.add_row(*map(str, row))
129
+
130
+ rich_print(RichPadding(table, pad=(0, 0, 0, 4)))
131
+ rich_print()
114
132
  if self.param_detailed:
115
133
  # print all parameter details of diagnostic service
116
134
  print_service_parameters(service, allow_unknown_bit_lengths=True)
@@ -121,16 +139,18 @@ class Display:
121
139
  # diagnostic layers
122
140
  if changes_variants["new_diagnostic_layers"] or changes_variants[
123
141
  "deleted_diagnostic_layers"]:
124
- print()
125
- print("[bright_blue]Changed diagnostic layers[/bright_blue]: ")
126
- print(" New diagnostic layers: ")
142
+ rich_print()
143
+ rich_print("[bright_blue]Changed diagnostic layers[/bright_blue]: ")
144
+ rich_print(" New diagnostic layers: ")
127
145
  for variant in changes_variants["new_diagnostic_layers"]:
128
146
  assert isinstance(variant, DiagLayer)
129
- print(f" [magenta]{variant.short_name}[/magenta] ({variant.variant_type.value})")
130
- print(" Deleted diagnostic layers: ")
147
+ rich_print(
148
+ f" [magenta]{variant.short_name}[/magenta] ({variant.variant_type.value})")
149
+ rich_print(" Deleted diagnostic layers: ")
131
150
  for variant in changes_variants["deleted_diagnostic_layers"]:
132
151
  assert isinstance(variant, DiagLayer)
133
- print(f" [magenta]{variant.short_name}[/magenta] ({variant.variant_type.value})")
152
+ rich_print(
153
+ f" [magenta]{variant.short_name}[/magenta] ({variant.variant_type.value})")
134
154
 
135
155
  # diagnostic services
136
156
  for _, value in changes_variants.items():
@@ -256,7 +276,8 @@ class Comparison(Display):
256
276
 
257
277
  return {"Property": property, "Old Value": old, "New Value": new}
258
278
 
259
- def compare_services(self, service1: DiagService, service2: DiagService) -> list:
279
+ def compare_services(self, service1: DiagService,
280
+ service2: DiagService) -> List[SpecsServiceDict]:
260
281
  # compares request, positive response and negative response parameters of two diagnostic services
261
282
 
262
283
  information: List[Union[str, Dict[str, Any]]] = [
@@ -271,8 +292,8 @@ class Comparison(Display):
271
292
  if res1_idx == res2_idx:
272
293
  # find changed request parameter properties
273
294
  table = self.compare_parameters(param1, param2)
274
- infotext = (f" Properties of request parameter '{param2.short_name}'"
275
- f" have changed\n")
295
+ infotext = (f" Properties of request parameter '{param2.short_name}' "
296
+ f"that have changed:\n")
276
297
  # array index starts with 0 -> param[0] is 1. service parameter
277
298
 
278
299
  if table["Property"]:
@@ -307,8 +328,8 @@ class Comparison(Display):
307
328
  # find changed positive response parameter properties
308
329
  table = self.compare_parameters(param1, param2)
309
330
  infotext = (
310
- f" Properties of positive response parameter '{param2.short_name}'"
311
- f"have changed\n")
331
+ f" Properties of positive response parameter '{param2.short_name}' that "
332
+ f"have changed:\n")
312
333
  # array index starts with 0 -> param[0] is first service parameter
313
334
 
314
335
  if table["Property"]:
@@ -350,7 +371,7 @@ class Comparison(Display):
350
371
  if param1_idx == param2_idx:
351
372
  # find changed negative response parameter properties
352
373
  table = self.compare_parameters(param1, param2)
353
- infotext = f" Properties of response parameter '{param2.short_name}' have changed\n"
374
+ infotext = f" Properties of response parameter '{param2.short_name}' that have changed:\n"
354
375
  # array index starts with 0 -> param[0] is 1. service parameter
355
376
 
356
377
  if table["Property"]:
@@ -382,9 +403,10 @@ class Comparison(Display):
382
403
  str(service2.negative_responses)]
383
404
  })
384
405
 
385
- return [information, changed_params]
406
+ return [information, changed_params] # type: ignore[list-item]
386
407
 
387
- def compare_diagnostic_layers(self, dl1: DiagLayer, dl2: DiagLayer) -> dict:
408
+ def compare_diagnostic_layers(self, dl1: DiagLayer,
409
+ dl2: DiagLayer) -> dict: # type: ignore[type-arg]
388
410
  # compares diagnostic services of two diagnostic layers with each other
389
411
  # save changes in dictionary (service_dict)
390
412
  # TODO: add comparison of SingleECUJobs
@@ -467,21 +489,23 @@ class Comparison(Display):
467
489
  # add parameters which have been changed (type: String)
468
490
  service_dict["changed_parameters_of_service"][
469
491
  1].append( # type: ignore[union-attr]
470
- detailed_information[1])
492
+ detailed_information[1]) # type: ignore[arg-type]
471
493
  # add detailed information about changed service parameters (type: list) [infotext1, table1, infotext2, table2, ...]
472
494
  service_dict["changed_parameters_of_service"][
473
495
  2].append( # type: ignore[union-attr]
474
- detailed_information[0])
496
+ detailed_information[0]) # type: ignore[arg-type]
475
497
 
476
498
  for service2_idx, service2 in enumerate(dl2.services):
477
499
 
478
500
  # check for deleted diagnostic services
479
501
  if service2.short_name not in dl1_service_names and dl2_request_prefixes[
480
- service2_idx] not in dl1_request_prefixes and service2 not in service_dict[
481
- "deleted_services"]:
502
+ service2_idx] not in dl1_request_prefixes:
482
503
 
483
- service_dict["deleted_services"].append( # type: ignore[union-attr]
484
- service2) # type: ignore[arg-type]
504
+ deleted_list = service_dict["deleted_services"]
505
+ assert isinstance(deleted_list, list)
506
+ if service2 not in deleted_list:
507
+ service_dict["deleted_services"].append( # type: ignore[union-attr]
508
+ service2) # type: ignore[arg-type]
485
509
 
486
510
  if service1.short_name == service2.short_name:
487
511
  # compare request, pos. response and neg. response parameters of both diagnostic services
@@ -496,13 +520,14 @@ class Comparison(Display):
496
520
  service1)
497
521
  # add parameters which have been changed (type: String)
498
522
  service_dict["changed_parameters_of_service"][ # type: ignore[union-attr]
499
- 1].append(detailed_information[1])
523
+ 1].append(detailed_information[1]) # type: ignore[arg-type]
500
524
  # add detailed information about changed service parameters (type: list) [infotext1, table1, infotext2, table2, ...]
501
525
  service_dict["changed_parameters_of_service"][ # type: ignore[union-attr]
502
- 2].append(detailed_information[0])
526
+ 2].append(detailed_information[0]) # type: ignore[arg-type]
503
527
  return service_dict
504
528
 
505
- def compare_databases(self, database_new: Database, database_old: Database) -> dict:
529
+ def compare_databases(self, database_new: Database,
530
+ database_old: Database) -> dict: # type: ignore[type-arg]
506
531
  # compares two PDX-files with each other
507
532
 
508
533
  new_variants: NewVariants = []
@@ -539,7 +564,7 @@ class Comparison(Display):
539
564
  return changes_variants
540
565
 
541
566
 
542
- def add_subparser(subparsers: "argparse._SubParsersAction") -> None:
567
+ def add_subparser(subparsers: SubparsersList) -> None:
543
568
  parser = subparsers.add_parser(
544
569
  "compare",
545
570
  description="\n".join([
@@ -614,7 +639,7 @@ def run(args: argparse.Namespace) -> None:
614
639
 
615
640
  for name in args.variants:
616
641
  if name not in task.diagnostic_layer_names:
617
- print(f"The variant '{name}' could not be found!")
642
+ rich_print(f"The variant '{name}' could not be found!")
618
643
  return
619
644
 
620
645
  task.db_indicator_1 = 0
@@ -624,19 +649,20 @@ def run(args: argparse.Namespace) -> None:
624
649
  break
625
650
  task.db_indicator_2 = db_idx + 1
626
651
 
627
- print()
628
- print(f"Changes in file '{os.path.basename(db_names[0])}'")
629
- print(f" (compared to '{os.path.basename(db_names[db_idx + 1])}')")
652
+ rich_print()
653
+ rich_print(f"Changes in file '{os.path.basename(db_names[0])}'")
654
+ rich_print(f" (compared to '{os.path.basename(db_names[db_idx + 1])}')")
630
655
 
631
- print()
632
- print(f"Overview of diagnostic layers (for {os.path.basename(db_names[0])})")
656
+ rich_print()
657
+ rich_print(f"Overview of diagnostic layers (for {os.path.basename(db_names[0])})")
633
658
  print_dl_metrics([
634
659
  variant for variant in task.databases[0].diag_layers
635
660
  if variant.short_name in task.diagnostic_layer_names
636
661
  ])
637
662
 
638
- print()
639
- print(f"Overview of diagnostic layers (for {os.path.basename(db_names[db_idx+1])})")
663
+ rich_print()
664
+ rich_print(
665
+ f"Overview of diagnostic layers (for {os.path.basename(db_names[db_idx+1])})")
640
666
  print_dl_metrics([
641
667
  variant for variant in task.databases[db_idx + 1].diag_layers
642
668
  if variant.short_name in task.diagnostic_layer_names
@@ -665,16 +691,17 @@ def run(args: argparse.Namespace) -> None:
665
691
  break
666
692
  task.db_indicator_2 = db_idx + 1
667
693
 
668
- print()
669
- print(f"Changes in file '{os.path.basename(db_names[0])}")
670
- print(f" (compared to '{os.path.basename(db_names[db_idx + 1])}')")
694
+ rich_print()
695
+ rich_print(f"Changes in file '{os.path.basename(db_names[0])}")
696
+ rich_print(f" (compared to '{os.path.basename(db_names[db_idx + 1])}')")
671
697
 
672
- print()
673
- print(f"Overview of diagnostic layers (for {os.path.basename(db_names[0])})")
698
+ rich_print()
699
+ rich_print(f"Overview of diagnostic layers (for {os.path.basename(db_names[0])})")
674
700
  print_dl_metrics(list(task.databases[0].diag_layers))
675
701
 
676
- print()
677
- print(f"Overview of diagnostic layers (for {os.path.basename(db_names[db_idx+1])})")
702
+ rich_print()
703
+ rich_print(
704
+ f"Overview of diagnostic layers (for {os.path.basename(db_names[db_idx+1])})")
678
705
  print_dl_metrics(list(task.databases[db_idx + 1].diag_layers))
679
706
 
680
707
  task.print_database_changes(
@@ -696,20 +723,20 @@ def run(args: argparse.Namespace) -> None:
696
723
 
697
724
  for name in args.variants:
698
725
  if name not in task.diagnostic_layer_names:
699
- print(f"The variant '{name}' could not be found!")
726
+ rich_print(f"The variant '{name}' could not be found!")
700
727
  return
701
728
 
702
- print()
703
- print(f"Overview of diagnostic layers: ")
729
+ rich_print()
730
+ rich_print(f"Overview of diagnostic layers: ")
704
731
  print_dl_metrics(task.diagnostic_layers)
705
732
 
706
733
  for db_idx, dl in enumerate(task.diagnostic_layers):
707
734
  if db_idx + 1 >= len(task.diagnostic_layers):
708
735
  break
709
736
 
710
- print()
711
- print(f"Changes in diagnostic layer '{dl.short_name}' ({dl.variant_type.value})")
712
- print(
737
+ rich_print()
738
+ rich_print(f"Changes in diagnostic layer '{dl.short_name}' ({dl.variant_type.value})")
739
+ rich_print(
713
740
  f" (compared to '{task.diagnostic_layers[db_idx + 1].short_name}' ({task.diagnostic_layers[db_idx + 1].variant_type.value}))"
714
741
  )
715
742
  task.print_dl_changes(
@@ -717,4 +744,4 @@ def run(args: argparse.Namespace) -> None:
717
744
 
718
745
  else:
719
746
  # no databases & no variants specified
720
- print("Please specify either a database or variant for a comparison")
747
+ rich_print("Please specify either a database or variant for a comparison")
odxtools/cli/decode.py CHANGED
@@ -9,6 +9,7 @@ from ..exceptions import odxraise
9
9
  from ..odxtypes import ParameterValue
10
10
  from ..singleecujob import SingleEcuJob
11
11
  from . import _parser_utils
12
+ from ._parser_utils import SubparsersList
12
13
 
13
14
  # name of the tool
14
15
  _odxtools_tool_name_ = "decode"
@@ -70,7 +71,7 @@ def print_summary(
70
71
  print(f" {param_name}={get_display_value(param_value)}")
71
72
 
72
73
 
73
- def add_subparser(subparsers: "argparse._SubParsersAction") -> None:
74
+ def add_subparser(subparsers: SubparsersList) -> None:
74
75
  parser = subparsers.add_parser(
75
76
  "decode",
76
77
  description="\n".join([
@@ -4,6 +4,8 @@ import sys
4
4
  import traceback
5
5
  from io import StringIO
6
6
 
7
+ from ._parser_utils import SubparsersList
8
+
7
9
 
8
10
  class DummyTool:
9
11
  """A tool which acts as a placeholder for a "real" tool that
@@ -20,7 +22,7 @@ class DummyTool:
20
22
  self._odxtools_tool_name_ = tool_name
21
23
  self._error = error
22
24
 
23
- def add_subparser(self, subparser_list: "argparse._SubParsersAction") -> None:
25
+ def add_subparser(self, subparser_list: SubparsersList) -> None:
24
26
  desc = StringIO()
25
27
 
26
28
  print(f"Tool '{self._odxtools_tool_name_}' is unavailable: {self._error}", file=desc)
odxtools/cli/find.py CHANGED
@@ -7,6 +7,7 @@ from ..diagservice import DiagService
7
7
  from ..odxtypes import ParameterValue
8
8
  from ..singleecujob import SingleEcuJob
9
9
  from . import _parser_utils
10
+ from ._parser_utils import SubparsersList
10
11
  from ._print_utils import print_diagnostic_service
11
12
 
12
13
  # name of the tool
@@ -66,7 +67,7 @@ def print_summary(odxdb: Database,
66
67
  print(f"Unknown service: {service}")
67
68
 
68
69
 
69
- def add_subparser(subparsers: "argparse._SubParsersAction") -> None:
70
+ def add_subparser(subparsers: SubparsersList) -> None:
70
71
  parser = subparsers.add_parser(
71
72
  "find",
72
73
  description="\n".join([
odxtools/cli/list.py CHANGED
@@ -4,12 +4,18 @@ from typing import Callable, List, Optional
4
4
 
5
5
  import rich
6
6
 
7
+ from ..comparaminstance import ComparamInstance
7
8
  from ..database import Database
9
+ from ..dataobjectproperty import DataObjectProperty
8
10
  from ..diagcomm import DiagComm
9
- from ..diaglayer import DiagLayer
11
+ from ..diaglayers.basevariant import BaseVariant
12
+ from ..diaglayers.diaglayer import DiagLayer
13
+ from ..diaglayers.ecuvariant import EcuVariant
14
+ from ..diaglayers.hierarchyelement import HierarchyElement
10
15
  from ..diagservice import DiagService
11
16
  from ..singleecujob import SingleEcuJob
12
17
  from . import _parser_utils
18
+ from ._parser_utils import SubparsersList
13
19
  from ._print_utils import format_desc, print_diagnostic_service, print_dl_metrics
14
20
 
15
21
  # name of the tool
@@ -46,7 +52,7 @@ def print_summary(odxdb: Database,
46
52
  if diag_layers:
47
53
  rich.print("\n")
48
54
  rich.print(f"Overview of diagnostic layers: ")
49
- print_dl_metrics(diag_layers, print_fn=rich.print)
55
+ print_dl_metrics(diag_layers)
50
56
 
51
57
  for dl in diag_layers:
52
58
  rich.print("\n")
@@ -55,15 +61,14 @@ def print_summary(odxdb: Database,
55
61
 
56
62
  all_services: List[DiagComm] = sorted(dl.services, key=lambda x: x.short_name)
57
63
 
58
- data_object_properties = dl.diag_data_dictionary_spec.data_object_props
59
- comparams = dl.comparams
64
+ if isinstance(dl, (BaseVariant, EcuVariant)):
65
+ for proto in dl.protocols:
66
+ if (can_rx_id := dl.get_can_receive_id(proto.short_name)) is not None:
67
+ rich.print(
68
+ f" CAN receive ID for protocol '{proto.short_name}': 0x{can_rx_id:x}")
60
69
 
61
- for proto in dl.protocols:
62
- if (can_rx_id := dl.get_can_receive_id(proto.short_name)) is not None:
63
- rich.print(f" CAN receive ID for protocol '{proto.short_name}': 0x{can_rx_id:x}")
64
-
65
- if (can_tx_id := dl.get_can_send_id(proto.short_name)) is not None:
66
- rich.print(f" CAN send ID for protocol '{proto.short_name}': 0x{can_tx_id:x}")
70
+ if (can_tx_id := dl.get_can_send_id(proto.short_name)) is not None:
71
+ rich.print(f" CAN send ID for protocol '{proto.short_name}': 0x{can_tx_id:x}")
67
72
 
68
73
  if dl.description:
69
74
  desc = format_desc(dl.description, indent=2)
@@ -88,13 +93,14 @@ def print_summary(odxdb: Database,
88
93
  print_pre_condition_states=print_pre_condition_states,
89
94
  print_state_transitions=print_state_transitions,
90
95
  print_audiences=print_audiences,
91
- allow_unknown_bit_lengths=allow_unknown_bit_lengths,
92
- print_fn=rich.print)
96
+ allow_unknown_bit_lengths=allow_unknown_bit_lengths)
93
97
  elif isinstance(service, SingleEcuJob):
94
98
  rich.print(f" Single ECU job: {service.odx_id}")
95
99
  else:
96
100
  rich.print(f" Unidentifiable service: {service}")
97
-
101
+ ddd_spec = dl.diag_data_dictionary_spec
102
+ data_object_properties: List[
103
+ DataObjectProperty] = [] if ddd_spec is None else ddd_spec.data_object_props
98
104
  if print_dops and len(data_object_properties) > 0:
99
105
  rich.print("\n")
100
106
  rich.print(f"The DOPs of the {dl.variant_type.value} '{dl.short_name}' are: ")
@@ -102,16 +108,20 @@ def print_summary(odxdb: Database,
102
108
  data_object_properties, key=lambda x: (type(x).__name__, x.short_name)):
103
109
  rich.print(" " + str(dop.short_name).replace("\n", "\n "))
104
110
 
105
- if print_comparams and len(comparams) > 0:
111
+ comparam_refs: List[ComparamInstance] = []
112
+ if isinstance(dl, HierarchyElement):
113
+ comparam_refs = dl.comparam_refs
114
+
115
+ if print_comparams and len(comparam_refs) > 0:
106
116
  rich.print("\n")
107
117
  rich.print(
108
118
  f"The communication parameters of the {dl.variant_type.value} '{dl.short_name}' are: "
109
119
  )
110
- for com_param in comparams:
120
+ for com_param in comparam_refs:
111
121
  rich.print(f" {com_param.short_name}: {com_param.value}")
112
122
 
113
123
 
114
- def add_subparser(subparsers: "argparse._SubParsersAction") -> None:
124
+ def add_subparser(subparsers: SubparsersList) -> None:
115
125
  parser = subparsers.add_parser(
116
126
  "list",
117
127
  description="\n".join([
odxtools/cli/main.py CHANGED
@@ -4,6 +4,7 @@ import importlib
4
4
  from typing import Any, List
5
5
 
6
6
  import odxtools
7
+ import odxtools.exceptions
7
8
 
8
9
  from ..version import __version__ as odxtools_version
9
10
  from .dummy_sub_parser import DummyTool
odxtools/cli/snoop.py CHANGED
@@ -4,17 +4,19 @@
4
4
  import argparse
5
5
  import asyncio
6
6
  import sys
7
- from typing import Any, Type
7
+ from typing import Any, List, Optional, Type
8
8
 
9
9
  import can
10
10
 
11
11
  import odxtools.isotp_state_machine as ism
12
12
  import odxtools.uds as uds
13
+ from odxtools.diaglayers.protocol import Protocol
13
14
  from odxtools.exceptions import DecodeError
14
15
  from odxtools.isotp_state_machine import IsoTpStateMachine
15
16
  from odxtools.response import Response, ResponseType
16
17
 
17
18
  from . import _parser_utils
19
+ from ._parser_utils import SubparsersList
18
20
 
19
21
  # name of the tool
20
22
  _odxtools_tool_name_ = "snoop"
@@ -30,6 +32,8 @@ def handle_telegram(telegram_id: int, payload: bytes) -> None:
30
32
  global odx_diag_layer
31
33
  global last_request
32
34
 
35
+ assert odx_diag_layer is not None
36
+
33
37
  if telegram_id == ecu_tx_id:
34
38
  if uds.is_response_pending(payload):
35
39
  print(f" ... (response pending)")
@@ -209,7 +213,7 @@ def add_cli_arguments(parser: argparse.ArgumentParser) -> None:
209
213
  _parser_utils.add_pdx_argument(parser)
210
214
 
211
215
 
212
- def add_subparser(subparsers: "argparse._SubParsersAction") -> None:
216
+ def add_subparser(subparsers: SubparsersList) -> None:
213
217
  parser = subparsers.add_parser(
214
218
  "snoop",
215
219
  description="Live decoding of a diagnostic session.",
@@ -242,21 +246,43 @@ def run(args: argparse.Namespace) -> None:
242
246
 
243
247
  protocol_name = args.protocol
244
248
  if odx_diag_layer is not None and protocol_name is not None:
245
- protocol_names = [x.short_name for x in odx_diag_layer.protocols]
249
+ protocols: Optional[List[Protocol]] = getattr(odx_diag_layer, "protocols", None)
250
+
251
+ if protocols is None:
252
+ print(f"ECU variant {odx_diag_layer.short_name} is of type "
253
+ f"{odx_diag_layer.variant_type.value} and thus does not "
254
+ f"feature any protocols")
255
+ sys.exit(1)
256
+
257
+ protocol_names = [x.short_name for x in protocols]
246
258
  if protocol_name not in protocol_names:
247
259
  print(f"ECU variant {odx_diag_layer.short_name} does not support "
248
260
  f"a protocol named '{protocol_name}'. Supported protocols are:")
249
- for x in odx_diag_layer.protocols:
261
+ for x in protocols:
250
262
  desc = "" if x.description is None else f": {x.description}"
251
263
  print(f" {x.short_name}{desc}")
252
264
  sys.exit(1)
253
265
 
254
266
  # if no can IDs have been explicitly specified, take them from the DL
255
267
  if args.rx is None and odx_diag_layer:
256
- args.rx = str(odx_diag_layer.get_can_receive_id(protocol=protocol_name))
268
+ get_can_rx_id = getattr(odx_diag_layer, "get_can_receive_id", None)
269
+ if get_can_rx_id is None:
270
+ print(f"ECU variant {odx_diag_layer.short_name} is of type "
271
+ f"{odx_diag_layer.variant_type.value} and thus does not "
272
+ f"provide any communication parameters")
273
+ sys.exit(1)
274
+
275
+ args.rx = str(get_can_rx_id(protocol=protocol_name))
257
276
 
258
277
  if args.tx is None and odx_diag_layer:
259
- args.tx = str(odx_diag_layer.get_can_send_id(protocol=protocol_name))
278
+ get_can_tx_id = getattr(odx_diag_layer, "get_can_send_id", None)
279
+ if get_can_tx_id is None:
280
+ print(f"ECU variant {odx_diag_layer.short_name} is of type "
281
+ f"{odx_diag_layer.variant_type.value} and thus does not "
282
+ f"provide any communication parameters")
283
+ sys.exit(1)
284
+
285
+ args.tx = str(get_can_tx_id(protocol=protocol_name))
260
286
 
261
287
  if args.rx is None:
262
288
  print(f"Could not determine a CAN receive ID.")