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.
- odxtools/__init__.py +6 -4
- odxtools/additionalaudience.py +3 -5
- odxtools/admindata.py +5 -7
- odxtools/audience.py +10 -13
- odxtools/basecomparam.py +3 -5
- odxtools/basicstructure.py +55 -240
- odxtools/cli/_parser_utils.py +1 -1
- odxtools/cli/_print_utils.py +168 -134
- odxtools/cli/browse.py +111 -92
- odxtools/cli/compare.py +90 -71
- odxtools/cli/list.py +24 -15
- odxtools/cli/snoop.py +28 -5
- odxtools/codec.py +211 -0
- odxtools/commrelation.py +122 -0
- odxtools/companydata.py +5 -7
- odxtools/companydocinfo.py +7 -8
- odxtools/companyrevisioninfo.py +3 -5
- odxtools/companyspecificinfo.py +8 -9
- odxtools/comparam.py +4 -6
- odxtools/comparaminstance.py +7 -9
- odxtools/comparamspec.py +16 -54
- odxtools/comparamsubset.py +22 -62
- odxtools/complexcomparam.py +5 -7
- odxtools/compumethods/compucodecompumethod.py +63 -0
- odxtools/compumethods/compuconst.py +31 -0
- odxtools/compumethods/compudefaultvalue.py +27 -0
- odxtools/compumethods/compuinternaltophys.py +56 -0
- odxtools/compumethods/compuinversevalue.py +7 -0
- odxtools/compumethods/compumethod.py +93 -12
- odxtools/compumethods/compuphystointernal.py +56 -0
- odxtools/compumethods/compurationalcoeffs.py +20 -9
- odxtools/compumethods/compuscale.py +30 -35
- odxtools/compumethods/createanycompumethod.py +28 -161
- odxtools/compumethods/identicalcompumethod.py +31 -6
- odxtools/compumethods/linearcompumethod.py +69 -189
- odxtools/compumethods/linearsegment.py +190 -0
- odxtools/compumethods/ratfunccompumethod.py +106 -0
- odxtools/compumethods/ratfuncsegment.py +87 -0
- odxtools/compumethods/scalelinearcompumethod.py +132 -26
- odxtools/compumethods/scaleratfunccompumethod.py +113 -0
- odxtools/compumethods/tabintpcompumethod.py +119 -99
- odxtools/compumethods/texttablecompumethod.py +107 -43
- odxtools/createanydiagcodedtype.py +10 -67
- odxtools/database.py +167 -87
- odxtools/dataobjectproperty.py +15 -25
- odxtools/decodestate.py +9 -15
- odxtools/description.py +47 -0
- odxtools/determinenumberofitems.py +4 -5
- odxtools/diagcodedtype.py +36 -106
- odxtools/diagcomm.py +24 -12
- odxtools/diagdatadictionaryspec.py +33 -34
- odxtools/diaglayercontainer.py +46 -54
- odxtools/diaglayers/basevariant.py +128 -0
- odxtools/diaglayers/basevariantraw.py +123 -0
- odxtools/diaglayers/diaglayer.py +432 -0
- odxtools/{diaglayerraw.py → diaglayers/diaglayerraw.py} +105 -120
- odxtools/diaglayers/ecushareddata.py +96 -0
- odxtools/diaglayers/ecushareddataraw.py +87 -0
- odxtools/diaglayers/ecuvariant.py +124 -0
- odxtools/diaglayers/ecuvariantraw.py +129 -0
- odxtools/diaglayers/functionalgroup.py +110 -0
- odxtools/diaglayers/functionalgroupraw.py +106 -0
- odxtools/{diaglayer.py → diaglayers/hierarchyelement.py} +209 -448
- odxtools/diaglayers/hierarchyelementraw.py +58 -0
- odxtools/diaglayers/protocol.py +64 -0
- odxtools/diaglayers/protocolraw.py +91 -0
- odxtools/diagnostictroublecode.py +8 -9
- odxtools/diagservice.py +56 -43
- odxtools/diagvariable.py +113 -0
- odxtools/docrevision.py +5 -7
- odxtools/dopbase.py +15 -17
- odxtools/dtcdop.py +168 -50
- odxtools/dynamicendmarkerfield.py +134 -0
- odxtools/dynamiclengthfield.py +41 -37
- odxtools/dyndefinedspec.py +177 -0
- odxtools/dynenddopref.py +38 -0
- odxtools/ecuvariantmatcher.py +6 -7
- odxtools/element.py +13 -15
- odxtools/encodestate.py +199 -22
- odxtools/endofpdufield.py +31 -18
- odxtools/environmentdata.py +8 -1
- odxtools/environmentdatadescription.py +198 -38
- odxtools/exceptions.py +11 -2
- odxtools/field.py +10 -10
- odxtools/functionalclass.py +3 -5
- odxtools/inputparam.py +3 -12
- odxtools/leadinglengthinfotype.py +37 -18
- odxtools/library.py +66 -0
- odxtools/loadfile.py +64 -0
- odxtools/matchingparameter.py +3 -3
- odxtools/message.py +0 -7
- odxtools/minmaxlengthtype.py +61 -33
- odxtools/modification.py +3 -5
- odxtools/multiplexer.py +128 -73
- odxtools/multiplexercase.py +13 -14
- odxtools/multiplexerdefaultcase.py +15 -12
- odxtools/multiplexerswitchkey.py +4 -5
- odxtools/nameditemlist.py +29 -5
- odxtools/negoutputparam.py +3 -5
- odxtools/odxcategory.py +83 -0
- odxtools/odxlink.py +60 -51
- odxtools/odxtypes.py +37 -5
- odxtools/outputparam.py +4 -15
- odxtools/parameterinfo.py +218 -67
- odxtools/parameters/codedconstparameter.py +16 -24
- odxtools/parameters/dynamicparameter.py +5 -4
- odxtools/parameters/lengthkeyparameter.py +60 -26
- odxtools/parameters/matchingrequestparameter.py +23 -11
- odxtools/parameters/nrcconstparameter.py +45 -46
- odxtools/parameters/parameter.py +54 -56
- odxtools/parameters/parameterwithdop.py +15 -25
- odxtools/parameters/physicalconstantparameter.py +15 -18
- odxtools/parameters/reservedparameter.py +6 -2
- odxtools/parameters/systemparameter.py +55 -11
- odxtools/parameters/tableentryparameter.py +3 -2
- odxtools/parameters/tablekeyparameter.py +103 -49
- odxtools/parameters/tablestructparameter.py +47 -48
- odxtools/parameters/valueparameter.py +16 -20
- odxtools/paramlengthinfotype.py +52 -32
- odxtools/parentref.py +16 -2
- odxtools/physicaldimension.py +3 -8
- odxtools/progcode.py +26 -11
- odxtools/protstack.py +3 -5
- odxtools/py.typed +0 -0
- odxtools/relateddoc.py +7 -9
- odxtools/request.py +120 -10
- odxtools/response.py +123 -23
- odxtools/scaleconstr.py +3 -3
- odxtools/servicebinner.py +1 -1
- odxtools/singleecujob.py +12 -10
- odxtools/snrefcontext.py +29 -0
- odxtools/specialdata.py +3 -5
- odxtools/specialdatagroup.py +7 -9
- odxtools/specialdatagroupcaption.py +3 -6
- odxtools/standardlengthtype.py +80 -14
- odxtools/state.py +3 -5
- odxtools/statechart.py +13 -19
- odxtools/statetransition.py +7 -17
- odxtools/staticfield.py +31 -25
- odxtools/subcomponent.py +288 -0
- odxtools/swvariable.py +21 -0
- odxtools/table.py +7 -8
- odxtools/tablerow.py +19 -11
- odxtools/teammember.py +3 -5
- odxtools/templates/comparam-spec.odx-c.xml.jinja2 +4 -24
- odxtools/templates/comparam-subset.odx-cs.xml.jinja2 +5 -26
- odxtools/templates/diag_layer_container.odx-d.xml.jinja2 +15 -31
- odxtools/templates/{index.xml.xml.jinja2 → index.xml.jinja2} +1 -1
- odxtools/templates/macros/printAudience.xml.jinja2 +1 -1
- odxtools/templates/macros/printBaseVariant.xml.jinja2 +53 -0
- odxtools/templates/macros/printCompanyData.xml.jinja2 +4 -7
- odxtools/templates/macros/printComparam.xml.jinja2 +6 -4
- odxtools/templates/macros/printComparamRef.xml.jinja2 +5 -12
- odxtools/templates/macros/printCompuMethod.xml.jinja2 +147 -0
- odxtools/templates/macros/printDOP.xml.jinja2 +27 -133
- odxtools/templates/macros/printDescription.xml.jinja2 +18 -0
- odxtools/templates/macros/printDiagComm.xml.jinja2 +1 -1
- odxtools/templates/macros/printDiagLayer.xml.jinja2 +222 -0
- odxtools/templates/macros/printDiagVariable.xml.jinja2 +66 -0
- odxtools/templates/macros/printDynDefinedSpec.xml.jinja2 +48 -0
- odxtools/templates/macros/printDynamicEndmarkerField.xml.jinja2 +16 -0
- odxtools/templates/macros/printDynamicLengthField.xml.jinja2 +1 -1
- odxtools/templates/macros/printEcuSharedData.xml.jinja2 +30 -0
- odxtools/templates/macros/printEcuVariant.xml.jinja2 +53 -0
- odxtools/templates/macros/printEcuVariantPattern.xml.jinja2 +1 -1
- odxtools/templates/macros/printElementId.xml.jinja2 +8 -3
- odxtools/templates/macros/printEndOfPdu.xml.jinja2 +1 -1
- odxtools/templates/macros/printEnvDataDesc.xml.jinja2 +1 -1
- odxtools/templates/macros/printFunctionalClass.xml.jinja2 +1 -1
- odxtools/templates/macros/printFunctionalGroup.xml.jinja2 +40 -0
- odxtools/templates/macros/printHierarchyElement.xml.jinja2 +24 -0
- odxtools/templates/macros/printLibrary.xml.jinja2 +21 -0
- odxtools/templates/macros/printMux.xml.jinja2 +4 -3
- odxtools/templates/macros/printOdxCategory.xml.jinja2 +28 -0
- odxtools/templates/macros/printParam.xml.jinja2 +11 -12
- odxtools/templates/macros/printProtStack.xml.jinja2 +1 -1
- odxtools/templates/macros/printProtocol.xml.jinja2 +30 -0
- odxtools/templates/macros/printRequest.xml.jinja2 +1 -1
- odxtools/templates/macros/printResponse.xml.jinja2 +1 -1
- odxtools/templates/macros/printService.xml.jinja2 +3 -2
- odxtools/templates/macros/printSingleEcuJob.xml.jinja2 +5 -26
- odxtools/templates/macros/printSpecialData.xml.jinja2 +1 -1
- odxtools/templates/macros/printState.xml.jinja2 +1 -1
- odxtools/templates/macros/printStateChart.xml.jinja2 +1 -1
- odxtools/templates/macros/printStateTransition.xml.jinja2 +1 -1
- odxtools/templates/macros/printStaticField.xml.jinja2 +1 -1
- odxtools/templates/macros/printStructure.xml.jinja2 +1 -1
- odxtools/templates/macros/printSubComponent.xml.jinja2 +104 -0
- odxtools/templates/macros/printTable.xml.jinja2 +4 -5
- odxtools/templates/macros/printUnitSpec.xml.jinja2 +3 -5
- odxtools/uds.py +2 -10
- odxtools/unit.py +4 -8
- odxtools/unitgroup.py +3 -5
- odxtools/unitspec.py +17 -17
- odxtools/utils.py +38 -20
- odxtools/variablegroup.py +32 -0
- odxtools/version.py +2 -2
- odxtools/{write_pdx_file.py → writepdxfile.py} +20 -10
- odxtools/xdoc.py +3 -5
- {odxtools-6.7.0.dist-info → odxtools-9.3.0.dist-info}/METADATA +20 -21
- odxtools-9.3.0.dist-info/RECORD +228 -0
- {odxtools-6.7.0.dist-info → odxtools-9.3.0.dist-info}/WHEEL +1 -1
- odxtools/createcompanydatas.py +0 -17
- odxtools/createsdgs.py +0 -19
- odxtools/load_file.py +0 -13
- odxtools/load_odx_d_file.py +0 -6
- odxtools/load_pdx_file.py +0 -8
- odxtools/templates/macros/printVariant.xml.jinja2 +0 -216
- odxtools-6.7.0.dist-info/RECORD +0 -182
- /odxtools/{diaglayertype.py → diaglayers/diaglayertype.py} +0 -0
- {odxtools-6.7.0.dist-info → odxtools-9.3.0.dist-info}/LICENSE +0 -0
- {odxtools-6.7.0.dist-info → odxtools-9.3.0.dist-info}/entry_points.txt +0 -0
- {odxtools-6.7.0.dist-info → odxtools-9.3.0.dist-info}/top_level.txt +0 -0
odxtools/cli/browse.py
CHANGED
@@ -5,13 +5,16 @@ import sys
|
|
5
5
|
from typing import List, Optional, Union, cast
|
6
6
|
|
7
7
|
import InquirerPy.prompt as IP_prompt
|
8
|
-
from tabulate import tabulate # TODO: switch to rich tables
|
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
|
@@ -110,23 +113,44 @@ def prompt_single_parameter_value(parameter: Parameter) -> Optional[AtomicOdxTyp
|
|
110
113
|
return cast(str, answer.get(parameter.short_name))
|
111
114
|
|
112
115
|
|
113
|
-
def encode_message_interactively(
|
116
|
+
def encode_message_interactively(codec: Union[Request, Response],
|
114
117
|
ask_user_confirmation: bool = False) -> None:
|
115
|
-
if
|
118
|
+
if sys.__stdin__ is None or sys.__stdout__ is None or not sys.__stdin__.isatty(
|
119
|
+
) or not sys.__stdout__.isatty():
|
116
120
|
raise SystemError("This command can only be used in an interactive shell!")
|
117
|
-
param_dict = sub_service.parameter_dict()
|
118
121
|
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
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
|
127
151
|
|
128
152
|
param_values: ParameterValueDict = {}
|
129
|
-
if
|
153
|
+
if has_settable_param:
|
130
154
|
# Ask whether user wants to encode a message
|
131
155
|
if ask_user_confirmation:
|
132
156
|
encode_message_prompt = [{
|
@@ -139,48 +163,40 @@ def encode_message_interactively(sub_service: Union[Request, Response],
|
|
139
163
|
if answer.get("yes_no_prompt") == "no":
|
140
164
|
return
|
141
165
|
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
f"What is the request you want to answer? (Enter the coded request as integer in hexadecimal format (e.g. 12 3B 5)",
|
151
|
-
"filter":
|
152
|
-
lambda input: _convert_string_to_bytes(input),
|
153
|
-
}]
|
154
|
-
answer = IP_prompt(answered_request_prompt)
|
155
|
-
answered_request = cast(bytes, answer.get("request"))
|
156
|
-
print(f"Input interpretation as list: {list(answered_request)}")
|
157
|
-
|
158
|
-
# Request values for parameters
|
159
|
-
for key, param_or_structure in param_dict.items():
|
160
|
-
if isinstance(param_or_structure, dict):
|
161
|
-
# 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
|
162
174
|
print(
|
163
|
-
f"The next {len(
|
175
|
+
f"The next {len(inner_params)} parameters belong to the structure '{dop.short_name}'"
|
164
176
|
)
|
165
177
|
structure_param_values: ParameterValueDict = {}
|
166
|
-
for
|
167
|
-
if
|
168
|
-
val = prompt_single_parameter_value(
|
178
|
+
for inner_param in inner_params:
|
179
|
+
if inner_param.is_settable:
|
180
|
+
val = prompt_single_parameter_value(inner_param)
|
169
181
|
if val is not None:
|
170
|
-
structure_param_values[
|
171
|
-
param_values[
|
172
|
-
elif
|
173
|
-
|
174
|
-
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)
|
175
186
|
if val is not None:
|
176
|
-
param_values[
|
177
|
-
|
178
|
-
|
187
|
+
param_values[param.short_name] = val
|
188
|
+
|
189
|
+
if isinstance(codec, Response):
|
190
|
+
payload = codec.encode(coded_request=answered_request, **param_values)
|
179
191
|
else:
|
180
|
-
payload =
|
192
|
+
payload = codec.encode(coded_request=b'', **param_values)
|
181
193
|
else:
|
182
|
-
# There are no
|
183
|
-
|
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
|
+
|
184
200
|
print(f"Message payload: 0x{bytes(payload).hex()}")
|
185
201
|
|
186
202
|
|
@@ -191,56 +207,58 @@ def encode_message_from_string_values(
|
|
191
207
|
if parameter_values is None:
|
192
208
|
parameter_values = {}
|
193
209
|
parameter_values = parameter_values.copy()
|
194
|
-
param_dict = sub_service.parameter_dict()
|
195
210
|
|
196
|
-
# Check if all needed parameters
|
211
|
+
# Check if all needed parameters have been specified
|
197
212
|
missing_parameter_names = []
|
198
|
-
for
|
199
|
-
if
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
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}")
|
208
224
|
else:
|
209
|
-
if param.is_required and parameter_values.get(
|
210
|
-
missing_parameter_names.append(
|
225
|
+
if param.is_required and parameter_values.get(param.short_name) is None:
|
226
|
+
missing_parameter_names.append(param.short_name)
|
211
227
|
|
212
228
|
if len(missing_parameter_names) > 0:
|
213
|
-
print("The following parameters are required but missing
|
214
|
-
print(" - " + "\n - ".join(missing_parameter_names))
|
229
|
+
print("The following parameters are required but missing:")
|
230
|
+
print(" - " + "\n - ".join(sorted(missing_parameter_names)))
|
215
231
|
return
|
216
232
|
|
217
233
|
# Request values for parameters
|
218
234
|
for parameter_sn, parameter_value in parameter_values.items():
|
219
|
-
parameter =
|
235
|
+
parameter = resolve_snref(parameter_sn, sub_service.parameters, Parameter)
|
220
236
|
if parameter is None:
|
221
237
|
print(f"I don't know the parameter {parameter_sn}")
|
222
238
|
continue
|
223
239
|
|
224
240
|
if isinstance(parameter_value, dict):
|
225
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
|
+
|
226
248
|
typed_dict = parameter_value.copy()
|
227
|
-
for
|
228
|
-
|
229
|
-
if
|
230
|
-
print(f"Unknown sub-parameter {
|
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}")
|
231
253
|
continue
|
232
|
-
if not isinstance(
|
233
|
-
print(f"The value specified for parameter {
|
254
|
+
if not isinstance(inner_param_value, str):
|
255
|
+
print(f"The value specified for parameter {inner_param_sn} is not a string")
|
234
256
|
continue
|
235
257
|
|
236
|
-
typed_dict[
|
237
|
-
|
238
|
-
|
239
|
-
)
|
240
|
-
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
|
241
261
|
else:
|
242
|
-
assert isinstance(parameter, Parameter)
|
243
|
-
|
244
262
|
if not isinstance(parameter_value, str):
|
245
263
|
print(f"Value for parameter {parameter_sn} is not a string")
|
246
264
|
continue
|
@@ -260,7 +278,8 @@ def encode_message_from_string_values(
|
|
260
278
|
|
261
279
|
|
262
280
|
def browse(odxdb: Database) -> None:
|
263
|
-
if
|
281
|
+
if sys.__stdin__ is None or sys.__stdout__ is None or not sys.__stdin__.isatty(
|
282
|
+
) or not sys.__stdout__.isatty():
|
264
283
|
raise SystemError("This command can only be used in an interactive shell!")
|
265
284
|
dl_names = [dl.short_name for dl in odxdb.diag_layers]
|
266
285
|
while True:
|
@@ -281,19 +300,20 @@ def browse(odxdb: Database) -> None:
|
|
281
300
|
print(f"{type(answer.get('variant'))=}")
|
282
301
|
assert isinstance(variant, DiagLayer)
|
283
302
|
|
284
|
-
if (
|
285
|
-
|
286
|
-
|
287
|
-
|
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"
|
288
308
|
|
289
|
-
|
290
|
-
|
291
|
-
|
292
|
-
|
309
|
+
if (tx_id := variant.get_send_id()) is not None:
|
310
|
+
send_id = hex(tx_id)
|
311
|
+
else:
|
312
|
+
send_id = "None"
|
293
313
|
|
294
|
-
|
295
|
-
|
296
|
-
|
314
|
+
print(
|
315
|
+
f"{variant.variant_type.value} '{variant.short_name}' (Receive ID: {recv_id}, Send ID: {send_id})"
|
316
|
+
)
|
297
317
|
|
298
318
|
while True:
|
299
319
|
services: List[DiagService] = [
|
@@ -352,8 +372,7 @@ def browse(odxdb: Database) -> None:
|
|
352
372
|
if codec is not None:
|
353
373
|
assert isinstance(codec, (Request, Response))
|
354
374
|
table = extract_parameter_tabulation_data(codec.parameters)
|
355
|
-
|
356
|
-
print(table_str)
|
375
|
+
print(table)
|
357
376
|
|
358
377
|
encode_message_interactively(codec, ask_user_confirmation=True)
|
359
378
|
|
odxtools/cli/compare.py
CHANGED
@@ -5,13 +5,14 @@ import argparse
|
|
5
5
|
import os
|
6
6
|
from typing import Any, Dict, List, Optional, Set, Union, cast
|
7
7
|
|
8
|
-
from rich import print
|
9
|
-
from
|
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 ..
|
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
|
@@ -46,11 +47,14 @@ class Display:
|
|
46
47
|
# class with variables and functions to display the result of the comparison
|
47
48
|
|
48
49
|
# TODO
|
49
|
-
# Idea: results as json export
|
50
|
-
#
|
51
|
-
#
|
52
|
-
|
53
|
-
#
|
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
|
54
58
|
param_detailed: bool
|
55
59
|
obj_detailed: bool
|
56
60
|
|
@@ -62,46 +66,45 @@ class Display:
|
|
62
66
|
if service_dict["new_services"] or service_dict["deleted_services"] or service_dict[
|
63
67
|
"changed_name_of_service"][0] or service_dict["changed_parameters_of_service"][0]:
|
64
68
|
assert isinstance(service_dict["diag_layer"], str)
|
65
|
-
|
66
|
-
|
69
|
+
rich_print()
|
70
|
+
rich_print(
|
67
71
|
f"Changed diagnostic services for diagnostic layer '{service_dict['diag_layer']}' ({service_dict['diag_layer_type']}):"
|
68
72
|
)
|
69
73
|
if service_dict["new_services"]:
|
70
74
|
assert isinstance(service_dict["new_services"], List)
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
service_dict["new_services"]) # type: ignore[arg-type]
|
75
|
-
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]
|
76
79
|
if service_dict["deleted_services"]:
|
77
80
|
assert isinstance(service_dict["deleted_services"], List)
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
service_dict["deleted_services"]) # type: ignore[arg-type]
|
82
|
-
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]
|
83
85
|
if service_dict["changed_name_of_service"][0]:
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
service_dict["changed_name_of_service"][0]) # type: ignore[arg-type]
|
88
|
-
table["Old service name"] = service_dict["changed_name_of_service"][1]
|
89
|
-
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]
|
90
90
|
if service_dict["changed_parameters_of_service"][0]:
|
91
|
-
|
92
|
-
|
91
|
+
rich_print()
|
92
|
+
rich_print(" [blue]Services with parameter changes[/blue]")
|
93
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
|
+
]
|
94
98
|
table = extract_service_tabulation_data(
|
95
|
-
service_dict["changed_parameters_of_service"][0]
|
96
|
-
|
97
|
-
table
|
98
|
-
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)
|
99
102
|
|
100
103
|
for service_idx, service in enumerate(
|
101
104
|
service_dict["changed_parameters_of_service"][0]): # type: ignore[arg-type]
|
102
105
|
assert isinstance(service, DiagService)
|
103
|
-
|
104
|
-
|
106
|
+
rich_print()
|
107
|
+
rich_print(
|
105
108
|
f" Detailed changes of diagnostic service [u cyan]{service.short_name}[/u cyan]"
|
106
109
|
)
|
107
110
|
# detailed_info in [infotext1, dict1, infotext2, dict2, ...]
|
@@ -110,10 +113,22 @@ class Display:
|
|
110
113
|
service_dict["changed_parameters_of_service"])[2][service_idx]
|
111
114
|
for detailed_info in info_list:
|
112
115
|
if isinstance(detailed_info, str):
|
113
|
-
|
114
|
-
|
116
|
+
rich_print()
|
117
|
+
rich_print(detailed_info)
|
115
118
|
elif isinstance(detailed_info, dict):
|
116
|
-
|
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()
|
117
132
|
if self.param_detailed:
|
118
133
|
# print all parameter details of diagnostic service
|
119
134
|
print_service_parameters(service, allow_unknown_bit_lengths=True)
|
@@ -124,16 +139,18 @@ class Display:
|
|
124
139
|
# diagnostic layers
|
125
140
|
if changes_variants["new_diagnostic_layers"] or changes_variants[
|
126
141
|
"deleted_diagnostic_layers"]:
|
127
|
-
|
128
|
-
|
129
|
-
|
142
|
+
rich_print()
|
143
|
+
rich_print("[bright_blue]Changed diagnostic layers[/bright_blue]: ")
|
144
|
+
rich_print(" New diagnostic layers: ")
|
130
145
|
for variant in changes_variants["new_diagnostic_layers"]:
|
131
146
|
assert isinstance(variant, DiagLayer)
|
132
|
-
|
133
|
-
|
147
|
+
rich_print(
|
148
|
+
f" [magenta]{variant.short_name}[/magenta] ({variant.variant_type.value})")
|
149
|
+
rich_print(" Deleted diagnostic layers: ")
|
134
150
|
for variant in changes_variants["deleted_diagnostic_layers"]:
|
135
151
|
assert isinstance(variant, DiagLayer)
|
136
|
-
|
152
|
+
rich_print(
|
153
|
+
f" [magenta]{variant.short_name}[/magenta] ({variant.variant_type.value})")
|
137
154
|
|
138
155
|
# diagnostic services
|
139
156
|
for _, value in changes_variants.items():
|
@@ -275,8 +292,8 @@ class Comparison(Display):
|
|
275
292
|
if res1_idx == res2_idx:
|
276
293
|
# find changed request parameter properties
|
277
294
|
table = self.compare_parameters(param1, param2)
|
278
|
-
infotext = (f" Properties of request parameter '{param2.short_name}'"
|
279
|
-
f" have changed
|
295
|
+
infotext = (f" Properties of request parameter '{param2.short_name}' "
|
296
|
+
f"that have changed:\n")
|
280
297
|
# array index starts with 0 -> param[0] is 1. service parameter
|
281
298
|
|
282
299
|
if table["Property"]:
|
@@ -311,8 +328,8 @@ class Comparison(Display):
|
|
311
328
|
# find changed positive response parameter properties
|
312
329
|
table = self.compare_parameters(param1, param2)
|
313
330
|
infotext = (
|
314
|
-
f" Properties of positive response parameter '{param2.short_name}'"
|
315
|
-
f"have changed
|
331
|
+
f" Properties of positive response parameter '{param2.short_name}' that "
|
332
|
+
f"have changed:\n")
|
316
333
|
# array index starts with 0 -> param[0] is first service parameter
|
317
334
|
|
318
335
|
if table["Property"]:
|
@@ -354,7 +371,7 @@ class Comparison(Display):
|
|
354
371
|
if param1_idx == param2_idx:
|
355
372
|
# find changed negative response parameter properties
|
356
373
|
table = self.compare_parameters(param1, param2)
|
357
|
-
infotext = f" Properties of response parameter '{param2.short_name}' have changed
|
374
|
+
infotext = f" Properties of response parameter '{param2.short_name}' that have changed:\n"
|
358
375
|
# array index starts with 0 -> param[0] is 1. service parameter
|
359
376
|
|
360
377
|
if table["Property"]:
|
@@ -622,7 +639,7 @@ def run(args: argparse.Namespace) -> None:
|
|
622
639
|
|
623
640
|
for name in args.variants:
|
624
641
|
if name not in task.diagnostic_layer_names:
|
625
|
-
|
642
|
+
rich_print(f"The variant '{name}' could not be found!")
|
626
643
|
return
|
627
644
|
|
628
645
|
task.db_indicator_1 = 0
|
@@ -632,19 +649,20 @@ def run(args: argparse.Namespace) -> None:
|
|
632
649
|
break
|
633
650
|
task.db_indicator_2 = db_idx + 1
|
634
651
|
|
635
|
-
|
636
|
-
|
637
|
-
|
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])}')")
|
638
655
|
|
639
|
-
|
640
|
-
|
656
|
+
rich_print()
|
657
|
+
rich_print(f"Overview of diagnostic layers (for {os.path.basename(db_names[0])})")
|
641
658
|
print_dl_metrics([
|
642
659
|
variant for variant in task.databases[0].diag_layers
|
643
660
|
if variant.short_name in task.diagnostic_layer_names
|
644
661
|
])
|
645
662
|
|
646
|
-
|
647
|
-
|
663
|
+
rich_print()
|
664
|
+
rich_print(
|
665
|
+
f"Overview of diagnostic layers (for {os.path.basename(db_names[db_idx+1])})")
|
648
666
|
print_dl_metrics([
|
649
667
|
variant for variant in task.databases[db_idx + 1].diag_layers
|
650
668
|
if variant.short_name in task.diagnostic_layer_names
|
@@ -673,16 +691,17 @@ def run(args: argparse.Namespace) -> None:
|
|
673
691
|
break
|
674
692
|
task.db_indicator_2 = db_idx + 1
|
675
693
|
|
676
|
-
|
677
|
-
|
678
|
-
|
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])}')")
|
679
697
|
|
680
|
-
|
681
|
-
|
698
|
+
rich_print()
|
699
|
+
rich_print(f"Overview of diagnostic layers (for {os.path.basename(db_names[0])})")
|
682
700
|
print_dl_metrics(list(task.databases[0].diag_layers))
|
683
701
|
|
684
|
-
|
685
|
-
|
702
|
+
rich_print()
|
703
|
+
rich_print(
|
704
|
+
f"Overview of diagnostic layers (for {os.path.basename(db_names[db_idx+1])})")
|
686
705
|
print_dl_metrics(list(task.databases[db_idx + 1].diag_layers))
|
687
706
|
|
688
707
|
task.print_database_changes(
|
@@ -704,20 +723,20 @@ def run(args: argparse.Namespace) -> None:
|
|
704
723
|
|
705
724
|
for name in args.variants:
|
706
725
|
if name not in task.diagnostic_layer_names:
|
707
|
-
|
726
|
+
rich_print(f"The variant '{name}' could not be found!")
|
708
727
|
return
|
709
728
|
|
710
|
-
|
711
|
-
|
729
|
+
rich_print()
|
730
|
+
rich_print(f"Overview of diagnostic layers: ")
|
712
731
|
print_dl_metrics(task.diagnostic_layers)
|
713
732
|
|
714
733
|
for db_idx, dl in enumerate(task.diagnostic_layers):
|
715
734
|
if db_idx + 1 >= len(task.diagnostic_layers):
|
716
735
|
break
|
717
736
|
|
718
|
-
|
719
|
-
|
720
|
-
|
737
|
+
rich_print()
|
738
|
+
rich_print(f"Changes in diagnostic layer '{dl.short_name}' ({dl.variant_type.value})")
|
739
|
+
rich_print(
|
721
740
|
f" (compared to '{task.diagnostic_layers[db_idx + 1].short_name}' ({task.diagnostic_layers[db_idx + 1].variant_type.value}))"
|
722
741
|
)
|
723
742
|
task.print_dl_changes(
|
@@ -725,4 +744,4 @@ def run(args: argparse.Namespace) -> None:
|
|
725
744
|
|
726
745
|
else:
|
727
746
|
# no databases & no variants specified
|
728
|
-
|
747
|
+
rich_print("Please specify either a database or variant for a comparison")
|