odxtools 9.3.0__py3-none-any.whl → 9.4.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- odxtools/companyrevisioninfo.py +1 -1
- odxtools/comparaminstance.py +1 -6
- odxtools/comparamspec.py +3 -2
- odxtools/comparamsubset.py +9 -7
- 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/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/unit.py +8 -12
- odxtools/unitspec.py +5 -2
- odxtools/version.py +9 -4
- {odxtools-9.3.0.dist-info → odxtools-9.4.1.dist-info}/METADATA +1 -1
- {odxtools-9.3.0.dist-info → odxtools-9.4.1.dist-info}/RECORD +60 -59
- {odxtools-9.3.0.dist-info → odxtools-9.4.1.dist-info}/WHEEL +1 -1
- {odxtools-9.3.0.dist-info → odxtools-9.4.1.dist-info}/LICENSE +0 -0
- {odxtools-9.3.0.dist-info → odxtools-9.4.1.dist-info}/entry_points.txt +0 -0
- {odxtools-9.3.0.dist-info → odxtools-9.4.1.dist-info}/top_level.txt +0 -0
odxtools/companyrevisioninfo.py
CHANGED
@@ -25,7 +25,7 @@ class CompanyRevisionInfo:
|
|
25
25
|
|
26
26
|
company_data_ref = odxrequire(
|
27
27
|
OdxLinkRef.from_et(et_element.find("COMPANY-DATA-REF"), doc_frags))
|
28
|
-
revision_label = et_element.findtext("
|
28
|
+
revision_label = et_element.findtext("REVISION-LABEL")
|
29
29
|
state = et_element.findtext("STATE")
|
30
30
|
|
31
31
|
return CompanyRevisionInfo(
|
odxtools/comparaminstance.py
CHANGED
@@ -138,9 +138,4 @@ class ComparamInstance:
|
|
138
138
|
|
139
139
|
@property
|
140
140
|
def short_name(self) -> str:
|
141
|
-
|
142
|
-
return self.spec.short_name
|
143
|
-
|
144
|
-
# ODXLINK IDs allow dots and hyphens, but short names do not.
|
145
|
-
# (This should not happen anyway in a correct PDX...)
|
146
|
-
return self.spec_ref.ref_id.replace(".", "__").replace("-", "_")
|
141
|
+
return self.spec.short_name
|
odxtools/comparamspec.py
CHANGED
@@ -5,7 +5,7 @@ from xml.etree import ElementTree
|
|
5
5
|
|
6
6
|
from .nameditemlist import NamedItemList
|
7
7
|
from .odxcategory import OdxCategory
|
8
|
-
from .odxlink import OdxDocFragment, OdxLinkDatabase, OdxLinkId
|
8
|
+
from .odxlink import DocType, OdxDocFragment, OdxLinkDatabase, OdxLinkId
|
9
9
|
from .protstack import ProtStack
|
10
10
|
from .snrefcontext import SnRefContext
|
11
11
|
from .utils import dataclass_fields_asdict
|
@@ -16,12 +16,13 @@ if TYPE_CHECKING:
|
|
16
16
|
|
17
17
|
@dataclass
|
18
18
|
class ComparamSpec(OdxCategory):
|
19
|
+
|
19
20
|
prot_stacks: NamedItemList[ProtStack]
|
20
21
|
|
21
22
|
@staticmethod
|
22
23
|
def from_et(et_element: ElementTree.Element, doc_frags: List[OdxDocFragment]) -> "ComparamSpec":
|
23
24
|
|
24
|
-
cat = OdxCategory.category_from_et(et_element, doc_frags, doc_type=
|
25
|
+
cat = OdxCategory.category_from_et(et_element, doc_frags, doc_type=DocType.COMPARAM_SPEC)
|
25
26
|
doc_frags = cat.odx_id.doc_fragments
|
26
27
|
kwargs = dataclass_fields_asdict(cat)
|
27
28
|
|
odxtools/comparamsubset.py
CHANGED
@@ -8,7 +8,7 @@ from .complexcomparam import ComplexComparam
|
|
8
8
|
from .dataobjectproperty import DataObjectProperty
|
9
9
|
from .nameditemlist import NamedItemList
|
10
10
|
from .odxcategory import OdxCategory
|
11
|
-
from .odxlink import OdxDocFragment, OdxLinkDatabase, OdxLinkId
|
11
|
+
from .odxlink import DocType, OdxDocFragment, OdxLinkDatabase, OdxLinkId
|
12
12
|
from .snrefcontext import SnRefContext
|
13
13
|
from .unitspec import UnitSpec
|
14
14
|
from .utils import dataclass_fields_asdict
|
@@ -19,24 +19,26 @@ if TYPE_CHECKING:
|
|
19
19
|
|
20
20
|
@dataclass
|
21
21
|
class ComparamSubset(OdxCategory):
|
22
|
-
# mandatory in ODX 2.2, but non-existent in ODX 2.0
|
23
|
-
category: Optional[str]
|
24
|
-
|
25
22
|
comparams: NamedItemList[Comparam]
|
26
23
|
complex_comparams: NamedItemList[ComplexComparam]
|
27
24
|
data_object_props: NamedItemList[DataObjectProperty]
|
28
25
|
unit_spec: Optional[UnitSpec]
|
26
|
+
category: Optional[str] # mandatory in ODX 2.2, but non-existent in ODX 2.0
|
29
27
|
|
30
28
|
@staticmethod
|
31
29
|
def from_et(et_element: ElementTree.Element,
|
32
30
|
doc_frags: List[OdxDocFragment]) -> "ComparamSubset":
|
33
31
|
|
34
|
-
|
32
|
+
category = et_element.get("CATEGORY")
|
33
|
+
|
34
|
+
# In ODX 2.0, COMPARAM-SPEC is used, whereas in ODX 2.2, it refers to something else and has been replaced by COMPARAM-SUBSET.
|
35
|
+
# - If 'category' is missing (ODX 2.0), use COMPARAM_SPEC,
|
36
|
+
# - else (ODX 2.2), use COMPARAM_SUBSET.
|
37
|
+
doc_type = DocType.COMPARAM_SUBSET if category else DocType.COMPARAM_SPEC
|
38
|
+
cat = OdxCategory.category_from_et(et_element, doc_frags, doc_type=doc_type)
|
35
39
|
doc_frags = cat.odx_id.doc_fragments
|
36
40
|
kwargs = dataclass_fields_asdict(cat)
|
37
41
|
|
38
|
-
category = et_element.get("CATEGORY")
|
39
|
-
|
40
42
|
data_object_props = NamedItemList([
|
41
43
|
DataObjectProperty.from_et(el, doc_frags)
|
42
44
|
for el in et_element.iterfind("DATA-OBJECT-PROPS/DATA-OBJECT-PROP")
|
odxtools/dataobjectproperty.py
CHANGED
@@ -10,7 +10,7 @@ from .decodestate import DecodeState
|
|
10
10
|
from .diagcodedtype import DiagCodedType
|
11
11
|
from .dopbase import DopBase
|
12
12
|
from .encodestate import EncodeState
|
13
|
-
from .exceptions import
|
13
|
+
from .exceptions import EncodeError, odxraise, odxrequire
|
14
14
|
from .internalconstr import InternalConstr
|
15
15
|
from .odxlink import OdxDocFragment, OdxLinkDatabase, OdxLinkId, OdxLinkRef
|
16
16
|
from .odxtypes import AtomicOdxType, ParameterValue
|
@@ -136,11 +136,17 @@ class DataObjectProperty(DopBase):
|
|
136
136
|
|
137
137
|
if self.compu_method.is_valid_internal_value(internal):
|
138
138
|
return self.compu_method.convert_internal_to_physical(internal)
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
139
|
+
|
140
|
+
internal_to_phys = self.compu_method.compu_internal_to_phys
|
141
|
+
default_value = internal_to_phys.compu_default_value if internal_to_phys else None
|
142
|
+
|
143
|
+
if default_value and default_value.value is not None:
|
144
|
+
return default_value.value
|
145
|
+
|
146
|
+
odxraise(f"DOP {self.short_name} could not convert the coded value "
|
147
|
+
f"{repr(internal)} to physical type {self.physical_type.base_data_type}.")
|
148
|
+
|
149
|
+
return None
|
144
150
|
|
145
151
|
def is_valid_physical_value(self, physical_value: ParameterValue) -> bool:
|
146
152
|
return self.compu_method.is_valid_physical_value(cast(AtomicOdxType, physical_value))
|
odxtools/decodestate.py
CHANGED
@@ -1,10 +1,9 @@
|
|
1
1
|
# SPDX-License-Identifier: MIT
|
2
2
|
from dataclasses import dataclass, field
|
3
|
-
from typing import TYPE_CHECKING, Dict, List, Optional, Tuple
|
3
|
+
from typing import TYPE_CHECKING, Dict, List, Optional, Tuple
|
4
4
|
|
5
|
-
|
6
|
-
|
7
|
-
from .exceptions import DecodeError
|
5
|
+
from .encoding import Encoding, get_string_encoding
|
6
|
+
from .exceptions import DecodeError, odxassert, odxraise, strict_mode
|
8
7
|
from .odxtypes import AtomicOdxType, DataType, ParameterValue
|
9
8
|
|
10
9
|
try:
|
@@ -54,8 +53,10 @@ class DecodeState:
|
|
54
53
|
|
55
54
|
def extract_atomic_value(
|
56
55
|
self,
|
56
|
+
*,
|
57
57
|
bit_length: int,
|
58
58
|
base_data_type: DataType,
|
59
|
+
base_type_encoding: Optional[Encoding],
|
59
60
|
is_highlow_byte_order: bool,
|
60
61
|
) -> AtomicOdxType:
|
61
62
|
"""Extract an internal value from a blob of raw bytes.
|
@@ -68,6 +69,13 @@ class DecodeState:
|
|
68
69
|
if bit_length == 0:
|
69
70
|
return base_data_type.python_type()
|
70
71
|
|
72
|
+
if base_data_type == DataType.A_FLOAT32 and bit_length != 32:
|
73
|
+
odxraise("The bit length of FLOAT32 values must be 32 bits")
|
74
|
+
bit_length = 32
|
75
|
+
elif base_data_type == DataType.A_FLOAT64 and bit_length != 64:
|
76
|
+
odxraise("The bit length of FLOAT64 values must be 64 bits")
|
77
|
+
bit_length = 64
|
78
|
+
|
71
79
|
byte_length = (bit_length + self.cursor_bit_position + 7) // 8
|
72
80
|
if self.cursor_byte_position + byte_length > len(self.coded_message):
|
73
81
|
raise DecodeError(f"Expected a longer message.")
|
@@ -87,32 +95,118 @@ class DecodeState:
|
|
87
95
|
extracted_bytes = extracted_bytes[::-1]
|
88
96
|
|
89
97
|
padding = (8 - (bit_length + self.cursor_bit_position) % 8) % 8
|
90
|
-
|
98
|
+
raw_value, = bitstruct.unpack_from(
|
91
99
|
f"{base_data_type.bitstruct_format_letter}{bit_length}",
|
92
100
|
extracted_bytes,
|
93
101
|
offset=padding)
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
102
|
+
internal_value: AtomicOdxType
|
103
|
+
|
104
|
+
# Deal with raw byte fields, ...
|
105
|
+
if base_data_type == DataType.A_BYTEFIELD:
|
106
|
+
odxassert(
|
107
|
+
base_type_encoding in (None, Encoding.NONE, Encoding.BCD_P, Encoding.BCD_UP),
|
108
|
+
f"Illegal encoding '{base_type_encoding}' for A_BYTEFIELD")
|
109
|
+
|
110
|
+
# note that we do not ensure that BCD-encoded byte fields
|
111
|
+
# only represent "legal" values
|
112
|
+
internal_value = raw_value
|
113
|
+
|
114
|
+
# ... string types, ...
|
115
|
+
elif base_data_type in (DataType.A_UTF8STRING, DataType.A_ASCIISTRING,
|
116
|
+
DataType.A_UNICODE2STRING):
|
117
|
+
text_errors = 'strict' if strict_mode else 'replace'
|
118
|
+
str_encoding = get_string_encoding(base_data_type, base_type_encoding,
|
119
|
+
is_highlow_byte_order)
|
120
|
+
if str_encoding is not None:
|
121
|
+
internal_value = raw_value.decode(str_encoding, errors=text_errors)
|
122
|
+
else:
|
123
|
+
internal_value = "ERROR"
|
124
|
+
|
125
|
+
# ... signed integers, ...
|
126
|
+
elif base_data_type == DataType.A_INT32:
|
127
|
+
if base_type_encoding == Encoding.ONEC:
|
128
|
+
# one-complement
|
129
|
+
sign_bit = 1 << (bit_length - 1)
|
130
|
+
if raw_value < sign_bit:
|
131
|
+
internal_value = raw_value
|
132
|
+
else:
|
133
|
+
# python defines the bitwise inversion of a
|
134
|
+
# positive integer value x as ~x = -(x + 1).
|
135
|
+
internal_value = -((1 << bit_length) - raw_value - 1)
|
136
|
+
elif base_type_encoding in (None, Encoding.TWOC):
|
137
|
+
# two-complement
|
138
|
+
sign_bit = 1 << (bit_length - 1)
|
139
|
+
if raw_value < sign_bit:
|
140
|
+
internal_value = raw_value
|
141
|
+
else:
|
142
|
+
internal_value = -((1 << bit_length) - raw_value)
|
143
|
+
elif base_type_encoding == Encoding.SM:
|
144
|
+
# sign-magnitude
|
145
|
+
sign_bit = 1 << (bit_length - 1)
|
146
|
+
if raw_value < sign_bit:
|
147
|
+
internal_value = raw_value
|
148
|
+
else:
|
149
|
+
internal_value = -(raw_value - sign_bit)
|
150
|
+
else:
|
151
|
+
odxraise(f"Illegal encoding ({base_type_encoding}) specified for "
|
152
|
+
f"{base_data_type.value}")
|
153
|
+
|
154
|
+
if base_type_encoding == Encoding.BCD_P:
|
155
|
+
internal_value = self.__decode_bcd_p(raw_value)
|
156
|
+
elif base_type_encoding == Encoding.BCD_UP:
|
157
|
+
internal_value = self.__decode_bcd_up(raw_value)
|
158
|
+
else:
|
159
|
+
internal_value = raw_value
|
160
|
+
|
161
|
+
# ... unsigned integers, ...
|
162
|
+
elif base_data_type == DataType.A_UINT32:
|
163
|
+
if base_type_encoding == Encoding.BCD_P:
|
164
|
+
internal_value = self.__decode_bcd_p(raw_value)
|
165
|
+
elif base_type_encoding == Encoding.BCD_UP:
|
166
|
+
internal_value = self.__decode_bcd_up(raw_value)
|
167
|
+
elif base_type_encoding in (None, Encoding.NONE):
|
168
|
+
# no encoding
|
169
|
+
internal_value = raw_value
|
170
|
+
else:
|
171
|
+
odxraise(f"Illegal encoding ({base_type_encoding}) specified for "
|
172
|
+
f"{base_data_type.value}")
|
173
|
+
|
174
|
+
internal_value = raw_value
|
175
|
+
|
176
|
+
# ... and others (floating point values)
|
177
|
+
else:
|
178
|
+
odxassert(base_data_type in (DataType.A_FLOAT32, DataType.A_FLOAT64))
|
179
|
+
odxassert(
|
180
|
+
base_type_encoding in (None, Encoding.NONE),
|
181
|
+
f"Specified illegal encoding '{base_type_encoding}' for float object")
|
182
|
+
|
183
|
+
internal_value = float(raw_value)
|
114
184
|
|
115
185
|
self.cursor_byte_position += byte_length
|
116
186
|
self.cursor_bit_position = 0
|
117
187
|
|
118
|
-
return
|
188
|
+
return internal_value
|
189
|
+
|
190
|
+
@staticmethod
|
191
|
+
def __decode_bcd_p(value: int) -> int:
|
192
|
+
# packed BCD
|
193
|
+
result = 0
|
194
|
+
factor = 1
|
195
|
+
while value > 0:
|
196
|
+
result += (value & 0xf) * factor
|
197
|
+
factor *= 10
|
198
|
+
value >>= 4
|
199
|
+
|
200
|
+
return result
|
201
|
+
|
202
|
+
@staticmethod
|
203
|
+
def __decode_bcd_up(value: int) -> int:
|
204
|
+
# unpacked BCD
|
205
|
+
result = 0
|
206
|
+
factor = 1
|
207
|
+
while value > 0:
|
208
|
+
result += (value & 0xf) * factor
|
209
|
+
factor *= 10
|
210
|
+
value >>= 8
|
211
|
+
|
212
|
+
return result
|
odxtools/description.py
CHANGED
@@ -6,10 +6,27 @@ from .exceptions import odxrequire
|
|
6
6
|
from .odxlink import OdxDocFragment
|
7
7
|
|
8
8
|
|
9
|
+
@dataclass
|
10
|
+
class ExternalDoc:
|
11
|
+
description: Optional[str]
|
12
|
+
href: str
|
13
|
+
|
14
|
+
@staticmethod
|
15
|
+
def from_et(et_element: Optional[ElementTree.Element],
|
16
|
+
doc_frags: List[OdxDocFragment]) -> Optional["ExternalDoc"]:
|
17
|
+
if et_element is None:
|
18
|
+
return None
|
19
|
+
|
20
|
+
description = et_element.text
|
21
|
+
href = odxrequire(et_element.get("HREF"))
|
22
|
+
|
23
|
+
return ExternalDoc(description=description, href=href)
|
24
|
+
|
25
|
+
|
9
26
|
@dataclass
|
10
27
|
class Description:
|
11
28
|
text: str
|
12
|
-
external_docs: List[
|
29
|
+
external_docs: List[ExternalDoc]
|
13
30
|
text_identifier: Optional[str]
|
14
31
|
|
15
32
|
@staticmethod
|
@@ -35,7 +52,7 @@ class Description:
|
|
35
52
|
|
36
53
|
external_docs = \
|
37
54
|
[
|
38
|
-
odxrequire(
|
55
|
+
odxrequire(ExternalDoc.from_et(ed, doc_frags)) for ed in et_element.iterfind("EXTERNAL-DOCS/EXTERNAL-DOC")
|
39
56
|
]
|
40
57
|
return Description(text=text, text_identifier=text_identifier, external_docs=external_docs)
|
41
58
|
|
odxtools/diagcodedtype.py
CHANGED
@@ -5,6 +5,7 @@ from xml.etree import ElementTree
|
|
5
5
|
|
6
6
|
from .decodestate import DecodeState
|
7
7
|
from .encodestate import EncodeState
|
8
|
+
from .encoding import Encoding
|
8
9
|
from .exceptions import odxassert, odxraise, odxrequire
|
9
10
|
from .odxlink import OdxDocFragment, OdxLinkDatabase, OdxLinkId
|
10
11
|
from .odxtypes import AtomicOdxType, DataType, odxstr_to_bool
|
@@ -23,7 +24,7 @@ DctType = Literal[
|
|
23
24
|
class DiagCodedType:
|
24
25
|
|
25
26
|
base_data_type: DataType
|
26
|
-
base_type_encoding: Optional[
|
27
|
+
base_type_encoding: Optional[Encoding]
|
27
28
|
is_highlow_byte_order_raw: Optional[bool]
|
28
29
|
|
29
30
|
@staticmethod
|
@@ -36,7 +37,13 @@ class DiagCodedType:
|
|
36
37
|
odxraise(f"Unknown base data type {base_data_type_str}")
|
37
38
|
base_data_type = cast(DataType, None)
|
38
39
|
|
39
|
-
base_type_encoding =
|
40
|
+
base_type_encoding = None
|
41
|
+
if (base_type_encoding_str := et_element.get("BASE-TYPE-ENCODING")) is not None:
|
42
|
+
try:
|
43
|
+
base_type_encoding = Encoding(base_type_encoding_str)
|
44
|
+
except ValueError:
|
45
|
+
odxraise(f"Encountered unknown BASE-TYPE-ENCODING '{base_type_encoding_str}'")
|
46
|
+
|
40
47
|
is_highlow_byte_order_raw = odxstr_to_bool(et_element.get("IS-HIGHLOW-BYTE-ORDER"))
|
41
48
|
|
42
49
|
return DiagCodedType(
|
odxtools/diagcomm.py
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
# SPDX-License-Identifier: MIT
|
2
2
|
from dataclasses import dataclass
|
3
3
|
from enum import Enum
|
4
|
-
from typing import TYPE_CHECKING, Any, Dict,
|
4
|
+
from typing import TYPE_CHECKING, Any, Dict, List, Optional
|
5
5
|
from xml.etree import ElementTree
|
6
6
|
|
7
7
|
from .admindata import AdminData
|
@@ -61,8 +61,8 @@ class DiagComm(IdentifiableElement):
|
|
61
61
|
audience: Optional[Audience]
|
62
62
|
protocol_snrefs: List[str]
|
63
63
|
related_diag_comm_refs: List[RelatedDiagCommRef]
|
64
|
-
pre_condition_state_refs:
|
65
|
-
state_transition_refs:
|
64
|
+
pre_condition_state_refs: List[OdxLinkRef]
|
65
|
+
state_transition_refs: List[OdxLinkRef]
|
66
66
|
|
67
67
|
# attributes
|
68
68
|
semantic: Optional[str]
|
@@ -119,7 +119,7 @@ class DiagComm(IdentifiableElement):
|
|
119
119
|
odxraise(f"Encountered unknown diagnostic class type '{diagnostic_class_str}'")
|
120
120
|
|
121
121
|
is_mandatory_raw = odxstr_to_bool(et_element.get("IS-MANDATORY"))
|
122
|
-
is_executable_raw = odxstr_to_bool(et_element.get("IS-
|
122
|
+
is_executable_raw = odxstr_to_bool(et_element.get("IS-EXECUTABLE"))
|
123
123
|
is_final_raw = odxstr_to_bool(et_element.get("IS-FINAL"))
|
124
124
|
|
125
125
|
return DiagComm(
|
odxtools/diaglayercontainer.py
CHANGED
@@ -12,7 +12,7 @@ from .diaglayers.functionalgroup import FunctionalGroup
|
|
12
12
|
from .diaglayers.protocol import Protocol
|
13
13
|
from .nameditemlist import NamedItemList
|
14
14
|
from .odxcategory import OdxCategory
|
15
|
-
from .odxlink import OdxDocFragment, OdxLinkDatabase, OdxLinkId
|
15
|
+
from .odxlink import DocType, OdxDocFragment, OdxLinkDatabase, OdxLinkId
|
16
16
|
from .snrefcontext import SnRefContext
|
17
17
|
from .utils import dataclass_fields_asdict
|
18
18
|
|
@@ -48,7 +48,7 @@ class DiagLayerContainer(OdxCategory):
|
|
48
48
|
def from_et(et_element: ElementTree.Element,
|
49
49
|
doc_frags: List[OdxDocFragment]) -> "DiagLayerContainer":
|
50
50
|
|
51
|
-
cat = OdxCategory.category_from_et(et_element, doc_frags, doc_type=
|
51
|
+
cat = OdxCategory.category_from_et(et_element, doc_frags, doc_type=DocType.CONTAINER)
|
52
52
|
doc_frags = cat.odx_id.doc_fragments
|
53
53
|
kwargs = dataclass_fields_asdict(cat)
|
54
54
|
|
@@ -15,7 +15,7 @@ from ..exceptions import odxassert, odxraise, odxrequire
|
|
15
15
|
from ..functionalclass import FunctionalClass
|
16
16
|
from ..library import Library
|
17
17
|
from ..nameditemlist import NamedItemList
|
18
|
-
from ..odxlink import OdxDocFragment, OdxLinkDatabase, OdxLinkId, OdxLinkRef
|
18
|
+
from ..odxlink import DocType, OdxDocFragment, OdxLinkDatabase, OdxLinkId, OdxLinkRef
|
19
19
|
from ..request import Request
|
20
20
|
from ..response import Response
|
21
21
|
from ..singleecujob import SingleEcuJob
|
@@ -81,7 +81,7 @@ class DiagLayerRaw(IdentifiableElement):
|
|
81
81
|
|
82
82
|
# extend the applicable ODX "document fragments" for the diag layer objects
|
83
83
|
doc_frags = copy(doc_frags)
|
84
|
-
doc_frags.append(OdxDocFragment(short_name,
|
84
|
+
doc_frags.append(OdxDocFragment(short_name, DocType.LAYER))
|
85
85
|
kwargs = dataclass_fields_asdict(IdentifiableElement.from_et(et_element, doc_frags))
|
86
86
|
|
87
87
|
admin_data = None
|
@@ -137,6 +137,7 @@ class HierarchyElement(DiagLayer):
|
|
137
137
|
unit_groups=NamedItemList(unit_groups),
|
138
138
|
units=NamedItemList([]),
|
139
139
|
physical_dimensions=NamedItemList([]),
|
140
|
+
admin_data=None,
|
140
141
|
sdgs=[])
|
141
142
|
else:
|
142
143
|
# locally defined unit spec and inherited unit groups
|
@@ -144,6 +145,7 @@ class HierarchyElement(DiagLayer):
|
|
144
145
|
unit_groups=NamedItemList(unit_groups),
|
145
146
|
units=local_unit_spec.units,
|
146
147
|
physical_dimensions=local_unit_spec.physical_dimensions,
|
148
|
+
admin_data=None,
|
147
149
|
sdgs=[])
|
148
150
|
############
|
149
151
|
|
@@ -15,7 +15,7 @@ from .utils import dataclass_fields_asdict
|
|
15
15
|
@dataclass
|
16
16
|
class DiagnosticTroubleCode(IdentifiableElement):
|
17
17
|
trouble_code: int
|
18
|
-
text:
|
18
|
+
text: str
|
19
19
|
display_trouble_code: Optional[str]
|
20
20
|
level: Optional[int]
|
21
21
|
is_temporary_raw: Optional[bool]
|
@@ -29,15 +29,11 @@ class DiagnosticTroubleCode(IdentifiableElement):
|
|
29
29
|
def from_et(et_element: ElementTree.Element,
|
30
30
|
doc_frags: List[OdxDocFragment]) -> "DiagnosticTroubleCode":
|
31
31
|
kwargs = dataclass_fields_asdict(IdentifiableElement.from_et(et_element, doc_frags))
|
32
|
-
|
33
|
-
display_trouble_code = et_element.findtext("DISPLAY-TROUBLE-CODE")
|
34
|
-
else:
|
35
|
-
display_trouble_code = None
|
32
|
+
display_trouble_code = et_element.findtext("DISPLAY-TROUBLE-CODE")
|
36
33
|
|
34
|
+
level = None
|
37
35
|
if (level_str := et_element.findtext("LEVEL")) is not None:
|
38
36
|
level = int(level_str)
|
39
|
-
else:
|
40
|
-
level = None
|
41
37
|
|
42
38
|
is_temporary_raw = odxstr_to_bool(et_element.get("IS-TEMPORARY"))
|
43
39
|
sdgs = [
|
@@ -46,7 +42,7 @@ class DiagnosticTroubleCode(IdentifiableElement):
|
|
46
42
|
|
47
43
|
return DiagnosticTroubleCode(
|
48
44
|
trouble_code=int(odxrequire(et_element.findtext("TROUBLE-CODE"))),
|
49
|
-
text=et_element.findtext("TEXT"),
|
45
|
+
text=odxrequire(et_element.findtext("TEXT")),
|
50
46
|
display_trouble_code=display_trouble_code,
|
51
47
|
level=level,
|
52
48
|
is_temporary_raw=is_temporary_raw,
|
odxtools/diagservice.py
CHANGED
@@ -31,6 +31,71 @@ class TransMode(Enum):
|
|
31
31
|
SEND_OR_RECEIVE = "SEND-OR-RECEIVE"
|
32
32
|
|
33
33
|
|
34
|
+
# note that the spec has a typo here: it calls the corresponding
|
35
|
+
# XML tag POS-RESPONSE-SUPPRESSABLE...
|
36
|
+
@dataclass
|
37
|
+
class PosResponseSuppressible:
|
38
|
+
bit_mask: int
|
39
|
+
|
40
|
+
coded_const_snref: Optional[str]
|
41
|
+
coded_const_snpathref: Optional[str]
|
42
|
+
|
43
|
+
value_snref: Optional[str]
|
44
|
+
value_snpathref: Optional[str]
|
45
|
+
|
46
|
+
phys_const_snref: Optional[str]
|
47
|
+
phys_const_snpathref: Optional[str]
|
48
|
+
|
49
|
+
table_key_snref: Optional[str]
|
50
|
+
table_key_snpathref: Optional[str]
|
51
|
+
|
52
|
+
@staticmethod
|
53
|
+
def from_et(et_element: ElementTree.Element,
|
54
|
+
doc_frags: List[OdxDocFragment]) -> "PosResponseSuppressible":
|
55
|
+
|
56
|
+
bit_mask = int(odxrequire(et_element.findtext("BITMASK")))
|
57
|
+
|
58
|
+
coded_const_snref = None
|
59
|
+
if (cc_snref_elem := et_element.find("CODED-CONST-SNREF")) is not None:
|
60
|
+
coded_const_snref = cc_snref_elem.attrib["SHORT-NAME"]
|
61
|
+
coded_const_snpathref = None
|
62
|
+
if (cc_snpathref_elem := et_element.find("CODED-CONST-SNPATHREF")) is not None:
|
63
|
+
coded_const_snpathref = cc_snpathref_elem.attrib["SHORT-NAME-PATH"]
|
64
|
+
|
65
|
+
value_snref = None
|
66
|
+
if (cc_snref_elem := et_element.find("VALUE-SNREF")) is not None:
|
67
|
+
value_snref = cc_snref_elem.attrib["SHORT-NAME"]
|
68
|
+
value_snpathref = None
|
69
|
+
if (cc_snpathref_elem := et_element.find("VALUE-SNPATHREF")) is not None:
|
70
|
+
value_snpathref = cc_snpathref_elem.attrib["SHORT-NAME-PATH"]
|
71
|
+
|
72
|
+
phys_const_snref = None
|
73
|
+
if (cc_snref_elem := et_element.find("PHYS-CONST-SNREF")) is not None:
|
74
|
+
phys_const_snref = cc_snref_elem.attrib["SHORT-NAME"]
|
75
|
+
phys_const_snpathref = None
|
76
|
+
if (cc_snpathref_elem := et_element.find("PHYS-CONST-SNPATHREF")) is not None:
|
77
|
+
phys_const_snpathref = cc_snpathref_elem.attrib["SHORT-NAME-PATH"]
|
78
|
+
|
79
|
+
table_key_snref = None
|
80
|
+
if (cc_snref_elem := et_element.find("TABLE-KEY-SNREF")) is not None:
|
81
|
+
table_key_snref = cc_snref_elem.attrib["SHORT-NAME"]
|
82
|
+
table_key_snpathref = None
|
83
|
+
if (cc_snpathref_elem := et_element.find("TABLE-KEY-SNPATHREF")) is not None:
|
84
|
+
table_key_snpathref = cc_snpathref_elem.attrib["SHORT-NAME-PATH"]
|
85
|
+
|
86
|
+
return PosResponseSuppressible(
|
87
|
+
bit_mask=bit_mask,
|
88
|
+
coded_const_snref=coded_const_snref,
|
89
|
+
coded_const_snpathref=coded_const_snpathref,
|
90
|
+
value_snref=value_snref,
|
91
|
+
value_snpathref=value_snpathref,
|
92
|
+
phys_const_snref=phys_const_snref,
|
93
|
+
phys_const_snpathref=phys_const_snpathref,
|
94
|
+
table_key_snref=table_key_snref,
|
95
|
+
table_key_snpathref=table_key_snpathref,
|
96
|
+
)
|
97
|
+
|
98
|
+
|
34
99
|
@dataclass
|
35
100
|
class DiagService(DiagComm):
|
36
101
|
"""Representation of a diagnostic service description.
|
@@ -42,9 +107,7 @@ class DiagService(DiagComm):
|
|
42
107
|
pos_response_refs: List[OdxLinkRef]
|
43
108
|
neg_response_refs: List[OdxLinkRef]
|
44
109
|
|
45
|
-
|
46
|
-
# XML tag POS-RESPONSE-SUPPRESSABLE...
|
47
|
-
# TODO: pos_response_suppressible: Optional[PosResponseSuppressible]
|
110
|
+
pos_response_suppressible: Optional[PosResponseSuppressible]
|
48
111
|
|
49
112
|
is_cyclic_raw: Optional[bool]
|
50
113
|
is_multiple_raw: Optional[bool]
|
@@ -73,7 +136,9 @@ class DiagService(DiagComm):
|
|
73
136
|
for el in et_element.iterfind("NEG-RESPONSE-REFS/NEG-RESPONSE-REF")
|
74
137
|
]
|
75
138
|
|
76
|
-
|
139
|
+
pos_response_suppressible = None
|
140
|
+
if (prs_elem := et_element.find("POS-RESPONSE-SUPPRESSABLE")) is not None:
|
141
|
+
pos_response_suppressible = PosResponseSuppressible.from_et(prs_elem, doc_frags)
|
77
142
|
|
78
143
|
is_cyclic_raw = odxstr_to_bool(et_element.get("IS-CYCLIC"))
|
79
144
|
is_multiple_raw = odxstr_to_bool(et_element.get("IS-MULTIPLE"))
|
@@ -99,6 +164,7 @@ class DiagService(DiagComm):
|
|
99
164
|
request_ref=request_ref,
|
100
165
|
pos_response_refs=pos_response_refs,
|
101
166
|
neg_response_refs=neg_response_refs,
|
167
|
+
pos_response_suppressible=pos_response_suppressible,
|
102
168
|
is_cyclic_raw=is_cyclic_raw,
|
103
169
|
is_multiple_raw=is_multiple_raw,
|
104
170
|
addressing_raw=addressing_raw,
|