odxtools 5.3.1__py3-none-any.whl → 6.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.
- odxtools/__init__.py +1 -1
- odxtools/basicstructure.py +76 -91
- odxtools/cli/_parser_utils.py +12 -9
- odxtools/cli/_print_utils.py +7 -7
- odxtools/cli/browse.py +94 -73
- odxtools/cli/find.py +42 -59
- odxtools/cli/list.py +21 -17
- odxtools/cli/snoop.py +19 -18
- odxtools/communicationparameterref.py +6 -3
- odxtools/companydocinfo.py +2 -2
- odxtools/companyrevisioninfo.py +1 -1
- odxtools/comparamsubset.py +6 -6
- odxtools/complexcomparam.py +1 -1
- odxtools/compumethods/compumethod.py +6 -9
- odxtools/compumethods/createanycompumethod.py +11 -9
- odxtools/compumethods/identicalcompumethod.py +5 -4
- odxtools/compumethods/limit.py +9 -9
- odxtools/compumethods/linearcompumethod.py +25 -17
- odxtools/compumethods/scalelinearcompumethod.py +6 -5
- odxtools/compumethods/tabintpcompumethod.py +30 -9
- odxtools/compumethods/texttablecompumethod.py +22 -24
- odxtools/database.py +5 -5
- odxtools/dataobjectproperty.py +10 -23
- odxtools/decodestate.py +1 -1
- odxtools/determinenumberofitems.py +37 -8
- odxtools/diagcodedtype.py +14 -9
- odxtools/diagdatadictionaryspec.py +60 -37
- odxtools/diaglayer.py +30 -21
- odxtools/diaglayercontainer.py +40 -40
- odxtools/diaglayerraw.py +92 -63
- odxtools/diagnostictroublecode.py +2 -2
- odxtools/diagservice.py +53 -35
- odxtools/docrevision.py +1 -1
- odxtools/dopbase.py +14 -3
- odxtools/dtcdop.py +15 -9
- odxtools/dynamiclengthfield.py +6 -4
- odxtools/endofpdufield.py +22 -23
- odxtools/environmentdata.py +2 -5
- odxtools/environmentdatadescription.py +6 -4
- odxtools/field.py +3 -8
- odxtools/isotp_state_machine.py +52 -38
- odxtools/leadinglengthinfotype.py +9 -7
- odxtools/load_file.py +2 -1
- odxtools/load_odx_d_file.py +2 -5
- odxtools/load_pdx_file.py +2 -6
- odxtools/message.py +11 -3
- odxtools/minmaxlengthtype.py +107 -78
- odxtools/modification.py +2 -2
- odxtools/multiplexer.py +23 -21
- odxtools/multiplexerswitchkey.py +37 -8
- odxtools/nameditemlist.py +59 -58
- odxtools/odxlink.py +4 -2
- odxtools/odxtypes.py +4 -3
- odxtools/parameterinfo.py +6 -6
- odxtools/parameters/codedconstparameter.py +15 -25
- odxtools/parameters/createanyparameter.py +1 -1
- odxtools/parameters/dynamicparameter.py +6 -5
- odxtools/parameters/lengthkeyparameter.py +2 -1
- odxtools/parameters/matchingrequestparameter.py +8 -11
- odxtools/parameters/nrcconstparameter.py +11 -21
- odxtools/parameters/parameter.py +4 -18
- odxtools/parameters/parameterwithdop.py +14 -29
- odxtools/parameters/physicalconstantparameter.py +7 -9
- odxtools/parameters/reservedparameter.py +17 -38
- odxtools/parameters/systemparameter.py +6 -5
- odxtools/parameters/tableentryparameter.py +6 -5
- odxtools/parameters/tablekeyparameter.py +8 -15
- odxtools/parameters/tablestructparameter.py +11 -12
- odxtools/parameters/valueparameter.py +9 -24
- odxtools/paramlengthinfotype.py +11 -9
- odxtools/physicaldimension.py +1 -1
- odxtools/physicaltype.py +2 -2
- odxtools/response.py +7 -3
- odxtools/singleecujob.py +48 -22
- odxtools/standardlengthtype.py +11 -6
- odxtools/uds.py +1 -1
- odxtools/unit.py +5 -5
- odxtools/unitgroup.py +1 -1
- odxtools/unitspec.py +2 -2
- odxtools/version.py +13 -3
- odxtools/write_pdx_file.py +7 -4
- {odxtools-5.3.1.dist-info → odxtools-6.0.1.dist-info}/METADATA +7 -5
- {odxtools-5.3.1.dist-info → odxtools-6.0.1.dist-info}/RECORD +87 -88
- odxtools/positioneddataobjectproperty.py +0 -74
- {odxtools-5.3.1.dist-info → odxtools-6.0.1.dist-info}/LICENSE +0 -0
- {odxtools-5.3.1.dist-info → odxtools-6.0.1.dist-info}/WHEEL +0 -0
- {odxtools-5.3.1.dist-info → odxtools-6.0.1.dist-info}/entry_points.txt +0 -0
- {odxtools-5.3.1.dist-info → odxtools-6.0.1.dist-info}/top_level.txt +0 -0
odxtools/dataobjectproperty.py
CHANGED
@@ -15,9 +15,8 @@ from .element import IdentifiableElement
|
|
15
15
|
from .encodestate import EncodeState
|
16
16
|
from .exceptions import DecodeError, EncodeError, odxassert, odxrequire
|
17
17
|
from .odxlink import OdxDocFragment, OdxLinkDatabase, OdxLinkId, OdxLinkRef
|
18
|
-
from .odxtypes import odxstr_to_bool
|
18
|
+
from .odxtypes import AtomicOdxType, ParameterValue, odxstr_to_bool
|
19
19
|
from .physicaltype import PhysicalType
|
20
|
-
from .standardlengthtype import StandardLengthType
|
21
20
|
from .unit import Unit
|
22
21
|
from .utils import dataclass_fields_asdict
|
23
22
|
|
@@ -65,7 +64,7 @@ class DataObjectProperty(DopBase):
|
|
65
64
|
sdgs=sdgs,
|
66
65
|
**kwargs)
|
67
66
|
else:
|
68
|
-
dtclist: List[Union[DiagnosticTroubleCode, OdxLinkRef]] =
|
67
|
+
dtclist: List[Union[DiagnosticTroubleCode, OdxLinkRef]] = []
|
69
68
|
if (dtcs_elem := et_element.find("DTCS")) is not None:
|
70
69
|
for dtc_proxy_elem in dtcs_elem:
|
71
70
|
if dtc_proxy_elem.tag == "DTC":
|
@@ -100,7 +99,7 @@ class DataObjectProperty(DopBase):
|
|
100
99
|
result.update(self.diag_coded_type._build_odxlinks())
|
101
100
|
return result
|
102
101
|
|
103
|
-
def _resolve_odxlinks(self, odxlinks: OdxLinkDatabase):
|
102
|
+
def _resolve_odxlinks(self, odxlinks: OdxLinkDatabase) -> None:
|
104
103
|
"""Resolves the reference to the unit"""
|
105
104
|
super()._resolve_odxlinks(odxlinks)
|
106
105
|
|
@@ -119,14 +118,8 @@ class DataObjectProperty(DopBase):
|
|
119
118
|
def unit(self) -> Optional[Unit]:
|
120
119
|
return self._unit
|
121
120
|
|
122
|
-
|
123
|
-
|
124
|
-
# TODO: The DiagCodedTypes except StandardLengthType don't have a bit length.
|
125
|
-
# Should we remove this bit_length property from DOP or return None?
|
126
|
-
if isinstance(self.diag_coded_type, StandardLengthType):
|
127
|
-
return self.diag_coded_type.bit_length
|
128
|
-
else:
|
129
|
-
return None
|
121
|
+
def get_static_bit_length(self) -> Optional[int]:
|
122
|
+
return self.diag_coded_type.get_static_bit_length()
|
130
123
|
|
131
124
|
def convert_physical_to_internal(self, physical_value: Any) -> Any:
|
132
125
|
"""
|
@@ -145,10 +138,7 @@ class DataObjectProperty(DopBase):
|
|
145
138
|
"""
|
146
139
|
if not self.is_valid_physical_value(physical_value):
|
147
140
|
raise EncodeError(f"The value {repr(physical_value)} of type {type(physical_value)}"
|
148
|
-
f" is not a valid."
|
149
|
-
(f" Valid values are {self.compu_method.get_valid_physical_values()}"
|
150
|
-
if self.compu_method.get_valid_physical_values(
|
151
|
-
) else f" Expected type {self.physical_type.base_data_type.value}."))
|
141
|
+
f" is not a valid.")
|
152
142
|
|
153
143
|
internal_val = self.convert_physical_to_internal(physical_value)
|
154
144
|
return self.diag_coded_type.convert_internal_to_bytes(
|
@@ -164,19 +154,16 @@ class DataObjectProperty(DopBase):
|
|
164
154
|
"""
|
165
155
|
odxassert(0 <= bit_position and bit_position < 8)
|
166
156
|
|
167
|
-
internal,
|
157
|
+
internal, cursor_position = self.diag_coded_type.convert_bytes_to_internal(
|
168
158
|
decode_state, bit_position=bit_position)
|
169
159
|
|
170
160
|
if self.compu_method.is_valid_internal_value(internal):
|
171
|
-
return self.compu_method.convert_internal_to_physical(internal),
|
161
|
+
return self.compu_method.convert_internal_to_physical(internal), cursor_position
|
172
162
|
else:
|
173
163
|
# TODO: How to prevent this?
|
174
164
|
raise DecodeError(
|
175
165
|
f"DOP {self.short_name} could not convert the coded value "
|
176
166
|
f" {repr(internal)} to physical type {self.physical_type.base_data_type}.")
|
177
167
|
|
178
|
-
def is_valid_physical_value(self, physical_value):
|
179
|
-
return self.compu_method.is_valid_physical_value(physical_value)
|
180
|
-
|
181
|
-
def get_valid_physical_values(self):
|
182
|
-
return self.compu_method.get_valid_physical_values()
|
168
|
+
def is_valid_physical_value(self, physical_value: ParameterValue) -> bool:
|
169
|
+
return self.compu_method.is_valid_physical_value(cast(AtomicOdxType, physical_value))
|
odxtools/decodestate.py
CHANGED
@@ -1,18 +1,47 @@
|
|
1
1
|
from dataclasses import dataclass
|
2
|
-
from typing import List
|
2
|
+
from typing import TYPE_CHECKING, Any, Dict, List, Optional
|
3
3
|
from xml.etree import ElementTree
|
4
4
|
|
5
|
-
from .
|
6
|
-
from .
|
7
|
-
from .
|
5
|
+
from .dataobjectproperty import DataObjectProperty
|
6
|
+
from .exceptions import odxrequire
|
7
|
+
from .odxlink import OdxDocFragment, OdxLinkDatabase, OdxLinkId, OdxLinkRef
|
8
|
+
|
9
|
+
if TYPE_CHECKING:
|
10
|
+
from ..diaglayer import DiagLayer
|
8
11
|
|
9
12
|
|
10
13
|
@dataclass
|
11
|
-
class DetermineNumberOfItems
|
14
|
+
class DetermineNumberOfItems:
|
15
|
+
"""
|
16
|
+
The object that determines the number of items of dynamic fields
|
17
|
+
"""
|
18
|
+
byte_position: int
|
19
|
+
bit_position: Optional[int]
|
20
|
+
dop_ref: OdxLinkRef
|
12
21
|
|
13
22
|
@staticmethod
|
14
23
|
def from_et(et_element: ElementTree.Element,
|
15
24
|
doc_frags: List[OdxDocFragment]) -> "DetermineNumberOfItems":
|
16
|
-
|
17
|
-
|
18
|
-
|
25
|
+
byte_position = int(odxrequire(et_element.findtext("BYTE-POSITION")))
|
26
|
+
bit_position_str = et_element.findtext("BIT-POSITION")
|
27
|
+
bit_position = int(bit_position_str) if bit_position_str is not None else None
|
28
|
+
dop_ref = odxrequire(OdxLinkRef.from_et(et_element.find("DATA-OBJECT-PROP-REF"), doc_frags))
|
29
|
+
|
30
|
+
return DetermineNumberOfItems(
|
31
|
+
byte_position=byte_position,
|
32
|
+
bit_position=bit_position,
|
33
|
+
dop_ref=dop_ref,
|
34
|
+
)
|
35
|
+
|
36
|
+
def _build_odxlinks(self) -> Dict[OdxLinkId, Any]:
|
37
|
+
return {}
|
38
|
+
|
39
|
+
def _resolve_odxlinks(self, odxlinks: OdxLinkDatabase) -> None:
|
40
|
+
self._dop = odxlinks.resolve(self.dop_ref, DataObjectProperty)
|
41
|
+
|
42
|
+
def _resolve_snrefs(self, diag_layer: "DiagLayer") -> None:
|
43
|
+
pass
|
44
|
+
|
45
|
+
@property
|
46
|
+
def dop(self) -> DataObjectProperty:
|
47
|
+
return self._dop
|
odxtools/diagcodedtype.py
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
# SPDX-License-Identifier: MIT
|
2
2
|
import abc
|
3
3
|
from dataclasses import dataclass
|
4
|
-
from typing import TYPE_CHECKING, Literal, Optional, Tuple, Union
|
4
|
+
from typing import TYPE_CHECKING, Any, Dict, Literal, Optional, Tuple, Union
|
5
5
|
|
6
6
|
import bitstruct
|
7
7
|
|
@@ -9,12 +9,13 @@ from . import exceptions
|
|
9
9
|
from .decodestate import DecodeState
|
10
10
|
from .encodestate import EncodeState
|
11
11
|
from .exceptions import DecodeError, EncodeError, odxassert, odxraise
|
12
|
-
from .odxlink import OdxLinkDatabase
|
12
|
+
from .odxlink import OdxLinkDatabase, OdxLinkId
|
13
13
|
from .odxtypes import AtomicOdxType, DataType
|
14
14
|
|
15
15
|
if TYPE_CHECKING:
|
16
16
|
from .diaglayer import DiagLayer
|
17
17
|
|
18
|
+
# format specifiers for the data type using the bitstruct module
|
18
19
|
ODX_TYPE_TO_FORMAT_LETTER = {
|
19
20
|
DataType.A_INT32: "s",
|
20
21
|
DataType.A_UINT32: "u",
|
@@ -26,6 +27,7 @@ ODX_TYPE_TO_FORMAT_LETTER = {
|
|
26
27
|
DataType.A_UTF8STRING: "t",
|
27
28
|
}
|
28
29
|
|
30
|
+
# Allowed diag-coded types
|
29
31
|
DctType = Literal[
|
30
32
|
"LEADING-LENGTH-INFO-TYPE",
|
31
33
|
"MIN-MAX-LENGTH-TYPE",
|
@@ -41,17 +43,20 @@ class DiagCodedType(abc.ABC):
|
|
41
43
|
base_type_encoding: Optional[str]
|
42
44
|
is_highlow_byte_order_raw: Optional[bool]
|
43
45
|
|
44
|
-
def _build_odxlinks(self):
|
46
|
+
def _build_odxlinks(self) -> Dict[OdxLinkId, Any]: # noqa: B027
|
45
47
|
return {}
|
46
48
|
|
47
|
-
def _resolve_odxlinks(self, odxlinks: OdxLinkDatabase) -> None:
|
49
|
+
def _resolve_odxlinks(self, odxlinks: OdxLinkDatabase) -> None: # noqa: B027
|
48
50
|
"""Recursively resolve any odxlinks references"""
|
49
51
|
pass
|
50
52
|
|
51
|
-
def _resolve_snrefs(self, diag_layer: "DiagLayer") -> None:
|
53
|
+
def _resolve_snrefs(self, diag_layer: "DiagLayer") -> None: # noqa: B027
|
52
54
|
"""Recursively resolve any short-name references"""
|
53
55
|
pass
|
54
56
|
|
57
|
+
def get_static_bit_length(self) -> Optional[int]:
|
58
|
+
return None
|
59
|
+
|
55
60
|
@property
|
56
61
|
@abc.abstractmethod
|
57
62
|
def dct_type(self) -> DctType:
|
@@ -81,8 +86,8 @@ class DiagCodedType(abc.ABC):
|
|
81
86
|
byte_length = (bit_length + bit_position + 7) // 8
|
82
87
|
if byte_position + byte_length > len(coded_message):
|
83
88
|
raise DecodeError(f"Expected a longer message.")
|
84
|
-
|
85
|
-
extracted_bytes = coded_message[byte_position:
|
89
|
+
cursor_position = byte_position + byte_length
|
90
|
+
extracted_bytes = coded_message[byte_position:cursor_position]
|
86
91
|
|
87
92
|
# Apply byteorder
|
88
93
|
if not is_highlow_byte_order and base_data_type not in [
|
@@ -120,7 +125,7 @@ class DiagCodedType(abc.ABC):
|
|
120
125
|
else:
|
121
126
|
internal_value = internal_value.decode("utf-16-le")
|
122
127
|
|
123
|
-
return internal_value,
|
128
|
+
return internal_value, cursor_position
|
124
129
|
|
125
130
|
def _to_bytes(
|
126
131
|
self,
|
@@ -159,7 +164,7 @@ class DiagCodedType(abc.ABC):
|
|
159
164
|
] and base_data_type != 0):
|
160
165
|
raise EncodeError(
|
161
166
|
f"The number {repr(internal_value)} cannot be encoded into {bit_length} bits.")
|
162
|
-
return
|
167
|
+
return b''
|
163
168
|
|
164
169
|
char = ODX_TYPE_TO_FORMAT_LETTER[base_data_type]
|
165
170
|
|
@@ -40,17 +40,6 @@ class DiagDataDictionarySpec:
|
|
40
40
|
unit_spec: Optional[UnitSpec]
|
41
41
|
sdgs: List[SpecialDataGroup]
|
42
42
|
|
43
|
-
def __post_init__(self):
|
44
|
-
self._all_data_object_properties = NamedItemList(
|
45
|
-
chain(
|
46
|
-
self.data_object_props,
|
47
|
-
self.structures,
|
48
|
-
self.end_of_pdu_fields,
|
49
|
-
self.dynamic_length_fields,
|
50
|
-
self.dtc_dops,
|
51
|
-
self.tables,
|
52
|
-
),)
|
53
|
-
|
54
43
|
@staticmethod
|
55
44
|
def from_et(et_element: ElementTree.Element,
|
56
45
|
doc_frags: List[OdxDocFragment]) -> "DiagDataDictionarySpec":
|
@@ -145,19 +134,26 @@ class DiagDataDictionarySpec:
|
|
145
134
|
# note that DataDictionarySpec objects do not exhibit an ODXLINK id.
|
146
135
|
odxlinks = {}
|
147
136
|
|
148
|
-
for
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
137
|
+
for data_object_prop in self.data_object_props:
|
138
|
+
odxlinks.update(data_object_prop._build_odxlinks())
|
139
|
+
for dtc_dop in self.dtc_dops:
|
140
|
+
odxlinks.update(dtc_dop._build_odxlinks())
|
141
|
+
for env_data_desc in self.env_data_descs:
|
142
|
+
odxlinks.update(env_data_desc._build_odxlinks())
|
143
|
+
for env_data in self.env_datas:
|
144
|
+
odxlinks.update(env_data._build_odxlinks())
|
145
|
+
for mux in self.muxs:
|
146
|
+
odxlinks.update(mux._build_odxlinks())
|
147
|
+
for sdg in self.sdgs:
|
148
|
+
odxlinks.update(sdg._build_odxlinks())
|
149
|
+
for structure in self.structures:
|
150
|
+
odxlinks.update(structure._build_odxlinks())
|
151
|
+
for dynamic_length_field in self.dynamic_length_fields:
|
152
|
+
odxlinks.update(dynamic_length_field._build_odxlinks())
|
153
|
+
for end_of_pdu_field in self.end_of_pdu_fields:
|
154
|
+
odxlinks.update(end_of_pdu_field._build_odxlinks())
|
155
|
+
for table in self.tables:
|
156
|
+
odxlinks.update(table._build_odxlinks())
|
161
157
|
|
162
158
|
if self.unit_spec is not None:
|
163
159
|
odxlinks.update(self.unit_spec._build_odxlinks())
|
@@ -165,24 +161,51 @@ class DiagDataDictionarySpec:
|
|
165
161
|
return odxlinks
|
166
162
|
|
167
163
|
def _resolve_odxlinks(self, odxlinks: OdxLinkDatabase) -> None:
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
164
|
+
for data_object_prop in self.data_object_props:
|
165
|
+
data_object_prop._resolve_odxlinks(odxlinks)
|
166
|
+
for dtc_dop in self.dtc_dops:
|
167
|
+
dtc_dop._resolve_odxlinks(odxlinks)
|
168
|
+
for dynamic_length_field in self.dynamic_length_fields:
|
169
|
+
dynamic_length_field._resolve_odxlinks(odxlinks)
|
170
|
+
for end_of_pdu_field in self.end_of_pdu_fields:
|
171
|
+
end_of_pdu_field._resolve_odxlinks(odxlinks)
|
172
|
+
for env_data_desc in self.env_data_descs:
|
173
|
+
env_data_desc._resolve_odxlinks(odxlinks)
|
174
|
+
for env_data in self.env_datas:
|
175
|
+
env_data._resolve_odxlinks(odxlinks)
|
176
|
+
for mux in self.muxs:
|
177
|
+
mux._resolve_odxlinks(odxlinks)
|
178
|
+
for sdg in self.sdgs:
|
179
|
+
sdg._resolve_odxlinks(odxlinks)
|
180
|
+
for structure in self.structures:
|
181
|
+
structure._resolve_odxlinks(odxlinks)
|
182
|
+
for table in self.tables:
|
183
|
+
table._resolve_odxlinks(odxlinks)
|
173
184
|
|
174
185
|
if self.unit_spec is not None:
|
175
186
|
self.unit_spec._resolve_odxlinks(odxlinks)
|
176
187
|
|
177
188
|
def _resolve_snrefs(self, diag_layer: "DiagLayer") -> None:
|
178
|
-
for
|
179
|
-
|
180
|
-
|
181
|
-
|
189
|
+
for data_object_prop in self.data_object_props:
|
190
|
+
data_object_prop._resolve_snrefs(diag_layer)
|
191
|
+
for dtc_dop in self.dtc_dops:
|
192
|
+
dtc_dop._resolve_snrefs(diag_layer)
|
193
|
+
for dynamic_length_field in self.dynamic_length_fields:
|
194
|
+
dynamic_length_field._resolve_snrefs(diag_layer)
|
195
|
+
for end_of_pdu_field in self.end_of_pdu_fields:
|
196
|
+
end_of_pdu_field._resolve_snrefs(diag_layer)
|
197
|
+
for env_data_desc in self.env_data_descs:
|
198
|
+
env_data_desc._resolve_snrefs(diag_layer)
|
199
|
+
for env_data in self.env_datas:
|
200
|
+
env_data._resolve_snrefs(diag_layer)
|
201
|
+
for mux in self.muxs:
|
202
|
+
mux._resolve_snrefs(diag_layer)
|
203
|
+
for sdg in self.sdgs:
|
204
|
+
sdg._resolve_snrefs(diag_layer)
|
205
|
+
for structure in self.structures:
|
206
|
+
structure._resolve_snrefs(diag_layer)
|
207
|
+
for table in self.tables:
|
208
|
+
table._resolve_snrefs(diag_layer)
|
182
209
|
|
183
210
|
if self.unit_spec is not None:
|
184
211
|
self.unit_spec._resolve_snrefs(diag_layer)
|
185
|
-
|
186
|
-
@property
|
187
|
-
def all_data_object_properties(self):
|
188
|
-
return self._all_data_object_properties
|
odxtools/diaglayer.py
CHANGED
@@ -208,7 +208,8 @@ class DiagLayer:
|
|
208
208
|
# scheme, cf the docstring of
|
209
209
|
# _compute_available_commmunication_parameters().
|
210
210
|
#####
|
211
|
-
self._communication_parameters =
|
211
|
+
self._communication_parameters = NamedItemList(
|
212
|
+
self._compute_available_commmunication_parameters())
|
212
213
|
|
213
214
|
#####
|
214
215
|
# resolve all SNREFs. TODO: We allow SNREFS to objects that
|
@@ -349,7 +350,7 @@ class DiagLayer:
|
|
349
350
|
#####
|
350
351
|
# <value inheritance mechanism helpers>
|
351
352
|
#####
|
352
|
-
def _get_parent_refs_sorted_by_priority(self, reverse=False) -> Iterable[ParentRef]:
|
353
|
+
def _get_parent_refs_sorted_by_priority(self, reverse: bool = False) -> Iterable[ParentRef]:
|
353
354
|
return sorted(
|
354
355
|
self.diag_layer_raw.parent_refs,
|
355
356
|
key=lambda pr: pr.layer.variant_type.inheritance_priority,
|
@@ -443,10 +444,10 @@ class DiagLayer:
|
|
443
444
|
def _compute_available_diag_comms(self, odxlinks: OdxLinkDatabase
|
444
445
|
) -> Iterable[Union[DiagService, SingleEcuJob]]:
|
445
446
|
|
446
|
-
def get_local_objects_fn(dl):
|
447
|
+
def get_local_objects_fn(dl: DiagLayer) -> Iterable[Union[DiagService, SingleEcuJob]]:
|
447
448
|
return dl._get_local_diag_comms(odxlinks)
|
448
449
|
|
449
|
-
def not_inherited_fn(parent_ref):
|
450
|
+
def not_inherited_fn(parent_ref: ParentRef) -> List[str]:
|
450
451
|
return parent_ref.not_inherited_diag_comms
|
451
452
|
|
452
453
|
return self._compute_available_objects(get_local_objects_fn, not_inherited_fn)
|
@@ -454,10 +455,10 @@ class DiagLayer:
|
|
454
455
|
def _compute_available_global_neg_responses(self, odxlinks: OdxLinkDatabase) \
|
455
456
|
-> Iterable[Response]:
|
456
457
|
|
457
|
-
def get_local_objects_fn(dl):
|
458
|
+
def get_local_objects_fn(dl: DiagLayer) -> Iterable[Response]:
|
458
459
|
return dl.diag_layer_raw.global_negative_responses
|
459
460
|
|
460
|
-
def not_inherited_fn(parent_ref):
|
461
|
+
def not_inherited_fn(parent_ref: ParentRef) -> List[str]:
|
461
462
|
return parent_ref.not_inherited_global_neg_responses
|
462
463
|
|
463
464
|
return self._compute_available_objects(get_local_objects_fn, not_inherited_fn)
|
@@ -468,7 +469,7 @@ class DiagLayer:
|
|
468
469
|
exclude: Callable[["ParentRef"], List[str]],
|
469
470
|
) -> NamedItemList[TNamed]:
|
470
471
|
|
471
|
-
def get_local_objects_fn(dl:
|
472
|
+
def get_local_objects_fn(dl: DiagLayer) -> Iterable[TNamed]:
|
472
473
|
if dl.diag_layer_raw.diag_data_dictionary_spec is None:
|
473
474
|
return []
|
474
475
|
return include(dl.diag_layer_raw.diag_data_dictionary_spec)
|
@@ -478,40 +479,40 @@ class DiagLayer:
|
|
478
479
|
|
479
480
|
def _compute_available_functional_classes(self) -> Iterable[FunctionalClass]:
|
480
481
|
|
481
|
-
def get_local_objects_fn(dl):
|
482
|
+
def get_local_objects_fn(dl: DiagLayer) -> Iterable[FunctionalClass]:
|
482
483
|
return dl.diag_layer_raw.functional_classes
|
483
484
|
|
484
|
-
def not_inherited_fn(parent_ref):
|
485
|
+
def not_inherited_fn(parent_ref: ParentRef) -> List[str]:
|
485
486
|
return []
|
486
487
|
|
487
488
|
return self._compute_available_objects(get_local_objects_fn, not_inherited_fn)
|
488
489
|
|
489
490
|
def _compute_available_additional_audiences(self) -> Iterable[AdditionalAudience]:
|
490
491
|
|
491
|
-
def get_local_objects_fn(dl):
|
492
|
+
def get_local_objects_fn(dl: DiagLayer) -> Iterable[AdditionalAudience]:
|
492
493
|
return dl.diag_layer_raw.additional_audiences
|
493
494
|
|
494
|
-
def not_inherited_fn(parent_ref):
|
495
|
+
def not_inherited_fn(parent_ref: ParentRef) -> List[str]:
|
495
496
|
return []
|
496
497
|
|
497
498
|
return self._compute_available_objects(get_local_objects_fn, not_inherited_fn)
|
498
499
|
|
499
500
|
def _compute_available_state_charts(self) -> Iterable[StateChart]:
|
500
501
|
|
501
|
-
def get_local_objects_fn(dl):
|
502
|
+
def get_local_objects_fn(dl: DiagLayer) -> Iterable[StateChart]:
|
502
503
|
return dl.diag_layer_raw.state_charts
|
503
504
|
|
504
|
-
def not_inherited_fn(parent_ref):
|
505
|
+
def not_inherited_fn(parent_ref: ParentRef) -> List[str]:
|
505
506
|
return []
|
506
507
|
|
507
508
|
return self._compute_available_objects(get_local_objects_fn, not_inherited_fn)
|
508
509
|
|
509
510
|
def _compute_available_unit_groups(self) -> Iterable[UnitGroup]:
|
510
511
|
|
511
|
-
def get_local_objects_fn(dl):
|
512
|
+
def get_local_objects_fn(dl: DiagLayer) -> Iterable[UnitGroup]:
|
512
513
|
return dl._get_local_unit_groups()
|
513
514
|
|
514
|
-
def not_inherited_fn(parent_ref):
|
515
|
+
def not_inherited_fn(parent_ref: ParentRef) -> List[str]:
|
515
516
|
return []
|
516
517
|
|
517
518
|
return self._compute_available_objects(get_local_objects_fn, not_inherited_fn)
|
@@ -547,7 +548,7 @@ class DiagLayer:
|
|
547
548
|
without a specified protocol are taken as fallbacks...
|
548
549
|
|
549
550
|
"""
|
550
|
-
com_params_dict: Dict[Tuple[str, Optional[str]], CommunicationParameterRef] =
|
551
|
+
com_params_dict: Dict[Tuple[str, Optional[str]], CommunicationParameterRef] = {}
|
551
552
|
|
552
553
|
# Look in parent refs for inherited communication
|
553
554
|
# parameters. First fetch the communication parameters from
|
@@ -563,7 +564,7 @@ class DiagLayer:
|
|
563
564
|
return list(com_params_dict.values())
|
564
565
|
|
565
566
|
@property
|
566
|
-
def communication_parameters(self) ->
|
567
|
+
def communication_parameters(self) -> NamedItemList[CommunicationParameterRef]:
|
567
568
|
"""All communication parameters applicable to this DiagLayer
|
568
569
|
|
569
570
|
Note that, although communication parameters use inheritance,
|
@@ -579,7 +580,7 @@ class DiagLayer:
|
|
579
580
|
Note that protocols are *not* explicitly inherited objects,
|
580
581
|
but the parent diagnostic layers of variant type "PROTOCOL".
|
581
582
|
"""
|
582
|
-
result_dict: Dict[str, DiagLayer] =
|
583
|
+
result_dict: Dict[str, DiagLayer] = {}
|
583
584
|
|
584
585
|
for parent_ref in self._get_parent_refs_sorted_by_priority():
|
585
586
|
for prot in parent_ref.layer.protocols:
|
@@ -613,6 +614,7 @@ class DiagLayer:
|
|
613
614
|
f"Communication parameter `{cp_short_name}` specified more "
|
614
615
|
f"than once. Using first occurence.",
|
615
616
|
OdxWarning,
|
617
|
+
stacklevel=1,
|
616
618
|
)
|
617
619
|
elif len(cps) == 0:
|
618
620
|
return None
|
@@ -897,7 +899,9 @@ class DiagLayer:
|
|
897
899
|
# global negative responses. (I.e., one for each
|
898
900
|
# service. This can be avoided by specifying the
|
899
901
|
# corresponding request for `decode_response()`.)
|
900
|
-
request_prefix =
|
902
|
+
request_prefix = b''
|
903
|
+
if s.request is not None:
|
904
|
+
request_prefix = s.request.coded_const_prefix()
|
901
905
|
prefixes = [request_prefix]
|
902
906
|
prefixes += [
|
903
907
|
x.coded_const_prefix(request_prefix=request_prefix) for x in chain(
|
@@ -954,11 +958,16 @@ class DiagLayer:
|
|
954
958
|
for gnr in self.global_negative_responses:
|
955
959
|
try:
|
956
960
|
decoded_gnr = gnr.decode(message)
|
961
|
+
if not isinstance(decoded_gnr, dict):
|
962
|
+
raise DecodeError(f"Expected the decoded value of a global "
|
963
|
+
f"negative response to be a dictionary, "
|
964
|
+
f"got {type(decoded_gnr)} for {self.short_name}")
|
965
|
+
|
957
966
|
decoded_messages.append(
|
958
967
|
Message(
|
959
968
|
coded_message=message,
|
960
969
|
service=service,
|
961
|
-
|
970
|
+
coding_object=gnr,
|
962
971
|
param_dict=decoded_gnr))
|
963
972
|
except DecodeError:
|
964
973
|
pass
|
@@ -974,7 +983,7 @@ class DiagLayer:
|
|
974
983
|
|
975
984
|
return self._decode(message, candidate_services)
|
976
985
|
|
977
|
-
def decode_response(self, response: bytes, request: Union[bytes, Message]) ->
|
986
|
+
def decode_response(self, response: bytes, request: Union[bytes, Message]) -> List[Message]:
|
978
987
|
if isinstance(request, Message):
|
979
988
|
candidate_services = [request.service]
|
980
989
|
else:
|
odxtools/diaglayercontainer.py
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
# SPDX-License-Identifier: MIT
|
2
2
|
from dataclasses import dataclass
|
3
3
|
from itertools import chain
|
4
|
-
from typing import List, Optional, Union
|
4
|
+
from typing import Any, Dict, List, Optional, Union
|
5
5
|
from xml.etree import ElementTree
|
6
6
|
|
7
7
|
from .admindata import AdminData
|
@@ -12,7 +12,7 @@ from .diaglayer import DiagLayer
|
|
12
12
|
from .element import IdentifiableElement
|
13
13
|
from .exceptions import odxrequire
|
14
14
|
from .nameditemlist import NamedItemList
|
15
|
-
from .odxlink import OdxDocFragment, OdxLinkDatabase
|
15
|
+
from .odxlink import OdxDocFragment, OdxLinkDatabase, OdxLinkId
|
16
16
|
from .specialdatagroup import SpecialDataGroup
|
17
17
|
from .utils import dataclass_fields_asdict
|
18
18
|
|
@@ -82,62 +82,62 @@ class DiagLayerContainer(IdentifiableElement):
|
|
82
82
|
sdgs=sdgs,
|
83
83
|
**kwargs)
|
84
84
|
|
85
|
-
def _build_odxlinks(self):
|
85
|
+
def _build_odxlinks(self) -> Dict[OdxLinkId, Any]:
|
86
86
|
result = {self.odx_id: self}
|
87
87
|
|
88
88
|
if self.admin_data is not None:
|
89
89
|
result.update(self.admin_data._build_odxlinks())
|
90
|
-
|
91
|
-
|
92
|
-
for cd in self.company_datas:
|
93
|
-
result.update(cd._build_odxlinks())
|
94
|
-
|
95
|
-
for dl in chain(
|
96
|
-
self.ecu_shared_datas,
|
97
|
-
self.protocols,
|
98
|
-
self.functional_groups,
|
99
|
-
self.base_variants,
|
100
|
-
self.ecu_variants,
|
101
|
-
):
|
102
|
-
result.update(dl._build_odxlinks())
|
103
|
-
|
90
|
+
for cd in self.company_datas:
|
91
|
+
result.update(cd._build_odxlinks())
|
104
92
|
for sdg in self.sdgs:
|
105
93
|
result.update(sdg._build_odxlinks())
|
106
94
|
|
95
|
+
for ecu_shared_data in self.ecu_shared_datas:
|
96
|
+
result.update(ecu_shared_data._build_odxlinks())
|
97
|
+
for protocol in self.protocols:
|
98
|
+
result.update(protocol._build_odxlinks())
|
99
|
+
for functional_group in self.functional_groups:
|
100
|
+
result.update(functional_group._build_odxlinks())
|
101
|
+
for base_variant in self.base_variants:
|
102
|
+
result.update(base_variant._build_odxlinks())
|
103
|
+
for ecu_variant in self.ecu_variants:
|
104
|
+
result.update(ecu_variant._build_odxlinks())
|
105
|
+
|
107
106
|
return result
|
108
107
|
|
109
108
|
def _resolve_odxlinks(self, odxlinks: OdxLinkDatabase) -> None:
|
110
109
|
if self.admin_data is not None:
|
111
110
|
self.admin_data._resolve_odxlinks(odxlinks)
|
112
|
-
|
113
|
-
|
114
|
-
for cd in self.company_datas:
|
115
|
-
cd._resolve_odxlinks(odxlinks)
|
116
|
-
|
117
|
-
for dl in chain(
|
118
|
-
self.ecu_shared_datas,
|
119
|
-
self.protocols,
|
120
|
-
self.functional_groups,
|
121
|
-
self.base_variants,
|
122
|
-
self.ecu_variants,
|
123
|
-
):
|
124
|
-
dl._resolve_odxlinks(odxlinks)
|
125
|
-
|
111
|
+
for cd in self.company_datas:
|
112
|
+
cd._resolve_odxlinks(odxlinks)
|
126
113
|
for sdg in self.sdgs:
|
127
114
|
sdg._resolve_odxlinks(odxlinks)
|
128
115
|
|
116
|
+
for ecu_shared_data in self.ecu_shared_datas:
|
117
|
+
ecu_shared_data._resolve_odxlinks(odxlinks)
|
118
|
+
for protocol in self.protocols:
|
119
|
+
protocol._resolve_odxlinks(odxlinks)
|
120
|
+
for functional_group in self.functional_groups:
|
121
|
+
functional_group._resolve_odxlinks(odxlinks)
|
122
|
+
for base_variant in self.base_variants:
|
123
|
+
base_variant._resolve_odxlinks(odxlinks)
|
124
|
+
for ecu_variant in self.ecu_variants:
|
125
|
+
ecu_variant._resolve_odxlinks(odxlinks)
|
126
|
+
|
129
127
|
def _finalize_init(self, odxlinks: OdxLinkDatabase) -> None:
|
130
|
-
for
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
128
|
+
for ecu_shared_data in self.ecu_shared_datas:
|
129
|
+
ecu_shared_data._finalize_init(odxlinks)
|
130
|
+
for protocol in self.protocols:
|
131
|
+
protocol._finalize_init(odxlinks)
|
132
|
+
for functional_group in self.functional_groups:
|
133
|
+
functional_group._finalize_init(odxlinks)
|
134
|
+
for base_variant in self.base_variants:
|
135
|
+
base_variant._finalize_init(odxlinks)
|
136
|
+
for ecu_variant in self.ecu_variants:
|
137
|
+
ecu_variant._finalize_init(odxlinks)
|
138
138
|
|
139
139
|
@property
|
140
|
-
def diag_layers(self):
|
140
|
+
def diag_layers(self) -> NamedItemList[DiagLayer]:
|
141
141
|
return self._diag_layers
|
142
142
|
|
143
143
|
def __getitem__(self, key: Union[int, str]) -> DiagLayer:
|