odxtools 7.4.0__py3-none-any.whl → 8.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 (50) hide show
  1. odxtools/cli/_print_utils.py +4 -3
  2. odxtools/cli/browse.py +13 -11
  3. odxtools/cli/compare.py +1 -1
  4. odxtools/cli/list.py +20 -10
  5. odxtools/cli/snoop.py +28 -5
  6. odxtools/database.py +46 -10
  7. odxtools/diagcomm.py +4 -4
  8. odxtools/diaglayercontainer.py +23 -11
  9. odxtools/diaglayers/basevariant.py +128 -0
  10. odxtools/diaglayers/basevariantraw.py +117 -0
  11. odxtools/diaglayers/diaglayer.py +422 -0
  12. odxtools/{diaglayerraw.py → diaglayers/diaglayerraw.py} +18 -156
  13. odxtools/diaglayers/ecushareddata.py +96 -0
  14. odxtools/diaglayers/ecushareddataraw.py +81 -0
  15. odxtools/diaglayers/ecuvariant.py +124 -0
  16. odxtools/diaglayers/ecuvariantraw.py +123 -0
  17. odxtools/diaglayers/functionalgroup.py +110 -0
  18. odxtools/diaglayers/functionalgroupraw.py +100 -0
  19. odxtools/{diaglayer.py → diaglayers/hierarchyelement.py} +183 -498
  20. odxtools/diaglayers/hierarchyelementraw.py +52 -0
  21. odxtools/diaglayers/protocol.py +64 -0
  22. odxtools/diaglayers/protocolraw.py +85 -0
  23. odxtools/diagvariable.py +10 -1
  24. odxtools/ecuvariantmatcher.py +6 -7
  25. odxtools/field.py +3 -4
  26. odxtools/matchingparameter.py +1 -1
  27. odxtools/nameditemlist.py +4 -3
  28. odxtools/parameterinfo.py +10 -1
  29. odxtools/parameters/parameterwithdop.py +2 -3
  30. odxtools/parentref.py +1 -1
  31. odxtools/snrefcontext.py +1 -1
  32. odxtools/templates/diag_layer_container.odx-d.xml.jinja2 +11 -6
  33. odxtools/templates/macros/printBaseVariant.xml.jinja2 +53 -0
  34. odxtools/templates/macros/printCompuMethod.xml.jinja2 +1 -1
  35. odxtools/templates/macros/printDiagLayer.xml.jinja2 +206 -0
  36. odxtools/templates/macros/printEcuSharedData.xml.jinja2 +30 -0
  37. odxtools/templates/macros/printEcuVariant.xml.jinja2 +53 -0
  38. odxtools/templates/macros/printFunctionalGroup.xml.jinja2 +40 -0
  39. odxtools/templates/macros/printHierarchyElement.xml.jinja2 +24 -0
  40. odxtools/templates/macros/printProtocol.xml.jinja2 +30 -0
  41. odxtools/variablegroup.py +11 -1
  42. odxtools/version.py +2 -2
  43. {odxtools-7.4.0.dist-info → odxtools-8.0.0.dist-info}/METADATA +1 -1
  44. {odxtools-7.4.0.dist-info → odxtools-8.0.0.dist-info}/RECORD +49 -31
  45. odxtools/templates/macros/printVariant.xml.jinja2 +0 -241
  46. /odxtools/{diaglayertype.py → diaglayers/diaglayertype.py} +0 -0
  47. {odxtools-7.4.0.dist-info → odxtools-8.0.0.dist-info}/LICENSE +0 -0
  48. {odxtools-7.4.0.dist-info → odxtools-8.0.0.dist-info}/WHEEL +0 -0
  49. {odxtools-7.4.0.dist-info → odxtools-8.0.0.dist-info}/entry_points.txt +0 -0
  50. {odxtools-7.4.0.dist-info → odxtools-8.0.0.dist-info}/top_level.txt +0 -0
@@ -7,7 +7,7 @@ import markdownify
7
7
  from tabulate import tabulate # TODO: switch to rich tables
8
8
 
9
9
  from ..description import Description
10
- from ..diaglayer import DiagLayer
10
+ from ..diaglayers.diaglayer import DiagLayer
11
11
  from ..diagservice import DiagService
12
12
  from ..parameters.codedconstparameter import CodedConstParameter
13
13
  from ..parameters.nrcconstparameter import NrcConstParameter
