odxtools 7.2.0__py3-none-any.whl → 7.4.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/basicstructure.py +10 -7
- 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/compumethods/linearsegment.py +0 -2
- odxtools/database.py +17 -11
- odxtools/decodestate.py +8 -2
- odxtools/diaglayer.py +23 -17
- odxtools/diaglayerraw.py +116 -23
- odxtools/diagnostictroublecode.py +2 -2
- odxtools/diagservice.py +33 -20
- odxtools/diagvariable.py +104 -0
- odxtools/dtcdop.py +39 -14
- odxtools/dyndefinedspec.py +179 -0
- odxtools/encodestate.py +14 -2
- odxtools/environmentdatadescription.py +137 -16
- odxtools/exceptions.py +10 -1
- odxtools/multiplexer.py +92 -56
- odxtools/multiplexercase.py +6 -5
- odxtools/multiplexerdefaultcase.py +7 -6
- odxtools/odxlink.py +19 -47
- odxtools/odxtypes.py +1 -1
- odxtools/parameterinfo.py +2 -2
- odxtools/parameters/nrcconstparameter.py +28 -37
- odxtools/parameters/systemparameter.py +1 -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.2.0.dist-info → odxtools-7.4.0.dist-info}/METADATA +18 -18
- {odxtools-7.2.0.dist-info → odxtools-7.4.0.dist-info}/RECORD +49 -42
- {odxtools-7.2.0.dist-info → odxtools-7.4.0.dist-info}/WHEEL +1 -1
- {odxtools-7.2.0.dist-info → odxtools-7.4.0.dist-info}/LICENSE +0 -0
- {odxtools-7.2.0.dist-info → odxtools-7.4.0.dist-info}/entry_points.txt +0 -0
- {odxtools-7.2.0.dist-info → odxtools-7.4.0.dist-info}/top_level.txt +0 -0
odxtools/basicstructure.py
CHANGED
@@ -10,7 +10,7 @@ from .complexdop import ComplexDop
|
|
10
10
|
from .dataobjectproperty import DataObjectProperty
|
11
11
|
from .decodestate import DecodeState
|
12
12
|
from .encodestate import EncodeState
|
13
|
-
from .exceptions import EncodeError, OdxWarning, odxassert, odxraise
|
13
|
+
from .exceptions import EncodeError, OdxWarning, odxassert, odxraise
|
14
14
|
from .nameditemlist import NamedItemList
|
15
15
|
from .odxlink import OdxDocFragment, OdxLinkDatabase, OdxLinkId
|
16
16
|
from .odxtypes import ParameterDict, ParameterValue, ParameterValueDict
|
@@ -18,7 +18,6 @@ from .parameters.codedconstparameter import CodedConstParameter
|
|
18
18
|
from .parameters.createanyparameter import create_any_parameter_from_et
|
19
19
|
from .parameters.lengthkeyparameter import LengthKeyParameter
|
20
20
|
from .parameters.matchingrequestparameter import MatchingRequestParameter
|
21
|
-
from .parameters.nrcconstparameter import NrcConstParameter
|
22
21
|
from .parameters.parameter import Parameter
|
23
22
|
from .parameters.parameterwithdop import ParameterWithDOP
|
24
23
|
from .parameters.physicalconstantparameter import PhysicalConstantParameter
|
@@ -75,10 +74,11 @@ class BasicStructure(ComplexDop):
|
|
75
74
|
|
76
75
|
for param in self.parameters:
|
77
76
|
if (isinstance(param, MatchingRequestParameter) and param.request_byte_position < len(request_prefix)) or \
|
78
|
-
isinstance(param, (CodedConstParameter,
|
77
|
+
isinstance(param, (CodedConstParameter, PhysicalConstantParameter)):
|
79
78
|
param.encode_into_pdu(physical_value=None, encode_state=encode_state)
|
80
79
|
else:
|
81
80
|
break
|
81
|
+
|
82
82
|
return encode_state.coded_message
|
83
83
|
|
84
84
|
@property
|
@@ -160,8 +160,8 @@ class BasicStructure(ComplexDop):
|
|
160
160
|
orig_is_end_of_pdu = encode_state.is_end_of_pdu
|
161
161
|
encode_state.is_end_of_pdu = False
|
162
162
|
|
163
|
-
#
|
164
|
-
if
|
163
|
+
# ensure that no values for unknown parameters are specified.
|
164
|
+
if not encode_state.allow_unknown_parameters:
|
165
165
|
param_names = {param.short_name for param in self.parameters}
|
166
166
|
for param_value_name in physical_value:
|
167
167
|
if param_value_name not in param_names:
|
@@ -193,8 +193,10 @@ class BasicStructure(ComplexDop):
|
|
193
193
|
odxraise(f"No value for required parameter {param.short_name} specified",
|
194
194
|
EncodeError)
|
195
195
|
|
196
|
-
param.
|
197
|
-
|
196
|
+
param_phys_value = physical_value.get(param.short_name)
|
197
|
+
param.encode_into_pdu(physical_value=param_phys_value, encode_state=encode_state)
|
198
|
+
|
199
|
+
encode_state.journal.append((param, param_phys_value))
|
198
200
|
|
199
201
|
encode_state.is_end_of_pdu = False
|
200
202
|
if self.byte_size is not None:
|
@@ -236,6 +238,7 @@ class BasicStructure(ComplexDop):
|
|
236
238
|
for param in self.parameters:
|
237
239
|
value = param.decode_from_pdu(decode_state)
|
238
240
|
|
241
|
+
decode_state.journal.append((param, value))
|
239
242
|
result[param.short_name] = value
|
240
243
|
|
241
244
|
# decoding of the structure finished. go back the original origin.
|
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())
|
@@ -46,8 +46,6 @@ class LinearSegment:
|
|
46
46
|
|
47
47
|
inverse_value: Union[int, float] = 0
|
48
48
|
if scale.compu_inverse_value is not None:
|
49
|
-
if abs(factor) < 1e-10:
|
50
|
-
odxraise(f"COMPU-INVERSE-VALUE for non-zero slope ({factor}) defined")
|
51
49
|
x = odxrequire(scale.compu_inverse_value).value
|
52
50
|
if not isinstance(x, (int, float)):
|
53
51
|
odxraise(f"Non-numeric COMPU-INVERSE-VALUE specified ({x!r})")
|
odxtools/database.py
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
# SPDX-License-Identifier: MIT
|
2
2
|
from itertools import chain
|
3
3
|
from pathlib import Path
|
4
|
-
from typing import IO, List, Optional, OrderedDict
|
4
|
+
from typing import IO, Any, Dict, List, Optional, OrderedDict
|
5
5
|
from xml.etree import ElementTree
|
6
6
|
from zipfile import ZipFile
|
7
7
|
|
@@ -13,7 +13,7 @@ from .diaglayer import DiagLayer
|
|
13
13
|
from .diaglayercontainer import DiagLayerContainer
|
14
14
|
from .exceptions import odxraise
|
15
15
|
from .nameditemlist import NamedItemList
|
16
|
-
from .odxlink import OdxLinkDatabase
|
16
|
+
from .odxlink import OdxLinkDatabase, OdxLinkId
|
17
17
|
|
18
18
|
|
19
19
|
class Database:
|
@@ -108,15 +108,7 @@ class Database:
|
|
108
108
|
|
109
109
|
# Build odxlinks
|
110
110
|
self._odxlinks = OdxLinkDatabase()
|
111
|
-
|
112
|
-
for subset in self.comparam_subsets:
|
113
|
-
self._odxlinks.update(subset._build_odxlinks())
|
114
|
-
|
115
|
-
for spec in self.comparam_specs:
|
116
|
-
self._odxlinks.update(spec._build_odxlinks())
|
117
|
-
|
118
|
-
for dlc in self.diag_layer_containers:
|
119
|
-
self._odxlinks.update(dlc._build_odxlinks())
|
111
|
+
self._odxlinks.update(self._build_odxlinks())
|
120
112
|
|
121
113
|
# Resolve ODXLINK references
|
122
114
|
for subset in self.comparam_subsets:
|
@@ -133,6 +125,20 @@ class Database:
|
|
133
125
|
for dlc in self.diag_layer_containers:
|
134
126
|
dlc._finalize_init(self, self._odxlinks)
|
135
127
|
|
128
|
+
def _build_odxlinks(self) -> Dict[OdxLinkId, Any]:
|
129
|
+
result: Dict[OdxLinkId, Any] = {}
|
130
|
+
|
131
|
+
for subset in self.comparam_subsets:
|
132
|
+
result.update(subset._build_odxlinks())
|
133
|
+
|
134
|
+
for spec in self.comparam_specs:
|
135
|
+
result.update(spec._build_odxlinks())
|
136
|
+
|
137
|
+
for dlc in self.diag_layer_containers:
|
138
|
+
result.update(dlc._build_odxlinks())
|
139
|
+
|
140
|
+
return result
|
141
|
+
|
136
142
|
@property
|
137
143
|
def odxlinks(self) -> OdxLinkDatabase:
|
138
144
|
"""A map from odx_id to object"""
|
odxtools/decodestate.py
CHANGED
@@ -1,11 +1,11 @@
|
|
1
1
|
# SPDX-License-Identifier: MIT
|
2
2
|
from dataclasses import dataclass, field
|
3
|
-
from typing import TYPE_CHECKING, Dict, cast
|
3
|
+
from typing import TYPE_CHECKING, Dict, List, Optional, Tuple, cast
|
4
4
|
|
5
5
|
import odxtools.exceptions as exceptions
|
6
6
|
|
7
7
|
from .exceptions import DecodeError
|
8
|
-
from .odxtypes import AtomicOdxType, DataType
|
8
|
+
from .odxtypes import AtomicOdxType, DataType, ParameterValue
|
9
9
|
|
10
10
|
try:
|
11
11
|
import bitstruct.c as bitstruct
|
@@ -13,6 +13,7 @@ except ImportError:
|
|
13
13
|
import bitstruct
|
14
14
|
|
15
15
|
if TYPE_CHECKING:
|
16
|
+
from .parameters.parameter import Parameter
|
16
17
|
from .tablerow import TableRow
|
17
18
|
|
18
19
|
|
@@ -46,6 +47,11 @@ class DecodeState:
|
|
46
47
|
#: values of the table key parameters decoded so far
|
47
48
|
table_keys: Dict[str, "TableRow"] = field(default_factory=dict)
|
48
49
|
|
50
|
+
#: List of parameters that have been decoded so far. The journal
|
51
|
+
#: is used by some types of parameters which depend on the values of
|
52
|
+
#: other parameters; i.e., environment data description parameters
|
53
|
+
journal: List[Tuple["Parameter", Optional[ParameterValue]]] = field(default_factory=list)
|
54
|
+
|
49
55
|
def extract_atomic_value(
|
50
56
|
self,
|
51
57
|
bit_length: int,
|
odxtools/diaglayer.py
CHANGED
@@ -161,11 +161,6 @@ class DiagLayer:
|
|
161
161
|
excessive memory consumption for large databases...
|
162
162
|
"""
|
163
163
|
|
164
|
-
# this attribute may be removed later. it is currently
|
165
|
-
# required to properly deal with auxiliary files within the
|
166
|
-
# diagnostic layer.
|
167
|
-
self._database = database
|
168
|
-
|
169
164
|
#####
|
170
165
|
# fill in all applicable objects that use value inheritance
|
171
166
|
#####
|
@@ -175,9 +170,9 @@ class DiagLayer:
|
|
175
170
|
self._diag_comms = NamedItemList(diag_comms)
|
176
171
|
|
177
172
|
# filter the diag comms for services and single-ECU jobs
|
178
|
-
|
173
|
+
diag_services = [dc for dc in diag_comms if isinstance(dc, DiagService)]
|
179
174
|
single_ecu_jobs = [dc for dc in diag_comms if isinstance(dc, SingleEcuJob)]
|
180
|
-
self.
|
175
|
+
self._diag_services = NamedItemList(diag_services)
|
181
176
|
self._single_ecu_jobs = NamedItemList(single_ecu_jobs)
|
182
177
|
|
183
178
|
global_negative_responses = self._compute_available_global_neg_responses(odxlinks)
|
@@ -294,7 +289,7 @@ class DiagLayer:
|
|
294
289
|
# scheme, cf the docstring of
|
295
290
|
# _compute_available_commmunication_parameters().
|
296
291
|
#####
|
297
|
-
self.
|
292
|
+
self._comparam_refs = NamedItemList(self._compute_available_commmunication_parameters())
|
298
293
|
|
299
294
|
#####
|
300
295
|
# resolve all SNREFs. TODO: We allow SNREFS to objects that
|
@@ -412,12 +407,17 @@ class DiagLayer:
|
|
412
407
|
|
413
408
|
@property
|
414
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]:
|
415
415
|
"""All diagnostic services applicable to this DiagLayer
|
416
416
|
|
417
417
|
This is a subset of all diagnostic communication
|
418
418
|
primitives. All references are resolved in the list returned.
|
419
419
|
"""
|
420
|
-
return self.
|
420
|
+
return self._diag_services
|
421
421
|
|
422
422
|
@property
|
423
423
|
def single_ecu_jobs(self) -> NamedItemList[SingleEcuJob]:
|
@@ -712,20 +712,20 @@ class DiagLayer:
|
|
712
712
|
com_params_dict[(cp.spec_ref.ref_id, cp.protocol_snref)] = cp
|
713
713
|
|
714
714
|
# finally, handle the locally defined communication parameters
|
715
|
-
for cp in self.diag_layer_raw.
|
715
|
+
for cp in self.diag_layer_raw.comparam_refs:
|
716
716
|
com_params_dict[(cp.spec_ref.ref_id, cp.protocol_snref)] = cp
|
717
717
|
|
718
718
|
return list(com_params_dict.values())
|
719
719
|
|
720
720
|
@property
|
721
|
-
def
|
721
|
+
def comparam_refs(self) -> NamedItemList[ComparamInstance]:
|
722
722
|
"""All communication parameters applicable to this DiagLayer
|
723
723
|
|
724
724
|
Note that, although communication parameters use inheritance,
|
725
725
|
it is *not* the "value inheritance" scheme used by e.g. DOPs,
|
726
726
|
tables, state charts, ...
|
727
727
|
"""
|
728
|
-
return self.
|
728
|
+
return self._comparam_refs
|
729
729
|
|
730
730
|
@cached_property
|
731
731
|
def protocols(self) -> NamedItemList["DiagLayer"]:
|
@@ -762,7 +762,7 @@ class DiagLayer:
|
|
762
762
|
protocol_name = protocol
|
763
763
|
|
764
764
|
# determine the set of applicable communication parameters
|
765
|
-
cps = [cp for cp in self.
|
765
|
+
cps = [cp for cp in self.comparam_refs if cp.short_name == cp_short_name]
|
766
766
|
if protocol_name is not None:
|
767
767
|
cps = [cp for cp in cps if cp.protocol_snref in (None, protocol_name)]
|
768
768
|
|
@@ -1200,16 +1200,19 @@ class DiagLayer:
|
|
1200
1200
|
for service in candidate_services:
|
1201
1201
|
try:
|
1202
1202
|
decoded_messages.append(service.decode_message(message))
|
1203
|
-
except DecodeError:
|
1203
|
+
except DecodeError as e:
|
1204
1204
|
# check if the message can be decoded as a global
|
1205
1205
|
# negative response for the service
|
1206
|
+
gnr_found = False
|
1206
1207
|
for gnr in self.global_negative_responses:
|
1207
1208
|
try:
|
1208
1209
|
decoded_gnr = gnr.decode(message)
|
1210
|
+
gnr_found = True
|
1209
1211
|
if not isinstance(decoded_gnr, dict):
|
1210
|
-
|
1211
|
-
|
1212
|
-
|
1212
|
+
odxraise(
|
1213
|
+
f"Expected the decoded value of a global "
|
1214
|
+
f"negative response to be a dictionary, "
|
1215
|
+
f"got {type(decoded_gnr)} for {self.short_name}", DecodeError)
|
1213
1216
|
|
1214
1217
|
decoded_messages.append(
|
1215
1218
|
Message(
|
@@ -1220,6 +1223,9 @@ class DiagLayer:
|
|
1220
1223
|
except DecodeError:
|
1221
1224
|
pass
|
1222
1225
|
|
1226
|
+
if not gnr_found:
|
1227
|
+
raise e
|
1228
|
+
|
1223
1229
|
if len(decoded_messages) == 0:
|
1224
1230
|
raise DecodeError(
|
1225
1231
|
f"None of the services {[x.short_name for x in candidate_services]} could parse {message.hex()}."
|