odxtools 7.4.1__py3-none-any.whl → 8.0.1__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 (49) 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/compumethods/createanycompumethod.py +2 -1
  7. odxtools/database.py +46 -10
  8. odxtools/diagcomm.py +4 -4
  9. odxtools/diaglayercontainer.py +23 -11
  10. odxtools/diaglayers/basevariant.py +128 -0
  11. odxtools/diaglayers/basevariantraw.py +117 -0
  12. odxtools/diaglayers/diaglayer.py +422 -0
  13. odxtools/{diaglayerraw.py → diaglayers/diaglayerraw.py} +18 -156
  14. odxtools/diaglayers/ecushareddata.py +96 -0
  15. odxtools/diaglayers/ecushareddataraw.py +81 -0
  16. odxtools/diaglayers/ecuvariant.py +124 -0
  17. odxtools/diaglayers/ecuvariantraw.py +123 -0
  18. odxtools/diaglayers/functionalgroup.py +110 -0
  19. odxtools/diaglayers/functionalgroupraw.py +100 -0
  20. odxtools/{diaglayer.py → diaglayers/hierarchyelement.py} +183 -498
  21. odxtools/diaglayers/hierarchyelementraw.py +52 -0
  22. odxtools/diaglayers/protocol.py +64 -0
  23. odxtools/diaglayers/protocolraw.py +85 -0
  24. odxtools/diagvariable.py +10 -1
  25. odxtools/ecuvariantmatcher.py +6 -7
  26. odxtools/field.py +3 -4
  27. odxtools/matchingparameter.py +1 -1
  28. odxtools/nameditemlist.py +4 -3
  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/printDiagLayer.xml.jinja2 +206 -0
  35. odxtools/templates/macros/printEcuSharedData.xml.jinja2 +30 -0
  36. odxtools/templates/macros/printEcuVariant.xml.jinja2 +53 -0
  37. odxtools/templates/macros/printFunctionalGroup.xml.jinja2 +40 -0
  38. odxtools/templates/macros/printHierarchyElement.xml.jinja2 +24 -0
  39. odxtools/templates/macros/printProtocol.xml.jinja2 +30 -0
  40. odxtools/variablegroup.py +11 -1
  41. odxtools/version.py +2 -2
  42. {odxtools-7.4.1.dist-info → odxtools-8.0.1.dist-info}/METADATA +1 -1
  43. {odxtools-7.4.1.dist-info → odxtools-8.0.1.dist-info}/RECORD +48 -30
  44. {odxtools-7.4.1.dist-info → odxtools-8.0.1.dist-info}/WHEEL +1 -1
  45. odxtools/templates/macros/printVariant.xml.jinja2 +0 -241
  46. /odxtools/{diaglayertype.py → diaglayers/diaglayertype.py} +0 -0
  47. {odxtools-7.4.1.dist-info → odxtools-8.0.1.dist-info}/LICENSE +0 -0
  48. {odxtools-7.4.1.dist-info → odxtools-8.0.1.dist-info}/entry_points.txt +0 -0
  49. {odxtools-7.4.1.dist-info → odxtools-8.0.1.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,422 @@
1
+ # SPDX-License-Identifier: MIT
2
+ from copy import copy, deepcopy
3
+ from dataclasses import dataclass
4
+ from functools import cached_property
5
+ from itertools import chain
6
+ from typing import Any, Callable, Dict, Iterable, List, Optional, Union, cast
7
+ from xml.etree import ElementTree
8
+
9
+ from ..admindata import AdminData
10
+ from ..companydata import CompanyData
11
+ from ..description import Description
12
+ from ..diagcomm import DiagComm
13
+ from ..diagdatadictionaryspec import DiagDataDictionarySpec
14
+ from ..diagservice import DiagService
15
+ from ..exceptions import DecodeError, odxassert, odxraise
16
+ from ..message import Message
17
+ from ..nameditemlist import NamedItemList, TNamed
18
+ from ..odxlink import OdxDocFragment, OdxLinkDatabase, OdxLinkId, OdxLinkRef
19
+ from ..parentref import ParentRef
20
+ from ..request import Request
21
+ from ..response import Response
22
+ from ..servicebinner import ServiceBinner
23
+ from ..singleecujob import SingleEcuJob
24
+ from ..snrefcontext import SnRefContext
25
+ from ..specialdatagroup import SpecialDataGroup
26
+ from ..unitgroup import UnitGroup
27
+ from .diaglayerraw import DiagLayerRaw
28
+ from .diaglayertype import DiagLayerType
29
+
30
+ PrefixTree = Dict[int, Union[List[DiagService], "PrefixTree"]]
31
+
32
+
33
+ @dataclass
34
+ class DiagLayer:
35
+ """This class represents a "logical view" upon a diagnostic layer
36
+ according to the ODX standard.
37
+
38
+ i.e. it handles the value inheritance, communication parameters,
39
+ encoding/decoding of data, etc.
40
+ """
41
+
42
+ diag_layer_raw: DiagLayerRaw
43
+
44
+ @staticmethod
45
+ def from_et(et_element: ElementTree.Element, doc_frags: List[OdxDocFragment]) -> "DiagLayer":
46
+ diag_layer_raw = DiagLayerRaw.from_et(et_element, doc_frags)
47
+
48
+ # Create DiagLayer
49
+ return DiagLayer(diag_layer_raw=diag_layer_raw)
50
+
51
+ def __post_init__(self) -> None:
52
+ if self.diag_layer_raw.diag_data_dictionary_spec is None:
53
+ # create an empry DiagDataDictionarySpec object if the raw
54
+ # layer does not define a DDDS...
55
+ self._diag_data_dictionary_spec = DiagDataDictionarySpec(
56
+ admin_data=None,
57
+ data_object_props=NamedItemList(),
58
+ dtc_dops=NamedItemList(),
59
+ structures=NamedItemList(),
60
+ static_fields=NamedItemList(),
61
+ end_of_pdu_fields=NamedItemList(),
62
+ dynamic_endmarker_fields=NamedItemList(),
63
+ dynamic_length_fields=NamedItemList(),
64
+ tables=NamedItemList(),
65
+ env_data_descs=NamedItemList(),
66
+ env_datas=NamedItemList(),
67
+ muxs=NamedItemList(),
68
+ unit_spec=None,
69
+ sdgs=[])
70
+ else:
71
+ self._diag_data_dictionary_spec = self.diag_layer_raw.diag_data_dictionary_spec
72
+
73
+ def _build_odxlinks(self) -> Dict[OdxLinkId, Any]:
74
+ """Construct a mapping from IDs to all objects that are contained in this diagnostic layer."""
75
+ result = self.diag_layer_raw._build_odxlinks()
76
+
77
+ # we want to get the full diag layer, not just the raw layer
78
+ # when referencing...
79
+ result[self.odx_id] = self
80
+
81
+ return result
82
+
83
+ def _resolve_odxlinks(self, odxlinks: OdxLinkDatabase) -> None:
84
+ """Recursively resolve all ODXLINK references."""
85
+
86
+ # deal with the import references: these basically extend the
87
+ # pool of objects that are referenceable without having to
88
+ # explicitly specify the DOCREF attribute in the
89
+ # reference. This mechanism can thus be seen as a kind of
90
+ # "poor man's inheritance".
91
+ if self.import_refs:
92
+ imported_links: Dict[OdxLinkId, Any] = {}
93
+ for import_ref in self.import_refs:
94
+ imported_dl = odxlinks.resolve(import_ref, DiagLayer)
95
+
96
+ odxassert(
97
+ imported_dl.variant_type == DiagLayerType.ECU_SHARED_DATA,
98
+ f"Tried to import references from diagnostic layer "
99
+ f"'{imported_dl.short_name}' of type {imported_dl.variant_type.value}. "
100
+ f"Only ECU-SHARED-DATA layers may be referenced using the "
101
+ f"IMPORT-REF mechanism")
102
+
103
+ # TODO: ensure that the imported diagnostic layer has
104
+ # not been referenced in any PARENT-REF of the current
105
+ # layer or any of its parents.
106
+
107
+ # TODO: detect and complain about cyclic IMPORT-REFs
108
+
109
+ # TODO (?): detect conflicts with locally-defined
110
+ # objects
111
+
112
+ imported_dl_links = imported_dl._build_odxlinks()
113
+ for link_id, obj in imported_dl_links.items():
114
+ # the imported objects shall behave as if they
115
+ # were defined by the importing layer. IOW, they
116
+ # must be visible in the same document fragments.
117
+ link_id = OdxLinkId(link_id.local_id, self.odx_id.doc_fragments)
118
+ imported_links[link_id] = obj
119
+
120
+ # We need to copy the odxlink database here since this
121
+ # function must not modify its argument because the
122
+ # imported references only apply within this specific
123
+ # diagnostic layer
124
+ extended_odxlinks = copy(odxlinks)
125
+ extended_odxlinks.update(imported_links)
126
+
127
+ self.diag_layer_raw._resolve_odxlinks(extended_odxlinks)
128
+ return
129
+
130
+ self.diag_layer_raw._resolve_odxlinks(odxlinks)
131
+
132
+ def _resolve_snrefs(self, context: SnRefContext) -> None:
133
+ self.diag_layer_raw._resolve_snrefs(context)
134
+
135
+ def _get_local_diag_comms(self, odxlinks: OdxLinkDatabase) -> Iterable[DiagComm]:
136
+ """Return the list of locally defined diagnostic communications.
137
+
138
+ This is not completely trivial as it requires to resolving the
139
+ references specified in the <DIAG-COMMS> XML tag.
140
+ """
141
+ return self.diag_layer_raw.diag_comms
142
+
143
+ def _get_local_unit_groups(self) -> Iterable[UnitGroup]:
144
+ if self.diag_layer_raw.diag_data_dictionary_spec is None:
145
+ return []
146
+
147
+ unit_spec = self.diag_layer_raw.diag_data_dictionary_spec.unit_spec
148
+ if unit_spec is None:
149
+ return []
150
+
151
+ return unit_spec.unit_groups
152
+
153
+ def _compute_available_objects(
154
+ self,
155
+ get_local_objects: Callable[["DiagLayer"], Iterable[TNamed]],
156
+ get_not_inherited: Callable[[ParentRef], Iterable[str]],
157
+ ) -> Iterable[TNamed]:
158
+ """Helper method to compute the set of all objects applicable
159
+ to the DiagLayer if these objects are subject to the value
160
+ inheritance mechanism
161
+
162
+ This is the simplified version for diag layers which do not
163
+ have parents and thus do not deal with value inheritance
164
+ (i.e., ECU-SHARED-DATA).
165
+
166
+ """
167
+ return get_local_objects(self)
168
+
169
+ def __deepcopy__(self, memo: Dict[int, Any]) -> Any:
170
+ """Create a deep copy of the diagnostic layer
171
+
172
+ Note that the copied diagnostic layer is not fully
173
+ initialized, so `_finalize_init()` should to be called on it
174
+ before it can be used normally.
175
+ """
176
+ cls = self.__class__
177
+ result = cls.__new__(cls)
178
+ memo[id(self)] = result
179
+
180
+ result.diag_layer_raw = deepcopy(self.diag_layer_raw, memo)
181
+
182
+ return result
183
+
184
+ #####
185
+ # <convenience functionality>
186
+ #####
187
+ @cached_property
188
+ def service_groups(self) -> ServiceBinner:
189
+ return ServiceBinner(self.services)
190
+
191
+ #####
192
+ # </convenience functionality>
193
+ #####
194
+
195
+ #####
196
+ # <properties forwarded to the "raw" diag layer>
197
+ #####
198
+ @property
199
+ def variant_type(self) -> DiagLayerType:
200
+ return self.diag_layer_raw.variant_type
201
+
202
+ @property
203
+ def odx_id(self) -> OdxLinkId:
204
+ return self.diag_layer_raw.odx_id
205
+
206
+ @property
207
+ def short_name(self) -> str:
208
+ return self.diag_layer_raw.short_name
209
+
210
+ @property
211
+ def long_name(self) -> Optional[str]:
212
+ return self.diag_layer_raw.long_name
213
+
214
+ @property
215
+ def description(self) -> Optional[Description]:
216
+ return self.diag_layer_raw.description
217
+
218
+ @property
219
+ def admin_data(self) -> Optional[AdminData]:
220
+ return self.diag_layer_raw.admin_data
221
+
222
+ @property
223
+ def diag_comms(self) -> NamedItemList[DiagComm]:
224
+ return self.diag_layer_raw.diag_comms
225
+
226
+ @property
227
+ def services(self) -> NamedItemList[DiagService]:
228
+ return self.diag_layer_raw.services
229
+
230
+ @property
231
+ def diag_services(self) -> NamedItemList[DiagService]:
232
+ return self.diag_layer_raw.diag_services
233
+
234
+ @property
235
+ def single_ecu_jobs(self) -> NamedItemList[SingleEcuJob]:
236
+ return self.diag_layer_raw.single_ecu_jobs
237
+
238
+ @property
239
+ def company_datas(self) -> NamedItemList[CompanyData]:
240
+ return self.diag_layer_raw.company_datas
241
+
242
+ @property
243
+ def requests(self) -> NamedItemList[Request]:
244
+ return self.diag_layer_raw.requests
245
+
246
+ @property
247
+ def positive_responses(self) -> NamedItemList[Response]:
248
+ return self.diag_layer_raw.positive_responses
249
+
250
+ @property
251
+ def negative_responses(self) -> NamedItemList[Response]:
252
+ return self.diag_layer_raw.negative_responses
253
+
254
+ @property
255
+ def global_negative_responses(self) -> NamedItemList[Response]:
256
+ return self.diag_layer_raw.global_negative_responses
257
+
258
+ @property
259
+ def import_refs(self) -> List[OdxLinkRef]:
260
+ return self.diag_layer_raw.import_refs
261
+
262
+ @property
263
+ def sdgs(self) -> List[SpecialDataGroup]:
264
+ return self.diag_layer_raw.sdgs
265
+
266
+ @property
267
+ def diag_data_dictionary_spec(self) -> DiagDataDictionarySpec:
268
+ """The DiagDataDictionarySpec applicable to this DiagLayer"""
269
+ return self._diag_data_dictionary_spec
270
+
271
+ #####
272
+ # </properties forwarded to the "raw" diag layer>
273
+ #####
274
+
275
+ #####
276
+ # <PDU decoding>
277
+ #####
278
+ @cached_property
279
+ def _prefix_tree(self) -> PrefixTree:
280
+ """Constructs the coded prefix tree of the services.
281
+
282
+ Each leaf node is a list of `DiagService`s. (This is because
283
+ navigating from a service to the request/ responses is easier
284
+ than finding the service for a given request/response object.)
285
+
286
+ Example:
287
+ Let there be four services with corresponding requests:
288
+ * Request 1 has the coded constant prefix `12 34`.
289
+ * Request 2 has the coded constant prefix `12 34`.
290
+ * Request 3 has the coded constant prefix `12 56`.
291
+ * Request 4 has the coded constant prefix `12 56 00`.
292
+
293
+ Then, the constructed prefix tree is the dict
294
+ ```
295
+ {0x12: {0x34: {-1: [<Service 1>, <Service 2>]},
296
+ 0x56: {-1: [<Service 3>],
297
+ 0x0: {-1: [<Service 4>]}
298
+ }}}
299
+ ```
300
+ Note, that the inner `-1` are constant to distinguish them
301
+ from possible service IDs.
302
+
303
+ Also note, that it is actually allowed that
304
+ (a) SIDs for different services are the same like for service
305
+ 1 and 2 (thus each leaf node is a list) and
306
+ (b) one SID is the prefix of another SID like for service 3
307
+ and 4 (thus the constant `-1` key).
308
+
309
+ """
310
+ prefix_tree: PrefixTree = {}
311
+ for s in self.services:
312
+ # Compute prefixes for the service's request and all
313
+ # possible responses. We need to consider the global
314
+ # negative responses here, because they might contain
315
+ # MATCHING-REQUEST parameters. If these global responses
316
+ # do not contain such parameters, this will potentially
317
+ # result in an enormous amount of decoded messages for
318
+ # global negative responses. (I.e., one for each
319
+ # service. This can be avoided by specifying the
320
+ # corresponding request for `decode_response()`.)
321
+ request_prefix = b''
322
+ if s.request is not None:
323
+ request_prefix = s.request.coded_const_prefix()
324
+ prefixes = [request_prefix]
325
+ gnrs = getattr(self, "global_negative_responses", [])
326
+ prefixes += [
327
+ x.coded_const_prefix(request_prefix=request_prefix)
328
+ for x in chain(s.positive_responses, s.negative_responses, gnrs)
329
+ ]
330
+ for coded_prefix in prefixes:
331
+ self._extend_prefix_tree(prefix_tree, coded_prefix, s)
332
+
333
+ return prefix_tree
334
+
335
+ @staticmethod
336
+ def _extend_prefix_tree(prefix_tree: PrefixTree, coded_prefix: bytes,
337
+ service: DiagService) -> None:
338
+
339
+ # make sure that tree has an entry for the given prefix
340
+ sub_tree = prefix_tree
341
+ for b in coded_prefix:
342
+ if b not in sub_tree:
343
+ sub_tree[b] = {}
344
+ sub_tree = cast(PrefixTree, sub_tree[b])
345
+
346
+ # Store the object as in the prefix tree. This is done by
347
+ # assigning the list of possible objects to the key -1 of the
348
+ # dictionary (this is quite hacky...)
349
+ if sub_tree.get(-1) is None:
350
+ sub_tree[-1] = [service]
351
+ else:
352
+ cast(List[DiagService], sub_tree[-1]).append(service)
353
+
354
+ def _find_services_for_uds(self, message: bytes) -> List[DiagService]:
355
+ prefix_tree = self._prefix_tree
356
+
357
+ # Find matching service(s) in prefix tree
358
+ possible_services: List[DiagService] = []
359
+ for b in message:
360
+ if b in prefix_tree:
361
+ odxassert(isinstance(prefix_tree[b], dict))
362
+ prefix_tree = cast(PrefixTree, prefix_tree[b])
363
+ else:
364
+ break
365
+ if -1 in prefix_tree:
366
+ possible_services += cast(List[DiagService], prefix_tree[-1])
367
+ return possible_services
368
+
369
+ def _decode(self, message: bytes, candidate_services: Iterable[DiagService]) -> List[Message]:
370
+ decoded_messages: List[Message] = []
371
+
372
+ for service in candidate_services:
373
+ try:
374
+ decoded_messages.append(service.decode_message(message))
375
+ except DecodeError as e:
376
+ # check if the message can be decoded as a global
377
+ # negative response for the service
378
+ gnr_found = False
379
+ for gnr in self.global_negative_responses:
380
+ try:
381
+ decoded_gnr = gnr.decode(message)
382
+ gnr_found = True
383
+ if not isinstance(decoded_gnr, dict):
384
+ odxraise(
385
+ f"Expected the decoded value of a global "
386
+ f"negative response to be a dictionary, "
387
+ f"got {type(decoded_gnr)} for {self.short_name}", DecodeError)
388
+
389
+ decoded_messages.append(
390
+ Message(
391
+ coded_message=message,
392
+ service=service,
393
+ coding_object=gnr,
394
+ param_dict=decoded_gnr))
395
+ except DecodeError:
396
+ pass
397
+
398
+ if not gnr_found:
399
+ raise e
400
+
401
+ if len(decoded_messages) == 0:
402
+ raise DecodeError(
403
+ f"None of the services {[x.short_name for x in candidate_services]} could parse {message.hex()}."
404
+ )
405
+
406
+ return decoded_messages
407
+
408
+ def decode(self, message: bytes) -> List[Message]:
409
+ candidate_services = self._find_services_for_uds(message)
410
+
411
+ return self._decode(message, candidate_services)
412
+
413
+ def decode_response(self, response: bytes, request: bytes) -> List[Message]:
414
+ candidate_services = self._find_services_for_uds(request)
415
+ if candidate_services is None:
416
+ raise DecodeError(f"Couldn't find corresponding service for request {request.hex()}.")
417
+
418
+ return self._decode(response, candidate_services)
419
+
420
+ #####
421
+ # </PDU decoding>
422
+ #####
@@ -4,34 +4,25 @@ from dataclasses import dataclass
4
4
  from typing import Any, Dict, List, Optional, Union, cast
5
5
  from xml.etree import ElementTree
6
6
 
7
- from .additionalaudience import AdditionalAudience
8
- from .admindata import AdminData
9
- from .companydata import CompanyData
10
- from .comparaminstance import ComparamInstance
11
- from .comparamspec import ComparamSpec
12
- from .comparamsubset import ComparamSubset
13
- from .diagcomm import DiagComm
14
- from .diagdatadictionaryspec import DiagDataDictionarySpec
7
+ from ..additionalaudience import AdditionalAudience
8
+ from ..admindata import AdminData
9
+ from ..companydata import CompanyData
10
+ from ..diagcomm import DiagComm
11
+ from ..diagdatadictionaryspec import DiagDataDictionarySpec
12
+ from ..diagservice import DiagService
13
+ from ..element import IdentifiableElement
14
+ from ..exceptions import odxassert, odxraise, odxrequire
15
+ from ..functionalclass import FunctionalClass
16
+ from ..nameditemlist import NamedItemList
17
+ from ..odxlink import OdxDocFragment, OdxLinkDatabase, OdxLinkId, OdxLinkRef
18
+ from ..request import Request
19
+ from ..response import Response
20
+ from ..singleecujob import SingleEcuJob
21
+ from ..snrefcontext import SnRefContext
22
+ from ..specialdatagroup import SpecialDataGroup
23
+ from ..statechart import StateChart
24
+ from ..utils import dataclass_fields_asdict
15
25
  from .diaglayertype import DiagLayerType
16
- from .diagservice import DiagService
17
- from .diagvariable import DiagVariable
18
- from .dyndefinedspec import DynDefinedSpec
19
- from .ecuvariantpattern import EcuVariantPattern
20
- from .element import IdentifiableElement
21
- from .exceptions import odxassert, odxraise, odxrequire
22
- from .functionalclass import FunctionalClass
23
- from .nameditemlist import NamedItemList
24
- from .odxlink import OdxDocFragment, OdxLinkDatabase, OdxLinkId, OdxLinkRef, resolve_snref
25
- from .parentref import ParentRef
26
- from .protstack import ProtStack
27
- from .request import Request
28
- from .response import Response
29
- from .singleecujob import SingleEcuJob
30
- from .snrefcontext import SnRefContext
31
- from .specialdatagroup import SpecialDataGroup
32
- from .statechart import StateChart
33
- from .utils import dataclass_fields_asdict
34
- from .variablegroup import VariableGroup
35
26
 
36
27
 
37
28
  @dataclass
@@ -59,18 +50,6 @@ class DiagLayerRaw(IdentifiableElement):
59
50
  # libraries: List[DiagLayer] # TODO
60
51
  sdgs: List[SpecialDataGroup]
61
52
 
62
- # these attributes are only defined for some kinds of diag layers!
63
- # TODO: make a proper class hierarchy!
64
- parent_refs: List[ParentRef]
65
- comparam_refs: List[ComparamInstance]
66
- ecu_variant_patterns: List[EcuVariantPattern]
67
- comparam_spec_ref: Optional[OdxLinkRef]
68
- prot_stack_snref: Optional[str]
69
- diag_variables_raw: List[Union[DiagVariable, OdxLinkRef]]
70
- variable_groups: NamedItemList[VariableGroup]
71
- dyn_defined_spec: Optional[DynDefinedSpec]
72
- # base_variant_patterns: List[EcuVariantPattern] # TODO
73
-
74
53
  @property
75
54
  def diag_comms(self) -> NamedItemList[DiagComm]:
76
55
  return self._diag_comms
@@ -88,10 +67,6 @@ class DiagLayerRaw(IdentifiableElement):
88
67
  def single_ecu_jobs(self) -> NamedItemList[SingleEcuJob]:
89
68
  return self._single_ecu_jobs
90
69
 
91
- @property
92
- def diag_variables(self) -> NamedItemList[DiagVariable]:
93
- return self._diag_variables
94
-
95
70
  @staticmethod
96
71
  def from_et(et_element: ElementTree.Element, doc_frags: List[OdxDocFragment]) -> "DiagLayerRaw":
97
72
  try:
@@ -178,53 +153,6 @@ class DiagLayerRaw(IdentifiableElement):
178
153
  SpecialDataGroup.from_et(sdge, doc_frags) for sdge in et_element.iterfind("SDGS/SDG")
179
154
  ]
