odxtools 6.4.2__py3-none-any.whl → 6.5.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.
- odxtools/basicstructure.py +7 -3
- odxtools/cli/_print_utils.py +210 -25
- odxtools/cli/compare.py +730 -0
- odxtools/cli/decode.py +1 -1
- odxtools/cli/find.py +13 -4
- odxtools/cli/list.py +74 -40
- odxtools/cli/main.py +1 -1
- odxtools/database.py +1 -0
- odxtools/dataobjectproperty.py +15 -2
- odxtools/diagcodedtype.py +85 -67
- odxtools/internalconstr.py +34 -0
- odxtools/leadinglengthinfotype.py +5 -5
- odxtools/minmaxlengthtype.py +3 -3
- odxtools/paramlengthinfotype.py +2 -2
- odxtools/scaleconstr.py +50 -0
- odxtools/standardlengthtype.py +2 -2
- odxtools/templates/macros/printDOP.xml.jinja2 +52 -5
- odxtools/version.py +2 -2
- {odxtools-6.4.2.dist-info → odxtools-6.5.0.dist-info}/METADATA +388 -47
- {odxtools-6.4.2.dist-info → odxtools-6.5.0.dist-info}/RECORD +24 -21
- {odxtools-6.4.2.dist-info → odxtools-6.5.0.dist-info}/LICENSE +0 -0
- {odxtools-6.4.2.dist-info → odxtools-6.5.0.dist-info}/WHEEL +0 -0
- {odxtools-6.4.2.dist-info → odxtools-6.5.0.dist-info}/entry_points.txt +0 -0
- {odxtools-6.4.2.dist-info → odxtools-6.5.0.dist-info}/top_level.txt +0 -0
odxtools/basicstructure.py
CHANGED
@@ -460,7 +460,10 @@ class BasicStructure(ComplexDop):
|
|
460
460
|
else:
|
461
461
|
return []
|
462
462
|
|
463
|
-
def print_message_format(self,
|
463
|
+
def print_message_format(self,
|
464
|
+
indent: int = 5,
|
465
|
+
allow_unknown_lengths: bool = False,
|
466
|
+
plumbing_output: bool = True) -> None:
|
464
467
|
"""
|
465
468
|
Print a description of the message format to `stdout`.
|
466
469
|
"""
|
@@ -470,5 +473,6 @@ class BasicStructure(ComplexDop):
|
|
470
473
|
print(f"{indent * ' '}" + f"\n{indent * ' '}".join(message_as_lines))
|
471
474
|
else:
|
472
475
|
print("Sorry, couldn't pretty print message layout. :(")
|
473
|
-
|
474
|
-
|
476
|
+
if plumbing_output:
|
477
|
+
for p in self.parameters:
|
478
|
+
print(indent * " " + str(p).replace("\n", f"\n{indent * ' '}"))
|
odxtools/cli/_print_utils.py
CHANGED
@@ -1,9 +1,22 @@
|
|
1
1
|
# SPDX-License-Identifier: MIT
|
2
2
|
import re
|
3
|
+
from typing import Any, Dict, List, Optional, Union
|
3
4
|
|
4
5
|
import markdownify
|
6
|
+
from rich import console, print
|
7
|
+
from tabulate import tabulate # TODO: switch to rich tables
|
5
8
|
|
9
|
+
from ..diaglayer import DiagLayer
|
6
10
|
from ..diagservice import DiagService
|
11
|
+
from ..parameters.codedconstparameter import CodedConstParameter
|
12
|
+
from ..parameters.nrcconstparameter import NrcConstParameter
|
13
|
+
from ..parameters.parameter import Parameter
|
14
|
+
from ..parameters.physicalconstantparameter import PhysicalConstantParameter
|
15
|
+
from ..parameters.systemparameter import SystemParameter
|
16
|
+
from ..parameters.valueparameter import ValueParameter
|
17
|
+
from ..singleecujob import SingleEcuJob
|
18
|
+
|
19
|
+
terminal = console.Console()
|
7
20
|
|
8
21
|
|
9
22
|
def format_desc(desc: str, ident: int = 0) -> str:
|
@@ -19,15 +32,14 @@ def format_desc(desc: str, ident: int = 0) -> str:
|
|
19
32
|
return desc
|
20
33
|
|
21
34
|
|
22
|
-
def print_diagnostic_service(
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
print(f" {service.short_name} <ID: {service.odx_id}>")
|
35
|
+
def print_diagnostic_service(service: DiagService,
|
36
|
+
print_params: bool = False,
|
37
|
+
print_pre_condition_states: bool = False,
|
38
|
+
print_state_transitions: bool = False,
|
39
|
+
print_audiences: bool = False,
|
40
|
+
allow_unknown_bit_lengths: bool = False,
|
41
|
+
plumbing_output: bool = False) -> None:
|
42
|
+
print(f" [cyan]{service.short_name}[/cyan] <ID: {service.odx_id}>")
|
31
43
|
|
32
44
|
if service.description:
|
33
45
|
desc = format_desc(service.description, ident=3)
|
@@ -53,27 +65,71 @@ def print_diagnostic_service(
|
|
53
65
|
print(f" Enabled-Audiences: {', '.join(enabled_audiences_short_names)}")
|
54
66
|
|
55
67
|
if print_params:
|
56
|
-
|
57
|
-
|
58
|
-
|
68
|
+
print_service_parameters(
|
69
|
+
service,
|
70
|
+
allow_unknown_bit_lengths=allow_unknown_bit_lengths,
|
71
|
+
plumbing_output=plumbing_output)
|
72
|
+
|
73
|
+
|
74
|
+
def print_service_parameters(service: DiagService,
|
75
|
+
allow_unknown_bit_lengths: bool = False,
|
76
|
+
plumbing_output: bool = False) -> None:
|
77
|
+
# prints parameter details of request, posivite response and negative response of diagnostic service
|
78
|
+
|
79
|
+
assert service.request is not None
|
80
|
+
assert service.positive_responses is not None
|
81
|
+
assert service.negative_responses is not None
|
82
|
+
|
83
|
+
# Request
|
84
|
+
print(f"\n [yellow]Request Properties[/yellow]:")
|
85
|
+
print(f" Request Name: {service.request.short_name}")
|
59
86
|
|
60
|
-
|
61
|
-
service
|
62
|
-
|
87
|
+
if service.request and not service.request.required_parameters:
|
88
|
+
ba = f" Byte-Array: {service()!r}"
|
89
|
+
hs = f" Hex-String: 0x{str(service().hex().upper())}"
|
90
|
+
terminal.print(ba, overflow="ellipsis", soft_wrap=True)
|
91
|
+
terminal.print(hs, overflow="ellipsis", soft_wrap=True)
|
92
|
+
else:
|
93
|
+
print(f" Byte-Array: ---\n Hex-String: ---")
|
63
94
|
|
64
|
-
|
65
|
-
|
66
|
-
|
95
|
+
print(f" Service Parameters: {service.request.parameters}\n")
|
96
|
+
table = extract_parameter_tabulation_data(list(service.request.parameters))
|
97
|
+
print(tabulate(table, headers='keys', tablefmt='presto'))
|
98
|
+
print(f"\n Message format of the request:")
|
99
|
+
service.request.print_message_format(
|
100
|
+
indent=0, allow_unknown_lengths=allow_unknown_bit_lengths, plumbing_output=plumbing_output)
|
67
101
|
|
68
|
-
|
69
|
-
|
102
|
+
# Positive Response
|
103
|
+
print(f"\n [yellow]Positive Response Properties[/yellow]:")
|
104
|
+
print(f" Number of Positive Responses: {len(service.positive_responses)}")
|
105
|
+
print(f" Positive Responses: {service.positive_responses}")
|
106
|
+
if len(service.positive_responses) == 1:
|
107
|
+
resp = service.positive_responses[0]
|
108
|
+
print(f" Service Parameters: {resp.parameters}\n")
|
109
|
+
table = extract_parameter_tabulation_data(list(resp.parameters))
|
110
|
+
print(tabulate(table, headers='keys', tablefmt='presto'))
|
111
|
+
print(f"\n Message format of the positive response:")
|
112
|
+
resp.print_message_format(
|
113
|
+
indent=0,
|
114
|
+
allow_unknown_lengths=allow_unknown_bit_lengths,
|
115
|
+
plumbing_output=plumbing_output)
|
70
116
|
|
71
|
-
|
72
|
-
|
73
|
-
|
117
|
+
# Negative Response
|
118
|
+
print(f"\n [yellow]Negative Response Properties[/yellow]:")
|
119
|
+
print(f" Number of Negative Responses: {len(service.negative_responses)}")
|
120
|
+
print(f" Negative Responses: {service.negative_responses}")
|
121
|
+
if len(service.negative_responses) == 1:
|
122
|
+
resp = service.negative_responses[0]
|
123
|
+
print(f" Service Parameters: {resp.parameters}\n")
|
124
|
+
table = extract_parameter_tabulation_data(list(resp.parameters))
|
125
|
+
print(tabulate(table, headers='keys', tablefmt='presto'))
|
126
|
+
print(f"\n Message format of a negative response:")
|
127
|
+
resp.print_message_format(
|
128
|
+
indent=0,
|
129
|
+
allow_unknown_lengths=allow_unknown_bit_lengths,
|
130
|
+
plumbing_output=plumbing_output)
|
74
131
|
|
75
|
-
|
76
|
-
resp.print_message_format(indent=3, allow_unknown_lengths=allow_unknown_bit_lengths)
|
132
|
+
print("\n")
|
77
133
|
|
78
134
|
if (service.positive_responses and
|
79
135
|
len(service.positive_responses) > 1) or (service.negative_responses and
|
@@ -81,3 +137,132 @@ def print_diagnostic_service(
|
|
81
137
|
# Does this ever happen?
|
82
138
|
raise NotImplementedError(
|
83
139
|
f"The diagnostic service {service.odx_id} offers more than one response!")
|
140
|
+
|
141
|
+
|
142
|
+
def extract_service_tabulation_data(services: List[DiagService]) -> Dict[str, Any]:
|
143
|
+
# extracts data of diagnostic services into Dictionary which can be printed by tabulate module
|
144
|
+
# TODO: consider indentation
|
145
|
+
|
146
|
+
name = []
|
147
|
+
semantic = []
|
148
|
+
request: List[Optional[str]] = []
|
149
|
+
|
150
|
+
for service in services:
|
151
|
+
name.append(service.short_name)
|
152
|
+
semantic.append(service.semantic)
|
153
|
+
|
154
|
+
if service.request and not service.request.required_parameters:
|
155
|
+
request.append(f"0x{str(s.hex().upper())[:32]}...") if len(
|
156
|
+
s := service()) > 32 else request.append(f"0x{str(s.hex().upper())}")
|
157
|
+
else:
|
158
|
+
request.append(None)
|
159
|
+
|
160
|
+
return {'Name': name, 'Semantic': semantic, 'Hex-Request': request}
|
161
|
+
|
162
|
+
|
163
|
+
def extract_parameter_tabulation_data(parameters: List[Parameter]) -> Dict[str, Any]:
|
164
|
+
# extracts data of parameters of diagnostic services into Dictionary which can be printed by tabulate module
|
165
|
+
# TODO: consider indentation
|
166
|
+
|
167
|
+
name = []
|
168
|
+
byte = []
|
169
|
+
bit_length: List[Optional[int]] = []
|
170
|
+
semantic = []
|
171
|
+
param_type = []
|
172
|
+
value: List[Optional[str]] = []
|
173
|
+
value_type: List[Optional[str]] = []
|
174
|
+
data_type: List[Optional[str]] = []
|
175
|
+
dop: List[Optional[str]] = []
|
176
|
+
|
177
|
+
for param in parameters:
|
178
|
+
name.append(param.short_name)
|
179
|
+
byte.append(param.byte_position)
|
180
|
+
semantic.append(param.semantic)
|
181
|
+
param_type.append(param.parameter_type)
|
182
|
+
if param.get_static_bit_length() is not None:
|
183
|
+
bit_length.append(param.get_static_bit_length())
|
184
|
+
length = (param.get_static_bit_length() or 0) // 4
|
185
|
+
else:
|
186
|
+
bit_length.append(None)
|
187
|
+
if isinstance(param, CodedConstParameter):
|
188
|
+
if isinstance(param.coded_value, int):
|
189
|
+
value.append(f"0x{param.coded_value:0{length}X}")
|
190
|
+
elif isinstance(param.coded_value, bytes) or isinstance(param.coded_value, bytearray):
|
191
|
+
value.append(f"0x{param.coded_value.hex().upper()}")
|
192
|
+
else:
|
193
|
+
value.append(f"{param.coded_value!r}")
|
194
|
+
data_type.append(param.diag_coded_type.base_data_type.name)
|
195
|
+
value_type.append('coded value')
|
196
|
+
dop.append(None)
|
197
|
+
elif isinstance(param, NrcConstParameter):
|
198
|
+
data_type.append(param.diag_coded_type.base_data_type.name)
|
199
|
+
value.append(str(param.coded_values))
|
200
|
+
value_type.append('coded values')
|
201
|
+
dop.append(None)
|
202
|
+
elif isinstance(param, (PhysicalConstantParameter, SystemParameter, ValueParameter)):
|
203
|
+
dop.append(param.dop.short_name)
|
204
|
+
if (tmp := getattr(param, "physical_type", None)) is not None:
|
205
|
+
data_type.append(tmp.base_data_type.name)
|
206
|
+
else:
|
207
|
+
data_type.append(None)
|
208
|
+
if isinstance(param, PhysicalConstantParameter):
|
209
|
+
if isinstance(param.physical_constant_value, bytes) or isinstance(
|
210
|
+
param.physical_constant_value, bytearray):
|
211
|
+
value.append(f"0x{param.physical_constant_value.hex().upper()}")
|
212
|
+
else:
|
213
|
+
value.append(f"{param.physical_constant_value!r}")
|
214
|
+
value_type.append('constant value')
|
215
|
+
elif isinstance(param, ValueParameter) and param.physical_default_value is not None:
|
216
|
+
if isinstance(param.physical_default_value, bytes) or isinstance(
|
217
|
+
param.physical_default_value, bytearray):
|
218
|
+
value.append(f"0x{param.physical_default_value.hex().upper()}")
|
219
|
+
else:
|
220
|
+
value.append(f"{param.physical_default_value!r}")
|
221
|
+
value_type.append('default value')
|
222
|
+
else:
|
223
|
+
value.append(None)
|
224
|
+
value_type.append(None)
|
225
|
+
else:
|
226
|
+
value.append(None)
|
227
|
+
data_type.append(None)
|
228
|
+
value_type.append(None)
|
229
|
+
dop.append(None)
|
230
|
+
|
231
|
+
return {
|
232
|
+
'Name': name,
|
233
|
+
'Byte Position': byte,
|
234
|
+
'Bit Length': bit_length,
|
235
|
+
'Semantic': semantic,
|
236
|
+
'Parameter Type': param_type,
|
237
|
+
'Data Type': data_type,
|
238
|
+
'Value': value,
|
239
|
+
'Value Description': value_type,
|
240
|
+
'Linked DOP': dop
|
241
|
+
}
|
242
|
+
|
243
|
+
|
244
|
+
def print_dl_metrics(variants: List[DiagLayer]) -> None:
|
245
|
+
|
246
|
+
name = []
|
247
|
+
type = []
|
248
|
+
num_services = []
|
249
|
+
num_dops = []
|
250
|
+
num_comparams = []
|
251
|
+
for variant in variants:
|
252
|
+
assert isinstance(variant, DiagLayer)
|
253
|
+
all_services: List[Union[DiagService, SingleEcuJob]] = sorted(
|
254
|
+
variant.services, key=lambda x: x.short_name)
|
255
|
+
name.append(variant.short_name)
|
256
|
+
type.append(variant.variant_type.value)
|
257
|
+
num_services.append(len(all_services))
|
258
|
+
num_dops.append(len(variant.diag_data_dictionary_spec.data_object_props))
|
259
|
+
num_comparams.append(len(variant.comparams))
|
260
|
+
|
261
|
+
table = {
|
262
|
+
'Name': name,
|
263
|
+
'Variant Type': type,
|
264
|
+
'Number of Services': num_services,
|
265
|
+
'Number of DOPs': num_dops,
|
266
|
+
'Number of communication parameters': num_comparams
|
267
|
+
}
|
268
|
+
print(tabulate(table, headers='keys', tablefmt='presto'))
|