odxtools 8.3.3__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/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 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
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
- # - write results of comparison in json structure
51
- # - use odxlinks to refer to dignostic services / objects if changes have already been detected (e.g. in another ecu variant / diagnostic layer)
52
-
53
- # 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
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
- print()
66
- print(
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
- print()
72
- print(" [blue]New services[/blue]")
73
- table = extract_service_tabulation_data(
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
- print()
79
- print(" [blue]Deleted services[/blue]")
80
- table = extract_service_tabulation_data(
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
- print()
85
- print(" [blue]Renamed services[/blue]")
86
- table = extract_service_tabulation_data(
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
- print()
92
- print(" [blue]Services with parameter changes[/blue]")
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]) # type: ignore[arg-type]
96
- # add information about changed parameters
97
- table["Changed parameters"] = service_dict["changed_parameters_of_service"][1]
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
- print()
104
- print(
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
- print()
114
- print(detailed_info)
116
+ rich_print()
117
+ rich_print(detailed_info)
115
118
  elif isinstance(detailed_info, dict):
116
- 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()
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
- print()
128
- print("[bright_blue]Changed diagnostic layers[/bright_blue]: ")
129
- print(" New diagnostic layers: ")
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
- print(f" [magenta]{variant.short_name}[/magenta] ({variant.variant_type.value})")
133
- print(" Deleted diagnostic layers: ")
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
- 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})")
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\n")
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\n")
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\n"
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
- print(f"The variant '{name}' could not be found!")
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
- print()
636
- print(f"Changes in file '{os.path.basename(db_names[0])}'")
637
- 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])}')")
638
655
 
639
- print()
640
- 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])})")
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
- print()
647
- 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])})")
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
- print()
677
- print(f"Changes in file '{os.path.basename(db_names[0])}")
678
- 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])}')")
679
697
 
680
- print()
681
- 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])})")
682
700
  print_dl_metrics(list(task.databases[0].diag_layers))
683
701
 
684
- print()
685
- 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])})")
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
- print(f"The variant '{name}' could not be found!")
726
+ rich_print(f"The variant '{name}' could not be found!")
708
727
  return
709
728
 
710
- print()
711
- print(f"Overview of diagnostic layers: ")
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
- print()
719
- print(f"Changes in diagnostic layer '{dl.short_name}' ({dl.variant_type.value})")
720
- print(
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
- 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/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, print_fn=rich.print)
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
@@ -124,7 +124,7 @@ class DiagLayer:
124
124
  # imported references only apply within this specific
125
125
  # diagnostic layer
126
126
  extended_odxlinks = copy(odxlinks)
127
- extended_odxlinks.update(imported_links)
127
+ extended_odxlinks.update(imported_links, overwrite=False)
128
128
 
129
129
  self.diag_layer_raw._resolve_odxlinks(extended_odxlinks)
130
130
  return
@@ -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 DOPs.
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]