odxtools 9.2.0__py3-none-any.whl → 9.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/companyrevisioninfo.py +1 -1
- odxtools/comparaminstance.py +1 -6
- odxtools/comparamspec.py +3 -2
- odxtools/comparamsubset.py +3 -5
- odxtools/dataobjectproperty.py +12 -6
- odxtools/decodestate.py +120 -26
- odxtools/description.py +19 -2
- odxtools/diagcodedtype.py +9 -2
- odxtools/diagcomm.py +4 -4
- odxtools/diaglayercontainer.py +2 -2
- odxtools/diaglayers/diaglayerraw.py +2 -2
- odxtools/diaglayers/hierarchyelement.py +2 -0
- odxtools/diagnostictroublecode.py +4 -8
- odxtools/diagservice.py +70 -4
- odxtools/diagvariable.py +45 -7
- odxtools/encodestate.py +147 -51
- odxtools/encoding.py +56 -0
- odxtools/environmentdatadescription.py +77 -47
- odxtools/functionalclass.py +7 -2
- odxtools/inputparam.py +9 -3
- odxtools/leadinglengthinfotype.py +4 -0
- odxtools/minmaxlengthtype.py +57 -36
- odxtools/modification.py +3 -2
- odxtools/odxcategory.py +2 -2
- odxtools/odxlink.py +31 -7
- odxtools/odxtypes.py +1 -1
- odxtools/outputparam.py +8 -3
- odxtools/parameters/matchingrequestparameter.py +1 -0
- odxtools/parameters/physicalconstantparameter.py +1 -0
- odxtools/parameters/reservedparameter.py +1 -0
- odxtools/parameters/tableentryparameter.py +15 -4
- odxtools/parameters/tablekeyparameter.py +20 -17
- odxtools/paramlengthinfotype.py +5 -3
- odxtools/physicaltype.py +2 -1
- odxtools/scaleconstr.py +4 -4
- odxtools/standardlengthtype.py +98 -22
- odxtools/statetransition.py +24 -3
- odxtools/structure.py +10 -2
- odxtools/swvariable.py +3 -1
- odxtools/table.py +55 -3
- odxtools/tablerow.py +91 -8
- odxtools/templates/macros/printDOP.xml.jinja2 +2 -2
- odxtools/templates/macros/printDescription.xml.jinja2 +5 -1
- odxtools/templates/macros/printDiagLayer.xml.jinja2 +1 -1
- odxtools/templates/macros/printDiagVariable.xml.jinja2 +12 -3
- odxtools/templates/macros/printFunctionalClass.xml.jinja2 +4 -0
- odxtools/templates/macros/printParentRef.xml.jinja2 +27 -0
- odxtools/templates/macros/printProtocol.xml.jinja2 +1 -1
- odxtools/templates/macros/printService.xml.jinja2 +30 -0
- odxtools/templates/macros/printStructure.xml.jinja2 +2 -1
- odxtools/templates/macros/printTable.xml.jinja2 +43 -0
- odxtools/templates/macros/printUnitSpec.xml.jinja2 +4 -0
- odxtools/uds.py +2 -2
- odxtools/unit.py +8 -12
- odxtools/unitspec.py +5 -2
- odxtools/utils.py +44 -1
- odxtools/version.py +2 -2
- {odxtools-9.2.0.dist-info → odxtools-9.4.0.dist-info}/METADATA +2 -2
- {odxtools-9.2.0.dist-info → odxtools-9.4.0.dist-info}/RECORD +63 -62
- {odxtools-9.2.0.dist-info → odxtools-9.4.0.dist-info}/WHEEL +1 -1
- {odxtools-9.2.0.dist-info → odxtools-9.4.0.dist-info}/LICENSE +0 -0
- {odxtools-9.2.0.dist-info → odxtools-9.4.0.dist-info}/entry_points.txt +0 -0
- {odxtools-9.2.0.dist-info → odxtools-9.4.0.dist-info}/top_level.txt +0 -0
@@ -72,6 +72,7 @@ class LeadingLengthInfoType(DiagCodedType):
|
|
72
72
|
used_mask=None,
|
73
73
|
bit_length=self.bit_length,
|
74
74
|
base_data_type=DataType.A_UINT32,
|
75
|
+
base_type_encoding=None,
|
75
76
|
is_highlow_byte_order=self.is_highlow_byte_order,
|
76
77
|
)
|
77
78
|
|
@@ -80,6 +81,7 @@ class LeadingLengthInfoType(DiagCodedType):
|
|
80
81
|
used_mask=None,
|
81
82
|
bit_length=8 * byte_length,
|
82
83
|
base_data_type=self.base_data_type,
|
84
|
+
base_type_encoding=None,
|
83
85
|
is_highlow_byte_order=self.is_highlow_byte_order,
|
84
86
|
)
|
85
87
|
|
@@ -90,6 +92,7 @@ class LeadingLengthInfoType(DiagCodedType):
|
|
90
92
|
byte_length = decode_state.extract_atomic_value(
|
91
93
|
bit_length=self.bit_length,
|
92
94
|
base_data_type=DataType.A_UINT32, # length is an integer
|
95
|
+
base_type_encoding=None,
|
93
96
|
is_highlow_byte_order=self.is_highlow_byte_order,
|
94
97
|
)
|
95
98
|
|
@@ -102,6 +105,7 @@ class LeadingLengthInfoType(DiagCodedType):
|
|
102
105
|
value = decode_state.extract_atomic_value(
|
103
106
|
bit_length=8 * byte_length,
|
104
107
|
base_data_type=self.base_data_type,
|
108
|
+
base_type_encoding=None,
|
105
109
|
is_highlow_byte_order=self.is_highlow_byte_order,
|
106
110
|
)
|
107
111
|
|
odxtools/minmaxlengthtype.py
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
# SPDX-License-Identifier: MIT
|
2
2
|
from dataclasses import dataclass
|
3
|
-
from
|
3
|
+
from enum import Enum
|
4
|
+
from typing import List, Optional, cast
|
4
5
|
from xml.etree import ElementTree
|
5
6
|
|
6
7
|
from typing_extensions import override
|
@@ -8,17 +9,24 @@ from typing_extensions import override
|
|
8
9
|
from .decodestate import DecodeState
|
9
10
|
from .diagcodedtype import DctType, DiagCodedType
|
10
11
|
from .encodestate import EncodeState
|
12
|
+
from .encoding import get_string_encoding
|
11
13
|
from .exceptions import DecodeError, EncodeError, odxassert, odxraise, odxrequire
|
12
14
|
from .odxlink import OdxDocFragment
|
13
15
|
from .odxtypes import AtomicOdxType, DataType
|
14
16
|
from .utils import dataclass_fields_asdict
|
15
17
|
|
16
18
|
|
19
|
+
class Termination(Enum):
|
20
|
+
END_OF_PDU = "END-OF-PDU"
|
21
|
+
ZERO = "ZERO"
|
22
|
+
HEX_FF = "HEX-FF"
|
23
|
+
|
24
|
+
|
17
25
|
@dataclass
|
18
26
|
class MinMaxLengthType(DiagCodedType):
|
19
27
|
min_length: int
|
20
28
|
max_length: Optional[int]
|
21
|
-
termination:
|
29
|
+
termination: Termination
|
22
30
|
|
23
31
|
@property
|
24
32
|
def dct_type(self) -> DctType:
|
@@ -34,7 +42,13 @@ class MinMaxLengthType(DiagCodedType):
|
|
34
42
|
max_length = None
|
35
43
|
if et_element.find("MAX-LENGTH") is not None:
|
36
44
|
max_length = int(odxrequire(et_element.findtext("MAX-LENGTH")))
|
37
|
-
|
45
|
+
|
46
|
+
termination_str = odxrequire(et_element.get("TERMINATION"))
|
47
|
+
try:
|
48
|
+
termination = Termination(termination_str)
|
49
|
+
except ValueError:
|
50
|
+
termination = cast(Termination, None)
|
51
|
+
odxraise(f"Encountered unknown termination type '{termination_str}'")
|
38
52
|
|
39
53
|
return MinMaxLengthType(
|
40
54
|
min_length=min_length, max_length=max_length, termination=termination, **kwargs)
|
@@ -48,23 +62,18 @@ class MinMaxLengthType(DiagCodedType):
|
|
48
62
|
DataType.A_UNICODE2STRING,
|
49
63
|
DataType.A_UTF8STRING,
|
50
64
|
], f"A min-max length type cannot have the base data type {self.base_data_type}.")
|
51
|
-
odxassert(self.termination in [
|
52
|
-
"ZERO",
|
53
|
-
"HEX-FF",
|
54
|
-
"END-OF-PDU",
|
55
|
-
], f"A min-max length type cannot have the termination {self.termination}")
|
56
65
|
|
57
66
|
def __termination_sequence(self) -> bytes:
|
58
67
|
"""Returns the termination byte sequence if it isn't defined."""
|
59
68
|
# The termination sequence is actually not specified by ASAM
|
60
69
|
# for A_BYTEFIELD but I assume it is only one byte.
|
61
70
|
termination_sequence = b''
|
62
|
-
if self.termination ==
|
71
|
+
if self.termination == Termination.ZERO:
|
63
72
|
if self.base_data_type not in [DataType.A_UNICODE2STRING]:
|
64
73
|
termination_sequence = bytes([0x0])
|
65
74
|
else:
|
66
75
|
termination_sequence = bytes([0x0, 0x0])
|
67
|
-
elif self.termination ==
|
76
|
+
elif self.termination == Termination.HEX_FF:
|
68
77
|
if self.base_data_type not in [DataType.A_UNICODE2STRING]:
|
69
78
|
termination_sequence = bytes([0xFF])
|
70
79
|
else:
|
@@ -78,29 +87,52 @@ class MinMaxLengthType(DiagCodedType):
|
|
78
87
|
odxraise("MinMaxLengthType is currently only implemented for strings and byte arrays",
|
79
88
|
EncodeError)
|
80
89
|
|
81
|
-
|
82
|
-
|
90
|
+
raw_value = b''
|
91
|
+
if isinstance(internal_value, str):
|
92
|
+
str_encoding = get_string_encoding(self.base_data_type, self.base_type_encoding,
|
93
|
+
self.is_highlow_byte_order)
|
94
|
+
|
95
|
+
if str_encoding is None:
|
96
|
+
odxraise(f"Internal string value specified for object which is "
|
97
|
+
f"'{self.base_data_type.value}' not a string")
|
98
|
+
raw_value = b''
|
99
|
+
else:
|
100
|
+
raw_value = internal_value.encode(str_encoding)
|
83
101
|
else:
|
84
|
-
|
102
|
+
raw_value = bytes(internal_value)
|
103
|
+
|
104
|
+
data_length = len(raw_value)
|
105
|
+
|
106
|
+
if data_length < self.min_length:
|
107
|
+
odxraise(
|
108
|
+
f"Encoded value for MinMaxLengthType "
|
109
|
+
f"must be at least {self.min_length} bytes long. "
|
110
|
+
f"(Is: {data_length} bytes.)", EncodeError)
|
111
|
+
data_length = self.min_length
|
112
|
+
elif self.max_length is not None and data_length > self.max_length:
|
113
|
+
odxraise(
|
114
|
+
f"Encoded value for MinMaxLengthType "
|
115
|
+
f"must not be longer than {self.max_length} bytes. "
|
116
|
+
f"(Is: {data_length} bytes.)", EncodeError)
|
117
|
+
data_length = self.max_length
|
85
118
|
|
86
|
-
orig_cursor = encode_state.cursor_byte_position
|
87
119
|
encode_state.emplace_atomic_value(
|
88
|
-
internal_value=
|
120
|
+
internal_value=raw_value,
|
89
121
|
used_mask=None,
|
90
122
|
bit_length=8 * data_length,
|
91
|
-
base_data_type=
|
92
|
-
|
123
|
+
base_data_type=DataType.A_BYTEFIELD,
|
124
|
+
base_type_encoding=None,
|
125
|
+
is_highlow_byte_order=True,
|
93
126
|
)
|
94
|
-
value_len = encode_state.cursor_byte_position - orig_cursor
|
95
127
|
|
96
128
|
# TODO: ensure that the termination delimiter is not
|
97
129
|
# encountered within the encoded value.
|
98
130
|
|
99
131
|
odxassert(
|
100
|
-
self.termination !=
|
132
|
+
self.termination != Termination.END_OF_PDU or encode_state.is_end_of_pdu,
|
101
133
|
"Encountered a MIN-MAX-LENGTH type with END-OF-PDU termination "
|
102
134
|
"which is not located at the end of the PDU")
|
103
|
-
if encode_state.is_end_of_pdu or
|
135
|
+
if encode_state.is_end_of_pdu or data_length == self.max_length:
|
104
136
|
# All termination types may be ended by the end of the PDU
|
105
137
|
# or once reaching the maximum length. In this case, we
|
106
138
|
# must not add the termination sequence
|
@@ -110,24 +142,11 @@ class MinMaxLengthType(DiagCodedType):
|
|
110
142
|
|
111
143
|
# ensure that we don't try to encode an odd-length
|
112
144
|
# value when using a two-byte terminator
|
113
|
-
odxassert(
|
145
|
+
odxassert(data_length % len(termination_sequence) == 0)
|
114
146
|
|
115
|
-
|
147
|
+
data_length += len(termination_sequence)
|
116
148
|
encode_state.emplace_bytes(termination_sequence)
|
117
149
|
|
118
|
-
if value_len < self.min_length:
|
119
|
-
odxraise(
|
120
|
-
f"Encoded value for MinMaxLengthType "
|
121
|
-
f"must be at least {self.min_length} bytes long. "
|
122
|
-
f"(Is: {value_len} bytes.)", EncodeError)
|
123
|
-
return
|
124
|
-
elif self.max_length is not None and value_len > self.max_length:
|
125
|
-
odxraise(
|
126
|
-
f"Encoded value for MinMaxLengthType "
|
127
|
-
f"must not be longer than {self.max_length} bytes. "
|
128
|
-
f"(Is: {value_len} bytes.)", EncodeError)
|
129
|
-
return
|
130
|
-
|
131
150
|
def decode_from_pdu(self, decode_state: DecodeState) -> AtomicOdxType:
|
132
151
|
odxassert(decode_state.cursor_bit_position == 0,
|
133
152
|
"No bit position can be specified for MIN-MAX-LENGTH-TYPE values.")
|
@@ -142,7 +161,7 @@ class MinMaxLengthType(DiagCodedType):
|
|
142
161
|
if self.max_length is not None:
|
143
162
|
max_terminator_pos = min(max_terminator_pos, orig_cursor_pos + self.max_length)
|
144
163
|
|
145
|
-
if self.termination !=
|
164
|
+
if self.termination != Termination.END_OF_PDU:
|
146
165
|
# The parameter either ends after the maximum length, at
|
147
166
|
# the end of the PDU or if a termination sequence is
|
148
167
|
# found.
|
@@ -180,6 +199,7 @@ class MinMaxLengthType(DiagCodedType):
|
|
180
199
|
value = decode_state.extract_atomic_value(
|
181
200
|
bit_length=8 * byte_length,
|
182
201
|
base_data_type=self.base_data_type,
|
202
|
+
base_type_encoding=self.base_type_encoding,
|
183
203
|
is_highlow_byte_order=self.is_highlow_byte_order,
|
184
204
|
)
|
185
205
|
|
@@ -198,6 +218,7 @@ class MinMaxLengthType(DiagCodedType):
|
|
198
218
|
value = decode_state.extract_atomic_value(
|
199
219
|
bit_length=8 * byte_length,
|
200
220
|
base_data_type=self.base_data_type,
|
221
|
+
base_type_encoding=self.base_type_encoding,
|
201
222
|
is_highlow_byte_order=self.is_highlow_byte_order,
|
202
223
|
)
|
203
224
|
|
odxtools/modification.py
CHANGED
@@ -3,18 +3,19 @@ from dataclasses import dataclass
|
|
3
3
|
from typing import Any, Dict, List, Optional
|
4
4
|
from xml.etree import ElementTree
|
5
5
|
|
6
|
+
from .exceptions import odxrequire
|
6
7
|
from .odxlink import OdxDocFragment, OdxLinkDatabase, OdxLinkId
|
7
8
|
from .snrefcontext import SnRefContext
|
8
9
|
|
9
10
|
|
10
11
|
@dataclass
|
11
12
|
class Modification:
|
12
|
-
change:
|
13
|
+
change: str
|
13
14
|
reason: Optional[str]
|
14
15
|
|
15
16
|
@staticmethod
|
16
17
|
def from_et(et_element: ElementTree.Element, doc_frags: List[OdxDocFragment]) -> "Modification":
|
17
|
-
change = et_element.findtext("CHANGE")
|
18
|
+
change = odxrequire(et_element.findtext("CHANGE"))
|
18
19
|
reason = et_element.findtext("REASON")
|
19
20
|
|
20
21
|
return Modification(change=change, reason=reason)
|
odxtools/odxcategory.py
CHANGED
@@ -8,7 +8,7 @@ from .companydata import CompanyData
|
|
8
8
|
from .element import IdentifiableElement
|
9
9
|
from .exceptions import odxrequire
|
10
10
|
from .nameditemlist import NamedItemList
|
11
|
-
from .odxlink import OdxDocFragment, OdxLinkDatabase, OdxLinkId
|
11
|
+
from .odxlink import DocType, OdxDocFragment, OdxLinkDatabase, OdxLinkId
|
12
12
|
from .snrefcontext import SnRefContext
|
13
13
|
from .specialdatagroup import SpecialDataGroup
|
14
14
|
from .utils import dataclass_fields_asdict
|
@@ -32,7 +32,7 @@ class OdxCategory(IdentifiableElement):
|
|
32
32
|
|
33
33
|
@staticmethod
|
34
34
|
def category_from_et(et_element: ElementTree.Element, doc_frags: List[OdxDocFragment], *,
|
35
|
-
doc_type:
|
35
|
+
doc_type: DocType) -> "OdxCategory":
|
36
36
|
|
37
37
|
short_name = odxrequire(et_element.findtext("SHORT-NAME"))
|
38
38
|
# create the current ODX "document fragment" (description of the
|
odxtools/odxlink.py
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
# SPDX-License-Identifier: MIT
|
2
2
|
import warnings
|
3
3
|
from dataclasses import dataclass
|
4
|
+
from enum import Enum
|
4
5
|
from typing import Any, Dict, Iterable, List, Optional, Type, TypeVar, overload
|
5
6
|
from xml.etree import ElementTree
|
6
7
|
|
@@ -8,10 +9,22 @@ from .exceptions import OdxWarning, odxassert, odxraise, odxrequire
|
|
8
9
|
from .nameditemlist import OdxNamed, TNamed
|
9
10
|
|
10
11
|
|
12
|
+
class DocType(Enum):
|
13
|
+
FLASH = "FLASH"
|
14
|
+
CONTAINER = "CONTAINER"
|
15
|
+
LAYER = "LAYER"
|
16
|
+
MULTIPLE_ECU_JOB_SPEC = "MULTIPLE-ECU-JOB-SPEC"
|
17
|
+
COMPARAM_SPEC = "COMPARAM-SPEC"
|
18
|
+
VEHICLE_INFO_SPEC = "VEHICLE-INFO-SPEC"
|
19
|
+
COMPARAM_SUBSET = "COMPARAM-SUBSET"
|
20
|
+
ECU_CONFIG = "ECU-CONFIG"
|
21
|
+
FUNCTION_DICTIONARY_SPEC = "FUNCTION-DICTIONARY-SPEC"
|
22
|
+
|
23
|
+
|
11
24
|
@dataclass(frozen=True)
|
12
25
|
class OdxDocFragment:
|
13
26
|
doc_name: str
|
14
|
-
doc_type:
|
27
|
+
doc_type: DocType
|
15
28
|
|
16
29
|
|
17
30
|
@dataclass(frozen=True)
|
@@ -83,6 +96,10 @@ class OdxLinkRef:
|
|
83
96
|
#: The document fragments to which the `ref_id` refers to (in reverse order)
|
84
97
|
ref_docs: List[OdxDocFragment]
|
85
98
|
|
99
|
+
# TODO: this is difficult because OdxLinkRef is derived from and
|
100
|
+
# we do not want having to specify it mandatorily
|
101
|
+
#revision: Optional[str] = None
|
102
|
+
|
86
103
|
@overload
|
87
104
|
@staticmethod
|
88
105
|
def from_et(et: None, source_doc_frags: List[OdxDocFragment]) -> None:
|
@@ -109,18 +126,25 @@ class OdxLinkRef:
|
|
109
126
|
odxraise(f"Tag {et.tag} is not a ODXLINK reference")
|
110
127
|
return None
|
111
128
|
|
112
|
-
|
113
|
-
|
129
|
+
docref = et.attrib.get("DOCREF")
|
130
|
+
doctype_attr = et.attrib.get("DOCTYPE")
|
114
131
|
|
115
|
-
odxassert((
|
116
|
-
(
|
132
|
+
odxassert((docref is not None and doctype_attr is not None) or
|
133
|
+
(docref is None and doctype_attr is None),
|
117
134
|
"DOCREF and DOCTYPE must both either be specified or omitted")
|
118
135
|
|
136
|
+
doctype = None
|
137
|
+
if doctype_attr is not None:
|
138
|
+
try:
|
139
|
+
doctype = DocType(doctype_attr)
|
140
|
+
except ValueError:
|
141
|
+
odxraise(f"Encountered unknown document type '{doctype_attr}'")
|
142
|
+
|
119
143
|
# if the target document fragment is specified by the
|
120
144
|
# reference, use it, else use the document fragment containing
|
121
145
|
# the reference.
|
122
|
-
if
|
123
|
-
doc_frags = [OdxDocFragment(
|
146
|
+
if docref is not None:
|
147
|
+
doc_frags = [OdxDocFragment(docref, odxrequire(doctype))]
|
124
148
|
else:
|
125
149
|
doc_frags = source_doc_frags
|
126
150
|
|
odxtools/odxtypes.py
CHANGED
@@ -157,7 +157,7 @@ def compare_odx_values(a: AtomicOdxType, b: AtomicOdxType) -> int:
|
|
157
157
|
|
158
158
|
# format specifiers for the data type using the bitstruct module
|
159
159
|
_BITSTRUCT_FORMAT_LETTER_MAP__ = {
|
160
|
-
"A_INT32": "
|
160
|
+
"A_INT32": "u", # the sign must be handled separately
|
161
161
|
"A_UINT32": "u",
|
162
162
|
"A_FLOAT32": "f",
|
163
163
|
"A_FLOAT64": "f",
|
odxtools/outputparam.py
CHANGED
@@ -3,6 +3,8 @@ 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
|
+
|
6
8
|
from .dopbase import DopBase
|
7
9
|
from .element import IdentifiableElement
|
8
10
|
from .exceptions import odxrequire
|
@@ -29,12 +31,15 @@ class OutputParam(IdentifiableElement):
|
|
29
31
|
return {}
|
30
32
|
|
31
33
|
def _resolve_odxlinks(self, odxlinks: OdxLinkDatabase) -> None:
|
32
|
-
self.
|
34
|
+
self._dop = odxlinks.resolve(self.dop_base_ref, DopBase)
|
33
35
|
|
34
36
|
def _resolve_snrefs(self, context: SnRefContext) -> None:
|
35
37
|
pass
|
36
38
|
|
37
39
|
@property
|
40
|
+
def dop(self) -> DopBase:
|
41
|
+
return self._dop
|
42
|
+
|
43
|
+
@deprecated(details="use .dop") # type: ignore[misc]
|
38
44
|
def dop_base(self) -> DopBase:
|
39
|
-
|
40
|
-
return self._dop_base
|
45
|
+
return self._dop
|
@@ -54,6 +54,7 @@ class PhysicalConstantParameter(ParameterWithDOP):
|
|
54
54
|
dop = odxrequire(self.dop)
|
55
55
|
if not isinstance(dop, DataObjectProperty):
|
56
56
|
odxraise("The type of PHYS-CONST parameters must be a simple DOP")
|
57
|
+
return
|
57
58
|
base_data_type = dop.physical_type.base_data_type
|
58
59
|
self._physical_constant_value = base_data_type.from_string(self.physical_constant_value_raw)
|
59
60
|
|
@@ -1,13 +1,14 @@
|
|
1
1
|
# SPDX-License-Identifier: MIT
|
2
2
|
from dataclasses import dataclass
|
3
|
-
from
|
3
|
+
from enum import Enum
|
4
|
+
from typing import TYPE_CHECKING, List, Optional, cast
|
4
5
|
from xml.etree import ElementTree
|
5
6
|
|
6
7
|
from typing_extensions import override
|
7
8
|
|
8
9
|
from ..decodestate import DecodeState
|
9
10
|
from ..encodestate import EncodeState
|
10
|
-
from ..exceptions import odxrequire
|
11
|
+
from ..exceptions import odxraise, odxrequire
|
11
12
|
from ..odxlink import OdxDocFragment, OdxLinkDatabase, OdxLinkRef
|
12
13
|
from ..odxtypes import ParameterValue
|
13
14
|
from ..utils import dataclass_fields_asdict
|
@@ -17,9 +18,14 @@ if TYPE_CHECKING:
|
|
17
18
|
from ..tablerow import TableRow
|
18
19
|
|
19
20
|
|
21
|
+
class RowFragment(Enum):
|
22
|
+
KEY = "KEY"
|
23
|
+
STRUCT = "STRUCT"
|
24
|
+
|
25
|
+
|
20
26
|
@dataclass
|
21
27
|
class TableEntryParameter(Parameter):
|
22
|
-
target:
|
28
|
+
target: RowFragment
|
23
29
|
table_row_ref: OdxLinkRef
|
24
30
|
|
25
31
|
@staticmethod
|
@@ -29,7 +35,12 @@ class TableEntryParameter(Parameter):
|
|
29
35
|
|
30
36
|
kwargs = dataclass_fields_asdict(Parameter.from_et(et_element, doc_frags))
|
31
37
|
|
32
|
-
|
38
|
+
target_str = odxrequire(et_element.findtext("TARGET"))
|
39
|
+
try:
|
40
|
+
target = RowFragment(target_str)
|
41
|
+
except ValueError:
|
42
|
+
odxraise(f"Encountered unknown target '{target_str}'")
|
43
|
+
target = cast(RowFragment, None)
|
33
44
|
table_row_ref = odxrequire(OdxLinkRef.from_et(et_element.find("TABLE-ROW-REF"), doc_frags))
|
34
45
|
|
35
46
|
return TableEntryParameter(target=target, table_row_ref=table_row_ref, **kwargs)
|
@@ -23,10 +23,16 @@ if TYPE_CHECKING:
|
|
23
23
|
class TableKeyParameter(Parameter):
|
24
24
|
|
25
25
|
odx_id: OdxLinkId
|
26
|
+
|
27
|
+
# the spec mandates that exactly one of the two attributes must
|
28
|
+
# be non-None
|
26
29
|
table_ref: Optional[OdxLinkRef]
|
27
30
|
table_snref: Optional[str]
|
28
|
-
|
31
|
+
|
32
|
+
# the spec mandates that exactly one of the two attributes must
|
33
|
+
# be non-None
|
29
34
|
table_row_ref: Optional[OdxLinkRef]
|
35
|
+
table_row_snref: Optional[str]
|
30
36
|
|
31
37
|
@staticmethod
|
32
38
|
@override
|
@@ -58,9 +64,6 @@ class TableKeyParameter(Parameter):
|
|
58
64
|
def __post_init__(self) -> None:
|
59
65
|
self._table: Table
|
60
66
|
self._table_row: Optional[TableRow] = None
|
61
|
-
if self.table_ref is None and self.table_snref is None and \
|
62
|
-
self.table_row_ref is None and self.table_row_snref is None:
|
63
|
-
odxraise("Either a table or a table row must be defined.")
|
64
67
|
|
65
68
|
@property
|
66
69
|
@override
|
@@ -86,17 +89,20 @@ class TableKeyParameter(Parameter):
|
|
86
89
|
else:
|
87
90
|
self._table = odxlinks.resolve(self.table_ref)
|
88
91
|
|
89
|
-
|
92
|
+
elif self.table_row_ref is not None:
|
90
93
|
if TYPE_CHECKING:
|
91
94
|
self._table_row = odxlinks.resolve(self.table_row_ref, TableRow)
|
92
95
|
else:
|
93
96
|
self._table_row = odxlinks.resolve(self.table_row_ref)
|
94
97
|
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
98
|
+
# be aware that we cannot simply use
|
99
|
+
# `self._table_row.table` here because the table object
|
100
|
+
# might not have resolved its references yet because the
|
101
|
+
# order of reference resolution is undefined
|
102
|
+
if TYPE_CHECKING:
|
103
|
+
self._table = odxlinks.resolve(self._table_row.table_ref, Table)
|
104
|
+
else:
|
105
|
+
self._table = odxlinks.resolve(self._table_row.table_ref)
|
100
106
|
|
101
107
|
@override
|
102
108
|
def _resolve_snrefs(self, context: SnRefContext) -> None:
|
@@ -108,16 +114,13 @@ class TableKeyParameter(Parameter):
|
|
108
114
|
self._table = resolve_snref(self.table_snref, tables, Table)
|
109
115
|
else:
|
110
116
|
self._table = resolve_snref(self.table_snref, tables)
|
117
|
+
|
111
118
|
if self.table_row_snref is not None:
|
112
|
-
# make sure that we know the table to which the table row
|
113
|
-
# SNREF is relative to.
|
114
|
-
table = odxrequire(
|
115
|
-
self._table, "If a table row is referenced via short name, a table must "
|
116
|
-
"be referenced as well")
|
117
119
|
if TYPE_CHECKING:
|
118
|
-
self._table_row = resolve_snref(self.table_row_snref,
|
120
|
+
self._table_row = resolve_snref(self.table_row_snref, self._table.table_rows,
|
121
|
+
TableRow)
|
119
122
|
else:
|
120
|
-
self._table_row = resolve_snref(self.table_row_snref,
|
123
|
+
self._table_row = resolve_snref(self.table_row_snref, self._table.table_rows)
|
121
124
|
|
122
125
|
@property
|
123
126
|
def table(self) -> "Table":
|
odxtools/paramlengthinfotype.py
CHANGED
@@ -96,6 +96,7 @@ class ParamLengthInfoType(DiagCodedType):
|
|
96
96
|
used_mask=None,
|
97
97
|
bit_length=bit_length,
|
98
98
|
base_data_type=self.base_data_type,
|
99
|
+
base_type_encoding=self.base_type_encoding,
|
99
100
|
is_highlow_byte_order=self.is_highlow_byte_order,
|
100
101
|
)
|
101
102
|
|
@@ -114,7 +115,8 @@ class ParamLengthInfoType(DiagCodedType):
|
|
114
115
|
|
115
116
|
# Extract the internal value and return.
|
116
117
|
return decode_state.extract_atomic_value(
|
117
|
-
bit_length,
|
118
|
-
self.base_data_type,
|
119
|
-
self.
|
118
|
+
bit_length=bit_length,
|
119
|
+
base_data_type=self.base_data_type,
|
120
|
+
base_type_encoding=self.base_type_encoding,
|
121
|
+
is_highlow_byte_order=self.is_highlow_byte_order,
|
120
122
|
)
|
odxtools/physicaltype.py
CHANGED
@@ -74,4 +74,5 @@ class PhysicalType:
|
|
74
74
|
precision_str = et_element.findtext("PRECISION")
|
75
75
|
precision = int(precision_str) if precision_str is not None else None
|
76
76
|
|
77
|
-
return PhysicalType(
|
77
|
+
return PhysicalType(
|
78
|
+
base_data_type=base_data_type, display_radix=display_radix, precision=precision)
|
odxtools/scaleconstr.py
CHANGED
@@ -25,8 +25,8 @@ class ScaleConstr:
|
|
25
25
|
|
26
26
|
short_label: Optional[str]
|
27
27
|
description: Optional[Description]
|
28
|
-
lower_limit:
|
29
|
-
upper_limit:
|
28
|
+
lower_limit: Limit
|
29
|
+
upper_limit: Limit
|
30
30
|
validity: ValidType
|
31
31
|
value_type: DataType
|
32
32
|
|
@@ -37,9 +37,9 @@ class ScaleConstr:
|
|
37
37
|
description = Description.from_et(et_element.find("DESC"), doc_frags)
|
38
38
|
|
39
39
|
lower_limit = Limit.limit_from_et(
|
40
|
-
et_element.find("LOWER-LIMIT"), doc_frags, value_type=value_type)
|
40
|
+
odxrequire(et_element.find("LOWER-LIMIT")), doc_frags, value_type=value_type)
|
41
41
|
upper_limit = Limit.limit_from_et(
|
42
|
-
et_element.find("UPPER-LIMIT"), doc_frags, value_type=value_type)
|
42
|
+
odxrequire(et_element.find("UPPER-LIMIT")), doc_frags, value_type=value_type)
|
43
43
|
|
44
44
|
validity_str = odxrequire(et_element.get("VALIDITY"))
|
45
45
|
try:
|