odxtools 9.6.1__py3-none-any.whl → 10.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.
Files changed (203) hide show
  1. odxtools/additionalaudience.py +3 -3
  2. odxtools/addressing.py +8 -0
  3. odxtools/admindata.py +8 -8
  4. odxtools/audience.py +10 -10
  5. odxtools/basecomparam.py +7 -20
  6. odxtools/basevariantpattern.py +4 -5
  7. odxtools/basicstructure.py +12 -11
  8. odxtools/cli/_print_utils.py +35 -23
  9. odxtools/cli/browse.py +9 -9
  10. odxtools/cli/compare.py +24 -24
  11. odxtools/cli/decode.py +3 -4
  12. odxtools/cli/find.py +4 -5
  13. odxtools/cli/list.py +7 -7
  14. odxtools/cli/main.py +2 -2
  15. odxtools/cli/snoop.py +3 -3
  16. odxtools/codec.py +3 -186
  17. odxtools/commrelation.py +12 -19
  18. odxtools/commrelationvaluetype.py +9 -0
  19. odxtools/companydata.py +5 -5
  20. odxtools/companydocinfo.py +8 -8
  21. odxtools/companyrevisioninfo.py +5 -5
  22. odxtools/companyspecificinfo.py +5 -5
  23. odxtools/comparam.py +3 -3
  24. odxtools/comparaminstance.py +10 -10
  25. odxtools/comparamspec.py +3 -3
  26. odxtools/comparamsubset.py +5 -5
  27. odxtools/complexcomparam.py +7 -7
  28. odxtools/compositecodec.py +191 -0
  29. odxtools/compumethods/compucategory.py +13 -0
  30. odxtools/compumethods/compucodecompumethod.py +6 -5
  31. odxtools/compumethods/compuconst.py +4 -5
  32. odxtools/compumethods/compudefaultvalue.py +1 -2
  33. odxtools/compumethods/compuinternaltophys.py +6 -6
  34. odxtools/compumethods/compumethod.py +6 -17
  35. odxtools/compumethods/compuphystointernal.py +6 -6
  36. odxtools/compumethods/compurationalcoeffs.py +4 -4
  37. odxtools/compumethods/compuscale.py +9 -10
  38. odxtools/compumethods/createanycompumethod.py +1 -2
  39. odxtools/compumethods/identicalcompumethod.py +1 -2
  40. odxtools/compumethods/intervaltype.py +8 -0
  41. odxtools/compumethods/limit.py +13 -19
  42. odxtools/compumethods/linearcompumethod.py +4 -3
  43. odxtools/compumethods/linearsegment.py +14 -15
  44. odxtools/compumethods/ratfunccompumethod.py +5 -4
  45. odxtools/compumethods/ratfuncsegment.py +7 -8
  46. odxtools/compumethods/scalelinearcompumethod.py +10 -9
  47. odxtools/compumethods/scaleratfunccompumethod.py +6 -5
  48. odxtools/compumethods/tabintpcompumethod.py +19 -20
  49. odxtools/compumethods/texttablecompumethod.py +5 -4
  50. odxtools/createanycomparam.py +2 -4
  51. odxtools/createanydiagcodedtype.py +1 -2
  52. odxtools/database.py +9 -8
  53. odxtools/dataobjectproperty.py +10 -10
  54. odxtools/decodestate.py +5 -5
  55. odxtools/description.py +6 -22
  56. odxtools/determinenumberofitems.py +4 -4
  57. odxtools/diagclasstype.py +11 -0
  58. odxtools/diagcodedtype.py +7 -7
  59. odxtools/diagcomm.py +19 -42
  60. odxtools/diagdatadictionaryspec.py +6 -6
  61. odxtools/diaglayercontainer.py +4 -4
  62. odxtools/diaglayers/basevariant.py +10 -9
  63. odxtools/diaglayers/basevariantraw.py +9 -9
  64. odxtools/diaglayers/diaglayer.py +20 -19
  65. odxtools/diaglayers/diaglayerraw.py +10 -10
  66. odxtools/diaglayers/diaglayertype.py +1 -2
  67. odxtools/diaglayers/ecushareddata.py +4 -4
  68. odxtools/diaglayers/ecushareddataraw.py +6 -6
  69. odxtools/diaglayers/ecuvariant.py +11 -10
  70. odxtools/diaglayers/ecuvariantraw.py +9 -9
  71. odxtools/diaglayers/functionalgroup.py +8 -7
  72. odxtools/diaglayers/functionalgroupraw.py +7 -7
  73. odxtools/diaglayers/hierarchyelement.py +43 -49
  74. odxtools/diaglayers/hierarchyelementraw.py +4 -4
  75. odxtools/diaglayers/protocol.py +4 -4
  76. odxtools/diaglayers/protocolraw.py +6 -6
  77. odxtools/diagnostictroublecode.py +8 -8
  78. odxtools/diagservice.py +21 -97
  79. odxtools/diagvariable.py +14 -14
  80. odxtools/docrevision.py +11 -11
  81. odxtools/dopbase.py +6 -6
  82. odxtools/dtcconnector.py +45 -0
  83. odxtools/dtcdop.py +15 -56
  84. odxtools/dynamicendmarkerfield.py +5 -4
  85. odxtools/dynamiclengthfield.py +5 -4
  86. odxtools/dyndefinedspec.py +7 -159
  87. odxtools/dynenddopref.py +5 -5
  88. odxtools/dyniddefmodeinfo.py +161 -0
  89. odxtools/ecuvariantpattern.py +4 -5
  90. odxtools/element.py +5 -6
  91. odxtools/encodestate.py +11 -11
  92. odxtools/encoding.py +2 -3
  93. odxtools/endofpdufield.py +6 -6
  94. odxtools/envdataconnector.py +49 -0
  95. odxtools/environmentdata.py +3 -4
  96. odxtools/environmentdatadescription.py +11 -11
  97. odxtools/exceptions.py +5 -5
  98. odxtools/externalaccessmethod.py +22 -0
  99. odxtools/externaldoc.py +23 -0
  100. odxtools/field.py +9 -10
  101. odxtools/functionalclass.py +4 -4
  102. odxtools/inputparam.py +6 -6
  103. odxtools/internalconstr.py +4 -5
  104. odxtools/isotp_state_machine.py +12 -11
  105. odxtools/leadinglengthinfotype.py +2 -3
  106. odxtools/library.py +5 -5
  107. odxtools/linkeddtcdop.py +62 -0
  108. odxtools/loadfile.py +5 -6
  109. odxtools/matchingbasevariantparameter.py +2 -3
  110. odxtools/matchingparameter.py +7 -7
  111. odxtools/minmaxlengthtype.py +5 -11
  112. odxtools/modification.py +4 -4
  113. odxtools/multiplexer.py +11 -11
  114. odxtools/multiplexercase.py +6 -6
  115. odxtools/multiplexerdefaultcase.py +6 -6
  116. odxtools/multiplexerswitchkey.py +4 -4
  117. odxtools/nameditemlist.py +14 -14
  118. odxtools/negoutputparam.py +3 -3
  119. odxtools/obd.py +1 -2
  120. odxtools/odxcategory.py +6 -6
  121. odxtools/odxlink.py +19 -20
  122. odxtools/odxtypes.py +21 -18
  123. odxtools/outputparam.py +4 -4
  124. odxtools/parameterinfo.py +2 -2
  125. odxtools/parameters/codedconstparameter.py +5 -5
  126. odxtools/parameters/createanyparameter.py +1 -2
  127. odxtools/parameters/dynamicparameter.py +2 -3
  128. odxtools/parameters/lengthkeyparameter.py +5 -5
  129. odxtools/parameters/matchingrequestparameter.py +3 -4
  130. odxtools/parameters/nrcconstparameter.py +7 -7
  131. odxtools/parameters/parameter.py +11 -11
  132. odxtools/parameters/parameterwithdop.py +9 -9
  133. odxtools/parameters/physicalconstantparameter.py +4 -4
  134. odxtools/parameters/reservedparameter.py +3 -4
  135. odxtools/parameters/rowfragment.py +7 -0
  136. odxtools/parameters/systemparameter.py +2 -3
  137. odxtools/parameters/tableentryparameter.py +4 -9
  138. odxtools/parameters/tablekeyparameter.py +10 -10
  139. odxtools/parameters/tablestructparameter.py +7 -7
  140. odxtools/parameters/valueparameter.py +7 -7
  141. odxtools/paramlengthinfotype.py +5 -3
  142. odxtools/parentref.py +9 -9
  143. odxtools/physicaldimension.py +11 -11
  144. odxtools/physicaltype.py +4 -12
  145. odxtools/posresponsesuppressible.py +72 -0
  146. odxtools/preconditionstateref.py +7 -7
  147. odxtools/progcode.py +6 -6
  148. odxtools/protstack.py +4 -4
  149. odxtools/radix.py +9 -0
  150. odxtools/relateddiagcommref.py +22 -0
  151. odxtools/relateddoc.py +6 -6
  152. odxtools/request.py +14 -12
  153. odxtools/response.py +15 -13
  154. odxtools/scaleconstr.py +4 -12
  155. odxtools/servicebinner.py +5 -5
  156. odxtools/singleecujob.py +4 -4
  157. odxtools/snrefcontext.py +2 -2
  158. odxtools/specialdata.py +5 -5
  159. odxtools/specialdatagroup.py +9 -9
  160. odxtools/specialdatagroupcaption.py +3 -3
  161. odxtools/standardizationlevel.py +9 -0
  162. odxtools/standardlengthtype.py +12 -21
  163. odxtools/state.py +3 -3
  164. odxtools/statechart.py +4 -4
  165. odxtools/statemachine.py +4 -3
  166. odxtools/statetransition.py +5 -18
  167. odxtools/statetransitionref.py +18 -18
  168. odxtools/staticfield.py +5 -4
  169. odxtools/structure.py +2 -3
  170. odxtools/subcomponent.py +12 -245
  171. odxtools/subcomponentparamconnector.py +103 -0
  172. odxtools/subcomponentpattern.py +42 -0
  173. odxtools/swvariable.py +3 -4
  174. odxtools/table.py +17 -55
  175. odxtools/tablediagcommconnector.py +47 -0
  176. odxtools/tablerow.py +30 -30
  177. odxtools/tablerowconnector.py +46 -0
  178. odxtools/teammember.py +11 -11
  179. odxtools/templates/macros/printService.xml.jinja2 +2 -1
  180. odxtools/termination.py +8 -0
  181. odxtools/text.py +2 -3
  182. odxtools/transmode.py +9 -0
  183. odxtools/uds.py +2 -3
  184. odxtools/unit.py +9 -9
  185. odxtools/unitgroup.py +6 -11
  186. odxtools/unitgroupcategory.py +7 -0
  187. odxtools/unitspec.py +6 -6
  188. odxtools/usage.py +9 -0
  189. odxtools/utils.py +31 -2
  190. odxtools/validtype.py +9 -0
  191. odxtools/variablegroup.py +2 -2
  192. odxtools/variantmatcher.py +10 -10
  193. odxtools/variantpattern.py +3 -3
  194. odxtools/version.py +2 -2
  195. odxtools/writepdxfile.py +5 -5
  196. odxtools/xdoc.py +9 -9
  197. {odxtools-9.6.1.dist-info → odxtools-10.0.0.dist-info}/METADATA +4 -5
  198. odxtools-10.0.0.dist-info/RECORD +264 -0
  199. odxtools-9.6.1.dist-info/RECORD +0 -238
  200. {odxtools-9.6.1.dist-info → odxtools-10.0.0.dist-info}/WHEEL +0 -0
  201. {odxtools-9.6.1.dist-info → odxtools-10.0.0.dist-info}/entry_points.txt +0 -0
  202. {odxtools-9.6.1.dist-info → odxtools-10.0.0.dist-info}/licenses/LICENSE +0 -0
  203. {odxtools-9.6.1.dist-info → odxtools-10.0.0.dist-info}/top_level.txt +0 -0
