odxtools 7.3.0__py3-none-any.whl → 7.4.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.
- odxtools/cli/_print_utils.py +3 -3
- odxtools/cli/browse.py +4 -2
- odxtools/cli/list.py +3 -3
- odxtools/commrelation.py +122 -0
- odxtools/comparaminstance.py +1 -1
- odxtools/comparamspec.py +1 -2
- odxtools/diaglayer.py +13 -8
- odxtools/diaglayerraw.py +116 -23
- odxtools/diagvariable.py +104 -0
- odxtools/dyndefinedspec.py +179 -0
- odxtools/environmentdatadescription.py +3 -2
- odxtools/odxlink.py +7 -21
- odxtools/parameterinfo.py +10 -1
- odxtools/parameters/tablekeyparameter.py +11 -4
- odxtools/servicebinner.py +1 -1
- odxtools/specialdatagroup.py +1 -1
- odxtools/swvariable.py +21 -0
- odxtools/templates/macros/printComparam.xml.jinja2 +4 -2
- odxtools/templates/macros/printCompuMethod.xml.jinja2 +1 -8
- odxtools/templates/macros/printDiagVariable.xml.jinja2 +66 -0
- odxtools/templates/macros/printDynDefinedSpec.xml.jinja2 +48 -0
- odxtools/templates/macros/printEcuVariantPattern.xml.jinja2 +1 -1
- odxtools/templates/macros/printParam.xml.jinja2 +7 -8
- odxtools/templates/macros/printService.xml.jinja2 +3 -2
- odxtools/templates/macros/printSingleEcuJob.xml.jinja2 +2 -2
- odxtools/templates/macros/printVariant.xml.jinja2 +30 -13
- odxtools/variablegroup.py +22 -0
- odxtools/version.py +2 -2
- {odxtools-7.3.0.dist-info → odxtools-7.4.1.dist-info}/METADATA +18 -18
- {odxtools-7.3.0.dist-info → odxtools-7.4.1.dist-info}/RECORD +34 -27
- {odxtools-7.3.0.dist-info → odxtools-7.4.1.dist-info}/WHEEL +1 -1
- {odxtools-7.3.0.dist-info → odxtools-7.4.1.dist-info}/LICENSE +0 -0
- {odxtools-7.3.0.dist-info → odxtools-7.4.1.dist-info}/entry_points.txt +0 -0
- {odxtools-7.3.0.dist-info → odxtools-7.4.1.dist-info}/top_level.txt +0 -0
odxtools/cli/_print_utils.py
CHANGED
@@ -235,7 +235,7 @@ def print_dl_metrics(variants: List[DiagLayer], print_fn: Callable[..., Any] = p
|
|
235
235
|
type = []
|
236
236
|
num_services = []
|
237
237
|
num_dops = []
|
238
|
-
|
238
|
+
num_comparam_refs = []
|
239
239
|
for variant in variants:
|
240
240
|
assert isinstance(variant, DiagLayer)
|
241
241
|
all_services: List[Union[DiagService, SingleEcuJob]] = sorted(
|
@@ -244,13 +244,13 @@ def print_dl_metrics(variants: List[DiagLayer], print_fn: Callable[..., Any] = p
|
|
244
244
|
type.append(variant.variant_type.value)
|
245
245
|
num_services.append(len(all_services))
|
246
246
|
num_dops.append(len(variant.diag_data_dictionary_spec.data_object_props))
|
247
|
-
|
247
|
+
num_comparam_refs.append(len(variant.comparam_refs))
|
248
248
|
|
249
249
|
table = {
|
250
250
|
'Name': name,
|
251
251
|
'Variant Type': type,
|
252
252
|
'Number of Services': num_services,
|
253
253
|
'Number of DOPs': num_dops,
|
254
|
-
'Number of communication parameters':
|
254
|
+
'Number of communication parameters': num_comparam_refs
|
255
255
|
}
|
256
256
|
print_fn(tabulate(table, headers='keys', tablefmt='presto'))
|
odxtools/cli/browse.py
CHANGED
@@ -112,7 +112,8 @@ def prompt_single_parameter_value(parameter: Parameter) -> Optional[AtomicOdxTyp
|
|
112
112
|
|
113
113
|
def encode_message_interactively(sub_service: Union[Request, Response],
|
114
114
|
ask_user_confirmation: bool = False) -> None:
|
115
|
-
if
|
115
|
+
if sys.__stdin__ is None or sys.__stdout__ is None or not sys.__stdin__.isatty(
|
116
|
+
) or not sys.__stdout__.isatty():
|
116
117
|
raise SystemError("This command can only be used in an interactive shell!")
|
117
118
|
param_dict = sub_service.parameter_dict()
|
118
119
|
|
@@ -260,7 +261,8 @@ def encode_message_from_string_values(
|
|
260
261
|
|
261
262
|
|
262
263
|
def browse(odxdb: Database) -> None:
|
263
|
-
if
|
264
|
+
if sys.__stdin__ is None or sys.__stdout__ is None or not sys.__stdin__.isatty(
|
265
|
+
) or not sys.__stdout__.isatty():
|
264
266
|
raise SystemError("This command can only be used in an interactive shell!")
|
265
267
|
dl_names = [dl.short_name for dl in odxdb.diag_layers]
|
266
268
|
while True:
|
odxtools/cli/list.py
CHANGED
@@ -57,7 +57,7 @@ def print_summary(odxdb: Database,
|
|
57
57
|
all_services: List[DiagComm] = sorted(dl.services, key=lambda x: x.short_name)
|
58
58
|
|
59
59
|
data_object_properties = dl.diag_data_dictionary_spec.data_object_props
|
60
|
-
|
60
|
+
comparam_refs = dl.comparam_refs
|
61
61
|
|
62
62
|
for proto in dl.protocols:
|
63
63
|
if (can_rx_id := dl.get_can_receive_id(proto.short_name)) is not None:
|
@@ -103,12 +103,12 @@ def print_summary(odxdb: Database,
|
|
103
103
|
data_object_properties, key=lambda x: (type(x).__name__, x.short_name)):
|
104
104
|
rich.print(" " + str(dop.short_name).replace("\n", "\n "))
|
105
105
|
|
106
|
-
if print_comparams and len(
|
106
|
+
if print_comparams and len(comparam_refs) > 0:
|
107
107
|
rich.print("\n")
|
108
108
|
rich.print(
|
109
109
|
f"The communication parameters of the {dl.variant_type.value} '{dl.short_name}' are: "
|
110
110
|
)
|
111
|
-
for com_param in
|
111
|
+
for com_param in comparam_refs:
|
112
112
|
rich.print(f" {com_param.short_name}: {com_param.value}")
|
113
113
|
|
114
114
|
|
odxtools/commrelation.py
ADDED
@@ -0,0 +1,122 @@
|
|
1
|
+
# SPDX-License-Identifier: MIT
|
2
|
+
import warnings
|
3
|
+
from dataclasses import dataclass
|
4
|
+
from enum import Enum
|
5
|
+
from typing import Any, Dict, List, Optional
|
6
|
+
from xml.etree import ElementTree
|
7
|
+
|
8
|
+
from .description import Description
|
9
|
+
from .diagcomm import DiagComm
|
10
|
+
from .diagservice import DiagService
|
11
|
+
from .exceptions import OdxWarning, odxraise, odxrequire
|
12
|
+
from .odxlink import OdxDocFragment, OdxLinkDatabase, OdxLinkId, OdxLinkRef, resolve_snref
|
13
|
+
from .parameters.parameter import Parameter
|
14
|
+
from .snrefcontext import SnRefContext
|
15
|
+
|
16
|
+
|
17
|
+
class CommRelationValueType(Enum):
|
18
|
+
CURRENT = "CURRENT"
|
19
|
+
STORED = "STORED"
|
20
|
+
STATIC = "STATIC"
|
21
|
+
SUBSTITUTED = "SUBSTITUTED"
|
22
|
+
|
23
|
+
|
24
|
+
@dataclass
|
25
|
+
class CommRelation:
|
26
|
+
description: Optional[Description]
|
27
|
+
relation_type: str
|
28
|
+
diag_comm_ref: Optional[OdxLinkRef]
|
29
|
+
diag_comm_snref: Optional[str]
|
30
|
+
in_param_if_snref: Optional[str]
|
31
|
+
#in_param_if_snpathref: Optional[str] # TODO
|
32
|
+
out_param_if_snref: Optional[str]
|
33
|
+
#out_param_if_snpathref: Optional[str] # TODO
|
34
|
+
value_type_raw: Optional[CommRelationValueType]
|
35
|
+
|
36
|
+
@property
|
37
|
+
def diag_comm(self) -> DiagComm:
|
38
|
+
return self._diag_comm
|
39
|
+
|
40
|
+
@property
|
41
|
+
def in_param_if(self) -> Optional[Parameter]:
|
42
|
+
return self._in_param_if
|
43
|
+
|
44
|
+
@property
|
45
|
+
def out_param_if(self) -> Optional[Parameter]:
|
46
|
+
return self._out_param_if
|
47
|
+
|
48
|
+
@property
|
49
|
+
def value_type(self) -> CommRelationValueType:
|
50
|
+
if self.value_type_raw is None:
|
51
|
+
return CommRelationValueType.CURRENT
|
52
|
+
|
53
|
+
return self.value_type_raw
|
54
|
+
|
55
|
+
@staticmethod
|
56
|
+
def from_et(et_element: ElementTree.Element, doc_frags: List[OdxDocFragment]) -> "CommRelation":
|
57
|
+
description = Description.from_et(et_element.find("DESC"), doc_frags)
|
58
|
+
relation_type = odxrequire(et_element.findtext("RELATION-TYPE"))
|
59
|
+
|
60
|
+
diag_comm_ref = OdxLinkRef.from_et(et_element.find("DIAG-COMM-REF"), doc_frags)
|
61
|
+
diag_comm_snref = None
|
62
|
+
if (diag_comm_snref_elem := et_element.find("DIAG-COMM-SNREF")) is not None:
|
63
|
+
diag_comm_snref = odxrequire(diag_comm_snref_elem.get("SHORT-NAME"))
|
64
|
+
|
65
|
+
in_param_if_snref = None
|
66
|
+
if (in_param_if_snref_elem := et_element.find("IN-PARAM-IF-SNREF")) is not None:
|
67
|
+
in_param_if_snref = odxrequire(in_param_if_snref_elem.get("SHORT-NAME"))
|
68
|
+
|
69
|
+
if et_element.find("IN-PARAM-IF-SNPATHREF") is not None:
|
70
|
+
warnings.warn("SNPATHREFs are not supported by odxtools yet", OdxWarning, stacklevel=1)
|
71
|
+
|
72
|
+
out_param_if_snref = None
|
73
|
+
if (out_param_if_snref_elem := et_element.find("OUT-PARAM-IF-SNREF")) is not None:
|
74
|
+
out_param_if_snref = odxrequire(out_param_if_snref_elem.get("SHORT-NAME"))
|
75
|
+
|
76
|
+
if et_element.find("OUT-PARAM-IF-SNPATHREF") is not None:
|
77
|
+
warnings.warn("SNPATHREFs are not supported by odxtools yet", OdxWarning, stacklevel=1)
|
78
|
+
|
79
|
+
value_type_raw = None
|
80
|
+
if (value_type_str := et_element.get("VALUE-TYPE")) is not None:
|
81
|
+
try:
|
82
|
+
value_type_raw = CommRelationValueType(value_type_str)
|
83
|
+
except ValueError:
|
84
|
+
odxraise(f"Encountered unknown comm relation value type '{value_type_str}'")
|
85
|
+
|
86
|
+
return CommRelation(
|
87
|
+
description=description,
|
88
|
+
relation_type=relation_type,
|
89
|
+
diag_comm_ref=diag_comm_ref,
|
90
|
+
diag_comm_snref=diag_comm_snref,
|
91
|
+
in_param_if_snref=in_param_if_snref,
|
92
|
+
out_param_if_snref=out_param_if_snref,
|
93
|
+
value_type_raw=value_type_raw)
|
94
|
+
|
95
|
+
def _build_odxlinks(self) -> Dict[OdxLinkId, Any]:
|
96
|
+
return {}
|
97
|
+
|
98
|
+
def _resolve_odxlinks(self, odxlinks: OdxLinkDatabase) -> None:
|
99
|
+
if self.diag_comm_ref is not None:
|
100
|
+
self._diag_comm = odxlinks.resolve(self.diag_comm_ref, DiagComm)
|
101
|
+
|
102
|
+
def _resolve_snrefs(self, context: SnRefContext) -> None:
|
103
|
+
diag_layer = odxrequire(context.diag_layer)
|
104
|
+
|
105
|
+
if self.diag_comm_snref is not None:
|
106
|
+
self._diag_comm = resolve_snref(self.diag_comm_snref, diag_layer.diag_comms, DiagComm)
|
107
|
+
|
108
|
+
service = self.diag_comm
|
109
|
+
if not isinstance(service, DiagService):
|
110
|
+
odxraise(f"DIAG-VARIABLE references non-service {type(service).__name__} "
|
111
|
+
f"diagnostic communication")
|
112
|
+
|
113
|
+
self._in_param_if = None
|
114
|
+
if self.in_param_if_snref is not None:
|
115
|
+
self._in_param_if = resolve_snref(self.in_param_if_snref,
|
116
|
+
odxrequire(service.request).parameters, Parameter)
|
117
|
+
|
118
|
+
self._out_param_if = None
|
119
|
+
if self.out_param_if_snref is not None:
|
120
|
+
self._out_param_if = resolve_snref(self.out_param_if_snref,
|
121
|
+
odxrequire(service.positive_responses[0]).parameters,
|
122
|
+
Parameter)
|
odxtools/comparaminstance.py
CHANGED
@@ -32,7 +32,7 @@ class ComparamInstance:
|
|
32
32
|
spec_ref = odxrequire(OdxLinkRef.from_et(et_element, doc_frags))
|
33
33
|
|
34
34
|
# ODX standard v2.0.0 defined only VALUE. ODX v2.0.1 decided
|
35
|
-
# to break things and change it to choice between SIMPLE-VALUE
|
35
|
+
# to break things and change it to a choice between SIMPLE-VALUE
|
36
36
|
# and COMPLEX-VALUE
|
37
37
|
value: Union[str, List[Union[str, ComplexValue]]]
|
38
38
|
if et_element.find("VALUE") is not None:
|
odxtools/comparamspec.py
CHANGED
@@ -51,8 +51,7 @@ class ComparamSpec(IdentifiableElement):
|
|
51
51
|
|
52
52
|
def _build_odxlinks(self) -> Dict[OdxLinkId, Any]:
|
53
53
|
odxlinks: Dict[OdxLinkId, Any] = {}
|
54
|
-
|
55
|
-
odxlinks[self.odx_id] = self
|
54
|
+
odxlinks[self.odx_id] = self
|
56
55
|
|
57
56
|
if self.admin_data is not None:
|
58
57
|
odxlinks.update(self.admin_data._build_odxlinks())
|
odxtools/diaglayer.py
CHANGED
@@ -170,9 +170,9 @@ class DiagLayer:
|
|
170
170
|
self._diag_comms = NamedItemList(diag_comms)
|
171
171
|
|
172
172
|
# filter the diag comms for services and single-ECU jobs
|
173
|
-
|
173
|
+
diag_services = [dc for dc in diag_comms if isinstance(dc, DiagService)]
|
174
174
|
single_ecu_jobs = [dc for dc in diag_comms if isinstance(dc, SingleEcuJob)]
|
175
|
-
self.
|
175
|
+
self._diag_services = NamedItemList(diag_services)
|
176
176
|
self._single_ecu_jobs = NamedItemList(single_ecu_jobs)
|
177
177
|
|
178
178
|
global_negative_responses = self._compute_available_global_neg_responses(odxlinks)
|
@@ -289,7 +289,7 @@ class DiagLayer:
|
|
289
289
|
# scheme, cf the docstring of
|
290
290
|
# _compute_available_commmunication_parameters().
|
291
291
|
#####
|
292
|
-
self.
|
292
|
+
self._comparam_refs = NamedItemList(self._compute_available_commmunication_parameters())
|
293
293
|
|
294
294
|
#####
|
295
295
|
# resolve all SNREFs. TODO: We allow SNREFS to objects that
|
@@ -407,12 +407,17 @@ class DiagLayer:
|
|
407
407
|
|
408
408
|
@property
|
409
409
|
def services(self) -> NamedItemList[DiagService]:
|
410
|
+
"""This property is an alias for `.diag_services`"""
|
411
|
+
return self._diag_services
|
412
|
+
|
413
|
+
@property
|
414
|
+
def diag_services(self) -> NamedItemList[DiagService]:
|
410
415
|
"""All diagnostic services applicable to this DiagLayer
|
411
416
|
|
412
417
|
This is a subset of all diagnostic communication
|
413
418
|
primitives. All references are resolved in the list returned.
|
414
419
|
"""
|
415
|
-
return self.
|
420
|
+
return self._diag_services
|
416
421
|
|
417
422
|
@property
|
418
423
|
def single_ecu_jobs(self) -> NamedItemList[SingleEcuJob]:
|
@@ -707,20 +712,20 @@ class DiagLayer:
|
|
707
712
|
com_params_dict[(cp.spec_ref.ref_id, cp.protocol_snref)] = cp
|
708
713
|
|
709
714
|
# finally, handle the locally defined communication parameters
|
710
|
-
for cp in self.diag_layer_raw.
|
715
|
+
for cp in self.diag_layer_raw.comparam_refs:
|
711
716
|
com_params_dict[(cp.spec_ref.ref_id, cp.protocol_snref)] = cp
|
712
717
|
|
713
718
|
return list(com_params_dict.values())
|
714
719
|
|
715
720
|
@property
|
716
|
-
def
|
721
|
+
def comparam_refs(self) -> NamedItemList[ComparamInstance]:
|
717
722
|
"""All communication parameters applicable to this DiagLayer
|
718
723
|
|
719
724
|
Note that, although communication parameters use inheritance,
|
720
725
|
it is *not* the "value inheritance" scheme used by e.g. DOPs,
|
721
726
|
tables, state charts, ...
|
722
727
|
"""
|
723
|
-
return self.
|
728
|
+
return self._comparam_refs
|
724
729
|
|
725
730
|
@cached_property
|
726
731
|
def protocols(self) -> NamedItemList["DiagLayer"]:
|
@@ -757,7 +762,7 @@ class DiagLayer:
|
|
757
762
|
protocol_name = protocol
|
758
763
|
|
759
764
|
# determine the set of applicable communication parameters
|
760
|
-
cps = [cp for cp in self.
|
765
|
+
cps = [cp for cp in self.comparam_refs if cp.short_name == cp_short_name]
|
761
766
|
if protocol_name is not None:
|
762
767
|
cps = [cp for cp in cps if cp.protocol_snref in (None, protocol_name)]
|
763
768
|
|
odxtools/diaglayerraw.py
CHANGED
@@ -14,6 +14,8 @@ from .diagcomm import DiagComm
|
|
14
14
|
from .diagdatadictionaryspec import DiagDataDictionarySpec
|
15
15
|
from .diaglayertype import DiagLayerType
|
16
16
|
from .diagservice import DiagService
|
17
|
+
from .diagvariable import DiagVariable
|
18
|
+
from .dyndefinedspec import DynDefinedSpec
|
17
19
|
from .ecuvariantpattern import EcuVariantPattern
|
18
20
|
from .element import IdentifiableElement
|
19
21
|
from .exceptions import odxassert, odxraise, odxrequire
|
@@ -29,6 +31,7 @@ from .snrefcontext import SnRefContext
|
|
29
31
|
from .specialdatagroup import SpecialDataGroup
|
30
32
|
from .statechart import StateChart
|
31
33
|
from .utils import dataclass_fields_asdict
|
34
|
+
from .variablegroup import VariableGroup
|
32
35
|
|
33
36
|
|
34
37
|
@dataclass
|
@@ -44,7 +47,7 @@ class DiagLayerRaw(IdentifiableElement):
|
|
44
47
|
company_datas: NamedItemList[CompanyData]
|
45
48
|
functional_classes: NamedItemList[FunctionalClass]
|
46
49
|
diag_data_dictionary_spec: Optional[DiagDataDictionarySpec]
|
47
|
-
|
50
|
+
diag_comms_raw: List[Union[OdxLinkRef, DiagComm]]
|
48
51
|
requests: NamedItemList[Request]
|
49
52
|
positive_responses: NamedItemList[Response]
|
50
53
|
negative_responses: NamedItemList[Response]
|
@@ -59,15 +62,36 @@ class DiagLayerRaw(IdentifiableElement):
|
|
59
62
|
# these attributes are only defined for some kinds of diag layers!
|
60
63
|
# TODO: make a proper class hierarchy!
|
61
64
|
parent_refs: List[ParentRef]
|
62
|
-
|
65
|
+
comparam_refs: List[ComparamInstance]
|
63
66
|
ecu_variant_patterns: List[EcuVariantPattern]
|
64
67
|
comparam_spec_ref: Optional[OdxLinkRef]
|
65
68
|
prot_stack_snref: Optional[str]
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
+
diag_variables_raw: List[Union[DiagVariable, OdxLinkRef]]
|
70
|
+
variable_groups: NamedItemList[VariableGroup]
|
71
|
+
dyn_defined_spec: Optional[DynDefinedSpec]
|
69
72
|
# base_variant_patterns: List[EcuVariantPattern] # TODO
|
70
73
|
|
74
|
+
@property
|
75
|
+
def diag_comms(self) -> NamedItemList[DiagComm]:
|
76
|
+
return self._diag_comms
|
77
|
+
|
78
|
+
@property
|
79
|
+
def diag_services(self) -> NamedItemList[DiagService]:
|
80
|
+
return self._diag_services
|
81
|
+
|
82
|
+
@property
|
83
|
+
def services(self) -> NamedItemList[DiagService]:
|
84
|
+
"""This property is an alias for `.diag_services`"""
|
85
|
+
return self._diag_services
|
86
|
+
|
87
|
+
@property
|
88
|
+
def single_ecu_jobs(self) -> NamedItemList[SingleEcuJob]:
|
89
|
+
return self._single_ecu_jobs
|
90
|
+
|
91
|
+
@property
|
92
|
+
def diag_variables(self) -> NamedItemList[DiagVariable]:
|
93
|
+
return self._diag_variables
|
94
|
+
|
71
95
|
@staticmethod
|
72
96
|
def from_et(et_element: ElementTree.Element, doc_frags: List[OdxDocFragment]) -> "DiagLayerRaw":
|
73
97
|
try:
|
@@ -101,7 +125,7 @@ class DiagLayerRaw(IdentifiableElement):
|
|
101
125
|
if (ddds_elem := et_element.find("DIAG-DATA-DICTIONARY-SPEC")) is not None:
|
102
126
|
diag_data_dictionary_spec = DiagDataDictionarySpec.from_et(ddds_elem, doc_frags)
|
103
127
|
|
104
|
-
|
128
|
+
diag_comms_raw: List[Union[OdxLinkRef, DiagComm]] = []
|
105
129
|
if (dc_elems := et_element.find("DIAG-COMMS")) is not None:
|
106
130
|
for dc_proxy_elem in dc_elems:
|
107
131
|
dc: Union[OdxLinkRef, DiagComm]
|
@@ -113,7 +137,7 @@ class DiagLayerRaw(IdentifiableElement):
|
|
113
137
|
odxassert(dc_proxy_elem.tag == "SINGLE-ECU-JOB")
|
114
138
|
dc = SingleEcuJob.from_et(dc_proxy_elem, doc_frags)
|
115
139
|
|
116
|
-
|
140
|
+
diag_comms_raw.append(dc)
|
117
141
|
|
118
142
|
requests = NamedItemList([
|
119
143
|
Request.from_et(rq_elem, doc_frags)
|
@@ -159,7 +183,7 @@ class DiagLayerRaw(IdentifiableElement):
|
|
159
183
|
for pr_el in et_element.iterfind("PARENT-REFS/PARENT-REF")
|
160
184
|
]
|
161
185
|
|
162
|
-
|
186
|
+
comparam_refs = [
|
163
187
|
ComparamInstance.from_et(el, doc_frags)
|
164
188
|
for el in et_element.iterfind("COMPARAM-REFS/COMPARAM-REF")
|
165
189
|
]
|
@@ -178,6 +202,29 @@ class DiagLayerRaw(IdentifiableElement):
|
|
178
202
|
if (prot_stack_snref_elem := et_element.find("PROT-STACK-SNREF")) is not None:
|
179
203
|
prot_stack_snref = odxrequire(prot_stack_snref_elem.get("SHORT-NAME"))
|
180
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
|
+
|
181
228
|
# Create DiagLayer
|
182
229
|
return DiagLayerRaw(
|
183
230
|
variant_type=variant_type,
|
@@ -185,7 +232,7 @@ class DiagLayerRaw(IdentifiableElement):
|
|
185
232
|
company_datas=NamedItemList(company_datas),
|
186
233
|
functional_classes=NamedItemList(functional_classes),
|
187
234
|
diag_data_dictionary_spec=diag_data_dictionary_spec,
|
188
|
-
|
235
|
+
diag_comms_raw=diag_comms_raw,
|
189
236
|
requests=requests,
|
190
237
|
positive_responses=positive_responses,
|
191
238
|
negative_responses=negative_responses,
|
@@ -195,10 +242,13 @@ class DiagLayerRaw(IdentifiableElement):
|
|
195
242
|
additional_audiences=NamedItemList(additional_audiences),
|
196
243
|
sdgs=sdgs,
|
197
244
|
parent_refs=parent_refs,
|
198
|
-
|
245
|
+
comparam_refs=comparam_refs,
|
199
246
|
ecu_variant_patterns=ecu_variant_patterns,
|
200
247
|
comparam_spec_ref=comparam_spec_ref,
|
201
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,
|
202
252
|
**kwargs)
|
203
253
|
|
204
254
|
def _build_odxlinks(self) -> Dict[OdxLinkId, Any]:
|
@@ -214,10 +264,10 @@ class DiagLayerRaw(IdentifiableElement):
|
|
214
264
|
odxlinks.update(company_data._build_odxlinks())
|
215
265
|
for functional_class in self.functional_classes:
|
216
266
|
odxlinks.update(functional_class._build_odxlinks())
|
217
|
-
for
|
218
|
-
if isinstance(
|
267
|
+
for dc_proxy in self.diag_comms_raw:
|
268
|
+
if isinstance(dc_proxy, OdxLinkRef):
|
219
269
|
continue
|
220
|
-
odxlinks.update(
|
270
|
+
odxlinks.update(dc_proxy._build_odxlinks())
|
221
271
|
for request in self.requests:
|
222
272
|
odxlinks.update(request._build_odxlinks())
|
223
273
|
for positive_response in self.positive_responses:
|
@@ -234,8 +284,13 @@ class DiagLayerRaw(IdentifiableElement):
|
|
234
284
|
odxlinks.update(sdg._build_odxlinks())
|
235
285
|
for parent_ref in self.parent_refs:
|
236
286
|
odxlinks.update(parent_ref._build_odxlinks())
|
237
|
-
for comparam in self.
|
287
|
+
for comparam in self.comparam_refs:
|
238
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())
|
239
294
|
|
240
295
|
return odxlinks
|
241
296
|
|
@@ -258,10 +313,28 @@ class DiagLayerRaw(IdentifiableElement):
|
|
258
313
|
company_data._resolve_odxlinks(odxlinks)
|
259
314
|
for functional_class in self.functional_classes:
|
260
315
|
functional_class._resolve_odxlinks(odxlinks)
|
261
|
-
|
262
|
-
|
263
|
-
|
264
|
-
|
316
|
+
|
317
|
+
# resolve references to diagnostic communication objects and
|
318
|
+
# separate them into services and single-ecu jobs
|
319
|
+
self._diag_comms = NamedItemList[DiagComm]()
|
320
|
+
self._diag_services = NamedItemList[DiagService]()
|
321
|
+
self._single_ecu_jobs = NamedItemList[SingleEcuJob]()
|
322
|
+
for dc_proxy in self.diag_comms_raw:
|
323
|
+
if isinstance(dc_proxy, OdxLinkRef):
|
324
|
+
dc = odxlinks.resolve(dc_proxy, DiagComm)
|
325
|
+
else:
|
326
|
+
dc = dc_proxy
|
327
|
+
dc._resolve_odxlinks(odxlinks)
|
328
|
+
|
329
|
+
self._diag_comms.append(dc)
|
330
|
+
|
331
|
+
if isinstance(dc, DiagService):
|
332
|
+
self._diag_services.append(dc)
|
333
|
+
elif isinstance(dc, SingleEcuJob):
|
334
|
+
self._single_ecu_jobs.append(dc)
|
335
|
+
else:
|
336
|
+
odxraise()
|
337
|
+
|
265
338
|
for request in self.requests:
|
266
339
|
request._resolve_odxlinks(odxlinks)
|
267
340
|
for positive_response in self.positive_responses:
|
@@ -278,9 +351,22 @@ class DiagLayerRaw(IdentifiableElement):
|
|
278
351
|
sdg._resolve_odxlinks(odxlinks)
|
279
352
|
for parent_ref in self.parent_refs:
|
280
353
|
parent_ref._resolve_odxlinks(odxlinks)
|
281
|
-
for comparam in self.
|
354
|
+
for comparam in self.comparam_refs:
|
282
355
|
comparam._resolve_odxlinks(odxlinks)
|
283
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
|
+
|
284
370
|
def _resolve_snrefs(self, context: SnRefContext) -> None:
|
285
371
|
self._prot_stack: Optional[ProtStack] = None
|
286
372
|
if self.prot_stack_snref is not None:
|
@@ -299,10 +385,10 @@ class DiagLayerRaw(IdentifiableElement):
|
|
299
385
|
company_data._resolve_snrefs(context)
|
300
386
|
for functional_class in self.functional_classes:
|
301
387
|
functional_class._resolve_snrefs(context)
|
302
|
-
for
|
303
|
-
if isinstance(
|
388
|
+
for dc_proxy in self.diag_comms_raw:
|
389
|
+
if isinstance(dc_proxy, OdxLinkRef):
|
304
390
|
continue
|
305
|
-
|
391
|
+
dc_proxy._resolve_snrefs(context)
|
306
392
|
for request in self.requests:
|
307
393
|
request._resolve_snrefs(context)
|
308
394
|
for positive_response in self.positive_responses:
|
@@ -319,9 +405,16 @@ class DiagLayerRaw(IdentifiableElement):
|
|
319
405
|
sdg._resolve_snrefs(context)
|
320
406
|
for parent_ref in self.parent_refs:
|
321
407
|
parent_ref._resolve_snrefs(context)
|
322
|
-
for comparam in self.
|
408
|
+
for comparam in self.comparam_refs:
|
323
409
|
comparam._resolve_snrefs(context)
|
324
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
|
+
|
325
418
|
@property
|
326
419
|
def comparam_spec(self) -> Optional[Union[ComparamSpec, ComparamSubset]]:
|
327
420
|
return self._comparam_spec
|
odxtools/diagvariable.py
ADDED
@@ -0,0 +1,104 @@
|
|
1
|
+
# SPDX-License-Identifier: MIT
|
2
|
+
from dataclasses import dataclass
|
3
|
+
from typing import Any, Dict, List, Optional
|
4
|
+
from xml.etree import ElementTree
|
5
|
+
|
6
|
+
from .admindata import AdminData
|
7
|
+
from .commrelation import CommRelation
|
8
|
+
from .element import IdentifiableElement
|
9
|
+
from .exceptions import odxrequire
|
10
|
+
from .nameditemlist import NamedItemList
|
11
|
+
from .odxlink import OdxDocFragment, OdxLinkDatabase, OdxLinkId, OdxLinkRef
|
12
|
+
from .odxtypes import odxstr_to_bool
|
13
|
+
from .snrefcontext import SnRefContext
|
14
|
+
from .specialdatagroup import SpecialDataGroup
|
15
|
+
from .swvariable import SwVariable
|
16
|
+
from .utils import dataclass_fields_asdict
|
17
|
+
from .variablegroup import VariableGroup
|
18
|
+
|
19
|
+
|
20
|
+
@dataclass
|
21
|
+
class DiagVariable(IdentifiableElement):
|
22
|
+
"""Representation of a diagnostic variable
|
23
|
+
"""
|
24
|
+
|
25
|
+
admin_data: Optional[AdminData]
|
26
|
+
variable_group_ref: OdxLinkRef
|
27
|
+
sw_variables: List[SwVariable]
|
28
|
+
comm_relations: List[CommRelation]
|
29
|
+
#snref_to_tablerow: Optional[SnrefToTableRow] # TODO
|
30
|
+
sdgs: List[SpecialDataGroup]
|
31
|
+
is_read_before_write_raw: Optional[bool]
|
32
|
+
|
33
|
+
@property
|
34
|
+
def variable_group(self) -> VariableGroup:
|
35
|
+
return self._variable_group
|
36
|
+
|
37
|
+
@property
|
38
|
+
def is_read_before_write(self) -> bool:
|
39
|
+
return self.is_read_before_write_raw is True
|
40
|
+
|
41
|
+
@staticmethod
|
42
|
+
def from_et(et_element: ElementTree.Element, doc_frags: List[OdxDocFragment]) -> "DiagVariable":
|
43
|
+
kwargs = dataclass_fields_asdict(IdentifiableElement.from_et(et_element, doc_frags))
|
44
|
+
|
45
|
+
admin_data = AdminData.from_et(et_element.find("ADMIN-DATA"), doc_frags)
|
46
|
+
variable_group_ref = odxrequire(
|
47
|
+
OdxLinkRef.from_et(et_element.find("VARIABLE-GROUP-REF"), doc_frags))
|
48
|
+
sw_variables = NamedItemList([
|
49
|
+
SwVariable.from_et(swv_elem, doc_frags)
|
50
|
+
for swv_elem in et_element.iterfind("SW-VARIABLES/SW-VARIABLE")
|
51
|
+
])
|
52
|
+
comm_relations = [
|
53
|
+
CommRelation.from_et(cr_elem, doc_frags)
|
54
|
+
for cr_elem in et_element.iterfind("COMM-RELATIONS/COMM-RELATION")
|
55
|
+
]
|
56
|
+
sdgs = [
|
57
|
+
SpecialDataGroup.from_et(sdge, doc_frags) for sdge in et_element.iterfind("SDGS/SDG")
|
58
|
+
]
|
59
|
+
is_read_before_write_raw = odxstr_to_bool(et_element.get("IS-READ-BEFORE-WRITE"))
|
60
|
+
|
61
|
+
return DiagVariable(
|
62
|
+
admin_data=admin_data,
|
63
|
+
variable_group_ref=variable_group_ref,
|
64
|
+
sw_variables=sw_variables,
|
65
|
+
comm_relations=comm_relations,
|
66
|
+
sdgs=sdgs,
|
67
|
+
is_read_before_write_raw=is_read_before_write_raw,
|
68
|
+
**kwargs)
|
69
|
+
|
70
|
+
def _build_odxlinks(self) -> Dict[OdxLinkId, Any]:
|
71
|
+
result = {self.odx_id: self}
|
72
|
+
|
73
|
+
if self.admin_data is not None:
|
74
|
+
result.update(self.admin_data._build_odxlinks())
|
75
|
+
|
76
|
+
for sdg in self.sdgs:
|
77
|
+
result.update(sdg._build_odxlinks())
|
78
|
+
|
79
|
+
for cr in self.comm_relations:
|
80
|
+
result.update(cr._build_odxlinks())
|
81
|
+
|
82
|
+
return result
|
83
|
+
|
84
|
+
def _resolve_odxlinks(self, odxlinks: OdxLinkDatabase) -> None:
|
85
|
+
self._variable_group = odxlinks.resolve(self.variable_group_ref, VariableGroup)
|
86
|
+
|
87
|
+
if self.admin_data is not None:
|
88
|
+
self.admin_data._resolve_odxlinks(odxlinks)
|
89
|
+
|
90
|
+
for sdg in self.sdgs:
|
91
|
+
sdg._resolve_odxlinks(odxlinks)
|
92
|
+
|
93
|
+
for cr in self.comm_relations:
|
94
|
+
cr._resolve_odxlinks(odxlinks)
|
95
|
+
|
96
|
+
def _resolve_snrefs(self, context: SnRefContext) -> None:
|
97
|
+
if self.admin_data is not None:
|
98
|
+
self.admin_data._resolve_snrefs(context)
|
99
|
+
|
100
|
+
for sdg in self.sdgs:
|
101
|
+
sdg._resolve_snrefs(context)
|
102
|
+
|
103
|
+
for cr in self.comm_relations:
|
104
|
+
cr._resolve_snrefs(context)
|