180
155
 
181
- parent_refs = [
182
- ParentRef.from_et(pr_el, doc_frags)
183
- for pr_el in et_element.iterfind("PARENT-REFS/PARENT-REF")
184
- ]
185
-
186
- comparam_refs = [
187
- ComparamInstance.from_et(el, doc_frags)
188
- for el in et_element.iterfind("COMPARAM-REFS/COMPARAM-REF")
189
- ]
190
-
191
- ecu_variant_patterns = [
192
- EcuVariantPattern.from_et(el, doc_frags)
193
- for el in et_element.iterfind("ECU-VARIANT-PATTERNS/ECU-VARIANT-PATTERN")
194
- ]
195
- if variant_type is not DiagLayerType.ECU_VARIANT:
196
- odxassert(
197
- len(ecu_variant_patterns) == 0,
198
- "DiagLayer of type other than 'ECU-VARIANT' must not define a ECU-VARIANT-PATTERN")
199
-
200
- comparam_spec_ref = OdxLinkRef.from_et(et_element.find("COMPARAM-SPEC-REF"), doc_frags)
201
- prot_stack_snref: Optional[str] = None
202
- if (prot_stack_snref_elem := et_element.find("PROT-STACK-SNREF")) is not None:
203
- prot_stack_snref = odxrequire(prot_stack_snref_elem.get("SHORT-NAME"))
204
-
205
- diag_variables_raw: List[Union[DiagVariable, OdxLinkRef]] = []
206
- if (dv_elems := et_element.find("DIAG-VARIABLES")) is not None:
207
- for dv_proxy_elem in dv_elems:
208
- dv_proxy: Union[OdxLinkRef, DiagVariable]
209
- if dv_proxy_elem.tag == "DIAG-VARIABLE-REF":
210
- dv_proxy = OdxLinkRef.from_et(dv_proxy_elem, doc_frags)
211
- elif dv_proxy_elem.tag == "DIAG-VARIABLE":
212
- dv_proxy = DiagVariable.from_et(dv_proxy_elem, doc_frags)
213
- else:
214
- odxraise("DIAG-VARIABLES tags may only contain "
215
- "DIAG-VARIABLE and DIAG-VARIABLE-REF subtags")
216
-
217
- diag_variables_raw.append(dv_proxy)
218
-
219
- variable_groups = NamedItemList([
220
- VariableGroup.from_et(vg_elem, doc_frags)
221
- for vg_elem in et_element.iterfind("VARIABLE-GROUPS/VARIABLE-GROUP")
222
- ])
223
-
224
- dyn_defined_spec = None
225
- if (dds_elem := et_element.find("DYN-DEFINED-SPEC")) is not None:
226
- dyn_defined_spec = DynDefinedSpec.from_et(dds_elem, doc_frags)
227
-
228
156
  # Create DiagLayer