@@ -1,11 +1,11 @@
1
1
  # SPDX-License-Identifier: MIT
2
2
  import re
3
3
  import warnings
4
+ from collections.abc import Callable, Iterable
4
5
  from copy import deepcopy
5
6
  from dataclasses import dataclass
6
7
  from functools import cached_property
7
- from typing import (TYPE_CHECKING, Any, Callable, Dict, Iterable, List, Optional, Tuple, TypeVar,
8
- Union, cast)
8
+ from typing import TYPE_CHECKING, Any, TypeVar, Union, cast
9
9
  from xml.etree import ElementTree
10
10
 
11
11
  from ..additionalaudience import AdditionalAudience
@@ -47,7 +47,7 @@ class HierarchyElement(DiagLayer):
47
47
 
48
48
  @staticmethod
49
49
  def from_et(et_element: ElementTree.Element,
50
- doc_frags: List[OdxDocFragment]) -> "HierarchyElement":
50
+ doc_frags: list[OdxDocFragment]) -> "HierarchyElement":
51
51
  hierarchy_element_raw = HierarchyElementRaw.from_et(et_element, doc_frags)
52
52
 
53
53
  return HierarchyElement(diag_layer_raw=hierarchy_element_raw)
@@ -62,7 +62,7 @@ class HierarchyElement(DiagLayer):
62
62
  "The raw diagnostic layer passed to HierarchyElement "