@@ -243,8 +243,9 @@ def print_dl_metrics(variants: List[DiagLayer], print_fn: Callable[..., Any] = p
243
243
  name.append(variant.short_name)
244
244
  type.append(variant.variant_type.value)
245
245
  num_services.append(len(all_services))
246
- num_dops.append(len(variant.diag_data_dictionary_spec.data_object_props))
247
- num_comparam_refs.append(len(variant.comparam_refs))
246
+ ddds = variant.diag_data_dictionary_spec
247
+ num_dops.append(len(ddds.data_object_props))
248
+ num_comparam_refs.append(len(getattr(variant, "comparams_refs", [])))
248
249
 
249
250
  table = {
250
251
  'Name': name,
odxtools/cli/browse.py CHANGED
@@ -12,6 +12,7 @@ from ..dataobjectproperty import DataObjectProperty
12
12
  from ..diaglayer import DiagLayer
13
13
  from ..diagservice import DiagService
14
14
  from ..exceptions import odxraise, odxrequire
15
+ from ..hierarchyelement import HierarchyElement
15
16
  from ..odxtypes import AtomicOdxType, DataType, ParameterValueDict
16
17
  from ..parameters.matchingrequestparameter import MatchingRequestParameter
17
18
  from ..parameters.parameter import Parameter
@@ -283,19 +284,20 @@ def browse(odxdb: Database) -> None:
283
284
  print(f"{type(answer.get('variant'))=}")
284
285
  assert isinstance(variant, DiagLayer)
285
286
 
286
- if (rx_id := variant.get_receive_id()) is not None:
287
- recv_id = hex(rx_id)
288
- else:
289
- recv_id = "None"
287
+ if isinstance(variant, HierarchyElement):
288
+ if (rx_id := variant.get_receive_id()) is not None:
289
+ recv_id = hex(rx_id)
290
+ else:
291
+ recv_id = "None"
290
292
 
291
- if (tx_id := variant.get_send_id()) is not None:
292
- send_id = hex(tx_id)
293
- else:
294
- send_id = "None"
293
+ if (tx_id := variant.get_send_id()) is not None:
294
+ send_id = hex(tx_id)
295
+ else:
296
+ send_id = "None"
295
297
 
296
- print(
297
- f"{variant.variant_type.value} '{variant.short_name}' (Receive ID: {recv_id}, Send ID: {send_id})"
298
- )
298
+ print(
299
+ f"{variant.variant_type.value} '{variant.short_name}' (Receive ID: {recv_id}, Send ID: {send_id})"
300
+ )
299
301
 
300
302
  while True:
301
303
  services: List[DiagService] = [
odxtools/cli/compare.py CHANGED
@@ -9,7 +9,7 @@ from rich import print
9
9
  from tabulate import tabulate # TODO: switch to rich tables
10
10
 
11
11
  from ..database import Database
12
- from ..diaglayer import DiagLayer
12
+ from ..diaglayers.diaglayer import DiagLayer
13
13
  from ..diagservice import DiagService
14
14
  from ..loadfile import load_file
15
15
  from ..odxtypes import AtomicOdxType
odxtools/cli/list.py CHANGED
@@ -4,9 +4,14 @@ from typing import Callable, List, Optional
4
4
 
5
5
  import rich
6
6
 
7
+ from ..comparaminstance import ComparamInstance
7
8
  from ..database import Database
9
+ from ..dataobjectproperty import DataObjectProperty
8
10
  from ..diagcomm import DiagComm
9
- from ..diaglayer import DiagLayer
11
+ from ..diaglayers.basevariant import BaseVariant
12
+ from ..diaglayers.diaglayer import DiagLayer
13
+ from ..diaglayers.ecuvariant import EcuVariant
14
+ from ..diaglayers.hierarchyelement import HierarchyElement
10
15
  from ..diagservice import DiagService
11
16
  from ..singleecujob import SingleEcuJob
12
17
  from . import _parser_utils
@@ -56,15 +61,14 @@ def print_summary(odxdb: Database,
56
61
 
57
62
  all_services: List[DiagComm] = sorted(dl.services, key=lambda x: x.short_name)
58
63
 
59
- data_object_properties = dl.diag_data_dictionary_spec.data_object_props
60
- comparam_refs = dl.comparam_refs
64
+ if isinstance(dl, (BaseVariant, EcuVariant)):
65
+ for proto in dl.protocols:
66
+ if (can_rx_id := dl.get_can_receive_id(proto.short_name)) is not None:
67
+ rich.print(
68
+ f" CAN receive ID for protocol '{proto.short_name}': 0x{can_rx_id:x}")
61
69
 
62
- for proto in dl.protocols:
63
- if (can_rx_id := dl.get_can_receive_id(proto.short_name)) is not None:
64
- rich.print(f" CAN receive ID for protocol '{proto.short_name}': 0x{can_rx_id:x}")
65
-
66
- if (can_tx_id := dl.get_can_send_id(proto.short_name)) is not None:
67
- rich.print(f" CAN send ID for protocol '{proto.short_name}': 0x{can_tx_id:x}")
70
+ if (can_tx_id := dl.get_can_send_id(proto.short_name)) is not None:
71
+ rich.print(f" CAN send ID for protocol '{proto.short_name}': 0x{can_tx_id:x}")
68
72
 
69
73
  if dl.description:
70
74
  desc = format_desc(dl.description, indent=2)
@@ -95,7 +99,9 @@ def print_summary(odxdb: Database,
95
99
  rich.print(f" Single ECU job: {service.odx_id}")
96
100
  else:
97
101
  rich.print(f" Unidentifiable service: {service}")
98
-
102
+ ddd_spec = dl.diag_data_dictionary_spec
103
+ data_object_properties: List[
104
+ DataObjectProperty] = [] if ddd_spec is None else ddd_spec.data_object_props
99
105
  if print_dops and len(data_object_properties) > 0:
100
106
  rich.print("\n")
101
107
  rich.print(f"The DOPs of the {dl.variant_type.value} '{dl.short_name}' are: ")
@@ -103,6 +109,10 @@ def print_summary(odxdb: Database,
103
109
  data_object_properties, key=lambda x: (type(x).__name__, x.short_name)):
104
110
  rich.print(" " + str(dop.short_name).replace("\n", "\n "))
105
111
 
112
+ comparam_refs: List[ComparamInstance] = []
113
+ if isinstance(dl, HierarchyElement):
114
+ comparam_refs = dl.comparam_refs
115
+
106
116
  if print_comparams and len(comparam_refs) > 0:
107
117
  rich.print("\n")
108
118
  rich.print(
odxtools/cli/snoop.py CHANGED
@@ -4,12 +4,13 @@
4
4
  import argparse
5
5
  import asyncio
6
6
  import sys
7
- from typing import Any, Type
7
+ from typing import Any, List, Optional, Type
8
8
 
9
9
  import can
10
10
 
11
11
  import odxtools.isotp_state_machine as ism
12
12
  import odxtools.uds as uds
13
+ from odxtools.diaglayers.protocol import Protocol
13
14
  from odxtools.exceptions import DecodeError
14
15
  from odxtools.isotp_state_machine import IsoTpStateMachine
15
16
  from odxtools.response import Response, ResponseType
@@ -245,21 +246,43 @@ def run(args: argparse.Namespace) -> None:
245
246
 
246
247
  protocol_name = args.protocol
247
248
  if odx_diag_layer is not None and protocol_name is not None:
248
- protocol_names = [x.short_name for x in odx_diag_layer.protocols]
249
+ protocols: Optional[List[Protocol]] = getattr(odx_diag_layer, "protocols", None)
250
+
251
+ if protocols is None:
252
+ print(f"ECU variant {odx_diag_layer.short_name} is of type "
253
+ f"{odx_diag_layer.variant_type.value} and thus does not "
254
+ f"feature any protocols")
255
+ sys.exit(1)
256
+
257
+ protocol_names = [x.short_name for x in protocols]
249
258
  if protocol_name not in protocol_names:
250
259
  print(f"ECU variant {odx_diag_layer.short_name} does not support "
251
260
  f"a protocol named '{protocol_name}'. Supported protocols are:")
252
- for x in odx_diag_layer.protocols:
261
+ for x in protocols:
253
262
  desc = "" if x.description is None else f": {x.description}"
254
263
  print(f" {x.short_name}{desc}")
255
264
  sys.exit(1)
256
265
 
257
266
  # if no can IDs have been explicitly specified, take them from the DL
258
267
  if args.rx is None and odx_diag_layer:
259
- args.rx = str(odx_diag_layer.get_can_receive_id(protocol=protocol_name))
268
+ get_can_rx_id = getattr(odx_diag_layer, "get_can_receive_id", None)
269
+ if get_can_rx_id is None:
270
+ print(f"ECU variant {odx_diag_layer.short_name} is of type "
271
+ f"{odx_diag_layer.variant_type.value} and thus does not "
272
+ f"provide any communication parameters")
273
+ sys.exit(1)
274
+
275
+ args.rx = str(get_can_rx_id(protocol=protocol_name))
260
276
 
261
277
  if args.tx is None and odx_diag_layer:
262
- args.tx = str(odx_diag_layer.get_can_send_id(protocol=protocol_name))
278
+ get_can_tx_id = getattr(odx_diag_layer, "get_can_send_id", None)
279
+ if get_can_tx_id is None:
280
+ print(f"ECU variant {odx_diag_layer.short_name} is of type "
281
+ f"{odx_diag_layer.variant_type.value} and thus does not "
282
+ f"provide any communication parameters")
283
+ sys.exit(1)
284
+
285
+ args.tx = str(get_can_tx_id(protocol=protocol_name))
263
286
 
264
287
  if args.rx is None:
265
288
  print(f"Could not determine a CAN receive ID.")
odxtools/database.py CHANGED
@@ -9,8 +9,13 @@ from packaging.version import Version
9
9
 
10
10
  from .comparamspec import ComparamSpec
11
11
  from .comparamsubset import ComparamSubset
12
- from .diaglayer import DiagLayer
13
12
  from .diaglayercontainer import DiagLayerContainer
13
+ from .diaglayers.basevariant import BaseVariant
14
+ from .diaglayers.diaglayer import DiagLayer
15
+ from .diaglayers.ecushareddata import EcuSharedData
16
+ from .diaglayers.ecuvariant import EcuVariant
17
+ from .diaglayers.functionalgroup import FunctionalGroup
18
+ from .diaglayers.protocol import Protocol
14
19
  from .exceptions import odxraise
15
20
  from .nameditemlist import NamedItemList
16
21
  from .odxlink import OdxLinkDatabase, OdxLinkId
@@ -101,10 +106,16 @@ class Database:
101
106
  self._diag_layers = NamedItemList(
102
107
  chain(*[dlc.diag_layers for dlc in self.diag_layer_containers]))
103
108
 
109
+ self._ecu_shared_datas = NamedItemList(
110
+ chain(*[dlc.ecu_shared_datas for dlc in self.diag_layer_containers]))
104
111
  self._protocols = NamedItemList(
105
112
  chain(*[dlc.protocols for dlc in self.diag_layer_containers]))
106
-
107
- self._ecus = NamedItemList(chain(*[dlc.ecu_variants for dlc in self.diag_layer_containers]))
113
+ self._functional_groups = NamedItemList(
114
+ chain(*[dlc.functional_groups for dlc in self.diag_layer_containers]))
115
+ self._base_variants = NamedItemList(
116
+ chain(*[dlc.base_variants for dlc in self.diag_layer_containers]))
117
+ self._ecu_variants = NamedItemList(
118
+ chain(*[dlc.ecu_variants for dlc in self.diag_layer_containers]))
108
119
 
109
120
  # Build odxlinks
110
121
  self._odxlinks = OdxLinkDatabase()
@@ -145,19 +156,44 @@ class Database:
145
156
  return self._odxlinks
146
157
 
147
158
  @property
148
- def protocols(self) -> NamedItemList[DiagLayer]:
149
- """
150
- All protocols defined by this database
159
+ def ecu_shared_datas(self) -> NamedItemList[EcuSharedData]:
160
+ """All ECU shared data layers defined by this database
161
+
162
+ ECU shared data layers act as a kind of shared library for
163
+ data that is common to multiple otherwise unrelated ECUs.
164
+
151
165
  """
166
+ return self._ecu_shared_datas
167
+
168
+ @property
169
+ def protocols(self) -> NamedItemList[Protocol]:
170
+ """All protocol layers defined by this database"""
152
171
  return self._protocols
153
172
 
154
173
  @property
155
- def ecus(self) -> NamedItemList[DiagLayer]:
156
- """ECU-variants defined in the database"""
157
- return self._ecus
174
+ def functional_groups(self) -> NamedItemList["FunctionalGroup"]:
175
+ """Functional group layers defined in the database"""
176
+ return self._functional_groups
177
+
178
+ @property
179
+ def base_variants(self) -> NamedItemList["BaseVariant"]:
180
+ """Base variants defined in the database"""
181
+ return self._base_variants
182
+
183
+ @property
184
+ def ecus(self) -> NamedItemList["EcuVariant"]:
185
+ """ECU variants defined in the database
186
+
187
+ This property is an alias for `.ecu_variants`"""
188
+ return self._ecu_variants
189
+
190
+ @property
191
+ def ecu_variants(self) -> NamedItemList["EcuVariant"]:
192
+ """ECU variants defined in the database"""
193
+ return self._ecu_variants
158
194
 
159
195
  @property
160
- def diag_layers(self) -> NamedItemList[DiagLayer]:
196
+ def diag_layers(self) -> NamedItemList["DiagLayer"]:
161
197
  """All diagnostic layers defined in the database"""
162
198
  return self._diag_layers
163
199
 
odxtools/diagcomm.py CHANGED
@@ -19,7 +19,7 @@ from .statetransition import StateTransition
19
19
  from .utils import dataclass_fields_asdict
20
20
 
21
21
  if TYPE_CHECKING:
22
- from .diaglayer import DiagLayer
22
+ from .diaglayers.protocol import Protocol
23
23
 
24
24
 
25
25
  class DiagClassType(Enum):
@@ -143,7 +143,7 @@ class DiagComm(IdentifiableElement):
143
143
  return self._functional_classes
144
144
 
145
145
  @property
146
- def protocols(self) -> NamedItemList["DiagLayer"]:
146
+ def protocols(self) -> NamedItemList["Protocol"]:
147
147
  return self._protocols
148
148
 
149
149
  @property
@@ -216,12 +216,12 @@ class DiagComm(IdentifiableElement):
216
216
  if TYPE_CHECKING:
217
217
  diag_layer = odxrequire(context.diag_layer)
218
218
  self._protocols = NamedItemList([
219
- resolve_snref(prot_snref, diag_layer.protocols, DiagLayer)
219
+ resolve_snref(prot_snref, getattr(diag_layer, "protocols", []), Protocol)
220
220
  for prot_snref in self.protocol_snrefs
221
221
  ])
222
222
  else:
223
223
  diag_layer = odxrequire(context.diag_layer)
224
224
  self._protocols = NamedItemList([
225
- resolve_snref(prot_snref, diag_layer.protocols)
225
+ resolve_snref(prot_snref, getattr(diag_layer, "protocols", []))
226
226
  for prot_snref in self.protocol_snrefs
227
227
  ])
@@ -6,7 +6,12 @@ from xml.etree import ElementTree
6
6
 
7
7
  from .admindata import AdminData
8
8
  from .companydata import CompanyData
9
- from .diaglayer import DiagLayer
9
+ from .diaglayers.basevariant import BaseVariant
10
+ from .diaglayers.diaglayer import DiagLayer
11
+ from .diaglayers.ecushareddata import EcuSharedData
12
+ from .diaglayers.ecuvariant import EcuVariant
13
+ from .diaglayers.functionalgroup import FunctionalGroup
14
+ from .diaglayers.protocol import Protocol
10
15
  from .element import IdentifiableElement
11
16
  from .exceptions import odxrequire
12
17
  from .nameditemlist import NamedItemList
@@ -22,13 +27,20 @@ if TYPE_CHECKING:
22
27
  class DiagLayerContainer(IdentifiableElement):
23
28
  admin_data: Optional[AdminData]
24
29
  company_datas: NamedItemList[CompanyData]
25
- ecu_shared_datas: NamedItemList[DiagLayer]
26
- protocols: NamedItemList[DiagLayer]
27
- functional_groups: NamedItemList[DiagLayer]
28
- base_variants: NamedItemList[DiagLayer]
29
- ecu_variants: NamedItemList[DiagLayer]
30
+ ecu_shared_datas: NamedItemList[EcuSharedData]
31
+ protocols: NamedItemList[Protocol]
32
+ functional_groups: NamedItemList[FunctionalGroup]
33
+ base_variants: NamedItemList[BaseVariant]
34
+ ecu_variants: NamedItemList[EcuVariant]
30
35
  sdgs: List[SpecialDataGroup]
31
36
 
37
+ @property
38
+ def ecus(self) -> NamedItemList[EcuVariant]:
39
+ """ECU variants defined in the container
40
+
41
+ This property is an alias for `.ecu_variants`"""
42
+ return self.ecu_variants
43
+
32
44
  def __post_init__(self) -> None:
33
45
  self._diag_layers = NamedItemList[DiagLayer](chain(
34
46
  self.ecu_shared_datas,
@@ -54,23 +66,23 @@ class DiagLayerContainer(IdentifiableElement):
54
66
  for cde in et_element.iterfind("COMPANY-DATAS/COMPANY-DATA")
55
67
  ])
56
68
  ecu_shared_datas = NamedItemList([
57
- DiagLayer.from_et(dl_element, doc_frags)
69
+ EcuSharedData.from_et(dl_element, doc_frags)
58
70
  for dl_element in et_element.iterfind("ECU-SHARED-DATAS/ECU-SHARED-DATA")
59
71
  ])
60
72
  protocols = NamedItemList([
61
- DiagLayer.from_et(dl_element, doc_frags)
73
+ Protocol.from_et(dl_element, doc_frags)
62
74
  for dl_element in et_element.iterfind("PROTOCOLS/PROTOCOL")
63
75
  ])
64
76
  functional_groups = NamedItemList([
65
- DiagLayer.from_et(dl_element, doc_frags)
77
+ FunctionalGroup.from_et(dl_element, doc_frags)
66
78
  for dl_element in et_element.iterfind("FUNCTIONAL-GROUPS/FUNCTIONAL-GROUP")
67
79
  ])
68
80
  base_variants = NamedItemList([
69
- DiagLayer.from_et(dl_element, doc_frags)
81
+ BaseVariant.from_et(dl_element, doc_frags)
70
82
  for dl_element in et_element.iterfind("BASE-VARIANTS/BASE-VARIANT")
71
83
  ])
72
84
  ecu_variants = NamedItemList([
73
- DiagLayer.from_et(dl_element, doc_frags)
85
+ EcuVariant.from_et(dl_element, doc_frags)
74
86
  for dl_element in et_element.iterfind("ECU-VARIANTS/ECU-VARIANT")
75
87
  ])
76
88
  sdgs = [
@@ -0,0 +1,128 @@
1
+ # SPDX-License-Identifier: MIT
2
+ from copy import deepcopy
3
+ from dataclasses import dataclass
4
+ from typing import Any, Dict, Iterable, List, Optional, Union, cast
5
+ from xml.etree import ElementTree
6
+
7
+ from typing_extensions import override
8
+
9
+ from ..diagvariable import DiagVariable
10
+ from ..dyndefinedspec import DynDefinedSpec
11
+ from ..exceptions import odxassert
12
+ from ..nameditemlist import NamedItemList
13
+ from ..odxlink import OdxDocFragment, OdxLinkDatabase, OdxLinkRef
14
+ from ..parentref import ParentRef
15
+ from ..variablegroup import VariableGroup
16
+ from .basevariantraw import BaseVariantRaw
17
+ from .diaglayer import DiagLayer
18
+ from .hierarchyelement import HierarchyElement
19
+
20
+
21
+ @dataclass
22
+ class BaseVariant(HierarchyElement):
23
+ """This is a diagnostic layer for common functionality of an ECU
24
+ """
25
+
26
+ @property
27
+ def base_variant_raw(self) -> BaseVariantRaw:
28
+ return cast(BaseVariantRaw, self.diag_layer_raw)
29
+
30
+ #####
31
+ # <properties forwarded to the "raw" base variant>
32
+ #####
33
+ @property
34
+ def diag_variables_raw(self) -> List[Union[DiagVariable, OdxLinkRef]]:
35
+ return self.base_variant_raw.diag_variables_raw
36
+
37
+ @property
38
+ def dyn_defined_spec(self) -> Optional[DynDefinedSpec]:
39
+ return self.base_variant_raw.dyn_defined_spec
40
+
41
+ @property
42
+ def parent_refs(self) -> List[ParentRef]:
43
+ return self.base_variant_raw.parent_refs
44
+
45
+ #####
46
+ # </properties forwarded to the "raw" base variant>
47
+ #####
48
+
49
+ #######
50
+ # <properties subject to value inheritance>
51
+ #######
52
+ @property
53
+ def diag_variables(self) -> NamedItemList[DiagVariable]:
54
+ return self._diag_variables
55
+
56
+ @property
57
+ def variable_groups(self) -> NamedItemList[VariableGroup]:
58
+ return self._variable_groups
59
+
60
+ #######
61
+ # </properties subject to value inheritance>
62
+ #######
63
+
64
+ @staticmethod
65
+ def from_et(et_element: ElementTree.Element, doc_frags: List[OdxDocFragment]) -> "BaseVariant":
66
+ base_variant_raw = BaseVariantRaw.from_et(et_element, doc_frags)
67
+
68
+ return BaseVariant(diag_layer_raw=base_variant_raw)
69
+
70
+ def __post_init__(self) -> None:
71
+ super().__post_init__()
72
+
73
+ odxassert(
74
+ isinstance(self.diag_layer_raw, BaseVariantRaw),
75
+ "The raw diagnostic layer passed to BaseVariant "
76
+ "must be a BaseVariantRaw")
77
+
78
+ def __deepcopy__(self, memo: Dict[int, Any]) -> Any:
79
+ """Create a deep copy of the base variant
80
+
81
+ Note that the copied diagnostic layer is not fully
82
+ initialized, so `_finalize_init()` should to be called on it
83
+ before it can be used normally.
84
+ """
85
+
86
+ result = super().__deepcopy__(memo)
87
+
88
+ # note that the self.base_variant_raw object is *not* copied at
89
+ # this place because the attribute points to the same object
90
+ # as self.diag_layer_raw.
91
+ result.base_variant_raw = deepcopy(self.base_variant_raw, memo)
92
+
93
+ return result
94
+
95
+ @override
96
+ def _compute_value_inheritance(self, odxlinks: OdxLinkDatabase) -> None:
97
+ super()._compute_value_inheritance(odxlinks)
98
+
99
+ self._diag_variables = NamedItemList(self._compute_available_diag_variables(odxlinks))
100
+ self._variable_groups = NamedItemList(self._compute_available_variable_groups(odxlinks))
101
+
102
+ def _compute_available_diag_variables(self,
103
+ odxlinks: OdxLinkDatabase) -> Iterable[DiagVariable]:
104
+
105
+ def get_local_objects_fn(dl: DiagLayer) -> Iterable[DiagVariable]:
106
+ if not hasattr(dl.diag_layer_raw, "diag_variables"):
107
+ return []
108
+
109
+ return dl.diag_layer_raw.diag_variables # type: ignore[no-any-return]
110
+
111
+ def not_inherited_fn(parent_ref: ParentRef) -> List[str]:
112
+ return parent_ref.not_inherited_variables
113
+
114
+ return self._compute_available_objects(get_local_objects_fn, not_inherited_fn)
115
+
116
+ def _compute_available_variable_groups(self,
117
+ odxlinks: OdxLinkDatabase) -> Iterable[VariableGroup]:
118
+
119
+ def get_local_objects_fn(dl: DiagLayer) -> Iterable[VariableGroup]:
120
+ if not hasattr(dl.diag_layer_raw, "variable_groups"):
121
+ return []
122
+
123
+ return dl.diag_layer_raw.variable_groups # type: ignore[no-any-return]
124
+
125
+ def not_inherited_fn(parent_ref: ParentRef) -> List[str]:
126
+ return []
127
+
128
+ return self._compute_available_objects(get_local_objects_fn, not_inherited_fn)
@@ -0,0 +1,117 @@
1
+ # SPDX-License-Identifier: MIT
2
+ from dataclasses import dataclass
3
+ from typing import Any, Dict, List, Optional, Union
4
+ from xml.etree import ElementTree
5
+
6
+ from ..diagvariable import DiagVariable
7
+ from ..dyndefinedspec import DynDefinedSpec
8
+ from ..exceptions import odxraise
9
+ from ..nameditemlist import NamedItemList
10
+ from ..odxlink import OdxDocFragment, OdxLinkDatabase, OdxLinkId, OdxLinkRef
11
+ from ..parentref import ParentRef
12
+ from ..snrefcontext import SnRefContext
13
+ from ..utils import dataclass_fields_asdict
14
+ from ..variablegroup import VariableGroup
15
+ from .hierarchyelementraw import HierarchyElementRaw
16
+
17
+
18
+ @dataclass
19
+ class BaseVariantRaw(HierarchyElementRaw):
20
+ """This is a diagnostic layer for common functionality of an ECU
21
+ """
22
+
23
+ diag_variables_raw: List[Union[DiagVariable, OdxLinkRef]]
24
+ variable_groups: NamedItemList[VariableGroup]
25
+ dyn_defined_spec: Optional[DynDefinedSpec]
26
+ # TODO: base_variant_pattern: Optional[BaseVariantPattern]
27
+ parent_refs: List[ParentRef]
28
+
29
+ @property
30
+ def diag_variables(self) -> NamedItemList[DiagVariable]:
31
+ return self._diag_variables
32
+
33
+ @staticmethod
34
+ def from_et(et_element: ElementTree.Element,
35
+ doc_frags: List[OdxDocFragment]) -> "BaseVariantRaw":
36
+ kwargs = dataclass_fields_asdict(HierarchyElementRaw.from_et(et_element, doc_frags))
37
+
38
+ diag_variables_raw: List[Union[DiagVariable, OdxLinkRef]] = []
39
+ if (dv_elems := et_element.find("DIAG-VARIABLES")) is not None:
40
+ for dv_proxy_elem in dv_elems:
41
+ dv_proxy: Union[OdxLinkRef, DiagVariable]
42
+ if dv_proxy_elem.tag == "DIAG-VARIABLE-REF":
43
+ dv_proxy = OdxLinkRef.from_et(dv_proxy_elem, doc_frags)
44
+ elif dv_proxy_elem.tag == "DIAG-VARIABLE":
45
+ dv_proxy = DiagVariable.from_et(dv_proxy_elem, doc_frags)
46
+ else:
47
+ odxraise()
48
+
49
+ diag_variables_raw.append(dv_proxy)
50
+
51
+ variable_groups = NamedItemList([
52
+ VariableGroup.from_et(vg_elem, doc_frags)
53
+ for vg_elem in et_element.iterfind("VARIABLE-GROUPS/VARIABLE-GROUP")
54
+ ])
55
+
56
+ dyn_defined_spec = None
57
+ if (dds_elem := et_element.find("DYN-DEFINED-SPEC")) is not None:
58
+ dyn_defined_spec = DynDefinedSpec.from_et(dds_elem, doc_frags)
59
+
60
+ parent_refs = [
61
+ ParentRef.from_et(pr_elem, doc_frags)
62
+ for pr_elem in et_element.iterfind("PARENT-REFS/PARENT-REF")
63
+ ]
64
+
65
+ return BaseVariantRaw(
66
+ diag_variables_raw=diag_variables_raw,
67
+ variable_groups=variable_groups,
68
+ dyn_defined_spec=dyn_defined_spec,
69
+ parent_refs=parent_refs,
70
+ **kwargs)
71
+
72
+ def _build_odxlinks(self) -> Dict[OdxLinkId, Any]:
73
+ result = super()._build_odxlinks()
74
+
75
+ for dv_proxy in self.diag_variables_raw:
76
+ if not isinstance(dv_proxy, OdxLinkRef):
77
+ result.update(dv_proxy._build_odxlinks())
78
+
79
+ if self.dyn_defined_spec is not None:
80
+ result.update(self.dyn_defined_spec._build_odxlinks())
81
+
82
+ for parent_ref in self.parent_refs:
83
+ result.update(parent_ref._build_odxlinks())
84
+
85
+ return result
86
+
87
+ def _resolve_odxlinks(self, odxlinks: OdxLinkDatabase) -> None:
88
+ super()._resolve_odxlinks(odxlinks)
89
+
90
+ self._diag_variables: NamedItemList[DiagVariable] = NamedItemList()
91
+ for dv_proxy in self.diag_variables_raw:
92
+ if isinstance(dv_proxy, OdxLinkRef):
93
+ dv = odxlinks.resolve(dv_proxy, DiagVariable)
94
+ else:
95
+ dv_proxy._resolve_odxlinks(odxlinks)
96
+ dv = dv_proxy
97
+
98
+ self._diag_variables.append(dv)
99
+
100
+ if self.dyn_defined_spec is not None:
101
+ self.dyn_defined_spec._resolve_odxlinks(odxlinks)
102
+
103
+ for parent_ref in self.parent_refs:
104
+ parent_ref._resolve_odxlinks(odxlinks)
105
+
106
+ def _resolve_snrefs(self, context: SnRefContext) -> None:
107
+ super()._resolve_snrefs(context)
108
+
109
+ for dv_proxy in self.diag_variables_raw:
110
+ if not isinstance(dv_proxy, OdxLinkRef):
111
+ dv_proxy._resolve_snrefs(context)
112
+
113
+ if self.dyn_defined_spec is not None:
114
+ self.dyn_defined_spec._resolve_snrefs(context)
115
+
116
+ for parent_ref in self.parent_refs:
117
+ parent_ref._resolve_snrefs(context)