odxtools 9.4.1__py3-none-any.whl → 9.6.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/additionalaudience.py +2 -2
- odxtools/admindata.py +3 -0
- odxtools/audience.py +9 -13
- odxtools/basecomparam.py +1 -2
- odxtools/basevariantpattern.py +38 -0
- odxtools/basicstructure.py +34 -35
- odxtools/commrelation.py +2 -1
- odxtools/companydata.py +1 -2
- odxtools/companyspecificinfo.py +3 -0
- odxtools/comparam.py +16 -8
- odxtools/comparaminstance.py +12 -12
- odxtools/comparamspec.py +4 -3
- odxtools/comparamsubset.py +26 -24
- odxtools/compumethods/compuconst.py +4 -4
- odxtools/compumethods/limit.py +9 -9
- odxtools/compumethods/linearsegment.py +8 -8
- odxtools/dataobjectproperty.py +16 -18
- odxtools/description.py +4 -2
- odxtools/determinenumberofitems.py +4 -4
- odxtools/diagcodedtype.py +20 -20
- odxtools/diagcomm.py +61 -41
- odxtools/diagdatadictionaryspec.py +51 -55
- odxtools/diaglayercontainer.py +25 -25
- odxtools/diaglayers/basevariant.py +5 -0
- odxtools/diaglayers/basevariantraw.py +7 -1
- odxtools/diaglayers/diaglayerraw.py +26 -27
- odxtools/diaglayers/ecuvariant.py +16 -0
- odxtools/diagnostictroublecode.py +13 -10
- odxtools/diagservice.py +48 -50
- odxtools/diagvariable.py +10 -8
- odxtools/docrevision.py +5 -5
- odxtools/dtcdop.py +17 -17
- odxtools/dynamicendmarkerfield.py +8 -8
- odxtools/dynamiclengthfield.py +2 -0
- odxtools/dyndefinedspec.py +21 -8
- odxtools/ecuvariantpattern.py +20 -9
- odxtools/encodestate.py +3 -3
- odxtools/endofpdufield.py +7 -9
- odxtools/environmentdatadescription.py +9 -20
- odxtools/field.py +21 -21
- odxtools/inputparam.py +15 -14
- odxtools/leadinglengthinfotype.py +4 -4
- odxtools/matchingbasevariantparameter.py +38 -0
- odxtools/matchingparameter.py +108 -28
- odxtools/minmaxlengthtype.py +6 -6
- odxtools/multiplexer.py +38 -39
- odxtools/multiplexercase.py +3 -6
- odxtools/multiplexerdefaultcase.py +3 -6
- odxtools/multiplexerswitchkey.py +4 -4
- odxtools/negoutputparam.py +6 -9
- odxtools/odxlink.py +21 -5
- odxtools/odxtypes.py +7 -6
- odxtools/outputparam.py +9 -8
- odxtools/parameterinfo.py +1 -1
- odxtools/parameters/codedconstparameter.py +28 -27
- odxtools/parameters/dynamicparameter.py +9 -9
- odxtools/parameters/lengthkeyparameter.py +18 -18
- odxtools/parameters/matchingrequestparameter.py +15 -15
- odxtools/parameters/nrcconstparameter.py +32 -24
- odxtools/parameters/parameter.py +35 -37
- odxtools/parameters/parameterwithdop.py +6 -6
- odxtools/parameters/physicalconstantparameter.py +19 -20
- odxtools/parameters/reservedparameter.py +10 -11
- odxtools/parameters/systemparameter.py +10 -11
- odxtools/parameters/tableentryparameter.py +19 -20
- odxtools/parameters/tablekeyparameter.py +0 -2
- odxtools/parameters/tablestructparameter.py +27 -21
- odxtools/parameters/valueparameter.py +20 -20
- odxtools/parentref.py +6 -7
- odxtools/physicaldimension.py +11 -11
- odxtools/physicaltype.py +9 -14
- odxtools/preconditionstateref.py +85 -0
- odxtools/progcode.py +1 -2
- odxtools/protstack.py +4 -4
- odxtools/relateddoc.py +3 -4
- odxtools/scaleconstr.py +0 -1
- odxtools/singleecujob.py +8 -4
- odxtools/specialdata.py +10 -9
- odxtools/specialdatagroup.py +1 -0
- odxtools/standardlengthtype.py +18 -18
- odxtools/statechart.py +10 -6
- odxtools/statemachine.py +186 -0
- odxtools/statetransitionref.py +231 -0
- odxtools/structure.py +4 -4
- odxtools/subcomponent.py +78 -11
- odxtools/table.py +23 -13
- odxtools/tablerow.py +86 -69
- odxtools/teammember.py +4 -4
- odxtools/templates/macros/printBaseVariant.xml.jinja2 +4 -9
- odxtools/templates/macros/printBaseVariantPattern.xml.jinja2 +32 -0
- odxtools/templates/macros/printCompanyData.xml.jinja2 +2 -2
- odxtools/templates/macros/printComparam.xml.jinja2 +3 -5
- odxtools/templates/macros/printDOP.xml.jinja2 +4 -1
- odxtools/templates/macros/printDiagComm.xml.jinja2 +6 -5
- odxtools/templates/macros/printEcuVariantPattern.xml.jinja2 +7 -6
- odxtools/templates/macros/printParam.xml.jinja2 +5 -5
- odxtools/templates/macros/printPreConditionStateRef.xml.jinja2 +18 -0
- odxtools/templates/macros/printStateTransitionRef.xml.jinja2 +18 -0
- odxtools/templates/macros/printTable.xml.jinja2 +13 -9
- odxtools/text.py +35 -0
- odxtools/unit.py +1 -3
- odxtools/unitgroup.py +6 -8
- odxtools/variantmatcher.py +209 -0
- odxtools/variantpattern.py +38 -0
- odxtools/version.py +2 -2
- {odxtools-9.4.1.dist-info → odxtools-9.6.0.dist-info}/METADATA +3 -2
- {odxtools-9.4.1.dist-info → odxtools-9.6.0.dist-info}/RECORD +111 -102
- {odxtools-9.4.1.dist-info → odxtools-9.6.0.dist-info}/WHEEL +1 -1
- odxtools/createecuvariantpatterns.py +0 -18
- odxtools/ecuvariantmatcher.py +0 -171
- {odxtools-9.4.1.dist-info → odxtools-9.6.0.dist-info}/entry_points.txt +0 -0
- {odxtools-9.4.1.dist-info → odxtools-9.6.0.dist-info/licenses}/LICENSE +0 -0
- {odxtools-9.4.1.dist-info → odxtools-9.6.0.dist-info}/top_level.txt +0 -0
@@ -23,6 +23,14 @@ class DynamicEndmarkerField(Field):
|
|
23
23
|
|
24
24
|
dyn_end_dop_ref: DynEndDopRef
|
25
25
|
|
26
|
+
@property
|
27
|
+
def dyn_end_dop(self) -> DataObjectProperty:
|
28
|
+
return self._dyn_end_dop
|
29
|
+
|
30
|
+
@property
|
31
|
+
def termination_value(self) -> AtomicOdxType:
|
32
|
+
return self._termination_value
|
33
|
+
|
26
34
|
@staticmethod
|
27
35
|
def from_et(et_element: ElementTree.Element,
|
28
36
|
doc_frags: List[OdxDocFragment]) -> "DynamicEndmarkerField":
|
@@ -52,14 +60,6 @@ class DynamicEndmarkerField(Field):
|
|
52
60
|
def _resolve_snrefs(self, context: SnRefContext) -> None:
|
53
61
|
super()._resolve_snrefs(context)
|
54
62
|
|
55
|
-
@property
|
56
|
-
def dyn_end_dop(self) -> DataObjectProperty:
|
57
|
-
return self._dyn_end_dop
|
58
|
-
|
59
|
-
@property
|
60
|
-
def termination_value(self) -> AtomicOdxType:
|
61
|
-
return self._termination_value
|
62
|
-
|
63
63
|
@override
|
64
64
|
def encode_into_pdu(self, physical_value: ParameterValue, encode_state: EncodeState) -> None:
|
65
65
|
|
odxtools/dynamiclengthfield.py
CHANGED
@@ -26,11 +26,13 @@ class DynamicLengthField(Field):
|
|
26
26
|
def from_et(et_element: ElementTree.Element,
|
27
27
|
doc_frags: List[OdxDocFragment]) -> "DynamicLengthField":
|
28
28
|
kwargs = dataclass_fields_asdict(Field.from_et(et_element, doc_frags))
|
29
|
+
|
29
30
|
offset = int(odxrequire(et_element.findtext('OFFSET')))
|
30
31
|
determine_number_of_items = DetermineNumberOfItems.from_et(
|
31
32
|
odxrequire(et_element.find('DETERMINE-NUMBER-OF-ITEMS')),
|
32
33
|
doc_frags,
|
33
34
|
)
|
35
|
+
|
34
36
|
return DynamicLengthField(
|
35
37
|
offset=offset, determine_number_of_items=determine_number_of_items, **kwargs)
|
36
38
|
|
odxtools/dyndefinedspec.py
CHANGED
@@ -4,7 +4,7 @@ from typing import Any, Dict, List, Optional, Union
|
|
4
4
|
from xml.etree import ElementTree
|
5
5
|
|
6
6
|
from .diagcomm import DiagClassType, DiagComm
|
7
|
-
from .exceptions import odxraise, odxrequire
|
7
|
+
from .exceptions import odxassert, odxraise, odxrequire
|
8
8
|
from .nameditemlist import NamedItemList
|
9
9
|
from .odxlink import OdxDocFragment, OdxLinkDatabase, OdxLinkId, OdxLinkRef, resolve_snref
|
10
10
|
from .snrefcontext import SnRefContext
|
@@ -50,17 +50,20 @@ class DynIdDefModeInfo:
|
|
50
50
|
|
51
51
|
clear_dyn_def_message_ref = OdxLinkRef.from_et(
|
52
52
|
et_element.find("CLEAR-DYN-DEF-MESSAGE-REF"), doc_frags)
|
53
|
+
clear_dyn_def_message_snref = None
|
53
54
|
if (snref_elem := et_element.find("CLEAR-DYN-DEF-MESSAGE-SNREF")) is not None:
|
54
|
-
clear_dyn_def_message_snref = snref_elem.attrib
|
55
|
+
clear_dyn_def_message_snref = odxrequire(snref_elem.attrib.get("SHORT-NAME"))
|
55
56
|
|
56
57
|
read_dyn_def_message_ref = OdxLinkRef.from_et(
|
57
58
|
et_element.find("READ-DYN-DEF-MESSAGE-REF"), doc_frags)
|
59
|
+
read_dyn_def_message_snref = None
|
58
60
|
if (snref_elem := et_element.find("READ-DYN-DEF-MESSAGE-SNREF")) is not None:
|
59
|
-
read_dyn_def_message_snref = snref_elem.attrib
|
61
|
+
read_dyn_def_message_snref = odxrequire(snref_elem.attrib.get("SHORT-NAME"))
|
60
62
|
|
61
63
|
dyn_def_message_ref = OdxLinkRef.from_et(et_element.find("DYN-DEF-MESSAGE-REF"), doc_frags)
|
64
|
+
dyn_def_message_snref = None
|
62
65
|
if (snref_elem := et_element.find("DYN-DEF-MESSAGE-SNREF")) is not None:
|
63
|
-
dyn_def_message_snref = snref_elem.attrib
|
66
|
+
dyn_def_message_snref = odxrequire(snref_elem.attrib.get("SHORT-NAME"))
|
64
67
|
|
65
68
|
supported_dyn_ids = [
|
66
69
|
bytes.fromhex(odxrequire(x.text))
|
@@ -89,14 +92,23 @@ class DynIdDefModeInfo:
|
|
89
92
|
selection_table_refs=selection_table_refs,
|
90
93
|
)
|
91
94
|
|
95
|
+
def __post_init__(self) -> None:
|
96
|
+
odxassert(
|
97
|
+
self.clear_dyn_def_message_ref is not None or
|
98
|
+
self.clear_dyn_def_message_snref is not None,
|
99
|
+
"A CLEAR-DYN-DEF-MESSAGE must be specified")
|
100
|
+
odxassert(
|
101
|
+
self.read_dyn_def_message_ref is not None or
|
102
|
+
self.read_dyn_def_message_snref is not None, "A READ-DYN-DEF-MESSAGE must be specified")
|
103
|
+
odxassert(self.dyn_def_message_ref is not None or self.dyn_def_message_snref is not None,
|
104
|
+
"A DYN-DEF-MESSAGE must be specified")
|
105
|
+
|
92
106
|
def _build_odxlinks(self) -> Dict[OdxLinkId, Any]:
|
93
107
|
result: Dict[OdxLinkId, Any] = {}
|
94
108
|
|
95
109
|
return result
|
96
110
|
|
97
111
|
def _resolve_odxlinks(self, odxlinks: OdxLinkDatabase) -> None:
|
98
|
-
self._selection_tables = NamedItemList[Table]()
|
99
|
-
|
100
112
|
if self.clear_dyn_def_message_ref is not None:
|
101
113
|
self._clear_dyn_def_message = odxlinks.resolve(self.clear_dyn_def_message_ref, DiagComm)
|
102
114
|
|
@@ -106,7 +118,8 @@ class DynIdDefModeInfo:
|
|
106
118
|
if self.dyn_def_message_ref is not None:
|
107
119
|
self._dyn_def_message = odxlinks.resolve(self.dyn_def_message_ref, DiagComm)
|
108
120
|
|
109
|
-
# resolve the selection tables referenced
|
121
|
+
# resolve the selection tables that are referenced via ODXLINK
|
122
|
+
self._selection_tables = NamedItemList[Table]()
|
110
123
|
for x in self.selection_table_refs:
|
111
124
|
if isinstance(x, OdxLinkRef):
|
112
125
|
self._selection_tables.append(odxlinks.resolve(x, Table))
|
@@ -141,9 +154,9 @@ class DynIdDefModeInfo:
|
|
141
154
|
f"DYN-DEF-MESSAGE)")
|
142
155
|
|
143
156
|
# resolve the remaining selection tables that are referenced via SNREF
|
157
|
+
ddd_spec = odxrequire(diag_layer.diag_data_dictionary_spec)
|
144
158
|
for i, x in enumerate(self.selection_table_refs):
|
145
159
|
if isinstance(x, str):
|
146
|
-
ddd_spec = odxrequire(diag_layer.diag_data_dictionary_spec)
|
147
160
|
self._selection_tables.insert(i, resolve_snref(x, ddd_spec.tables, Table))
|
148
161
|
|
149
162
|
|
odxtools/ecuvariantpattern.py
CHANGED
@@ -1,27 +1,38 @@
|
|
1
1
|
# SPDX-License-Identifier: MIT
|
2
2
|
from dataclasses import dataclass
|
3
|
-
from typing import List
|
3
|
+
from typing import List, Union
|
4
4
|
from xml.etree import ElementTree
|
5
5
|
|
6
|
-
from
|
6
|
+
from typing_extensions import override
|
7
|
+
|
8
|
+
from .exceptions import odxassert
|
9
|
+
from .matchingbasevariantparameter import MatchingBaseVariantParameter
|
7
10
|
from .matchingparameter import MatchingParameter
|
8
11
|
from .odxlink import OdxDocFragment
|
12
|
+
from .variantpattern import VariantPattern
|
9
13
|
|
10
14
|
|
11
15
|
@dataclass
|
12
|
-
class EcuVariantPattern:
|
16
|
+
class EcuVariantPattern(VariantPattern):
|
17
|
+
"""ECU variant patterns are variant patterns used to identify the
|
18
|
+
concrete variant of an ECU.
|
19
|
+
"""
|
13
20
|
matching_parameters: List[MatchingParameter]
|
14
21
|
|
22
|
+
@override
|
23
|
+
def get_matching_parameters(
|
24
|
+
self) -> Union[List[MatchingParameter], List[MatchingBaseVariantParameter]]:
|
25
|
+
return self.matching_parameters
|
26
|
+
|
15
27
|
@staticmethod
|
16
28
|
def from_et(et_element: ElementTree.Element,
|
17
29
|
doc_frags: List[OdxDocFragment]) -> "EcuVariantPattern":
|
18
30
|
|
19
|
-
|
20
|
-
|
21
|
-
matching_params = [
|
31
|
+
matching_parameters = [
|
22
32
|
MatchingParameter.from_et(mp_el, doc_frags)
|
23
|
-
for mp_el in
|
33
|
+
for mp_el in et_element.iterfind("MATCHING-PARAMETERS/"
|
34
|
+
"MATCHING-PARAMETER")
|
24
35
|
]
|
25
36
|
|
26
|
-
odxassert(len(
|
27
|
-
return EcuVariantPattern(
|
37
|
+
odxassert(len(matching_parameters) > 0) # required by ISO 22901-1 Figure 141
|
38
|
+
return EcuVariantPattern(matching_parameters=matching_parameters)
|
odxtools/encodestate.py
CHANGED
@@ -1,11 +1,11 @@
|
|
1
1
|
# SPDX-License-Identifier: MIT
|
2
2
|
import warnings
|
3
3
|
from dataclasses import dataclass, field
|
4
|
-
from typing import TYPE_CHECKING, Dict, List, Optional,
|
4
|
+
from typing import TYPE_CHECKING, Dict, List, Optional, Tuple
|
5
5
|
|
6
6
|
from .encoding import Encoding, get_string_encoding
|
7
7
|
from .exceptions import EncodeError, OdxWarning, odxassert, odxraise
|
8
|
-
from .odxtypes import AtomicOdxType, DataType, ParameterValue
|
8
|
+
from .odxtypes import AtomicOdxType, BytesTypes, DataType, ParameterValue
|
9
9
|
|
10
10
|
try:
|
11
11
|
import bitstruct.c as bitstruct
|
@@ -97,7 +97,7 @@ class EncodeState:
|
|
97
97
|
|
98
98
|
# Deal with raw byte fields, ...
|
99
99
|
if base_data_type == DataType.A_BYTEFIELD:
|
100
|
-
if not isinstance(internal_value,
|
100
|
+
if not isinstance(internal_value, BytesTypes):
|
101
101
|
odxraise(f"{internal_value!r} is not a bytefield", EncodeError)
|
102
102
|
return
|
103
103
|
|
odxtools/endofpdufield.py
CHANGED
@@ -17,30 +17,28 @@ from .utils import dataclass_fields_asdict
|
|
17
17
|
@dataclass
|
18
18
|
class EndOfPduField(Field):
|
19
19
|
"""End of PDU fields are structures that are repeated until the end of the PDU"""
|
20
|
-
min_number_of_items: Optional[int]
|
21
20
|
max_number_of_items: Optional[int]
|
21
|
+
min_number_of_items: Optional[int]
|
22
22
|
|
23
23
|
@staticmethod
|
24
24
|
def from_et(et_element: ElementTree.Element,
|
25
25
|
doc_frags: List[OdxDocFragment]) -> "EndOfPduField":
|
26
26
|
kwargs = dataclass_fields_asdict(Field.from_et(et_element, doc_frags))
|
27
27
|
|
28
|
-
if (min_n_str := et_element.findtext("MIN-NUMBER-OF-ITEMS")) is not None:
|
29
|
-
min_number_of_items = int(min_n_str)
|
30
|
-
else:
|
31
|
-
min_number_of_items = None
|
32
28
|
if (max_n_str := et_element.findtext("MAX-NUMBER-OF-ITEMS")) is not None:
|
33
29
|
max_number_of_items = int(max_n_str)
|
34
30
|
else:
|
35
31
|
max_number_of_items = None
|
32
|
+
if (min_n_str := et_element.findtext("MIN-NUMBER-OF-ITEMS")) is not None:
|
33
|
+
min_number_of_items = int(min_n_str)
|
34
|
+
else:
|
35
|
+
min_number_of_items = None
|
36
36
|
|
37
|
-
|
38
|
-
min_number_of_items=min_number_of_items,
|
37
|
+
return EndOfPduField(
|
39
38
|
max_number_of_items=max_number_of_items,
|
39
|
+
min_number_of_items=min_number_of_items,
|
40
40
|
**kwargs)
|
41
41
|
|
42
|
-
return eopf
|
43
|
-
|
44
42
|
@override
|
45
43
|
def encode_into_pdu(self, physical_value: Optional[ParameterValue],
|
46
44
|
encode_state: EncodeState) -> None:
|
@@ -1,6 +1,6 @@
|
|
1
1
|
# SPDX-License-Identifier: MIT
|
2
2
|
from dataclasses import dataclass
|
3
|
-
from typing import Any, Dict, List, Optional
|
3
|
+
from typing import Any, Dict, List, Optional
|
4
4
|
from xml.etree import ElementTree
|
5
5
|
|
6
6
|
from typing_extensions import override
|
@@ -44,18 +44,6 @@ class EnvironmentDataDescription(ComplexDop):
|
|
44
44
|
env_datas: NamedItemList[EnvironmentData]
|
45
45
|
env_data_refs: List[OdxLinkRef]
|
46
46
|
|
47
|
-
@property
|
48
|
-
def param(self) -> Parameter:
|
49
|
-
# the parameter referenced via SNREF cannot be resolved here
|
50
|
-
# because the relevant list of parameters depends on the
|
51
|
-
# concrete codec object processed, whilst an environment data
|
52
|
-
# description object can be featured in an arbitrary number of
|
53
|
-
# responses. Instead, lookup of the appropriate parameter is
|
54
|
-
# done within the encode and decode methods.
|
55
|
-
odxraise("The parameter of ENV-DATA-DESC objects cannot be resolved "
|
56
|
-
"because it depends on the context")
|
57
|
-
return cast(None, Parameter)
|
58
|
-
|
59
47
|
@staticmethod
|
60
48
|
def from_et(et_element: ElementTree.Element,
|
61
49
|
doc_frags: List[OdxDocFragment]) -> "EnvironmentDataDescription":
|
@@ -74,20 +62,20 @@ class EnvironmentDataDescription(ComplexDop):
|
|
74
62
|
# situation is reversed. This means that we will create one
|
75
63
|
# empty and one non-empty list here. (Which is which depends
|
76
64
|
# on the version of the standard used by the file.)
|
77
|
-
env_data_refs = [
|
78
|
-
odxrequire(OdxLinkRef.from_et(env_data_ref, doc_frags))
|
79
|
-
for env_data_ref in et_element.iterfind("ENV-DATA-REFS/ENV-DATA-REF")
|
80
|
-
]
|
81
65
|
env_datas = NamedItemList([
|
82
66
|
EnvironmentData.from_et(env_data_elem, doc_frags)
|
83
67
|
for env_data_elem in et_element.iterfind("ENV-DATAS/ENV-DATA")
|
84
68
|
])
|
69
|
+
env_data_refs = [
|
70
|
+
odxrequire(OdxLinkRef.from_et(env_data_ref, doc_frags))
|
71
|
+
for env_data_ref in et_element.iterfind("ENV-DATA-REFS/ENV-DATA-REF")
|
72
|
+
]
|
85
73
|
|
86
74
|
return EnvironmentDataDescription(
|
87
75
|
param_snref=param_snref,
|
88
76
|
param_snpathref=param_snpathref,
|
89
|
-
env_data_refs=env_data_refs,
|
90
77
|
env_datas=env_datas,
|
78
|
+
env_data_refs=env_data_refs,
|
91
79
|
**kwargs)
|
92
80
|
|
93
81
|
def _build_odxlinks(self) -> Dict[OdxLinkId, Any]:
|
@@ -103,7 +91,8 @@ class EnvironmentDataDescription(ComplexDop):
|
|
103
91
|
# ODX 2.0 specifies environment data objects here, ODX 2.2
|
104
92
|
# uses references
|
105
93
|
if self.env_data_refs:
|
106
|
-
self.env_datas = NamedItemList(
|
94
|
+
self.env_datas = NamedItemList(
|
95
|
+
[odxlinks.resolve(x, EnvironmentData) for x in self.env_data_refs])
|
107
96
|
else:
|
108
97
|
for ed in self.env_datas:
|
109
98
|
ed._resolve_odxlinks(odxlinks)
|
@@ -221,7 +210,7 @@ class EnvironmentDataDescription(ComplexDop):
|
|
221
210
|
if not isinstance(dop, (DataObjectProperty, DtcDop)):
|
222
211
|
odxraise(f"The DOP of the parameter referenced by environment data descriptions "
|
223
212
|
f"must use either be DataObjectProperty or a DtcDop (encountered "
|
224
|
-
f"{type(param).__name__} for parameter '{
|
213
|
+
f"{type(param).__name__} for parameter '{param.short_name}' "
|
225
214
|
f"of ENV-DATA-DESC '{self.short_name}')")
|
226
215
|
return 0
|
227
216
|
|
odxtools/field.py
CHANGED
@@ -21,19 +21,14 @@ class Field(ComplexDop):
|
|
21
21
|
env_data_desc_snref: Optional[str]
|
22
22
|
is_visible_raw: Optional[bool]
|
23
23
|
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
num_struct_refs += 0 if self.structure_snref is None else 1
|
29
|
-
|
30
|
-
num_edd_refs = 0 if self.env_data_desc_ref is None else 1
|
31
|
-
num_edd_refs += 0 if self.env_data_desc_snref is None else 1
|
24
|
+
@property
|
25
|
+
def structure(self) -> BasicStructure:
|
26
|
+
"""may be a Structure or a env-data-desc"""
|
27
|
+
return odxrequire(self._structure, "EnvironmentDataDescription is not supported")
|
32
28
|
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
"structure or an environment data description")
|
29
|
+
@property
|
30
|
+
def is_visible(self) -> bool:
|
31
|
+
return self.is_visible_raw in (None, True)
|
37
32
|
|
38
33
|
@staticmethod
|
39
34
|
def from_et(et_element: ElementTree.Element, doc_frags: List[OdxDocFragment]) -> "Field":
|
@@ -58,17 +53,19 @@ class Field(ComplexDop):
|
|
58
53
|
is_visible_raw=is_visible_raw,
|
59
54
|
**kwargs)
|
60
55
|
|
61
|
-
|
62
|
-
|
63
|
-
|
56
|
+
def __post_init__(self) -> None:
|
57
|
+
self._structure: Optional[BasicStructure] = None
|
58
|
+
self._env_data_desc: Optional[EnvironmentDataDescription] = None
|
59
|
+
num_struct_refs = 0 if self.structure_ref is None else 1
|
60
|
+
num_struct_refs += 0 if self.structure_snref is None else 1
|
64
61
|
|
65
|
-
|
66
|
-
|
67
|
-
"""may be a Structure or a env-data-desc"""
|
68
|
-
return odxrequire(self._structure, "EnvironmentDataDescription is not supported")
|
62
|
+
num_edd_refs = 0 if self.env_data_desc_ref is None else 1
|
63
|
+
num_edd_refs += 0 if self.env_data_desc_snref is None else 1
|
69
64
|
|
70
|
-
|
71
|
-
|
65
|
+
odxassert(
|
66
|
+
num_struct_refs + num_edd_refs == 1,
|
67
|
+
"FIELDs need to specify exactly one reference to a "
|
68
|
+
"structure or an environment data description")
|
72
69
|
|
73
70
|
def _resolve_odxlinks(self, odxlinks: OdxLinkDatabase) -> None:
|
74
71
|
"""Recursively resolve any odxlinks references"""
|
@@ -89,3 +86,6 @@ class Field(ComplexDop):
|
|
89
86
|
if self.env_data_desc_snref is not None:
|
90
87
|
self._env_data_desc = resolve_snref(self.env_data_desc_snref, ddds.env_data_descs,
|
91
88
|
EnvironmentDataDescription)
|
89
|
+
|
90
|
+
def get_static_bit_length(self) -> Optional[int]:
|
91
|
+
return None
|
odxtools/inputparam.py
CHANGED
@@ -15,25 +15,35 @@ from .utils import dataclass_fields_asdict
|
|
15
15
|
|
16
16
|
@dataclass
|
17
17
|
class InputParam(NamedElement):
|
18
|
+
physical_default_value: Optional[str]
|
18
19
|
dop_base_ref: OdxLinkRef
|
19
20
|
oid: Optional[str]
|
20
21
|
semantic: Optional[str]
|
21
|
-
|
22
|
+
|
23
|
+
@property
|
24
|
+
def dop(self) -> DopBase:
|
25
|
+
"""The data object property describing this parameter."""
|
26
|
+
return self._dop
|
27
|
+
|
28
|
+
@deprecated(details="use .dop") # type: ignore[misc]
|
29
|
+
def dop_base(self) -> DopBase:
|
30
|
+
return self._dop
|
22
31
|
|
23
32
|
@staticmethod
|
24
33
|
def from_et(et_element: ElementTree.Element, doc_frags: List[OdxDocFragment]) -> "InputParam":
|
25
34
|
kwargs = dataclass_fields_asdict(NamedElement.from_et(et_element, doc_frags))
|
26
|
-
|
35
|
+
|
27
36
|
physical_default_value = et_element.findtext("PHYSICAL-DEFAULT-VALUE")
|
37
|
+
dop_base_ref = odxrequire(OdxLinkRef.from_et(et_element.find("DOP-BASE-REF"), doc_frags))
|
28
38
|
|
29
|
-
semantic = et_element.get("SEMANTIC")
|
30
39
|
oid = et_element.get("OID")
|
40
|
+
semantic = et_element.get("SEMANTIC")
|
31
41
|
|
32
42
|
return InputParam(
|
33
|
-
dop_base_ref=dop_base_ref,
|
34
43
|
physical_default_value=physical_default_value,
|
35
|
-
|
44
|
+
dop_base_ref=dop_base_ref,
|
36
45
|
oid=oid,
|
46
|
+
semantic=semantic,
|
37
47
|
**kwargs)
|
38
48
|
|
39
49
|
def _build_odxlinks(self) -> Dict[OdxLinkId, Any]:
|
@@ -44,12 +54,3 @@ class InputParam(NamedElement):
|
|
44
54
|
|
45
55
|
def _resolve_snrefs(self, context: SnRefContext) -> None:
|
46
56
|
pass
|
47
|
-
|
48
|
-
@property
|
49
|
-
def dop(self) -> DopBase:
|
50
|
-
"""The data object property describing this parameter."""
|
51
|
-
return self._dop
|
52
|
-
|
53
|
-
@deprecated(details="use .dop") # type: ignore[misc]
|
54
|
-
def dop_base(self) -> DopBase:
|
55
|
-
return self._dop
|
@@ -23,6 +23,10 @@ class LeadingLengthInfoType(DiagCodedType):
|
|
23
23
|
#: object.
|
24
24
|
bit_length: int
|
25
25
|
|
26
|
+
@property
|
27
|
+
def dct_type(self) -> DctType:
|
28
|
+
return "LEADING-LENGTH-INFO-TYPE"
|
29
|
+
|
26
30
|
@staticmethod
|
27
31
|
@override
|
28
32
|
def from_et(et_element: ElementTree.Element,
|
@@ -46,10 +50,6 @@ class LeadingLengthInfoType(DiagCodedType):
|
|
46
50
|
f"A leading length info type cannot have the base data type {self.base_data_type.name}."
|
47
51
|
)
|
48
52
|
|
49
|
-
@property
|
50
|
-
def dct_type(self) -> DctType:
|
51
|
-
return "LEADING-LENGTH-INFO-TYPE"
|
52
|
-
|
53
53
|
@override
|
54
54
|
def encode_into_pdu(self, internal_value: AtomicOdxType, encode_state: EncodeState) -> None:
|
55
55
|
|
@@ -0,0 +1,38 @@
|
|
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 .matchingparameter import MatchingParameter
|
7
|
+
from .odxlink import OdxDocFragment
|
8
|
+
from .odxtypes import odxstr_to_bool
|
9
|
+
from .utils import dataclass_fields_asdict
|
10
|
+
|
11
|
+
|
12
|
+
@dataclass
|
13
|
+
class MatchingBaseVariantParameter(MatchingParameter):
|
14
|
+
"""A description of a parameter used for base variant matching.
|
15
|
+
|
16
|
+
This is very similar to `MatchingParameter` for ECU variant
|
17
|
+
matching, but `MatchingBaseVariantParameter` features the
|
18
|
+
additional subtag `USE-PHYSICAL-ADDRESSING`.
|
19
|
+
"""
|
20
|
+
|
21
|
+
use_physical_addressing_raw: Optional[bool]
|
22
|
+
|
23
|
+
@property
|
24
|
+
def use_physical_addressing(self) -> bool:
|
25
|
+
return self.use_physical_addressing_raw in [None, True]
|
26
|
+
|
27
|
+
@staticmethod
|
28
|
+
def from_et(et_element: ElementTree.Element,
|
29
|
+
doc_frags: List[OdxDocFragment]) -> "MatchingBaseVariantParameter":
|
30
|
+
|
31
|
+
kwargs = dataclass_fields_asdict(MatchingParameter.from_et(et_element, doc_frags))
|
32
|
+
|
33
|
+
use_physical_addressing_raw = odxstr_to_bool(et_element.findtext("USE-PHYSICAL-ADDRESSING"))
|
34
|
+
|
35
|
+
return MatchingBaseVariantParameter(
|
36
|
+
use_physical_addressing_raw=use_physical_addressing_raw,
|
37
|
+
**kwargs,
|
38
|
+
)
|
odxtools/matchingparameter.py
CHANGED
@@ -1,58 +1,138 @@
|
|
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 .
|
7
|
-
from .
|
8
|
-
from .
|
6
|
+
from .diaglayers.diaglayer import DiagLayer
|
7
|
+
from .diagnostictroublecode import DiagnosticTroubleCode
|
8
|
+
from .diagservice import DiagService
|
9
|
+
from .exceptions import odxraise, odxrequire
|
10
|
+
from .odxlink import OdxDocFragment, OdxLinkDatabase, OdxLinkId, resolve_snref
|
11
|
+
from .odxtypes import BytesTypes, ParameterValue, ParameterValueDict
|
12
|
+
from .snrefcontext import SnRefContext
|
9
13
|
|
10
14
|
|
11
15
|
@dataclass
|
12
16
|
class MatchingParameter:
|
13
|
-
"""According to ISO 22901, a MatchingParameter contains a string
|
14
|
-
the active ECU variant. Moreover, it
|
15
|
-
|
17
|
+
"""According to ISO 22901, a MatchingParameter contains a string
|
18
|
+
value identifying the active ECU or base variant. Moreover, it
|
19
|
+
references a DIAG-COMM via snref and one of its positive
|
20
|
+
response's OUT-PARAM-IF via snref or snpathref.
|
21
|
+
|
22
|
+
Unlike other parameters defined in the `parameters` package, a
|
23
|
+
MatchingParameter is not transferred over the network.
|
16
24
|
|
17
|
-
Unlike other parameters defined in the `parameters` package, a MatchingParameter is
|
18
|
-
not transferred over the network.
|
19
25
|
"""
|
20
26
|
|
21
|
-
# datatype according to ISO 22901-1 Figure 141
|
22
27
|
expected_value: str
|
23
28
|
diag_comm_snref: str
|
24
|
-
out_param_if: str
|
25
29
|
|
26
|
-
|
27
|
-
|
28
|
-
|
30
|
+
# be aware that the checker rules 23 and 110 contradict each other
|
31
|
+
# here: the first specifies that the referenced parameter must be
|
32
|
+
# present in *all* positive responses of the referenced service,
|
33
|
+
# whilst the latter mandates it to be present least one positive
|
34
|
+
# or negative response. What it probably actually wants to say is
|
35
|
+
# that any response that can possibly be received shall exhibit
|
36
|
+
# the referenced parameter.
|
37
|
+
out_param_if_snref: Optional[str]
|
38
|
+
out_param_if_snpathref: Optional[str]
|
29
39
|
|
30
40
|
@staticmethod
|
31
41
|
def from_et(et_element: ElementTree.Element,
|
32
42
|
doc_frags: List[OdxDocFragment]) -> "MatchingParameter":
|
33
43
|
|
34
44
|
expected_value = odxrequire(et_element.findtext("EXPECTED-VALUE"))
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
if out_param_snref_el is not None:
|
41
|
-
|
42
|
-
elif out_param_snpathref_el is not None:
|
43
|
-
|
44
|
-
|
45
|
-
odxraise("Output parameter must not left unspecified")
|
45
|
+
diag_comm_snref = odxrequire(
|
46
|
+
odxrequire(et_element.find("DIAG-COMM-SNREF")).get("SHORT-NAME"))
|
47
|
+
|
48
|
+
out_param_if_snref = None
|
49
|
+
out_param_if_snpathref = None
|
50
|
+
if (out_param_snref_el := et_element.find("OUT-PARAM-IF-SNREF")) is not None:
|
51
|
+
out_param_if_snref = odxrequire(out_param_snref_el.get("SHORT-NAME"))
|
52
|
+
elif (out_param_snpathref_el := et_element.find("OUT-PARAM-IF-SNPATHREF")) is not None:
|
53
|
+
out_param_if_snpathref = odxrequire(out_param_snpathref_el.get("SHORT-NAME-PATH"))
|
54
|
+
else:
|
55
|
+
odxraise("Output parameter must not be left unspecified")
|
46
56
|
|
47
57
|
return MatchingParameter(
|
48
58
|
expected_value=expected_value,
|
49
59
|
diag_comm_snref=diag_comm_snref,
|
50
|
-
|
60
|
+
out_param_if_snref=out_param_if_snref,
|
61
|
+
out_param_if_snpathref=out_param_if_snpathref,
|
51
62
|
)
|
52
63
|
|
53
|
-
def
|
64
|
+
def _build_odxlinks(self) -> Dict[OdxLinkId, Any]:
|
65
|
+
return {}
|
66
|
+
|
67
|
+
def _resolve_odxlinks(self, odxlinks: OdxLinkDatabase) -> None:
|
68
|
+
pass
|
69
|
+
|
70
|
+
def _resolve_snrefs(self, context: SnRefContext) -> None:
|
71
|
+
# note that we do not resolve the SNREF to the diag layer here
|
72
|
+
# because it is a component which changes depending on the
|
73
|
+
# execution context and for variant patterns the execution
|
74
|
+
# context is highly volatile w.r.t. the diag layer in question
|
75
|
+
pass
|
76
|
+
|
77
|
+
def get_ident_service(self, diag_layer: DiagLayer) -> DiagService:
|
78
|
+
return resolve_snref(self.diag_comm_snref, diag_layer.services, DiagService)
|
79
|
+
|
80
|
+
def matches(self, param_dict: ParameterValueDict) -> bool:
|
54
81
|
"""
|
55
82
|
Returns true iff the provided identification value matches this MatchingParameter's
|
56
83
|
expected value.
|
57
84
|
"""
|
58
|
-
|
85
|
+
|
86
|
+
if self.out_param_if_snref is not None:
|
87
|
+
snpath_chunks = [self.out_param_if_snref]
|
88
|
+
elif self.out_param_if_snpathref is not None:
|
89
|
+
snpath_chunks = self.out_param_if_snpathref.split(".")
|
90
|
+
else:
|
91
|
+
odxraise("no out_param_if specified")
|
92
|
+
return False
|
93
|
+
|
94
|
+
return self.__matches(param_dict, snpath_chunks)
|
95
|
+
|
96
|
+
def __matches(self, param_dict: ParameterValue, snpath_chunks: List[str]) -> bool:
|
97
|
+
if len(snpath_chunks) == 0:
|
98
|
+
parameter_value = param_dict
|
99
|
+
if isinstance(parameter_value, dict):
|
100
|
+
odxraise(f"Parameter must not be a structure")
|
101
|
+
return False
|
102
|
+
|
103
|
+
if isinstance(parameter_value, float):
|
104
|
+
# allow a slight tolerance if the expected value is
|
105
|
+
# floating point
|
106
|
+
return abs(float(self.expected_value) - parameter_value) < 1e-8
|
107
|
+
elif isinstance(parameter_value, BytesTypes):
|
108
|
+
return parameter_value.hex().upper() == self.expected_value.upper()
|
109
|
+
elif isinstance(parameter_value, DiagnosticTroubleCode):
|
110
|
+
# TODO: what happens if non-numerical DTCs like
|
111
|
+
# "U123456" are specified? Is specifying DTCs even
|
112
|
+
# allowed in variant patterns?
|
113
|
+
return hex(parameter_value.trouble_code).upper() == self.expected_value.upper()
|
114
|
+
else:
|
115
|
+
return self.expected_value == str(parameter_value)
|
116
|
+
|
117
|
+
if not isinstance(param_dict, dict):
|
118
|
+
odxraise(f"Parameter {snpath_chunks[0]} must be a structure")
|
119
|
+
return False
|
120
|
+
|
121
|
+
sub_value = param_dict.get(snpath_chunks[0])
|
122
|
+
if sub_value is None:
|
123
|
+
return False
|
124
|
+
|
125
|
+
if isinstance(sub_value, tuple) and len(sub_value) == 2:
|
126
|
+
# table struct parameter
|
127
|
+
sub_value = sub_value[1]
|
128
|
+
|
129
|
+
if isinstance(sub_value, list):
|
130
|
+
# the spec mandates that if any item of a field matches,
|
131
|
+
# the parameter as a whole matches
|
132
|
+
for x in sub_value:
|
133
|
+
if self.__matches(x, snpath_chunks[1:]):
|
134
|
+
return True
|
135
|
+
|
136
|
+
return False
|
137
|
+
|
138
|
+
return self.__matches(cast(ParameterValue, sub_value), snpath_chunks[1:])
|