63
63
  "must be a HierarchyElementRaw")
64
64
 
65
- def _build_odxlinks(self) -> Dict[OdxLinkId, Any]:
65
+ def _build_odxlinks(self) -> dict[OdxLinkId, Any]:
66
66
  result = super()._build_odxlinks()
67
67
 
68
68
  return result
@@ -73,7 +73,7 @@ class HierarchyElement(DiagLayer):
73
73
  def _resolve_snrefs(self, context: SnRefContext) -> None:
74
74
  super()._resolve_snrefs(context)
75
75
 
76
- def __deepcopy__(self, memo: Dict[int, Any]) -> Any:
76
+ def __deepcopy__(self, memo: dict[int, Any]) -> Any:
77
77
  """Create a deep copy of the hierarchy element
78
78
 
79
79
  Note that the copied diagnostic layer is not fully
@@ -121,13 +121,13 @@ class HierarchyElement(DiagLayer):
121
121
  unit_groups = self._compute_available_unit_groups()
122
122
 
123
123
  # convenience variable for the locally-defined unit spec
124
- local_unit_spec: Optional[UnitSpec]
124
+ local_unit_spec: UnitSpec | None
125
125
  if self.diag_layer_raw.diag_data_dictionary_spec is not None:
126
126
  local_unit_spec = self.diag_layer_raw.diag_data_dictionary_spec.unit_spec
127
127
  else:
128
128
  local_unit_spec = None
129
129
 
130
- unit_spec: Optional[UnitSpec]
130
+ unit_spec: UnitSpec | None
131
131
  if local_unit_spec is None and not unit_groups:
132
132
  # no locally defined unit spec and no inherited unit groups
133
133
  unit_spec = None
@@ -188,8 +188,8 @@ class HierarchyElement(DiagLayer):
188
188
  tables = self._compute_available_ddd_spec_items(
189
189
  lambda ddd_spec: ddd_spec.tables, lambda parent_ref: parent_ref.not_inherited_tables)
190
190
 
191
- ddds_admin_data: Optional[AdminData] = None
192
- ddds_sdgs: List[SpecialDataGroup] = []
191
+ ddds_admin_data: AdminData | None = None
192
+ ddds_sdgs: list[SpecialDataGroup] = []
193
193
  if self.diag_layer_raw.diag_data_dictionary_spec:
194
194
  ddds_admin_data = self.diag_layer_raw.diag_data_dictionary_spec.admin_data
195
195
  ddds_sdgs = self.diag_layer_raw.diag_data_dictionary_spec.sdgs
@@ -291,7 +291,7 @@ class HierarchyElement(DiagLayer):
291
291
 
292
292
  local_objects = get_local_objects(self)
293
293
  local_object_short_names = {x.short_name for x in local_objects}
294
- result_dict: Dict[str, Tuple[TNamed, DiagLayer]] = {}
294
+ result_dict: dict[str, tuple[TNamed, DiagLayer]] = {}
295
295
 
296
296
  # populate the result dictionary with the inherited objects
297
297
  for parent_ref in self._get_parent_refs_sorted_by_priority(reverse=True):
@@ -361,7 +361,7 @@ class HierarchyElement(DiagLayer):
361
361
  def get_local_objects_fn(dl: DiagLayer) -> Iterable[DiagComm]:
362
362
  return dl._get_local_diag_comms(odxlinks)
363
363
 
364
- def not_inherited_fn(parent_ref: ParentRef) -> List[str]:
364
+ def not_inherited_fn(parent_ref: ParentRef) -> list[str]:
365
365
  return parent_ref.not_inherited_diag_comms
366
366
 
367
367
  return self._compute_available_objects(get_local_objects_fn, not_inherited_fn)
@@ -372,7 +372,7 @@ class HierarchyElement(DiagLayer):
372
372
  def get_local_objects_fn(dl: DiagLayer) -> Iterable[Response]:
373
373
  return dl.diag_layer_raw.global_negative_responses
374
374
 
375
- def not_inherited_fn(parent_ref: ParentRef) -> List[str]:
375
+ def not_inherited_fn(parent_ref: ParentRef) -> list[str]:
376
376
  return parent_ref.not_inherited_global_neg_responses
377
377
 
378
378
  return self._compute_available_objects(get_local_objects_fn, not_inherited_fn)
@@ -380,7 +380,7 @@ class HierarchyElement(DiagLayer):
380
380
  def _compute_available_ddd_spec_items(
381
381
  self,
382
382
  include: Callable[[DiagDataDictionarySpec], Iterable[TNamed]],
383
- exclude: Callable[["ParentRef"], List[str]],
383
+ exclude: Callable[["ParentRef"], list[str]],
384
384
  ) -> NamedItemList[TNamed]:
385
385
 
386
386
  def get_local_objects_fn(dl: DiagLayer) -> Iterable[TNamed]:
@@ -396,7 +396,7 @@ class HierarchyElement(DiagLayer):
396
396
  def get_local_objects_fn(dl: DiagLayer) -> Iterable[FunctionalClass]:
397
397
  return dl.diag_layer_raw.functional_classes
398
398
 
399
- def not_inherited_fn(parent_ref: ParentRef) -> List[str]:
399
+ def not_inherited_fn(parent_ref: ParentRef) -> list[str]:
400
400
  return []
401
401
 
402
402
  return self._compute_available_objects(get_local_objects_fn, not_inherited_fn)
@@ -406,7 +406,7 @@ class HierarchyElement(DiagLayer):
406
406
  def get_local_objects_fn(dl: DiagLayer) -> Iterable[AdditionalAudience]:
407
407
  return dl.diag_layer_raw.additional_audiences
408
408
 
409
- def not_inherited_fn(parent_ref: ParentRef) -> List[str]:
409
+ def not_inherited_fn(parent_ref: ParentRef) -> list[str]:
410
410
  return []
411
411
 
412
412
  return self._compute_available_objects(get_local_objects_fn, not_inherited_fn)
@@ -416,7 +416,7 @@ class HierarchyElement(DiagLayer):
416
416
  def get_local_objects_fn(dl: DiagLayer) -> Iterable[StateChart]:
417
417
  return dl.diag_layer_raw.state_charts
418
418
 
419
- def not_inherited_fn(parent_ref: ParentRef) -> List[str]:
419
+ def not_inherited_fn(parent_ref: ParentRef) -> list[str]:
420
420
  return []
421
421
 
422
422
  return self._compute_available_objects(get_local_objects_fn, not_inherited_fn)
@@ -426,7 +426,7 @@ class HierarchyElement(DiagLayer):
426
426
  def get_local_objects_fn(dl: DiagLayer) -> Iterable[UnitGroup]:
427
427
  return dl._get_local_unit_groups()
428
428
 
429
- def not_inherited_fn(parent_ref: ParentRef) -> List[str]:
429
+ def not_inherited_fn(parent_ref: ParentRef) -> list[str]:
430
430
  return []
431
431
 
432
432
  return self._compute_available_objects(get_local_objects_fn, not_inherited_fn)
@@ -502,7 +502,7 @@ class HierarchyElement(DiagLayer):
502
502
  #####
503
503
  # <communication parameter handling>
504
504
  #####
505
- def _compute_available_commmunication_parameters(self) -> List[ComparamInstance]:
505
+ def _compute_available_commmunication_parameters(self) -> list[ComparamInstance]:
506
506
  """Compute the list of communication parameters that apply to