229
157
  return DiagLayerRaw(
230
158
  variant_type=variant_type,
@@ -241,14 +169,6 @@ class DiagLayerRaw(IdentifiableElement):
241
169
  state_charts=NamedItemList(state_charts),
242
170
  additional_audiences=NamedItemList(additional_audiences),
243
171
  sdgs=sdgs,
244
- parent_refs=parent_refs,
245
- comparam_refs=comparam_refs,
246
- ecu_variant_patterns=ecu_variant_patterns,
247
- comparam_spec_ref=comparam_spec_ref,
248
- prot_stack_snref=prot_stack_snref,
249
- diag_variables_raw=diag_variables_raw,
250
- variable_groups=variable_groups,
251
- dyn_defined_spec=dyn_defined_spec,
252
172
  **kwargs)
253
173
 
254
174
  def _build_odxlinks(self) -> Dict[OdxLinkId, Any]:
@@ -282,27 +202,12 @@ class DiagLayerRaw(IdentifiableElement):
282
202
  odxlinks.update(additional_audience._build_odxlinks())
283
203
  for sdg in self.sdgs:
284
204
  odxlinks.update(sdg._build_odxlinks())
285
- for parent_ref in self.parent_refs:
286
- odxlinks.update(parent_ref._build_odxlinks())
287
- for comparam in self.comparam_refs:
288
- odxlinks.update(comparam._build_odxlinks())
289
- for dv_proxy in self.diag_variables_raw:
290
- if not isinstance(dv_proxy, OdxLinkRef):
291
- odxlinks.update(dv_proxy._build_odxlinks())
292
- if self.dyn_defined_spec is not None:
293
- odxlinks.update(self.dyn_defined_spec._build_odxlinks())
294
205
 
