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.
- odxtools/cli/_print_utils.py +4 -3
- odxtools/cli/browse.py +13 -11
- odxtools/cli/compare.py +1 -1
- odxtools/cli/list.py +20 -10
- odxtools/cli/snoop.py +28 -5
- odxtools/database.py +46 -10
- odxtools/diagcomm.py +4 -4
- odxtools/diaglayercontainer.py +23 -11
- odxtools/diaglayers/basevariant.py +128 -0
- odxtools/diaglayers/basevariantraw.py +117 -0
- odxtools/diaglayers/diaglayer.py +422 -0
- odxtools/{diaglayerraw.py → diaglayers/diaglayerraw.py} +18 -156
- odxtools/diaglayers/ecushareddata.py +96 -0
- odxtools/diaglayers/ecushareddataraw.py +81 -0
- odxtools/diaglayers/ecuvariant.py +124 -0
- odxtools/diaglayers/ecuvariantraw.py +123 -0
- odxtools/diaglayers/functionalgroup.py +110 -0
- odxtools/diaglayers/functionalgroupraw.py +100 -0
- odxtools/{diaglayer.py → diaglayers/hierarchyelement.py} +183 -498
- odxtools/diaglayers/hierarchyelementraw.py +52 -0
- odxtools/diaglayers/protocol.py +64 -0
- odxtools/diaglayers/protocolraw.py +85 -0
- odxtools/diagvariable.py +10 -1
- odxtools/ecuvariantmatcher.py +6 -7
- odxtools/field.py +3 -4
- odxtools/matchingparameter.py +1 -1
- odxtools/nameditemlist.py +4 -3
- odxtools/parameterinfo.py +10 -1
- odxtools/parameters/parameterwithdop.py +2 -3
- odxtools/parentref.py +1 -1
- odxtools/snrefcontext.py +1 -1
- odxtools/templates/diag_layer_container.odx-d.xml.jinja2 +11 -6
- odxtools/templates/macros/printBaseVariant.xml.jinja2 +53 -0
- odxtools/templates/macros/printCompuMethod.xml.jinja2 +1 -1
- odxtools/templates/macros/printDiagLayer.xml.jinja2 +206 -0
- odxtools/templates/macros/printEcuSharedData.xml.jinja2 +30 -0
- odxtools/templates/macros/printEcuVariant.xml.jinja2 +53 -0
- odxtools/templates/macros/printFunctionalGroup.xml.jinja2 +40 -0
- odxtools/templates/macros/printHierarchyElement.xml.jinja2 +24 -0
- odxtools/templates/macros/printProtocol.xml.jinja2 +30 -0
- odxtools/variablegroup.py +11 -1
- odxtools/version.py +2 -2
- {odxtools-7.4.0.dist-info → odxtools-8.0.0.dist-info}/METADATA +1 -1
- {odxtools-7.4.0.dist-info → odxtools-8.0.0.dist-info}/RECORD +49 -31
- odxtools/templates/macros/printVariant.xml.jinja2 +0 -241
- /odxtools/{diaglayertype.py → diaglayers/diaglayertype.py} +0 -0
- {odxtools-7.4.0.dist-info → odxtools-8.0.0.dist-info}/LICENSE +0 -0
- {odxtools-7.4.0.dist-info → odxtools-8.0.0.dist-info}/WHEEL +0 -0
- {odxtools-7.4.0.dist-info → odxtools-8.0.0.dist-info}/entry_points.txt +0 -0
- {odxtools-7.4.0.dist-info → odxtools-8.0.0.dist-info}/top_level.txt +0 -0
odxtools/cli/_print_utils.py
CHANGED
@@ -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
|
-
|
247
|
-
|
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 (
|
287
|
-
|
288
|
-
|
289
|
-
|
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
|
-
|
292
|
-
|
293
|
-
|
294
|
-
|
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
|
-
|
297
|
-
|
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 ..
|
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
|
-
|
60
|
-
|
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
|
-
|
63
|
-
|
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
|
-
|
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
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
149
|
-
"""
|
150
|
-
|
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
|
156
|
-
"""
|
157
|
-
return self.
|
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 .
|
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["
|
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
|
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
|
225
|
+
resolve_snref(prot_snref, getattr(diag_layer, "protocols", []))
|
226
226
|
for prot_snref in self.protocol_snrefs
|
227
227
|
])
|
odxtools/diaglayercontainer.py
CHANGED
@@ -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 .
|
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[
|
26
|
-
protocols: NamedItemList[
|
27
|
-
functional_groups: NamedItemList[
|
28
|
-
base_variants: NamedItemList[
|
29
|
-
ecu_variants: NamedItemList[
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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)
|