507
507
  the diagnostic layer
508
508
 
@@ -526,7 +526,7 @@ class HierarchyElement(DiagLayer):
526
526
  without a specified protocol are taken as fallbacks...
527
527
 
528
528
  """
529
- com_params_dict: Dict[Tuple[str, Optional[str]], ComparamInstance] = {}
529
+ com_params_dict: dict[tuple[str, str | None], ComparamInstance] = {}
530
530
 
531
531
  # Look in parent refs for inherited communication
532
532
  # parameters. First fetch the communication parameters from
@@ -564,7 +564,7 @@ class HierarchyElement(DiagLayer):
564
564
  """
565
565
  from .protocol import Protocol
566
566
 
567
- result_dict: Dict[str, Protocol] = {}
567
+ result_dict: dict[str, Protocol] = {}
568
568
 
569
569
  for parent_ref in getattr(self, "parent_refs", []):
570
570
  for prot in getattr(parent_ref.layer, "protocols", []):
@@ -579,15 +579,15 @@ class HierarchyElement(DiagLayer):
579
579
  self,
580
580
  cp_short_name: str,
581
581
  *,
582
- protocol: Optional[Union[str, "Protocol"]] = None,
583
- ) -> Optional[ComparamInstance]:
582
+ protocol: Union[str, "Protocol"] | None = None,
583
+ ) -> ComparamInstance | None:
584
584
  """Find a specific communication parameter according to some criteria.
585
585
 
586
586
  Setting a given parameter to `None` means "don't care"."""
587
587
 
588
588
  from .protocol import Protocol
589
589
 
590
- protocol_name: Optional[str]
590
+ protocol_name: str | None
591
591
  if isinstance(protocol, Protocol):
592
592
  protocol_name = protocol.short_name
593
593
  else:
@@ -611,8 +611,7 @@ class HierarchyElement(DiagLayer):
611
611
  return cps[0]
612
612
 
613
613
  def get_max_can_payload_size(self,
614
- protocol: Optional[Union[str,
615
- "Protocol"]] = None) -> Optional[int]:
614
+ protocol: Union[str, "Protocol"] | None = None) -> int | None:
616
615
  """Return the maximum size of a CAN frame payload that can be
617
616
  transmitted in bytes.
618
617
 
@@ -642,13 +641,13 @@ class HierarchyElement(DiagLayer):
642
641
  # unexpected format of parameter value
643
642
  return 8
644
643
 
645
- def uses_can(self, protocol: Optional[Union[str, "Protocol"]] = None) -> bool:
644
+ def uses_can(self, protocol: Union[str, "Protocol"] | None = None) -> bool:
646
645
  """
647
646
  Check if CAN ought to be used as the link layer protocol.
648
647
  """
649
648
  return self.get_can_receive_id(protocol=protocol) is not None
650
649
 
651
- def uses_can_fd(self, protocol: Optional[Union[str, "Protocol"]] = None) -> bool:
650
+ def uses_can_fd(self, protocol: Union[str, "Protocol"] | None = None) -> bool:
652
651
  """Check if CAN-FD ought to be used.
653
652
 
654
653
  If the ECU is not using CAN-FD for the specified protocol, `False`
@@ -667,7 +666,7 @@ class HierarchyElement(DiagLayer):
667
666
 