295
206
  return odxlinks
296
207
 
297
208
  def _resolve_odxlinks(self, odxlinks: OdxLinkDatabase) -> None:
298
209
  """Recursively resolve all references."""
299
210
 
300
- if self.comparam_spec_ref is not None:
301
- spec = odxlinks.resolve(self.comparam_spec_ref)
302
- if not isinstance(spec, (ComparamSubset, ComparamSpec)):
303
- odxraise(f"Type {type(spec).__name__} is not allowed for comparam specs")
304
- self._comparam_spec = spec
305
-
306
211
  # do ODXLINK reference resolution
307
212
  if self.admin_data is not None:
308
213
  self.admin_data._resolve_odxlinks(odxlinks)
@@ -349,32 +254,8 @@ class DiagLayerRaw(IdentifiableElement):
349
254
  additional_audience._resolve_odxlinks(odxlinks)
350
255
  for sdg in self.sdgs:
351
256
  sdg._resolve_odxlinks(odxlinks)
352
- for parent_ref in self.parent_refs:
353
- parent_ref._resolve_odxlinks(odxlinks)
354
- for comparam in self.comparam_refs:
355
- comparam._resolve_odxlinks(odxlinks)
356
-
357
- self._diag_variables: NamedItemList[DiagVariable] = NamedItemList()
358
- for dv_proxy in self.diag_variables_raw:
359
- if isinstance(dv_proxy, OdxLinkRef):
360
- dv = odxlinks.resolve(dv_proxy, DiagVariable)
361
- else:
362
- dv_proxy._resolve_odxlinks(odxlinks)
363
- dv = dv_proxy
364
-
365
- self._diag_variables.append(dv)
366
-
367
- if self.dyn_defined_spec is not None:
368
- self.dyn_defined_spec._resolve_odxlinks(odxlinks)
369
257
 
