odxtools 7.2.0__py3-none-any.whl → 7.4.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- odxtools/basicstructure.py +10 -7
- odxtools/cli/_print_utils.py +3 -3
- odxtools/cli/browse.py +4 -2
- odxtools/cli/list.py +3 -3
- odxtools/commrelation.py +122 -0
- odxtools/comparaminstance.py +1 -1
- odxtools/comparamspec.py +1 -2
- odxtools/compumethods/linearsegment.py +0 -2
- odxtools/database.py +17 -11
- odxtools/decodestate.py +8 -2
- odxtools/diaglayer.py +23 -17
- odxtools/diaglayerraw.py +116 -23
- odxtools/diagnostictroublecode.py +2 -2
- odxtools/diagservice.py +33 -20
- odxtools/diagvariable.py +104 -0
- odxtools/dtcdop.py +39 -14
- odxtools/dyndefinedspec.py +179 -0
- odxtools/encodestate.py +14 -2
- odxtools/environmentdatadescription.py +137 -16
- odxtools/exceptions.py +10 -1
- odxtools/multiplexer.py +92 -56
- odxtools/multiplexercase.py +6 -5
- odxtools/multiplexerdefaultcase.py +7 -6
- odxtools/odxlink.py +19 -47
- odxtools/odxtypes.py +1 -1
- odxtools/parameterinfo.py +2 -2
- odxtools/parameters/nrcconstparameter.py +28 -37
- odxtools/parameters/systemparameter.py +1 -1
- odxtools/parameters/tablekeyparameter.py +11 -4
- odxtools/servicebinner.py +1 -1
- odxtools/specialdatagroup.py +1 -1
- odxtools/swvariable.py +21 -0
- odxtools/templates/macros/printComparam.xml.jinja2 +4 -2
- odxtools/templates/macros/printCompuMethod.xml.jinja2 +1 -8
- odxtools/templates/macros/printDiagVariable.xml.jinja2 +66 -0
- odxtools/templates/macros/printDynDefinedSpec.xml.jinja2 +48 -0
- odxtools/templates/macros/printEcuVariantPattern.xml.jinja2 +1 -1
- odxtools/templates/macros/printParam.xml.jinja2 +7 -8
- odxtools/templates/macros/printService.xml.jinja2 +3 -2
- odxtools/templates/macros/printSingleEcuJob.xml.jinja2 +2 -2
- odxtools/templates/macros/printVariant.xml.jinja2 +30 -13
- odxtools/variablegroup.py +22 -0
- odxtools/version.py +2 -2
- {odxtools-7.2.0.dist-info → odxtools-7.4.0.dist-info}/METADATA +18 -18
- {odxtools-7.2.0.dist-info → odxtools-7.4.0.dist-info}/RECORD +49 -42
- {odxtools-7.2.0.dist-info → odxtools-7.4.0.dist-info}/WHEEL +1 -1
- {odxtools-7.2.0.dist-info → odxtools-7.4.0.dist-info}/LICENSE +0 -0
- {odxtools-7.2.0.dist-info → odxtools-7.4.0.dist-info}/entry_points.txt +0 -0
- {odxtools-7.2.0.dist-info → odxtools-7.4.0.dist-info}/top_level.txt +0 -0
odxtools/multiplexercase.py
CHANGED
@@ -3,13 +3,13 @@ from dataclasses import dataclass
|
|
3
3
|
from typing import Any, Dict, List, Optional
|
4
4
|
from xml.etree import ElementTree
|
5
5
|
|
6
|
-
from .basicstructure import BasicStructure
|
7
6
|
from .compumethods.limit import Limit
|
8
7
|
from .element import NamedElement
|
9
8
|
from .exceptions import odxrequire
|
10
9
|
from .odxlink import OdxDocFragment, OdxLinkDatabase, OdxLinkId, OdxLinkRef, resolve_snref
|
11
10
|
from .odxtypes import AtomicOdxType, DataType
|
12
11
|
from .snrefcontext import SnRefContext
|
12
|
+
from .structure import Structure
|
13
13
|
from .utils import dataclass_fields_asdict
|
14
14
|
|
15
15
|
|
@@ -23,7 +23,7 @@ class MultiplexerCase(NamedElement):
|
|
23
23
|
upper_limit: Limit
|
24
24
|
|
25
25
|
def __post_init__(self) -> None:
|
26
|
-
self._structure:
|
26
|
+
self._structure: Optional[Structure]
|
27
27
|
|
28
28
|
@staticmethod
|
29
29
|
def from_et(et_element: ElementTree.Element,
|
@@ -62,8 +62,9 @@ class MultiplexerCase(NamedElement):
|
|
62
62
|
|
63
63
|
def _mux_case_resolve_odxlinks(self, odxlinks: OdxLinkDatabase, *,
|
64
64
|
key_physical_type: DataType) -> None:
|
65
|
+
self._structure = None
|
65
66
|
if self.structure_ref:
|
66
|
-
self._structure = odxlinks.resolve(self.structure_ref)
|
67
|
+
self._structure = odxlinks.resolve(self.structure_ref, Structure)
|
67
68
|
|
68
69
|
self.lower_limit.set_value_type(key_physical_type)
|
69
70
|
self.upper_limit.set_value_type(key_physical_type)
|
@@ -71,12 +72,12 @@ class MultiplexerCase(NamedElement):
|
|
71
72
|
def _resolve_snrefs(self, context: SnRefContext) -> None:
|
72
73
|
if self.structure_snref:
|
73
74
|
ddds = odxrequire(context.diag_layer).diag_data_dictionary_spec
|
74
|
-
self._structure = resolve_snref(self.structure_snref, ddds.structures,
|
75
|
+
self._structure = resolve_snref(self.structure_snref, ddds.structures, Structure)
|
75
76
|
|
76
77
|
def applies(self, value: AtomicOdxType) -> bool:
|
77
78
|
return self.lower_limit.complies_to_lower(value) \
|
78
79
|
and self.upper_limit.complies_to_upper(value)
|
79
80
|
|
80
81
|
@property
|
81
|
-
def structure(self) ->
|
82
|
+
def structure(self) -> Optional[Structure]:
|
82
83
|
return self._structure
|
@@ -3,11 +3,11 @@ from dataclasses import dataclass
|
|
3
3
|
from typing import Any, Dict, List, Optional
|
4
4
|
from xml.etree import ElementTree
|
5
5
|
|
6
|
-
from .basicstructure import BasicStructure
|
7
6
|
from .element import NamedElement
|
8
7
|
from .exceptions import odxrequire
|
9
8
|
from .odxlink import OdxDocFragment, OdxLinkDatabase, OdxLinkId, OdxLinkRef, resolve_snref
|
10
9
|
from .snrefcontext import SnRefContext
|
10
|
+
from .structure import Structure
|
11
11
|
from .utils import dataclass_fields_asdict
|
12
12
|
|
13
13
|
|
@@ -18,12 +18,12 @@ class MultiplexerDefaultCase(NamedElement):
|
|
18
18
|
structure_snref: Optional[str]
|
19
19
|
|
20
20
|
def __post_init__(self) -> None:
|
21
|
-
self._structure:
|
21
|
+
self._structure: Optional[Structure]
|
22
22
|
|
23
23
|
@staticmethod
|
24
24
|
def from_et(et_element: ElementTree.Element,
|
25
25
|
doc_frags: List[OdxDocFragment]) -> "MultiplexerDefaultCase":
|
26
|
-
"""Reads a
|
26
|
+
"""Reads a default case for a multiplexer."""
|
27
27
|
kwargs = dataclass_fields_asdict(NamedElement.from_et(et_element, doc_frags))
|
28
28
|
|
29
29
|
structure_ref = OdxLinkRef.from_et(et_element.find("STRUCTURE-REF"), doc_frags)
|
@@ -38,14 +38,15 @@ class MultiplexerDefaultCase(NamedElement):
|
|
38
38
|
return {}
|
39
39
|
|
40
40
|
def _resolve_odxlinks(self, odxlinks: OdxLinkDatabase) -> None:
|
41
|
+
self._structure = None
|
41
42
|
if self.structure_ref is not None:
|
42
|
-
self._structure = odxlinks.resolve(self.structure_ref)
|
43
|
+
self._structure = odxlinks.resolve(self.structure_ref, Structure)
|
43
44
|
|
44
45
|
def _resolve_snrefs(self, context: SnRefContext) -> None:
|
45
46
|
if self.structure_snref:
|
46
47
|
ddds = odxrequire(context.diag_layer).diag_data_dictionary_spec
|
47
|
-
self._structure = resolve_snref(self.structure_snref, ddds.structures,
|
48
|
+
self._structure = resolve_snref(self.structure_snref, ddds.structures, Structure)
|
48
49
|
|
49
50
|
@property
|
50
|
-
def structure(self) ->
|
51
|
+
def structure(self) -> Optional[Structure]:
|
51
52
|
return self._structure
|
odxtools/odxlink.py
CHANGED
@@ -4,30 +4,14 @@ from dataclasses import dataclass
|
|
4
4
|
from typing import Any, Dict, Iterable, List, Optional, Type, TypeVar, overload
|
5
5
|
from xml.etree import ElementTree
|
6
6
|
|
7
|
-
from .exceptions import OdxWarning, odxassert, odxraise
|
7
|
+
from .exceptions import OdxWarning, odxassert, odxraise, odxrequire
|
8
8
|
from .nameditemlist import OdxNamed, TNamed
|
9
9
|
|
10
10
|
|
11
11
|
@dataclass(frozen=True)
|
12
12
|
class OdxDocFragment:
|
13
13
|
doc_name: str
|
14
|
-
doc_type:
|
15
|
-
|
16
|
-
def __eq__(self, other: Any) -> bool:
|
17
|
-
if other is None:
|
18
|
-
# if the other document fragment is not specified, we
|
19
|
-
# treat it as a wildcard...
|
20
|
-
return True
|
21
|
-
|
22
|
-
if not isinstance(other, OdxDocFragment):
|
23
|
-
return False
|
24
|
-
|
25
|
-
# the ODX spec says that the doctype can be ignored...
|
26
|
-
return self.doc_name == other.doc_name
|
27
|
-
|
28
|
-
def __hash__(self) -> int:
|
29
|
-
# only the document name is relevant for the hash value
|
30
|
-
return hash(self.doc_name) + hash(self.doc_type)
|
14
|
+
doc_type: str
|
31
15
|
|
32
16
|
|
33
17
|
@dataclass(frozen=True)
|
@@ -59,10 +43,12 @@ class OdxLinkId:
|
|
59
43
|
if not isinstance(other, OdxLinkId):
|
60
44
|
return False
|
61
45
|
|
62
|
-
#
|
63
|
-
|
64
|
-
|
65
|
-
|
46
|
+
# if the local ID is different, the whole id is different
|
47
|
+
if self.local_id != other.local_id:
|
48
|
+
return False
|
49
|
+
|
50
|
+
# the document fragments must be identical for the IDs to be identical
|
51
|
+
return self.doc_fragments == other.doc_fragments
|
66
52
|
|
67
53
|
def __str__(self) -> str:
|
68
54
|
return f"OdxLinkId('{self.local_id}')"
|
@@ -120,6 +106,7 @@ class OdxLinkRef:
|
|
120
106
|
|
121
107
|
id_ref = et.attrib.get("ID-REF")
|
122
108
|
if id_ref is None:
|
109
|
+
odxraise(f"Tag {et.tag} is not a ODXLINK reference")
|
123
110
|
return None
|
124
111
|
|
125
112
|
doc_ref = et.attrib.get("DOCREF")
|
@@ -133,7 +120,7 @@ class OdxLinkRef:
|
|
133
120
|
# reference, use it, else use the document fragment containing
|
134
121
|
# the reference.
|
135
122
|
if doc_ref is not None:
|
136
|
-
doc_frags = [OdxDocFragment(doc_ref, doc_type)]
|
123
|
+
doc_frags = [OdxDocFragment(doc_ref, odxrequire(doc_type))]
|
137
124
|
else:
|
138
125
|
doc_frags = source_doc_frags
|
139
126
|
|
@@ -147,32 +134,19 @@ class OdxLinkRef:
|
|
147
134
|
def __str__(self) -> str:
|
148
135
|
return f"OdxLinkRef('{self.ref_id}')"
|
149
136
|
|
150
|
-
def __contains__(self, odx_id: OdxLinkId) -> bool:
|
151
|
-
"""
|
152
|
-
Returns true iff a given OdxLinkId object is referenced.
|
153
|
-
"""
|
154
|
-
|
155
|
-
# we must reference at to at least of the ID's document
|
156
|
-
# fragments
|
157
|
-
if not any(ref_doc in odx_id.doc_fragments for ref_doc in self.ref_docs):
|
158
|
-
return False
|
159
|
-
|
160
|
-
# the local ID of the reference and the object ID must match
|
161
|
-
return odx_id.local_id == self.ref_id
|
162
|
-
|
163
137
|
|
164
138
|
T = TypeVar("T")
|
165
139
|
|
166
140
|
|
167
141
|
class OdxLinkDatabase:
|
168
142
|
"""
|
169
|
-
A database holding all objects which
|
143
|
+
A database holding all objects which exhibit OdxLinkIds
|
170
144
|
|
171
|
-
This can resolve references
|
145
|
+
This can resolve ODXLINK references.
|
172
146
|
"""
|
173
147
|
|
174
148
|
def __init__(self) -> None:
|
175
|
-
self._db: Dict[OdxDocFragment, Dict[
|
149
|
+
self._db: Dict[OdxDocFragment, Dict[str, Any]] = {}
|
176
150
|
|
177
151
|
@overload
|
178
152
|
def resolve(self, ref: OdxLinkRef, expected_type: None = None) -> Any:
|
@@ -189,13 +163,12 @@ class OdxLinkDatabase:
|
|
189
163
|
If the database does not contain any object which is referred to, a
|
190
164
|
KeyError exception is raised.
|
191
165
|
"""
|
192
|
-
odx_id = OdxLinkId(ref.ref_id, ref.ref_docs)
|
193
166
|
for ref_frag in reversed(ref.ref_docs):
|
194
167
|
doc_frag_db = self._db.get(ref_frag)
|
195
168
|
if doc_frag_db is None:
|
196
169
|
# No object featured by the database uses the document
|
197
170
|
# fragment mentioned by the reference. This should not
|
198
|
-
# happen for correct databases...
|
171
|
+
# happen for correct ODX databases...
|
199
172
|
warnings.warn(
|
200
173
|
f"Warning: Unknown document fragment {ref_frag} "
|
201
174
|
f"when resolving reference {ref}",
|
@@ -204,8 +177,9 @@ class OdxLinkDatabase:
|
|
204
177
|
)
|
205
178
|
continue
|
206
179
|
|
207
|
-
|
208
|
-
|
180
|
+
# locate an object exhibiting with the referenced local ID
|
181
|
+
# in the ID database for the document fragment
|
182
|
+
if (obj := doc_frag_db.get(ref.ref_id)) is not None:
|
209
183
|
if expected_type is not None:
|
210
184
|
odxassert(isinstance(obj, expected_type))
|
211
185
|
|
@@ -234,7 +208,6 @@ class OdxLinkDatabase:
|
|
234
208
|
is returned.
|
235
209
|
"""
|
236
210
|
|
237
|
-
odx_id = OdxLinkId(ref.ref_id, ref.ref_docs)
|
238
211
|
for ref_frag in reversed(ref.ref_docs):
|
239
212
|
doc_frag_db = self._db.get(ref_frag)
|
240
213
|
if doc_frag_db is None:
|
@@ -249,8 +222,7 @@ class OdxLinkDatabase:
|
|
249
222
|
)
|
250
223
|
continue
|
251
224
|
|
252
|
-
obj
|
253
|
-
if obj is not None:
|
225
|
+
if (obj := doc_frag_db.get(ref.ref_id)) is not None:
|
254
226
|
if expected_type is not None:
|
255
227
|
odxassert(isinstance(obj, expected_type))
|
256
228
|
|
@@ -272,7 +244,7 @@ class OdxLinkDatabase:
|
|
272
244
|
if doc_frag not in self._db:
|
273
245
|
self._db[doc_frag] = {}
|
274
246
|
|
275
|
-
self._db[doc_frag][odx_id] = obj
|
247
|
+
self._db[doc_frag][odx_id.local_id] = obj
|
276
248
|
|
277
249
|
|
278
250
|
@overload
|
odxtools/odxtypes.py
CHANGED
@@ -235,7 +235,7 @@ class DataType(Enum):
|
|
235
235
|
expected_type = self.python_type
|
236
236
|
if isinstance(value, expected_type):
|
237
237
|
return True
|
238
|
-
elif expected_type
|
238
|
+
elif expected_type is float and isinstance(value, (int, float)):
|
239
239
|
return True
|
240
240
|
elif self == DataType.A_BYTEFIELD and isinstance(value, (bytearray, bytes)):
|
241
241
|
return True
|
odxtools/parameterinfo.py
CHANGED
@@ -108,8 +108,8 @@ def parameter_info(param_list: Iterable[Parameter], quoted_names: bool = False)
|
|
108
108
|
of.write(f"multiplexer; choices:\n")
|
109
109
|
for mux_case in dop.cases:
|
110
110
|
of.write(f" ({repr(mux_case.short_name)}, {{\n")
|
111
|
-
|
112
|
-
textwrap.indent(parameter_info(
|
111
|
+
if (struc := mux_case.structure) is not None:
|
112
|
+
of.write(textwrap.indent(parameter_info(struc.parameters, True), " "))
|
113
113
|
of.write(f" }})\n")
|
114
114
|
continue
|
115
115
|
|
@@ -1,7 +1,6 @@
|
|
1
1
|
# SPDX-License-Identifier: MIT
|
2
|
-
import warnings
|
3
2
|
from dataclasses import dataclass
|
4
|
-
from typing import Any, Dict, List, Optional
|
3
|
+
from typing import Any, Dict, List, Optional
|
5
4
|
from xml.etree import ElementTree
|
6
5
|
|
7
6
|
from typing_extensions import override
|
@@ -10,7 +9,7 @@ from ..createanydiagcodedtype import create_any_diag_coded_type_from_et
|
|
10
9
|
from ..decodestate import DecodeState
|
11
10
|
from ..diagcodedtype import DiagCodedType
|
12
11
|
from ..encodestate import EncodeState
|
13
|
-
from ..exceptions import
|
12
|
+
from ..exceptions import DecodeMismatch, EncodeError, odxraise, odxrequire
|
14
13
|
from ..odxlink import OdxDocFragment, OdxLinkId
|
15
14
|
from ..odxtypes import AtomicOdxType, DataType, ParameterValue
|
16
15
|
from ..utils import dataclass_fields_asdict
|
@@ -19,7 +18,8 @@ from .parameter import Parameter, ParameterType
|
|
19
18
|
|
20
19
|
@dataclass
|
21
20
|
class NrcConstParameter(Parameter):
|
22
|
-
"""A
|
21
|
+
"""A parameter of type NRC-CONST defines a set of values to be
|
22
|
+
matched for a negative response object to apply
|
23
23
|
|
24
24
|
The behaviour of NRC-CONST parameters is similar to CODED-CONST
|
25
25
|
parameters in that they allow to specify which coding objects
|
@@ -88,49 +88,40 @@ class NrcConstParameter(Parameter):
|
|
88
88
|
@override
|
89
89
|
def _encode_positioned_into_pdu(self, physical_value: Optional[ParameterValue],
|
90
90
|
encode_state: EncodeState) -> None:
|
91
|
-
|
91
|
+
# NRC-CONST parameters are not encoding any value on its
|
92
|
+
# own. instead, it is supposed to overlap with a value
|
93
|
+
# parameter.
|
92
94
|
if physical_value is not None:
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
odxraise("The diag coded type of NRC-CONST parameters must "
|
109
|
-
"exhibit a static size")
|
110
|
-
return
|
111
|
-
|
112
|
-
encode_state.cursor_byte_position += (bit_pos + bit_len + 7) // 8
|
113
|
-
encode_state.cursor_bit_position = 0
|
95
|
+
odxraise("The value of NRC-CONST parameters cannot be set directly!", EncodeError)
|
96
|
+
|
97
|
+
# TODO (?): extract the parameter and check if it is one of
|
98
|
+
# the values of self.coded_values. if not, throw an
|
99
|
+
# EncodeMismatch exception! This is probably a bad idea
|
100
|
+
# because the parameter which determines the value of the
|
101
|
+
# NRC-CONST might possibly be specified after the NRC-CONST.
|
102
|
+
|
103
|
+
# move the cursor forward by the size of the parameter
|
104
|
+
bit_pos = encode_state.cursor_bit_position
|
105
|
+
bit_len = self.diag_coded_type.get_static_bit_length()
|
106
|
+
|
107
|
+
if bit_len is None:
|
108
|
+
odxraise("The diag coded type of NRC-CONST parameters must "
|
109
|
+
"exhibit a static size")
|
114
110
|
return
|
115
111
|
|
116
|
-
|
112
|
+
encode_state.cursor_byte_position += (bit_pos + bit_len + 7) // 8
|
113
|
+
encode_state.cursor_bit_position = 0
|
114
|
+
|
115
|
+
encode_state.emplace_bytes(b'', self.short_name)
|
117
116
|
|
118
117
|
@override
|
119
118
|
def _decode_positioned_from_pdu(self, decode_state: DecodeState) -> AtomicOdxType:
|
120
|
-
# Extract coded
|
119
|
+
# Extract coded value
|
121
120
|
coded_value = self.diag_coded_type.decode_from_pdu(decode_state)
|
122
121
|
|
123
122
|
# Check if the coded value in the message is correct.
|
124
123
|
if coded_value not in self.coded_values:
|
125
|
-
|
126
|
-
f"Coded constant parameter does not match! "
|
127
|
-
f"The parameter {self.short_name} expected a coded "
|
128
|
-
f"value in {str(self.coded_values)} but got {str(coded_value)} "
|
129
|
-
f"at byte position {decode_state.cursor_byte_position} "
|
130
|
-
f"in coded message {decode_state.coded_message.hex()}.",
|
131
|
-
DecodeError,
|
132
|
-
stacklevel=1,
|
133
|
-
)
|
124
|
+
raise DecodeMismatch(f"NRC-CONST parameter {self.short_name} does not apply")
|
134
125
|
|
135
126
|
return coded_value
|
136
127
|
|
@@ -26,7 +26,7 @@ class SystemParameter(ParameterWithDOP):
|
|
26
26
|
|
27
27
|
kwargs = dataclass_fields_asdict(ParameterWithDOP.from_et(et_element, doc_frags))
|
28
28
|
|
29
|
-
sysparam = odxrequire(et_element.
|
29
|
+
sysparam = odxrequire(et_element.get("SYSPARAM"))
|
30
30
|
|
31
31
|
return SystemParameter(sysparam=sysparam, **kwargs)
|
32
32
|
|
@@ -99,13 +99,20 @@ class TableKeyParameter(Parameter):
|
|
99
99
|
|
100
100
|
if self.table_snref is not None:
|
101
101
|
tables = odxrequire(context.diag_layer).diag_data_dictionary_spec.tables
|
102
|
-
|
102
|
+
if TYPE_CHECKING:
|
103
|
+
self._table = resolve_snref(self.table_snref, tables, Table)
|
104
|
+
else:
|
105
|
+
self._table = resolve_snref(self.table_snref, tables)
|
103
106
|
if self.table_row_snref is not None:
|
104
107
|
# make sure that we know the table to which the table row
|
105
108
|
# SNREF is relative to.
|
106
|
-
table = odxrequire(
|
107
|
-
|
108
|
-
|
109
|
+
table = odxrequire(
|
110
|
+
self._table, "If a table row is referenced via short name, a table must "
|
111
|
+
"be referenced as well")
|
112
|
+
if TYPE_CHECKING:
|
113
|
+
self._table_row = resolve_snref(self.table_row_snref, table.table_rows, TableRow)
|
114
|
+
else:
|
115
|
+
self._table_row = resolve_snref(self.table_row_snref, table.table_rows)
|
109
116
|
|
110
117
|
@property
|
111
118
|
def table(self) -> "Table":
|
odxtools/servicebinner.py
CHANGED
@@ -91,7 +91,7 @@ class ServiceBinner:
|
|
91
91
|
"""
|
92
92
|
result = StringIO()
|
93
93
|
result.write("[ ")
|
94
|
-
result.write(", ".join([f"0x{x}" for x in self._service_groups if x is not None]))
|
94
|
+
result.write(", ".join([f"0x{x:x}" for x in self._service_groups if x is not None]))
|
95
95
|
result.write(" ]")
|
96
96
|
|
97
97
|
return result.getvalue()
|
odxtools/specialdatagroup.py
CHANGED
@@ -51,7 +51,7 @@ class SpecialDataGroup:
|
|
51
51
|
def _build_odxlinks(self) -> Dict[OdxLinkId, Any]:
|
52
52
|
result = {}
|
53
53
|
|
54
|
-
if self.sdg_caption is not None:
|
54
|
+
if self.sdg_caption_ref is None and self.sdg_caption is not None:
|
55
55
|
result.update(self.sdg_caption._build_odxlinks())
|
56
56
|
|
57
57
|
for val in self.values:
|
odxtools/swvariable.py
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
# SPDX-License-Identifier: MIT
|
2
|
+
from dataclasses import dataclass
|
3
|
+
from typing import List, Optional
|
4
|
+
from xml.etree import ElementTree
|
5
|
+
|
6
|
+
from .element import NamedElement
|
7
|
+
from .odxlink import OdxDocFragment
|
8
|
+
from .utils import dataclass_fields_asdict
|
9
|
+
|
10
|
+
|
11
|
+
@dataclass
|
12
|
+
class SwVariable(NamedElement):
|
13
|
+
origin: Optional[str]
|
14
|
+
|
15
|
+
@staticmethod
|
16
|
+
def from_et(et_element: ElementTree.Element, doc_frags: List[OdxDocFragment]) -> "SwVariable":
|
17
|
+
kwargs = dataclass_fields_asdict(NamedElement.from_et(et_element, doc_frags))
|
18
|
+
|
19
|
+
origin = et_element.findtext("ORIGIN")
|
20
|
+
|
21
|
+
return SwVariable(origin=origin, **kwargs)
|
@@ -44,7 +44,9 @@
|
|
44
44
|
{{make_xml_attrib("DISPLAY-LEVEL", cp.display_level)}}{#- #}
|
45
45
|
CPUSAGE="{{cp.cpusage.value}}">
|
46
46
|
{{ peid.printElementIdSubtags(cp)|indent(1) }}
|
47
|
-
|
47
|
+
{%- if cp.physical_default_value is not none %}
|
48
|
+
<PHYSICAL-DEFAULT-VALUE>{{cp.physical_default_value | e}}</PHYSICAL-DEFAULT-VALUE>
|
49
|
+
{%- endif %}
|
48
50
|
<DATA-OBJECT-PROP-REF ID-REF="{{cp.dop_ref.ref_id}}" />
|
49
51
|
</COMPARAM>
|
50
52
|
{%- endmacro %}
|
@@ -61,7 +63,7 @@
|
|
61
63
|
{%- for sub_cp in cp.subparams %}
|
62
64
|
{{- printAnyComparam(sub_cp) | indent(1, first=True) }}
|
63
65
|
{%- endfor %}
|
64
|
-
{%- if
|
66
|
+
{%- if cp.physical_default_value is not none %}
|
65
67
|
{{ printComplexValue(cp.physical_default_value, "COMPLEX-PHYSICAL-DEFAULT-VALUE") | indent(1) }}
|
66
68
|
{%- endif %}
|
67
69
|
</COMPLEX-COMPARAM>
|
@@ -18,14 +18,7 @@
|
|
18
18
|
{%- if limit_obj.value_raw is none %}
|
19
19
|
{#- #}/>
|
20
20
|
{%- else %}
|
21
|
-
{#- #}>
|
22
|
-
{%- if hasattr(limit_obj._value, 'hex') -%}
|
23
|
-
{#- bytes or bytarray limit #}
|
24
|
-
{{- limit_obj._value.hex().upper() }}
|
25
|
-
{%- else -%}
|
26
|
-
{{- limit_obj._value }}
|
27
|
-
{%- endif -%}
|
28
|
-
</{{tag_name}}>
|
21
|
+
{#- #}>{{- limit_obj.value_raw }}</{{tag_name}}>
|
29
22
|
{%- endif -%}
|
30
23
|
{%- endif -%}
|
31
24
|
{%- endmacro -%}
|
@@ -0,0 +1,66 @@
|
|
1
|
+
{#- -*- mode: sgml; tab-width: 2; indent-tabs-mode: nil -*-
|
2
|
+
#
|
3
|
+
# SPDX-License-Identifier: MIT
|
4
|
+
-#}
|
5
|
+
{%- import('macros/printElementId.xml.jinja2') as peid %}
|
6
|
+
{%- import('macros/printAdminData.xml.jinja2') as pad %}
|
7
|
+
{%- import('macros/printDescription.xml.jinja2') as pd %}
|
8
|
+
|
9
|
+
{%- macro printDiagVariable(diag_var) -%}
|
10
|
+
<DIAG-VARIABLE>
|
11
|
+
{{ peid.printElementIdSubtags(diag_variable)|indent(2) }}
|
12
|
+
{%- if diag_variable.admin_data is not none %}
|
13
|
+
{{ pad.printAdminData(diag_variable.admin_data)|indent(2) }}
|
14
|
+
{%- endif %}
|
15
|
+
<VARIABLE-GROUP-REF ID-REF="{{ diag_var.ref_id }}" />
|
16
|
+
{%- if diag_variable.sw_variables %}
|
17
|
+
<SW-VARIABLES>
|
18
|
+
{%- for sw_var in diag_variable.sw_variables %}
|
19
|
+
<SW-VARIABLE>
|
20
|
+
{{ peid.printElementIdSubtags(sw_var)|indent(6) }}
|
21
|
+
{%- if sw_var.origin is not none %}
|
22
|
+
<ORIGIN>{{ sw_var.origin }}</ORIGIN>
|
23
|
+
{%- endif %}
|
24
|
+
</SW-VARIABLE>
|
25
|
+
{%- endfor %}
|
26
|
+
</SW-VARIABLES>
|
27
|
+
{%- endif %}
|
28
|
+
{%- if diag_variable.comm_relations %}
|
29
|
+
<COMM-RELATIONS>
|
30
|
+
{%- for comm_relation in diag_variable.comm_relations %}
|
31
|
+
<COMM-RELATION>
|
32
|
+
{{ pd.printDescription(comm_relation.description) }}
|
33
|
+
<RELATION-TYPE>{{comm_relation.relation_type}}</RELATION-TYPE>
|
34
|
+
{%- if comm_relation.diag_comm_ref is not none %}
|
35
|
+
<DIAG-COMM-REF ID-REF="{{comm_relation.diag_comm_ref.ref_id}}" />
|
36
|
+
{%- endif %}
|
37
|
+
{%- if comm_relation.diag_comm_snref is not none %}
|
38
|
+
<DIAG-COMM-SNREF SHORT-NAME="{{comm_relation.diag_comm_snref}}" />
|
39
|
+
{%- endif %}
|
40
|
+
{%- if comm_relation.in_param_if_ref is not none %}
|
41
|
+
<IN-PARAM-IF-REF ID-REF="{{comm_relation.in_param_if_ref.ref_id}}" />
|
42
|
+
{%- endif %}
|
43
|
+
{%- if comm_relation.in_param_if_snref is not none %}
|
44
|
+
<IN-PARAM-IF-SNREF SHORT-NAME="{{comm_relation.in_param_if_snref}}" />
|
45
|
+
{%- endif %}
|
46
|
+
{%- if comm_relation.out_param_if_ref is not none %}
|
47
|
+
<OUT-PARAM-IF-REF ID-REF="{{comm_relation.out_param_if_ref.ref_id}}" />
|
48
|
+
{%- endif %}
|
49
|
+
{%- if comm_relation.out_param_if_snref is not none %}
|
50
|
+
<OUT-PARAM-IF-SNREF SHORT-NAME="{{comm_relation.out_param_if_snref}}" />
|
51
|
+
{%- endif %}
|
52
|
+
{%- if comm_relation.value_type_raw is not none %}
|
53
|
+
<VALUE-TYPE>{{comm_relation.value_type_raw.value}}</VALUE-TYPE>
|
54
|
+
{%- endif %}
|
55
|
+
<COMM-RELATION>
|
56
|
+
{%- endfor %}
|
57
|
+
</COMM-RELATIONS>
|
58
|
+
{%- endif %}
|
59
|
+
</DIAG-VARIABLE>
|
60
|
+
{%- endmacro -%}
|
61
|
+
|
62
|
+
{%- macro printVariableGroup(var_group) -%}
|
63
|
+
<VARIABLE-GROUP>
|
64
|
+
{{ peid.printElementIdSubtags(diag_variable)|indent(2) }}
|
65
|
+
</VARIABLE-GROUP>
|
66
|
+
{%- endmacro -%}
|
@@ -0,0 +1,48 @@
|
|
1
|
+
{#- -*- mode: sgml; tab-width: 2; indent-tabs-mode: nil -*-
|
2
|
+
#
|
3
|
+
# SPDX-License-Identifier: MIT
|
4
|
+
-#}
|
5
|
+
|
6
|
+
{%- macro printDynDefinedSpec(dd_spec) -%}
|
7
|
+
<DYN-DEFINED-SPEC>
|
8
|
+
{%- if dd_spec.dyn_id_def_mode_infos %}
|
9
|
+
<DYN-ID-DEF-MODE-INFOS>
|
10
|
+
{%- for diddmi in dd_spec.dyn_id_def_mode_infos %}
|
11
|
+
<DYN-ID-DEF-MODE-INFO>
|
12
|
+
<DEF-MODE>{{ diddmi.def_mode }}</DEF-MODE>
|
13
|
+
{%- if diddmi.clear_dyn_def_message_ref is not none %}
|
14
|
+
<CLEAR-DYN-DEF-MESSAGE-REF ID-REF="{{diddmi.clear_dyn_def_message_ref.ref_id}}" />
|
15
|
+
{%- endif %}
|
16
|
+
{%- if diddmi.clear_dyn_def_message_snref is not none %}
|
17
|
+
<CLEAR-DYN-DEF-MESSAGE-SNREF SHORT-NAME="{{diddmi.clear_dyn_def_message_snref}}" />
|
18
|
+
{%- endif %}
|
19
|
+
{%- if diddmi.read_dyn_def_message_ref is not none %}
|
20
|
+
<READ-DYN-DEF-MESSAGE-REF ID-REF="{{diddmi.read_dyn_def_message_ref.ref_id}}" />
|
21
|
+
{%- endif %}
|
22
|
+
{%- if diddmi.read_dyn_def_message_snref is not none %}
|
23
|
+
<READ-DYN-DEF-MESSAGE-SNREF SHORT-NAME="{{diddmi.read_dyn_def_message_snref}}" />
|
24
|
+
{%- endif %}
|
25
|
+
{%- if diddmi.supported_dyn_ids %}
|
26
|
+
<SUPPORTED-DYN-IDS>
|
27
|
+
{%- for dynid in diddmi.supported_dyn_ids %}
|
28
|
+
<SUPPORTED-DYN-ID>{{ dynid.hex() }}</SUPPORTED-DYN-ID>
|
29
|
+
{%- endfor %}
|
30
|
+
</SUPPORTED-DYN-IDS>
|
31
|
+
{%- endif %}
|
32
|
+
{%- if diddmi.selection_table_refs %}
|
33
|
+
<SELECTION-TABLE-REFS>
|
34
|
+
{%- for seltref in diddmi.selection_table_refs %}
|
35
|
+
{%- if hasattr(seltref, "ref_id") %}
|
36
|
+
<SELECTION-TABLE-REF ID-REF="{{ seltref.ref_id }}" />
|
37
|
+
{%- else %}
|
38
|
+
<SELECTION-TABLE-SNREF SHORT-NAME="{{ seltref }}" />
|
39
|
+
{%- endif %}
|
40
|
+
{%- endfor %}
|
41
|
+
</SELECTION-TABLE-REFS>
|
42
|
+
{%- endif %}
|
43
|
+
</DYN-ID-DEF-MODE-INFO>
|
44
|
+
{%- endfor %}
|
45
|
+
</DYN-ID-DEF-MODE-INFOS>
|
46
|
+
{%- endif %}
|
47
|
+
</DYN-DEFINED-SPEC>
|
48
|
+
{%- endmacro -%}
|
@@ -9,7 +9,7 @@
|
|
9
9
|
|
10
10
|
{%- macro printMatchingParameter(mp) -%}
|
11
11
|
<MATCHING-PARAMETER>
|
12
|
-
<EXPECTED-VALUE>{{mp.expected_value}}</EXPECTED-VALUE>
|
12
|
+
<EXPECTED-VALUE>{{mp.expected_value | e}}</EXPECTED-VALUE>
|
13
13
|
<DIAG-COMM-SNREF SHORT-NAME="{{mp.diag_comm_snref}}" />
|
14
14
|
{#- TODO: OUT-PARAM-IF-SNPATHREF #}
|
15
15
|
<OUT-PARAM-IF-SNREF SHORT-NAME="{{mp.out_param_if}}" />
|
@@ -35,8 +35,6 @@
|
|
35
35
|
{%- endif %}
|
36
36
|
{%- if param.coded_value is defined %}
|
37
37
|
<CODED-VALUE>{{param.coded_value}}</CODED-VALUE>
|
38
|
-
{%- elif param.physical_constant_value_raw is defined %}
|
39
|
-
<PHYS-CONSTANT-VALUE>{{param.physical_constant_value_raw}}</PHYS-CONSTANT-VALUE>
|
40
38
|
{%- elif param.coded_values is defined %}
|
41
39
|
<CODED-VALUES>
|
42
40
|
{%- for coded_value in param.coded_values %}
|
@@ -44,8 +42,11 @@
|
|
44
42
|
{%- endfor %}
|
45
43
|
</CODED-VALUES>
|
46
44
|
{%- endif %}
|
47
|
-
{%- if param.
|
48
|
-
<
|
45
|
+
{%- if param.physical_constant_value_raw is defined %}
|
46
|
+
<PHYS-CONSTANT-VALUE>{{param.physical_constant_value_raw}}</PHYS-CONSTANT-VALUE>
|
47
|
+
{%- endif %}
|
48
|
+
{%- if param.physical_default_value_raw is defined and param.physical_default_value_raw is not none %}
|
49
|
+
<PHYSICAL-DEFAULT-VALUE>{{param.physical_default_value_raw | e}}</PHYSICAL-DEFAULT-VALUE>
|
49
50
|
{%- endif %}
|
50
51
|
{%- if param.dop_ref %}
|
51
52
|
<DOP-REF ID-REF="{{param.dop_ref.ref_id}}"/>
|
@@ -53,10 +54,8 @@
|
|
53
54
|
<DOP-SNREF SN="{{param.dop_snref}}"/>
|
54
55
|
{%- elif param.diag_coded_type is defined %}
|
55
56
|
{{pdop.printDiagCodedType(param.diag_coded_type)|indent(2)}}
|
56
|
-
{%- elif param.
|
57
|
-
{
|
58
|
-
<BIT-LENGTH>{{param.bit_length}}</BIT-LENGTH>
|
59
|
-
{%- endif %}
|
57
|
+
{%- elif param.bit_length is defined and param.bit_length is not none %}
|
58
|
+
<BIT-LENGTH>{{param.bit_length}}</BIT-LENGTH>
|
60
59
|
{%- endif %}
|
61
60
|
{%- if param.parameter_type == "TABLE-KEY" %}
|
62
61
|
{%- if param.table_ref %}
|