668
667
  return "CANFD" in com_param.value
669
668
 
670
- def get_can_baudrate(self, protocol: Optional[Union[str, "Protocol"]] = None) -> Optional[int]:
669
+ def get_can_baudrate(self, protocol: Union[str, "Protocol"] | None = None) -> int | None:
671
670
  """Baudrate of the CAN bus which is used by the ECU [bits/s]
672
671
 
673
672
  If the ECU is not using CAN for the specified protocol, None
@@ -684,8 +683,7 @@ class HierarchyElement(DiagLayer):
684
683
 
685
684
  return int(val)
686
685
 
687
- def get_can_fd_baudrate(self,
688
- protocol: Optional[Union[str, "Protocol"]] = None) -> Optional[int]:
686
+ def get_can_fd_baudrate(self, protocol: Union[str, "Protocol"] | None = None) -> int | None:
689
687
  """Data baudrate of the CAN bus which is used by the ECU [bits/s]
690
688
 
691
689
  If the ECU is not using CAN-FD for the specified protocol,
@@ -704,8 +702,7 @@ class HierarchyElement(DiagLayer):
704
702
 
705
703
  return int(val)
706
704
 
707
- def get_can_receive_id(self,
708
- protocol: Optional[Union[str, "Protocol"]] = None) -> Optional[int]:
705
+ def get_can_receive_id(self, protocol: Union[str, "Protocol"] | None = None) -> int | None:
709
706
  """CAN ID to which the ECU listens for diagnostic messages"""
710
707
  com_param = self.get_comparam("CP_UniqueRespIdTable", protocol=protocol)
711
708
  if com_param is None:
@@ -724,7 +721,7 @@ class HierarchyElement(DiagLayer):
724
721
 
725
722
  return int(result)
726
723
 
727
- def get_can_send_id(self, protocol: Optional[Union[str, "Protocol"]] = None) -> Optional[int]:
724
+ def get_can_send_id(self, protocol: Union[str, "Protocol"] | None = None) -> int | None:
728
725
  """CAN ID to which the ECU sends replies to diagnostic messages"""
729
726
 
730
727
  # this hopefully resolves to the 'CP_UniqueRespIdTable'
@@ -749,8 +746,7 @@ class HierarchyElement(DiagLayer):
749
746
 
750
747
  return int(result)
751
748
 
752
- def get_can_func_req_id(self,
753
- protocol: Optional[Union[str, "Protocol"]] = None) -> Optional[int]:
749
+ def get_can_func_req_id(self, protocol: Union[str, "Protocol"] | None = None) -> int | None:
754
750
  """CAN Functional Request Id."""
755
751
  com_param = self.get_comparam("CP_CanFuncReqId", protocol=protocol)
756
752
  if com_param is None:
@@ -764,8 +760,7 @@ class HierarchyElement(DiagLayer):
764
760
  return int(result)
765
761
 
766
762
  def get_doip_logical_ecu_address(self,
767
- protocol: Optional[Union[str,
768
- "Protocol"]] = None) -> Optional[int]:
763
+ protocol: Union[str, "Protocol"] | None = None) -> int | None:
769
764
  """Return the address of the ECU when using functional addressing.
770
765
 
771
766
  The parameter protocol is used to distinguish between
@@ -796,8 +791,8 @@ class HierarchyElement(DiagLayer):
796
791
  return int(ecu_addr)
797
792
 
798
793
  def get_doip_logical_gateway_address(self,
799
- protocol: Optional[Union[str, "Protocol"]] = None
800
- ) -> Optional[int]:
794
+ protocol: Union[str, "Protocol"] | None = None
795
+ ) -> int | None:
801
796
  """The logical gateway address for the diagnosis over IP transport protocol"""
802
797
 
803
798
  # retrieve CP_DoIPLogicalGatewayAddress from the
@@ -814,8 +809,8 @@ class HierarchyElement(DiagLayer):
814
809
  return int(result)
815
810
 
816
811
  def get_doip_logical_tester_address(self,
817
- protocol: Optional[Union[str, "Protocol"]] = None
818
- ) -> Optional[int]:
812
+ protocol: Union[str, "Protocol"] | None = None
813
+ ) -> int | None:
819
814
  """DoIp logical gateway address"""
820
815
 
821
816
  # retrieve CP_DoIPLogicalTesterAddress from the
@@ -832,8 +827,8 @@ class HierarchyElement(DiagLayer):
832
827
  return int(result)
833
828
 
834
829
  def get_doip_logical_functional_address(self,
835
- protocol: Optional[Union[str, "Protocol"]] = None
836
- ) -> Optional[int]:
830
+ protocol: Union[str, "Protocol"] | None = None
831
+ ) -> int | None:
837
832
  """The logical functional DoIP address of the ECU."""
838
833
 
839
834
  # retrieve CP_DoIPLogicalFunctionalAddress from the
@@ -853,8 +848,8 @@ class HierarchyElement(DiagLayer):
853
848
  return int(result)
854
849
 
855
850
  def get_doip_routing_activation_timeout(self,
856
- protocol: Optional[Union[str, "Protocol"]] = None
857
- ) -> Optional[float]:
851
+ protocol: Union[str, "Protocol"] | None = None
852
+ ) -> float | None:
858
853
  """The timout for the DoIP routing activation request in seconds"""
859
854
 
860
855
  # retrieve CP_DoIPRoutingActivationTimeout from the
@@ -871,8 +866,8 @@ class HierarchyElement(DiagLayer):
871
866
  return float(result) / 1e6
872
867
 
873
868
  def get_doip_routing_activation_type(self,
874
- protocol: Optional[Union[str, "Protocol"]] = None
875
- ) -> Optional[int]:
869
+ protocol: Union[str, "Protocol"] | None = None
870
+ ) -> int | None:
876
871
  """The DoIP routing activation type
877
872
 
878
873
  The number returned has the following meaning:
@@ -897,8 +892,7 @@ class HierarchyElement(DiagLayer):
897
892
  return int(result)
898
893
 