370
258
  def _resolve_snrefs(self, context: SnRefContext) -> None:
371
- self._prot_stack: Optional[ProtStack] = None
372
- if self.prot_stack_snref is not None:
373
- cp_spec = self.comparam_spec
374
- if isinstance(cp_spec, ComparamSpec):
375
- self._prot_stack = resolve_snref(self.prot_stack_snref, cp_spec.prot_stacks,
376
- ProtStack)
377
-
378
259
  # do short-name reference resolution
379
260
  if self.admin_data is not None:
380
261
  self.admin_data._resolve_snrefs(context)
@@ -403,22 +284,3 @@ class DiagLayerRaw(IdentifiableElement):
403
284
  additional_audience._resolve_snrefs(context)
404
285
  for sdg in self.sdgs:
405
286
  sdg._resolve_snrefs(context)
406
- for parent_ref in self.parent_refs:
407
- parent_ref._resolve_snrefs(context)
408
- for comparam in self.comparam_refs:
409
- comparam._resolve_snrefs(context)
410
-
411
- for dv_proxy in self.diag_variables_raw:
412
- if not isinstance(dv_proxy, OdxLinkRef):
413
- dv_proxy._resolve_snrefs(context)
414
-
415
- if self.dyn_defined_spec is not None:
416
- self.dyn_defined_spec._resolve_snrefs(context)
417
-
418
- @property
419
- def comparam_spec(self) -> Optional[Union[ComparamSpec, ComparamSubset]]:
420
- return self._comparam_spec
421
-
422
- @property
423
- def prot_stack(self) -> Optional[ProtStack]:
424
- return self._prot_stack