odxtools 8.3.4__py3-none-any.whl → 9.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/basicstructure.py +36 -207
- odxtools/cli/_print_utils.py +163 -131
- odxtools/cli/browse.py +94 -79
- odxtools/cli/compare.py +88 -69
- odxtools/cli/list.py +2 -3
- odxtools/codec.py +211 -0
- odxtools/diaglayers/hierarchyelement.py +0 -10
- odxtools/dopbase.py +5 -3
- odxtools/dtcdop.py +101 -14
- odxtools/inputparam.py +0 -7
- odxtools/leadinglengthinfotype.py +1 -8
- odxtools/message.py +0 -7
- odxtools/minmaxlengthtype.py +4 -4
- odxtools/outputparam.py +0 -7
- odxtools/parameterinfo.py +12 -12
- odxtools/parameters/parameter.py +6 -4
- odxtools/paramlengthinfotype.py +8 -9
- odxtools/request.py +109 -16
- odxtools/response.py +115 -15
- odxtools/specialdatagroup.py +1 -1
- odxtools/templates/macros/printDOP.xml.jinja2 +16 -0
- odxtools/uds.py +0 -8
- odxtools/version.py +2 -2
- {odxtools-8.3.4.dist-info → odxtools-9.0.0.dist-info}/METADATA +7 -8
- {odxtools-8.3.4.dist-info → odxtools-9.0.0.dist-info}/RECORD +29 -28
- {odxtools-8.3.4.dist-info → odxtools-9.0.0.dist-info}/WHEEL +1 -1
- {odxtools-8.3.4.dist-info → odxtools-9.0.0.dist-info}/LICENSE +0 -0
- {odxtools-8.3.4.dist-info → odxtools-9.0.0.dist-info}/entry_points.txt +0 -0
- {odxtools-8.3.4.dist-info → odxtools-9.0.0.dist-info}/top_level.txt +0 -0
odxtools/dtcdop.py
CHANGED
@@ -15,13 +15,59 @@ from .dopbase import DopBase
|
|
15
15
|
from .encodestate import EncodeState
|
16
16
|
from .exceptions import DecodeError, EncodeError, odxassert, odxraise, odxrequire
|
17
17
|
from .nameditemlist import NamedItemList
|
18
|
-
from .odxlink import OdxDocFragment, OdxLinkDatabase, OdxLinkId, OdxLinkRef
|
18
|
+
from .odxlink import OdxDocFragment, OdxLinkDatabase, OdxLinkId, OdxLinkRef, resolve_snref
|
19
19
|
from .odxtypes import ParameterValue, odxstr_to_bool
|
20
20
|
from .physicaltype import PhysicalType
|
21
21
|
from .snrefcontext import SnRefContext
|
22
22
|
from .utils import dataclass_fields_asdict
|
23
23
|
|
24
24
|
|
25
|
+
@dataclass
|
26
|
+
class LinkedDtcDop:
|
27
|
+
not_inherited_dtc_snrefs: List[str]
|
28
|
+
dtc_dop_ref: OdxLinkRef
|
29
|
+
|
30
|
+
@property
|
31
|
+
def dtc_dop(self) -> "DtcDop":
|
32
|
+
return self._dtc_dop
|
33
|
+
|
34
|
+
@property
|
35
|
+
def short_name(self) -> str:
|
36
|
+
return self._dtc_dop.short_name
|
37
|
+
|
38
|
+
@property
|
39
|
+
def not_inherited_dtcs(self) -> NamedItemList[DiagnosticTroubleCode]:
|
40
|
+
return self._not_inherited_dtcs
|
41
|
+
|
42
|
+
@staticmethod
|
43
|
+
def from_et(et_element: ElementTree.Element, doc_frags: List[OdxDocFragment]) -> "LinkedDtcDop":
|
44
|
+
not_inherited_dtc_snrefs = [
|
45
|
+
odxrequire(el.get("SHORT-NAME"))
|
46
|
+
for el in et_element.iterfind("NOT-INHERITED-DTC-SNREFS/"
|
47
|
+
"NOT-INHERITED-DTC-SNREF")
|
48
|
+
]
|
49
|
+
|
50
|
+
dtc_dop_ref = odxrequire(OdxLinkRef.from_et(et_element.find("DTC-DOP-REF"), doc_frags))
|
51
|
+
|
52
|
+
return LinkedDtcDop(
|
53
|
+
not_inherited_dtc_snrefs=not_inherited_dtc_snrefs, dtc_dop_ref=dtc_dop_ref)
|
54
|
+
|
55
|
+
def _build_odxlinks(self) -> Dict[OdxLinkId, Any]:
|
56
|
+
return {}
|
57
|
+
|
58
|
+
def _resolve_odxlinks(self, odxlinks: OdxLinkDatabase) -> None:
|
59
|
+
self._dtc_dop = odxlinks.resolve(self.dtc_dop_ref, DtcDop)
|
60
|
+
|
61
|
+
def _resolve_snrefs(self, context: SnRefContext) -> None:
|
62
|
+
dtc_dop = self._dtc_dop
|
63
|
+
not_inherited_dtcs = [
|
64
|
+
resolve_snref(ni_snref, dtc_dop.dtcs, DiagnosticTroubleCode)
|
65
|
+
for ni_snref in self.not_inherited_dtc_snrefs
|
66
|
+
]
|
67
|
+
|
68
|
+
self._not_inherited_dtcs = NamedItemList(not_inherited_dtcs)
|
69
|
+
|
70
|
+
|
25
71
|
@dataclass
|
26
72
|
class DtcDop(DopBase):
|
27
73
|
"""A DOP describing a diagnostic trouble code"""
|
@@ -30,9 +76,12 @@ class DtcDop(DopBase):
|
|
30
76
|
physical_type: PhysicalType
|
31
77
|
compu_method: CompuMethod
|
32
78
|
dtcs_raw: List[Union[DiagnosticTroubleCode, OdxLinkRef]]
|
33
|
-
|
79
|
+
linked_dtc_dops_raw: List[LinkedDtcDop]
|
34
80
|
is_visible_raw: Optional[bool]
|
35
81
|
|
82
|
+
def __post_init__(self) -> None:
|
83
|
+
self._init_finished = False
|
84
|
+
|
36
85
|
@staticmethod
|
37
86
|
def from_et(et_element: ElementTree.Element, doc_frags: List[OdxDocFragment]) -> "DtcDop":
|
38
87
|
"""Reads a DTC-DOP."""
|
@@ -56,12 +105,10 @@ class DtcDop(DopBase):
|
|
56
105
|
elif dtc_proxy_elem.tag == "DTC-REF":
|
57
106
|
dtcs_raw.append(OdxLinkRef.from_et(dtc_proxy_elem, doc_frags))
|
58
107
|
|
59
|
-
|
60
|
-
|
61
|
-
OdxLinkRef.from_et(dtc_ref_elem, doc_frags)
|
108
|
+
linked_dtc_dops_raw = [
|
109
|
+
LinkedDtcDop.from_et(dtc_ref_elem, doc_frags)
|
62
110
|
for dtc_ref_elem in et_element.iterfind("LINKED-DTC-DOPS/"
|
63
|
-
"LINKED-DTC-DOP
|
64
|
-
"DTC-DOP-REF")
|
111
|
+
"LINKED-DTC-DOP")
|
65
112
|
]
|
66
113
|
is_visible_raw = odxstr_to_bool(et_element.get("IS-VISIBLE"))
|
67
114
|
|
@@ -70,7 +117,7 @@ class DtcDop(DopBase):
|
|
70
117
|
physical_type=physical_type,
|
71
118
|
compu_method=compu_method,
|
72
119
|
dtcs_raw=dtcs_raw,
|
73
|
-
|
120
|
+
linked_dtc_dops_raw=linked_dtc_dops_raw,
|
74
121
|
is_visible_raw=is_visible_raw,
|
75
122
|
**kwargs)
|
76
123
|
|
@@ -79,12 +126,12 @@ class DtcDop(DopBase):
|
|
79
126
|
return self._dtcs
|
80
127
|
|
81
128
|
@property
|
82
|
-
def
|
83
|
-
return self.
|
129
|
+
def linked_dtc_dops(self) -> NamedItemList[LinkedDtcDop]:
|
130
|
+
return self._linked_dtc_dops
|
84
131
|
|
85
132
|
@property
|
86
|
-
def
|
87
|
-
return self.
|
133
|
+
def is_visible(self) -> bool:
|
134
|
+
return self.is_visible_raw is True
|
88
135
|
|
89
136
|
@override
|
90
137
|
def decode_from_pdu(self, decode_state: DecodeState) -> ParameterValue:
|
@@ -185,6 +232,9 @@ class DtcDop(DopBase):
|
|
185
232
|
if isinstance(dtc_proxy, DiagnosticTroubleCode):
|
186
233
|
odxlinks.update(dtc_proxy._build_odxlinks())
|
187
234
|
|
235
|
+
for linked_dtc_dop in self.linked_dtc_dops_raw:
|
236
|
+
odxlinks.update(linked_dtc_dop._build_odxlinks())
|
237
|
+
|
188
238
|
return odxlinks
|
189
239
|
|
190
240
|
def _resolve_odxlinks(self, odxlinks: OdxLinkDatabase) -> None:
|
@@ -201,10 +251,19 @@ class DtcDop(DopBase):
|
|
201
251
|
dtc = odxlinks.resolve(dtc_proxy, DiagnosticTroubleCode)
|
202
252
|
self._dtcs.append(dtc)
|
203
253
|
|
204
|
-
|
205
|
-
|
254
|
+
for linked_dtc_dop in self.linked_dtc_dops_raw:
|
255
|
+
linked_dtc_dop._resolve_odxlinks(odxlinks)
|
206
256
|
|
207
257
|
def _resolve_snrefs(self, context: SnRefContext) -> None:
|
258
|
+
# hack to avoid initializing the DtcDop object multiple
|
259
|
+
# times. This is required, because the linked DTC DOP feature
|
260
|
+
# requires the "parent" DTC DOPs to be fully initialized but
|
261
|
+
# the standard does not define a formal ordering of DTC DOPs.
|
262
|
+
if self._init_finished:
|
263
|
+
return
|
264
|
+
|
265
|
+
self._init_finished = True
|
266
|
+
|
208
267
|
super()._resolve_snrefs(context)
|
209
268
|
|
210
269
|
self.compu_method._resolve_snrefs(context)
|
@@ -212,3 +271,31 @@ class DtcDop(DopBase):
|
|
212
271
|
for dtc_proxy in self.dtcs_raw:
|
213
272
|
if isinstance(dtc_proxy, DiagnosticTroubleCode):
|
214
273
|
dtc_proxy._resolve_snrefs(context)
|
274
|
+
|
275
|
+
for linked_dtc_dop in self.linked_dtc_dops_raw:
|
276
|
+
linked_dtc_dop._resolve_snrefs(context)
|
277
|
+
|
278
|
+
# add the inherited DTCs from linked DTC DOPs. Note that this
|
279
|
+
# requires that there are no cycles in the "link-hierarchy"
|
280
|
+
dtc_short_names = {dtc.short_name for dtc in self._dtcs}
|
281
|
+
for linked_dtc_dop in self.linked_dtc_dops_raw:
|
282
|
+
linked_dtc_dop.dtc_dop._resolve_snrefs(context)
|
283
|
+
|
284
|
+
for dtc in linked_dtc_dop.dtc_dop.dtcs:
|
285
|
+
if dtc.short_name in dtc_short_names:
|
286
|
+
# we already have a DTC with that name. Since we
|
287
|
+
# are not supposed to overwrite the local DTCs, we
|
288
|
+
# skip processing this one. TODO: Are inheritance
|
289
|
+
# conflicts for DTCs allowed?
|
290
|
+
continue
|
291
|
+
|
292
|
+
if dtc.short_name in linked_dtc_dop.not_inherited_dtc_snrefs:
|
293
|
+
# DTC is explicitly not inherited
|
294
|
+
continue
|
295
|
+
|
296
|
+
self._dtcs.append(dtc)
|
297
|
+
dtc_short_names.add(dtc.short_name)
|
298
|
+
|
299
|
+
# at this place, the linked DTC DOPs exhibit .short_name, so
|
300
|
+
# we can create a NamedItemList...
|
301
|
+
self._linked_dtc_dops = NamedItemList(self.linked_dtc_dops_raw)
|
odxtools/inputparam.py
CHANGED
@@ -3,8 +3,6 @@ from dataclasses import dataclass
|
|
3
3
|
from typing import Any, Dict, List, Optional
|
4
4
|
from xml.etree import ElementTree
|
5
5
|
|
6
|
-
from deprecation import deprecated
|
7
|
-
|
8
6
|
from .dopbase import DopBase
|
9
7
|
from .element import NamedElement
|
10
8
|
from .exceptions import odxrequire
|
@@ -49,8 +47,3 @@ class InputParam(NamedElement):
|
|
49
47
|
def dop_base(self) -> DopBase:
|
50
48
|
"""The data object property describing this parameter."""
|
51
49
|
return self._dop_base
|
52
|
-
|
53
|
-
@property
|
54
|
-
@deprecated(details="use .dop_base") # type: ignore[misc]
|
55
|
-
def dop(self) -> DopBase:
|
56
|
-
return self._dop_base
|
@@ -1,6 +1,6 @@
|
|
1
1
|
# SPDX-License-Identifier: MIT
|
2
2
|
from dataclasses import dataclass
|
3
|
-
from typing import List
|
3
|
+
from typing import List
|
4
4
|
from xml.etree import ElementTree
|
5
5
|
|
6
6
|
from typing_extensions import override
|
@@ -50,13 +50,6 @@ class LeadingLengthInfoType(DiagCodedType):
|
|
50
50
|
def dct_type(self) -> DctType:
|
51
51
|
return "LEADING-LENGTH-INFO-TYPE"
|
52
52
|
|
53
|
-
def get_static_bit_length(self) -> Optional[int]:
|
54
|
-
# note that self.bit_length is just the length of the length
|
55
|
-
# specifier field. This is then followed by the same number of
|
56
|
-
# bytes as the value of this field, i.e., the length of this
|
57
|
-
# DCT is dynamic!
|
58
|
-
return None
|
59
|
-
|
60
53
|
@override
|
61
54
|
def encode_into_pdu(self, internal_value: AtomicOdxType, encode_state: EncodeState) -> None:
|
62
55
|
|
odxtools/message.py
CHANGED
@@ -2,8 +2,6 @@
|
|
2
2
|
from dataclasses import dataclass
|
3
3
|
from typing import TYPE_CHECKING, Union
|
4
4
|
|
5
|
-
from deprecation import deprecated
|
6
|
-
|
7
5
|
from .odxtypes import ParameterValue, ParameterValueDict
|
8
6
|
|
9
7
|
if TYPE_CHECKING:
|
@@ -29,8 +27,3 @@ class Message:
|
|
29
27
|
|
30
28
|
def __getitem__(self, key: str) -> ParameterValue:
|
31
29
|
return self.param_dict[key]
|
32
|
-
|
33
|
-
@property
|
34
|
-
@deprecated("use .coding_object") # type: ignore[misc]
|
35
|
-
def structure(self) -> Union["Request", "Response"]:
|
36
|
-
return self.coding_object
|
odxtools/minmaxlengthtype.py
CHANGED
@@ -20,6 +20,10 @@ class MinMaxLengthType(DiagCodedType):
|
|
20
20
|
max_length: Optional[int]
|
21
21
|
termination: str
|
22
22
|
|
23
|
+
@property
|
24
|
+
def dct_type(self) -> DctType:
|
25
|
+
return "MIN-MAX-LENGTH-TYPE"
|
26
|
+
|
23
27
|
@staticmethod
|
24
28
|
@override
|
25
29
|
def from_et(et_element: ElementTree.Element,
|
@@ -50,10 +54,6 @@ class MinMaxLengthType(DiagCodedType):
|
|
50
54
|
"END-OF-PDU",
|
51
55
|
], f"A min-max length type cannot have the termination {self.termination}")
|
52
56
|
|
53
|
-
@property
|
54
|
-
def dct_type(self) -> DctType:
|
55
|
-
return "MIN-MAX-LENGTH-TYPE"
|
56
|
-
|
57
57
|
def __termination_sequence(self) -> bytes:
|
58
58
|
"""Returns the termination byte sequence if it isn't defined."""
|
59
59
|
# The termination sequence is actually not specified by ASAM
|
odxtools/outputparam.py
CHANGED
@@ -3,8 +3,6 @@ from dataclasses import dataclass
|
|
3
3
|
from typing import Any, Dict, List, Optional
|
4
4
|
from xml.etree import ElementTree
|
5
5
|
|
6
|
-
from deprecation import deprecated
|
7
|
-
|
8
6
|
from .dopbase import DopBase
|
9
7
|
from .element import IdentifiableElement
|
10
8
|
from .exceptions import odxrequire
|
@@ -40,8 +38,3 @@ class OutputParam(IdentifiableElement):
|
|
40
38
|
def dop_base(self) -> DopBase:
|
41
39
|
"""The data object property describing this parameter."""
|
42
40
|
return self._dop_base
|
43
|
-
|
44
|
-
@property
|
45
|
-
@deprecated(details="use .dop_base") # type: ignore[misc]
|
46
|
-
def dop(self) -> DopBase:
|
47
|
-
return self._dop_base
|
odxtools/parameterinfo.py
CHANGED
@@ -34,36 +34,36 @@ from .staticfield import StaticField
|
|
34
34
|
|
35
35
|
def _get_linear_segment_info(segment: LinearSegment) -> str:
|
36
36
|
ll = segment.physical_lower_limit
|
37
|
-
ul = segment.physical_upper_limit
|
38
37
|
if ll is None or ll.interval_type == IntervalType.INFINITE:
|
39
38
|
ll_str = "(-inf"
|
40
39
|
else:
|
41
40
|
ll_delim = '(' if ll.interval_type == IntervalType.OPEN else '['
|
42
41
|
ll_str = f"{ll_delim}{ll._value!r}"
|
43
42
|
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
43
|
+
ul = segment.physical_upper_limit
|
44
|
+
if ul is None or ul.interval_type == IntervalType.INFINITE:
|
45
|
+
ul_str = "inf)"
|
46
|
+
else:
|
47
|
+
ul_delim = ')' if ul.interval_type == IntervalType.OPEN else ']'
|
48
|
+
ul_str = f"{ul._value!r}{ul_delim}"
|
49
49
|
|
50
50
|
return f"{ll_str}, {ul_str}"
|
51
51
|
|
52
52
|
|
53
53
|
def _get_rat_func_segment_info(segment: RatFuncSegment) -> str:
|
54
54
|
ll = segment.lower_limit
|
55
|
-
ul = segment.upper_limit
|
56
55
|
if ll is None or ll.interval_type == IntervalType.INFINITE:
|
57
56
|
ll_str = "(-inf"
|
58
57
|
else:
|
59
58
|
ll_delim = '(' if ll.interval_type == IntervalType.OPEN else '['
|
60
59
|
ll_str = f"{ll_delim}{ll._value!r}"
|
61
60
|
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
61
|
+
ul = segment.upper_limit
|
62
|
+
if ul is None or ul.interval_type == IntervalType.INFINITE:
|
63
|
+
ul_str = "inf)"
|
64
|
+
else:
|
65
|
+
ul_delim = ')' if ul.interval_type == IntervalType.OPEN else ']'
|
66
|
+
ul_str = f"{ul._value!r}{ul_delim}"
|
67
67
|
|
68
68
|
return f"{ll_str}, {ul_str}"
|
69
69
|
|
odxtools/parameters/parameter.py
CHANGED
@@ -33,11 +33,13 @@ ParameterType = Literal[
|
|
33
33
|
@dataclass
|
34
34
|
class Parameter(NamedElement):
|
35
35
|
"""This class corresponds to POSITIONABLE-PARAM in the ODX
|
36
|
-
specification
|
36
|
+
specification
|
37
37
|
|
38
|
-
|
39
|
-
|
40
|
-
|
38
|
+
All parameter classes must adhere to the `Codec` type protocol, so
|
39
|
+
`isinstance(param, Codec)` ought to be true. Be aware that, even
|
40
|
+
though the ODX specification seems to make the distinction of
|
41
|
+
"positionable" and "normal" parameters, it does not define any
|
42
|
+
non-positionable parameter types.
|
41
43
|
|
42
44
|
"""
|
43
45
|
oid: Optional[str]
|
odxtools/paramlengthinfotype.py
CHANGED
@@ -20,9 +20,16 @@ if TYPE_CHECKING:
|
|
20
20
|
|
21
21
|
@dataclass
|
22
22
|
class ParamLengthInfoType(DiagCodedType):
|
23
|
-
|
24
23
|
length_key_ref: OdxLinkRef
|
25
24
|
|
25
|
+
@property
|
26
|
+
def dct_type(self) -> DctType:
|
27
|
+
return "PARAM-LENGTH-INFO-TYPE"
|
28
|
+
|
29
|
+
@property
|
30
|
+
def length_key(self) -> "LengthKeyParameter":
|
31
|
+
return self._length_key
|
32
|
+
|
26
33
|
@staticmethod
|
27
34
|
@override
|
28
35
|
def from_et(et_element: ElementTree.Element,
|
@@ -34,10 +41,6 @@ class ParamLengthInfoType(DiagCodedType):
|
|
34
41
|
|
35
42
|
return ParamLengthInfoType(length_key_ref=length_key_ref, **kwargs)
|
36
43
|
|
37
|
-
@property
|
38
|
-
def dct_type(self) -> DctType:
|
39
|
-
return "PARAM-LENGTH-INFO-TYPE"
|
40
|
-
|
41
44
|
def _build_odxlinks(self) -> Dict[OdxLinkId, Any]:
|
42
45
|
return super()._build_odxlinks()
|
43
46
|
|
@@ -54,10 +57,6 @@ class ParamLengthInfoType(DiagCodedType):
|
|
54
57
|
"""Recursively resolve any short-name references"""
|
55
58
|
super()._resolve_snrefs(context)
|
56
59
|
|
57
|
-
@property
|
58
|
-
def length_key(self) -> "LengthKeyParameter":
|
59
|
-
return self._length_key
|
60
|
-
|
61
60
|
@override
|
62
61
|
def encode_into_pdu(self, internal_value: AtomicOdxType, encode_state: EncodeState) -> None:
|
63
62
|
bit_length = encode_state.length_keys.get(self.length_key.short_name)
|
odxtools/request.py
CHANGED
@@ -1,39 +1,132 @@
|
|
1
1
|
# SPDX-License-Identifier: MIT
|
2
2
|
from dataclasses import dataclass
|
3
|
-
from typing import List
|
3
|
+
from typing import Any, Dict, List, Optional, cast
|
4
4
|
from xml.etree import ElementTree
|
5
5
|
|
6
|
-
from .
|
6
|
+
from .admindata import AdminData
|
7
|
+
from .codec import (composite_codec_decode_from_pdu, composite_codec_encode_into_pdu,
|
8
|
+
composite_codec_get_coded_const_prefix, composite_codec_get_free_parameters,
|
9
|
+
composite_codec_get_required_parameters, composite_codec_get_static_bit_length)
|
10
|
+
from .decodestate import DecodeState
|
11
|
+
from .element import IdentifiableElement
|
7
12
|
from .encodestate import EncodeState
|
8
|
-
from .
|
9
|
-
from .
|
13
|
+
from .exceptions import odxraise
|
14
|
+
from .nameditemlist import NamedItemList
|
15
|
+
from .odxlink import OdxDocFragment, OdxLinkDatabase, OdxLinkId
|
16
|
+
from .odxtypes import ParameterValue, ParameterValueDict
|
17
|
+
from .parameters.createanyparameter import create_any_parameter_from_et
|
18
|
+
from .parameters.parameter import Parameter
|
10
19
|
from .snrefcontext import SnRefContext
|
20
|
+
from .specialdatagroup import SpecialDataGroup
|
11
21
|
from .utils import dataclass_fields_asdict
|
12
22
|
|
13
23
|
|
14
|
-
# TODO: The spec does not say that requests are basic structures. For
|
15
|
-
# now, we derive from it anyway because it simplifies the en- and
|
16
|
-
# decoding machinery...
|
17
24
|
@dataclass
|
18
|
-
class Request(
|
25
|
+
class Request(IdentifiableElement):
|
26
|
+
"""Represents all information related to an UDS request
|
27
|
+
|
28
|
+
This class implements the `CompositeCodec` interface.
|
29
|
+
"""
|
30
|
+
admin_data: Optional[AdminData]
|
31
|
+
parameters: NamedItemList[Parameter]
|
32
|
+
sdgs: List[SpecialDataGroup]
|
33
|
+
|
34
|
+
@property
|
35
|
+
def required_parameters(self) -> List[Parameter]:
|
36
|
+
return composite_codec_get_required_parameters(self)
|
37
|
+
|
38
|
+
@property
|
39
|
+
def free_parameters(self) -> List[Parameter]:
|
40
|
+
return composite_codec_get_free_parameters(self)
|
19
41
|
|
20
42
|
@staticmethod
|
21
43
|
def from_et(et_element: ElementTree.Element, doc_frags: List[OdxDocFragment]) -> "Request":
|
22
|
-
|
23
|
-
kwargs = dataclass_fields_asdict(BasicStructure.from_et(et_element, doc_frags))
|
44
|
+
kwargs = dataclass_fields_asdict(IdentifiableElement.from_et(et_element, doc_frags))
|
24
45
|
|
25
|
-
|
46
|
+
admin_data = AdminData.from_et(et_element.find("ADMIN-DATA"), doc_frags)
|
47
|
+
parameters = NamedItemList([
|
48
|
+
create_any_parameter_from_et(et_parameter, doc_frags)
|
49
|
+
for et_parameter in et_element.iterfind("PARAMS/PARAM")
|
50
|
+
])
|
51
|
+
sdgs = [
|
52
|
+
SpecialDataGroup.from_et(sdge, doc_frags) for sdge in et_element.iterfind("SDGS/SDG")
|
53
|
+
]
|
26
54
|
|
27
|
-
|
28
|
-
encode_state = EncodeState(is_end_of_pdu=True)
|
55
|
+
return Request(admin_data=admin_data, parameters=parameters, sdgs=sdgs, **kwargs)
|
29
56
|
|
30
|
-
|
57
|
+
def _build_odxlinks(self) -> Dict[OdxLinkId, Any]:
|
58
|
+
result = {self.odx_id: self}
|
31
59
|
|
32
|
-
|
60
|
+
if self.admin_data is not None:
|
61
|
+
result.update(self.admin_data._build_odxlinks())
|
62
|
+
|
63
|
+
for param in self.parameters:
|
64
|
+
result.update(param._build_odxlinks())
|
65
|
+
|
66
|
+
for sdg in self.sdgs:
|
67
|
+
result.update(sdg._build_odxlinks())
|
68
|
+
|
69
|
+
return result
|
70
|
+
|
71
|
+
def _resolve_odxlinks(self, odxlinks: OdxLinkDatabase) -> None:
|
72
|
+
if self.admin_data is not None:
|
73
|
+
self.admin_data._resolve_odxlinks(odxlinks)
|
74
|
+
|
75
|
+
for param in self.parameters:
|
76
|
+
param._resolve_odxlinks(odxlinks)
|
77
|
+
|
78
|
+
for sdg in self.sdgs:
|
79
|
+
sdg._resolve_odxlinks(odxlinks)
|
33
80
|
|
34
81
|
def _resolve_snrefs(self, context: SnRefContext) -> None:
|
35
82
|
context.request = self
|
83
|
+
context.parameters = self.parameters
|
36
84
|
|
37
|
-
|
85
|
+
if self.admin_data is not None:
|
86
|
+
self.admin_data._resolve_snrefs(context)
|
87
|
+
|
88
|
+
for param in self.parameters:
|
89
|
+
param._resolve_snrefs(context)
|
90
|
+
|
91
|
+
for sdg in self.sdgs:
|
92
|
+
sdg._resolve_snrefs(context)
|
38
93
|
|
39
94
|
context.request = None
|
95
|
+
context.parameters = None
|
96
|
+
|
97
|
+
def get_static_bit_length(self) -> Optional[int]:
|
98
|
+
return composite_codec_get_static_bit_length(self)
|
99
|
+
|
100
|
+
def print_free_parameters_info(self) -> None:
|
101
|
+
"""Print a human readable description of the composite codec's
|
102
|
+
free parameters to `stdout`
|
103
|
+
"""
|
104
|
+
from .parameterinfo import parameter_info
|
105
|
+
|
106
|
+
print(parameter_info(self.free_parameters), end="")
|
107
|
+
|
108
|
+
def encode(self, **kwargs: ParameterValue) -> bytearray:
|
109
|
+
encode_state = EncodeState(is_end_of_pdu=True)
|
110
|
+
|
111
|
+
self.encode_into_pdu(physical_value=kwargs, encode_state=encode_state)
|
112
|
+
|
113
|
+
return encode_state.coded_message
|
114
|
+
|
115
|
+
def decode(self, message: bytes) -> ParameterValueDict:
|
116
|
+
decode_state = DecodeState(coded_message=message)
|
117
|
+
param_values = self.decode_from_pdu(decode_state)
|
118
|
+
|
119
|
+
if not isinstance(param_values, dict):
|
120
|
+
odxraise("Decoding a request must result in a dictionary")
|
121
|
+
|
122
|
+
return cast(ParameterValueDict, param_values)
|
123
|
+
|
124
|
+
def encode_into_pdu(self, physical_value: Optional[ParameterValue],
|
125
|
+
encode_state: EncodeState) -> None:
|
126
|
+
composite_codec_encode_into_pdu(self, physical_value, encode_state)
|
127
|
+
|
128
|
+
def decode_from_pdu(self, decode_state: DecodeState) -> ParameterValue:
|
129
|
+
return composite_codec_decode_from_pdu(self, decode_state)
|
130
|
+
|
131
|
+
def coded_const_prefix(self, request_prefix: bytes = b'') -> bytes:
|
132
|
+
return composite_codec_get_coded_const_prefix(self, request_prefix)
|