899
894
  def get_tester_present_time(self,
900
- protocol: Optional[Union[str,
901
- "Protocol"]] = None) -> Optional[float]:
895
+ protocol: Union[str, "Protocol"] | None = None) -> float | None:
902
896
  """Timeout on inactivity in seconds.
903
897
 
904
898
  This is defined by the communication parameter "CP_TesterPresentTime".
@@ -1,6 +1,6 @@
1
1
  # SPDX-License-Identifier: MIT
2
2
  from dataclasses import dataclass
3
- from typing import Any, Dict, List
3
+ from typing import Any
4
4
  from xml.etree import ElementTree
5
5
 
6
6
  from ..comparaminstance import ComparamInstance
@@ -17,11 +17,11 @@ class HierarchyElementRaw(DiagLayerRaw):
17
17
  This class represents the data present in the XML, not the "logical" view.
18
18
  """
19
19
 
20
- comparam_refs: List[ComparamInstance]
20
+ comparam_refs: list[ComparamInstance]
21
21
 
22
22
  @staticmethod
23
23
  def from_et(et_element: ElementTree.Element,
24
- doc_frags: List[OdxDocFragment]) -> "HierarchyElementRaw":
24
+ doc_frags: list[OdxDocFragment]) -> "HierarchyElementRaw":
25
25
  # objects contained by diagnostic layers exibit an additional
26
26
  # document fragment for the diag layer, so we use the document
27
27
  # fragments of the odx id of the diag layer for IDs of
@@ -37,7 +37,7 @@ class HierarchyElementRaw(DiagLayerRaw):
37
37
 
38
38
  return HierarchyElementRaw(comparam_refs=comparam_refs, **kwargs)
39
39
 
40
- def _build_odxlinks(self) -> Dict[OdxLinkId, Any]:
40
+ def _build_odxlinks(self) -> dict[OdxLinkId, Any]:
41
41
  result = super()._build_odxlinks()
42
42
 
43
43
  for comparam_ref in self.comparam_refs:
@@ -1,7 +1,7 @@
1
1
  # SPDX-License-Identifier: MIT
2
2
  from copy import deepcopy
3
3
  from dataclasses import dataclass
4
- from typing import Any, Dict, List, Optional, cast
4
+ from typing import Any, cast
5
5
  from xml.etree import ElementTree
6
6
 
7
7
  from ..comparamspec import ComparamSpec
@@ -29,11 +29,11 @@ class Protocol(HierarchyElement):
29
29
  return self.protocol_raw.comparam_spec
30
30
 
31
31
  @property
32
- def prot_stack(self) -> Optional[ProtStack]:
32
+ def prot_stack(self) -> ProtStack | None:
33
33
  return self.protocol_raw.prot_stack
34
34
 
35
35
  @staticmethod
36
- def from_et(et_element: ElementTree.Element, doc_frags: List[OdxDocFragment]) -> "Protocol":
36
+ def from_et(et_element: ElementTree.Element, doc_frags: list[OdxDocFragment]) -> "Protocol":
37
37
  protocol_raw = ProtocolRaw.from_et(et_element, doc_frags)
38
38
 
39
39
  return Protocol(diag_layer_raw=protocol_raw)
@@ -46,7 +46,7 @@ class Protocol(HierarchyElement):
46
46
  "The raw diagnostic layer passed to Protocol "
47
47
  "must be a ProtocolRaw")
48
48
 
49
- def __deepcopy__(self, memo: Dict[int, Any]) -> Any:
49
+ def __deepcopy__(self, memo: dict[int, Any]) -> Any:
50
50
  """Create a deep copy of the protocol layer
51
51
 
52
52
  Note that the copied diagnostic layer is not fully
@@ -1,6 +1,6 @@
1
1
  # SPDX-License-Identifier: MIT
2
2
  from dataclasses import dataclass
3
- from typing import Any, Dict, List, Optional
3
+ from typing import Any
4
4
  from xml.etree import ElementTree
5
5
 
6
6
  from ..comparamspec import ComparamSpec
@@ -24,19 +24,19 @@ class ProtocolRaw(HierarchyElementRaw):
24
24
  """
25
25
 
26
26
  comparam_spec_ref: OdxLinkRef
27
- prot_stack_snref: Optional[str]
28
- parent_refs: List[ParentRef]
27
+ prot_stack_snref: str | None
28
+ parent_refs: list[ParentRef]
29
29
 
30
30
  @property
31
31
  def comparam_spec(self) -> ComparamSpec:
32
32
  return self._comparam_spec
33
33
 
34
34
  @property
35
- def prot_stack(self) -> Optional[ProtStack]:
35
+ def prot_stack(self) -> ProtStack | None:
36
36
  return self._prot_stack
37
37
 
38
38
  @staticmethod
39
- def from_et(et_element: ElementTree.Element, doc_frags: List[OdxDocFragment]) -> "ProtocolRaw":
39
+ def from_et(et_element: ElementTree.Element, doc_frags: list[OdxDocFragment]) -> "ProtocolRaw":
40
40
  # objects contained by diagnostic layers exibit an additional
41
41
  # document fragment for the diag layer, so we use the document
42
42
  # fragments of the odx id of the diag layer for IDs of
@@ -63,7 +63,7 @@ class ProtocolRaw(HierarchyElementRaw):
63
63
  parent_refs=parent_refs,
64
64
  **kwargs)
65
65
 
66
- def _build_odxlinks(self) -> Dict[OdxLinkId, Any]:
66
+ def _build_odxlinks(self) -> dict[OdxLinkId, Any]:
67
67
  result = super()._build_odxlinks()
68
68
 
69
69
  for parent_ref in self.parent_refs:
@@ -1,6 +1,6 @@
1
1
  # SPDX-License-Identifier: MIT
2
2
  from dataclasses import dataclass
3
- from typing import Any, Dict, List, Optional
3
+ from typing import Any
4
4
  from xml.etree import ElementTree
5
5
 
6
6
  from .element import IdentifiableElement
@@ -16,12 +16,12 @@ from .utils import dataclass_fields_asdict
16
16
  @dataclass
