odxtools 8.3.4__py3-none-any.whl → 9.0.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 +36 -207
- odxtools/cli/_print_utils.py +163 -131
- odxtools/cli/browse.py +94 -79
- odxtools/cli/compare.py +88 -69
- odxtools/cli/list.py +2 -3
- odxtools/codec.py +211 -0
- odxtools/diaglayers/hierarchyelement.py +0 -10
- odxtools/dopbase.py +5 -3
- odxtools/dtcdop.py +101 -14
- odxtools/inputparam.py +0 -7
- odxtools/leadinglengthinfotype.py +1 -8
- odxtools/message.py +0 -7
- odxtools/minmaxlengthtype.py +4 -4
- odxtools/outputparam.py +0 -7
- odxtools/parameterinfo.py +12 -12
- odxtools/parameters/parameter.py +6 -4
- odxtools/paramlengthinfotype.py +8 -9
- odxtools/request.py +109 -16
- odxtools/response.py +115 -15
- odxtools/specialdatagroup.py +1 -1
- odxtools/templates/macros/printDOP.xml.jinja2 +16 -0
- odxtools/uds.py +0 -8
- odxtools/version.py +2 -2
- {odxtools-8.3.4.dist-info → odxtools-9.0.0.dist-info}/METADATA +7 -8
- {odxtools-8.3.4.dist-info → odxtools-9.0.0.dist-info}/RECORD +29 -28
- {odxtools-8.3.4.dist-info → odxtools-9.0.0.dist-info}/WHEEL +1 -1
- {odxtools-8.3.4.dist-info → odxtools-9.0.0.dist-info}/LICENSE +0 -0
- {odxtools-8.3.4.dist-info → odxtools-9.0.0.dist-info}/entry_points.txt +0 -0
- {odxtools-8.3.4.dist-info → odxtools-9.0.0.dist-info}/top_level.txt +0 -0
odxtools/cli/compare.py
CHANGED
@@ -5,8 +5,9 @@ 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
13
|
from ..diaglayers.diaglayer import DiagLayer
|
@@ -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")
|
odxtools/cli/list.py
CHANGED
@@ -52,7 +52,7 @@ def print_summary(odxdb: Database,
|
|
52
52
|
if diag_layers:
|
53
53
|
rich.print("\n")
|
54
54
|
rich.print(f"Overview of diagnostic layers: ")
|
55
|
-
print_dl_metrics(diag_layers
|
55
|
+
print_dl_metrics(diag_layers)
|
56
56
|
|
57
57
|
for dl in diag_layers:
|
58
58
|
rich.print("\n")
|
@@ -93,8 +93,7 @@ def print_summary(odxdb: Database,
|
|
93
93
|
print_pre_condition_states=print_pre_condition_states,
|
94
94
|
print_state_transitions=print_state_transitions,
|
95
95
|
print_audiences=print_audiences,
|
96
|
-
allow_unknown_bit_lengths=allow_unknown_bit_lengths
|
97
|
-
print_fn=rich.print)
|
96
|
+
allow_unknown_bit_lengths=allow_unknown_bit_lengths)
|
98
97
|
elif isinstance(service, SingleEcuJob):
|
99
98
|
rich.print(f" Single ECU job: {service.odx_id}")
|
100
99
|
else:
|
odxtools/codec.py
ADDED
@@ -0,0 +1,211 @@
|
|
1
|
+
# SPDX-License-Identifier: MIT
|
2
|
+
import typing
|
3
|
+
from typing import List, Optional, runtime_checkable
|
4
|
+
|
5
|
+
from .decodestate import DecodeState
|
6
|
+
from .encodestate import EncodeState
|
7
|
+
from .exceptions import EncodeError, odxraise
|
8
|
+
from .odxtypes import ParameterValue
|
9
|
+
from .parameters.codedconstparameter import CodedConstParameter
|
10
|
+
from .parameters.matchingrequestparameter import MatchingRequestParameter
|
11
|
+
from .parameters.parameter import Parameter
|
12
|
+
from .parameters.physicalconstantparameter import PhysicalConstantParameter
|
13
|
+
|
14
|
+
|
15
|
+
@runtime_checkable
|
16
|
+
class Codec(typing.Protocol):
|
17
|
+
"""Any object which can be en- or decoded to be transferred over
|
18
|
+
the wire implements this API.
|
19
|
+
"""
|
20
|
+
|
21
|
+
@property
|
22
|
+
def short_name(self) -> str:
|
23
|
+
return ""
|
24
|
+
|
25
|
+
def encode_into_pdu(self, physical_value: Optional[ParameterValue],
|
26
|
+
encode_state: EncodeState) -> None:
|
27
|
+
...
|
28
|
+
|
29
|
+
def decode_from_pdu(self, decode_state: DecodeState) -> ParameterValue:
|
30
|
+
...
|
31
|
+
|
32
|
+
def get_static_bit_length(self) -> Optional[int]:
|
33
|
+
...
|
34
|
+
|
35
|
+
|
36
|
+
@runtime_checkable
|
37
|
+
class CompositeCodec(Codec, typing.Protocol):
|
38
|
+
"""Any object which can be en- or decoded to be transferred over
|
39
|
+
the wire which is composed of multiple parameter implements this
|
40
|
+
API.
|
41
|
+
|
42
|
+
"""
|
43
|
+
|
44
|
+
@property
|
45
|
+
def parameters(self) -> List[Parameter]:
|
46
|
+
return []
|
47
|
+
|
48
|
+
@property
|
49
|
+
def required_parameters(self) -> List[Parameter]:
|
50
|
+
return []
|
51
|
+
|
52
|
+
@property
|
53
|
+
def free_parameters(self) -> List[Parameter]:
|
54
|
+
return []
|
55
|
+
|
56
|
+
|
57
|
+
# some helper functions useful for composite codec objects
|
58
|
+
def composite_codec_get_static_bit_length(codec: CompositeCodec) -> Optional[int]:
|
59
|
+
"""Compute the length of a composite codec object in bits
|
60
|
+
|
61
|
+
This is basically the sum of the lengths of all parameters. If the
|
62
|
+
length of any parameter can only determined at runtime, `None` is
|
63
|
+
returned.
|
64
|
+
"""
|
65
|
+
|
66
|
+
cursor = 0
|
67
|
+
byte_length = 0
|
68
|
+
for param in codec.parameters:
|
69
|
+
param_bit_length = param.get_static_bit_length()
|
70
|
+
if param_bit_length is None:
|
71
|
+
# We were not able to calculate a static bit length
|
72
|
+
return None
|
73
|
+
elif param.byte_position is not None:
|
74
|
+
cursor = param.byte_position
|
75
|
+
|
76
|
+
cursor += ((param.bit_position or 0) + param_bit_length + 7) // 8
|
77
|
+
byte_length = max(byte_length, cursor)
|
78
|
+
|
79
|
+
return byte_length * 8
|
80
|
+
|
81
|
+
|
82
|
+
def composite_codec_get_required_parameters(codec: CompositeCodec) -> List[Parameter]:
|
83
|
+
"""Return the list of parameters which are required to be
|
84
|
+
specified for encoding the composite codec object
|
85
|
+
|
86
|
+
I.e., all free parameters that do not exhibit a default value.
|
87
|
+
"""
|
88
|
+
return [p for p in codec.parameters if p.is_required]
|
89
|
+
|
90
|
+
|
91
|
+
def composite_codec_get_free_parameters(codec: CompositeCodec) -> List[Parameter]:
|
92
|
+
"""Return the list of parameters which can be freely specified by
|
93
|
+
the user when encoding the composite codec object
|
94
|
+
|
95
|
+
This means all required parameters plus parameters that can be
|
96
|
+
omitted because they specify a default.
|
97
|
+
"""
|
98
|
+
return [p for p in codec.parameters if p.is_settable]
|
99
|
+
|
100
|
+
|
101
|
+
def composite_codec_get_coded_const_prefix(codec: CompositeCodec,
|
102
|
+
request_prefix: bytes = b'') -> bytes:
|
103
|
+
encode_state = EncodeState(coded_message=bytearray(), triggering_request=request_prefix)
|
104
|
+
|
105
|
+
for param in codec.parameters:
|
106
|
+
if (isinstance(param, MatchingRequestParameter) and param.request_byte_position < len(request_prefix)) or \
|
107
|
+
isinstance(param, (CodedConstParameter, PhysicalConstantParameter)):
|
108
|
+
param.encode_into_pdu(physical_value=None, encode_state=encode_state)
|
109
|
+
else:
|
110
|
+
break
|
111
|
+
|
112
|
+
return encode_state.coded_message
|
113
|
+
|
114
|
+
|
115
|
+
def composite_codec_encode_into_pdu(codec: CompositeCodec, physical_value: Optional[ParameterValue],
|
116
|
+
encode_state: EncodeState) -> None:
|
117
|
+
from .parameters.lengthkeyparameter import LengthKeyParameter
|
118
|
+
from .parameters.tablekeyparameter import TableKeyParameter
|
119
|
+
|
120
|
+
if not isinstance(physical_value, dict):
|
121
|
+
odxraise(
|
122
|
+
f"Expected a dictionary for the values of {codec.short_name}, "
|
123
|
+
f"got {type(physical_value).__name__}", EncodeError)
|
124
|
+
elif encode_state.cursor_bit_position != 0:
|
125
|
+
odxraise(
|
126
|
+
f"Compositional codec objecs must be byte aligned, but "
|
127
|
+
f"{codec.short_name} requested to be at bit position "
|
128
|
+
f"{encode_state.cursor_bit_position}", EncodeError)
|
129
|
+
encode_state.bit_position = 0
|
130
|
+
|
131
|
+
orig_origin = encode_state.origin_byte_position
|
132
|
+
encode_state.origin_byte_position = encode_state.cursor_byte_position
|
133
|
+
|
134
|
+
orig_is_end_of_pdu = encode_state.is_end_of_pdu
|
135
|
+
encode_state.is_end_of_pdu = False
|
136
|
+
|
137
|
+
# ensure that no values for unknown parameters are specified.
|
138
|
+
if not encode_state.allow_unknown_parameters:
|
139
|
+
param_names = {param.short_name for param in codec.parameters}
|
140
|
+
for param_value_name in physical_value:
|
141
|
+
if param_value_name not in param_names:
|
142
|
+
odxraise(f"Value for unknown parameter '{param_value_name}' specified "
|
143
|
+
f"for composite codec object {codec.short_name}")
|
144
|
+
|
145
|
+
for param in codec.parameters:
|
146
|
+
if id(param) == id(codec.parameters[-1]):
|
147
|
+
# The last parameter of the composite codec object is at
|
148
|
+
# the end of the PDU if the codec object itself is at the
|
149
|
+
# end of the PDU.
|
150
|
+
#
|
151
|
+
# TODO: This assumes that the last parameter specified in
|
152
|
+
# the ODX is located last in the PDU...
|
153
|
+
encode_state.is_end_of_pdu = orig_is_end_of_pdu
|
154
|
+
|
155
|
+
if isinstance(param, (LengthKeyParameter, TableKeyParameter)):
|
156
|
+
# At this point, we encode a placeholder value for length-
|
157
|
+
# and table keys, since these can be specified
|
158
|
+
# implicitly (i.e., by means of parameters that use
|
159
|
+
# these keys). To avoid getting an "overlapping
|
160
|
+
# parameter" warning, we must encode a value of zero
|
161
|
+
# into the PDU here and add the real value of the
|
162
|
+
# parameter in a post-processing step.
|
163
|
+
param.encode_placeholder_into_pdu(
|
164
|
+
physical_value=physical_value.get(param.short_name), encode_state=encode_state)
|
165
|
+
|
166
|
+
continue
|
167
|
+
|
168
|
+
if param.is_required and param.short_name not in physical_value:
|
169
|
+
odxraise(f"No value for required parameter {param.short_name} specified", EncodeError)
|
170
|
+
|
171
|
+
param_phys_value = physical_value.get(param.short_name)
|
172
|
+
param.encode_into_pdu(physical_value=param_phys_value, encode_state=encode_state)
|
173
|
+
|
174
|
+
encode_state.journal.append((param, param_phys_value))
|
175
|
+
|
176
|
+
encode_state.is_end_of_pdu = False
|
177
|
+
|
178
|
+
# encode the length- and table keys. This cannot be done above
|
179
|
+
# because we allow these to be defined implicitly (i.e. they
|
180
|
+
# are defined by their respective users)
|
181
|
+
for param in codec.parameters:
|
182
|
+
if not isinstance(param, (LengthKeyParameter, TableKeyParameter)):
|
183
|
+
# the current parameter is neither a length- nor a table key
|
184
|
+
continue
|
185
|
+
|
186
|
+
# Encode the value of the key parameter into the message
|
187
|
+
param.encode_value_into_pdu(encode_state=encode_state)
|
188
|
+
|
189
|
+
encode_state.origin_byte_position = orig_origin
|
190
|
+
|
191
|
+
|
192
|
+
def composite_codec_decode_from_pdu(codec: CompositeCodec,
|
193
|
+
decode_state: DecodeState) -> ParameterValue:
|
194
|
+
# move the origin since positions specified by sub-parameters of
|
195
|
+
# composite codec objects are relative to the beginning of the
|
196
|
+
# object.
|
197
|
+
orig_origin = decode_state.origin_byte_position
|
198
|
+
decode_state.origin_byte_position = decode_state.cursor_byte_position
|
199
|
+
|
200
|
+
result = {}
|
201
|
+
for param in codec.parameters:
|
202
|
+
value = param.decode_from_pdu(decode_state)
|
203
|
+
|
204
|
+
decode_state.journal.append((param, value))
|
205
|
+
result[param.short_name] = value
|
206
|
+
|
207
|
+
# decoding of the composite codec object finished. go back the
|
208
|
+
# original origin.
|
209
|
+
decode_state.origin_byte_position = orig_origin
|
210
|
+
|
211
|
+
return result
|
@@ -8,8 +8,6 @@ from typing import (TYPE_CHECKING, Any, Callable, Dict, Iterable, List, Optional
|
|
8
8
|
Union, cast)
|
9
9
|
from xml.etree import ElementTree
|
10
10
|
|
11
|
-
from deprecation import deprecated
|
12
|
-
|
13
11
|
from ..additionalaudience import AdditionalAudience
|
14
12
|
from ..admindata import AdminData
|
15
13
|
from ..comparaminstance import ComparamInstance
|
@@ -724,10 +722,6 @@ class HierarchyElement(DiagLayer):
|
|
724
722
|
|
725
723
|
return int(result)
|
726
724
|
|
727
|
-
@deprecated(details="use get_can_receive_id()") # type: ignore[misc]
|
728
|
-
def get_receive_id(self) -> Optional[int]:
|
729
|
-
return self.get_can_receive_id()
|
730
|
-
|
731
725
|
def get_can_send_id(self, protocol: Optional[Union[str, "Protocol"]] = None) -> Optional[int]:
|
732
726
|
"""CAN ID to which the ECU sends replies to diagnostic messages"""
|
733
727
|
|
@@ -753,10 +747,6 @@ class HierarchyElement(DiagLayer):
|
|
753
747
|
|
754
748
|
return int(result)
|
755
749
|
|
756
|
-
@deprecated(details="use get_can_send_id()") # type: ignore[misc]
|
757
|
-
def get_send_id(self) -> Optional[int]:
|
758
|
-
return self.get_can_send_id()
|
759
|
-
|
760
750
|
def get_can_func_req_id(self,
|
761
751
|
protocol: Optional[Union[str, "Protocol"]] = None) -> Optional[int]:
|
762
752
|
"""CAN Functional Request Id."""
|
odxtools/dopbase.py
CHANGED
@@ -16,10 +16,12 @@ from .utils import dataclass_fields_asdict
|
|
16
16
|
|
17
17
|
@dataclass
|
18
18
|
class DopBase(IdentifiableElement):
|
19
|
-
"""Base class for all
|
19
|
+
"""Base class for all (simple and complex) data object properties.
|
20
|
+
|
21
|
+
Any class that a parameter can reference via a DOP-REF (Simple
|
22
|
+
DOPs, structures, ...) inherits from this class. All DOPs objects
|
23
|
+
implement the `Codec` type protocol.
|
20
24
|
|
21
|
-
Any class that a parameter can reference via a DOP-REF should
|
22
|
-
inherit from this class.
|
23
25
|
"""
|
24
26
|
|
25
27
|
admin_data: Optional[AdminData]
|