17
17
  class DiagnosticTroubleCode(IdentifiableElement):
18
18
  trouble_code: int
19
- display_trouble_code: Optional[str]
19
+ display_trouble_code: str | None
20
20
  text: Text
21
- level: Optional[int]
22
- sdgs: List[SpecialDataGroup]
21
+ level: int | None
22
+ sdgs: list[SpecialDataGroup]
23
23
 
24
- is_temporary_raw: Optional[bool]
24
+ is_temporary_raw: bool | None
25
25
 
26
26
  @property
27
27
  def is_temporary(self) -> bool:
@@ -29,7 +29,7 @@ class DiagnosticTroubleCode(IdentifiableElement):
29
29
 
30
30
  @staticmethod
31
31
  def from_et(et_element: ElementTree.Element,
32
- doc_frags: List[OdxDocFragment]) -> "DiagnosticTroubleCode":
32
+ doc_frags: list[OdxDocFragment]) -> "DiagnosticTroubleCode":
33
33
  kwargs = dataclass_fields_asdict(IdentifiableElement.from_et(et_element, doc_frags))
34
34
 
35
35
  trouble_code = int(odxrequire(et_element.findtext("TROUBLE-CODE")))
@@ -53,8 +53,8 @@ class DiagnosticTroubleCode(IdentifiableElement):
53
53
  is_temporary_raw=is_temporary_raw,
54
54
  **kwargs)
55
55
 
56
- def _build_odxlinks(self) -> Dict[OdxLinkId, Any]:
57
- result: Dict[OdxLinkId, Any] = {}
56
+ def _build_odxlinks(self) -> dict[OdxLinkId, Any]:
57
+ result: dict[OdxLinkId, Any] = {}
58
58
 
59
59
  result[self.odx_id] = self
60
60
 
odxtools/diagservice.py CHANGED
@@ -1,9 +1,9 @@
1
1
  # SPDX-License-Identifier: MIT
2
2
  from dataclasses import dataclass
3
- from enum import Enum
4
- from typing import Any, Dict, List, Optional, Union, cast
3
+ from typing import Any, cast
5
4
  from xml.etree import ElementTree
6
5
 
6
+ from .addressing import Addressing
7
7
  from .comparaminstance import ComparamInstance
8
8
  from .diagcomm import DiagComm
9
9
  from .exceptions import DecodeError, DecodeMismatch, odxassert, odxraise, odxrequire
@@ -12,112 +12,36 @@ from .nameditemlist import NamedItemList
12
12
  from .odxlink import OdxDocFragment, OdxLinkDatabase, OdxLinkId, OdxLinkRef
13
13
  from .odxtypes import ParameterValue, odxstr_to_bool
14
14
  from .parameters.parameter import Parameter
15
+ from .posresponsesuppressible import PosResponseSuppressible
15
16
  from .request import Request
16
17
  from .response import Response
17
18
  from .snrefcontext import SnRefContext
19
+ from .transmode import TransMode
18
20
  from .utils import dataclass_fields_asdict
19
21
 
20
22
 
21
- class Addressing(Enum):
22
- FUNCTIONAL = "FUNCTIONAL"
23
- PHYSICAL = "PHYSICAL"
24
- FUNCTIONAL_OR_PHYSICAL = "FUNCTIONAL-OR-PHYSICAL"
25
-
26
-
27
- class TransMode(Enum):
28
- SEND_ONLY = "SEND-ONLY"
29
- RECEIVE_ONLY = "RECEIVE-ONLY"
30
- SEND_AND_RECEIVE = "SEND-AND-RECEIVE"
31
- SEND_OR_RECEIVE = "SEND-OR-RECEIVE"
32
-
33
-
34
- # note that the spec has a typo here: it calls the corresponding
35
- # XML tag POS-RESPONSE-SUPPRESSABLE...
36
- @dataclass
37
- class PosResponseSuppressible:
38
- bit_mask: int
39
-
40
- coded_const_snref: Optional[str]
41
- coded_const_snpathref: Optional[str]
42
-
43
- value_snref: Optional[str]
44
- value_snpathref: Optional[str]
45
-
46
- phys_const_snref: Optional[str]
47
- phys_const_snpathref: Optional[str]
48
-
49
- table_key_snref: Optional[str]
50
- table_key_snpathref: Optional[str]
51
-
52
- @staticmethod
53
- def from_et(et_element: ElementTree.Element,
54
- doc_frags: List[OdxDocFragment]) -> "PosResponseSuppressible":
55
-
56
- bit_mask = int(odxrequire(et_element.findtext("BIT-MASK")))
57
-
58
- coded_const_snref = None
59
- if (cc_snref_elem := et_element.find("CODED-CONST-SNREF")) is not None:
60
- coded_const_snref = cc_snref_elem.attrib["SHORT-NAME"]
61
- coded_const_snpathref = None
62
- if (cc_snpathref_elem := et_element.find("CODED-CONST-SNPATHREF")) is not None:
63
- coded_const_snpathref = cc_snpathref_elem.attrib["SHORT-NAME-PATH"]
64
-
65
- value_snref = None
66
- if (cc_snref_elem := et_element.find("VALUE-SNREF")) is not None:
67
- value_snref = cc_snref_elem.attrib["SHORT-NAME"]
68
- value_snpathref = None
69
- if (cc_snpathref_elem := et_element.find("VALUE-SNPATHREF")) is not None:
70
- value_snpathref = cc_snpathref_elem.attrib["SHORT-NAME-PATH"]
71
-
72
- phys_const_snref = None
73
- if (cc_snref_elem := et_element.find("PHYS-CONST-SNREF")) is not None:
74
- phys_const_snref = cc_snref_elem.attrib["SHORT-NAME"]
75
- phys_const_snpathref = None
76
- if (cc_snpathref_elem := et_element.find("PHYS-CONST-SNPATHREF")) is not None:
77
- phys_const_snpathref = cc_snpathref_elem.attrib["SHORT-NAME-PATH"]
78
-
79
- table_key_snref = None
80
- if (cc_snref_elem := et_element.find("TABLE-KEY-SNREF")) is not None:
81
- table_key_snref = cc_snref_elem.attrib["SHORT-NAME"]
82
- table_key_snpathref = None
83
- if (cc_snpathref_elem := et_element.find("TABLE-KEY-SNPATHREF")) is not None:
84
- table_key_snpathref = cc_snpathref_elem.attrib["SHORT-NAME-PATH"]
85
-
86
- return PosResponseSuppressible(
87
- bit_mask=bit_mask,
88
- coded_const_snref=coded_const_snref,
89
- coded_const_snpathref=coded_const_snpathref,
90
- value_snref=value_snref,
91
- value_snpathref=value_snpathref,
92
- phys_const_snref=phys_const_snref,
93
- phys_const_snpathref=phys_const_snpathref,
94
- table_key_snref=table_key_snref,
95
- table_key_snpathref=table_key_snpathref,
96
- )
97
-
98
-
99
23
  @dataclass
100
24
  class DiagService(DiagComm):
101
25
  """Representation of a diagnostic service description.
102
26
  """
103
27
 
104
- comparam_refs: List[ComparamInstance]
28
+ comparam_refs: list[ComparamInstance]
105
29
  request_ref: OdxLinkRef
106
- pos_response_refs: List[OdxLinkRef]
107
- neg_response_refs: List[OdxLinkRef]
108
- pos_response_suppressible: Optional[PosResponseSuppressible]
30
+ pos_response_refs: list[OdxLinkRef]
31
+ neg_response_refs: list[OdxLinkRef]
32
+ pos_response_suppressible: PosResponseSuppressible | None
109
33
 
110
- is_cyclic_raw: Optional[bool]
111
- is_multiple_raw: Optional[bool]
112
- addressing_raw: Optional[Addressing]
113
- transmission_mode_raw: Optional[TransMode]
34
+ is_cyclic_raw: bool | None
35
+ is_multiple_raw: bool | None
36
+ addressing_raw: Addressing | None
37
+ transmission_mode_raw: TransMode | None
114
38
 
115
39
  @property
116
40
  def comparams(self) -> NamedItemList[ComparamInstance]:
117
41
  return self._comparams
118
42
 
119
43
  @property
120
- def request(self) -> Optional[Request]:
44
+ def request(self) -> Request | None:
121
45
  return self._request
122
46
 
123
47
  @property
@@ -145,14 +69,14 @@ class DiagService(DiagComm):
145
69
  return self.transmission_mode_raw or TransMode.SEND_AND_RECEIVE
146
70
 
147
71
  @property
148
- def free_parameters(self) -> List[Parameter]:
72
+ def free_parameters(self) -> list[Parameter]:
149
73
  """Return the list of parameters which can be freely specified by
150
74
  the user when encoding the service's request.
151
75
  """
152
76
  return self.request.free_parameters if self.request is not None else []
153
77
 
154
78
  @staticmethod
155
- def from_et(et_element: ElementTree.Element, doc_frags: List[OdxDocFragment]) -> "DiagService":
79
+ def from_et(et_element: ElementTree.Element, doc_frags: list[OdxDocFragment]) -> "DiagService":
156
80
 
157
81
  kwargs = dataclass_fields_asdict(DiagComm.from_et(et_element, doc_frags))
158
82
 
@@ -180,7 +104,7 @@ class DiagService(DiagComm):
180
104
  is_cyclic_raw = odxstr_to_bool(et_element.get("IS-CYCLIC"))
181
105
  is_multiple_raw = odxstr_to_bool(et_element.get("IS-MULTIPLE"))
182
106
 
183
- addressing_raw: Optional[Addressing] = None
107
+ addressing_raw: Addressing | None = None
184
108
  if (addressing_raw_str := et_element.get("ADDRESSING")) is not None:
185
109
  try:
186
110
  addressing_raw = Addressing(addressing_raw_str)
@@ -188,7 +112,7 @@ class DiagService(DiagComm):
188
112
  addressing_raw = cast(Addressing, None)
189
113
  odxraise(f"Encountered unknown addressing type '{addressing_raw_str}'")
190
114
 
191
- transmission_mode_raw: Optional[TransMode] = None
115
+ transmission_mode_raw: TransMode | None = None
192
116
  if (transmission_mode_raw_str := et_element.get("TRANSMISSION-MODE")) is not None:
193
117
  try:
194
118
  transmission_mode_raw = TransMode(transmission_mode_raw_str)
@@ -208,7 +132,7 @@ class DiagService(DiagComm):
208
132
  transmission_mode_raw=transmission_mode_raw,
209
133
  **kwargs)
210
134
 
211
- def _build_odxlinks(self) -> Dict[OdxLinkId, Any]:
135
+ def _build_odxlinks(self) -> dict[OdxLinkId, Any]:
212
136
  result = super()._build_odxlinks()
213
137
 
214
138
  for cpr in self.comparam_refs:
@@ -256,20 +180,20 @@ class DiagService(DiagComm):
256
180
 
257
181
  def decode_message(self, raw_message: bytes) -> Message:
258
182
  request_prefix = b''
259
- candidate_coding_objects: List[Union[Request, Response]] = [
183
+ candidate_coding_objects: list[Request | Response] = [
260
184
  *self.positive_responses, *self.negative_responses
261
185
  ]
262
186
  if self.request is not None:
263
187
  request_prefix = self.request.coded_const_prefix()
264
188
  candidate_coding_objects.append(self.request)
265
189
 
266
- coding_objects: List[Union[Request, Response]] = []
190
+ coding_objects: list[Request | Response] = []
267
191
  for candidate_coding_object in candidate_coding_objects:
268
192
  prefix = candidate_coding_object.coded_const_prefix(request_prefix=request_prefix)
269
193
  if len(raw_message) >= len(prefix) and prefix == raw_message[:len(prefix)]:
270
194
  coding_objects.append(candidate_coding_object)
271
195
 
272
- result_list: List[Message] = []
196
+ result_list: list[Message] = []
273
197
  for coding_object in coding_objects:
274
198
  try:
275
199
  result_list.append(