odxtools 5.3.1__py3-none-any.whl → 6.0.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/__init__.py +1 -1
- odxtools/basicstructure.py +76 -91
- odxtools/cli/_parser_utils.py +12 -9
- odxtools/cli/_print_utils.py +7 -7
- odxtools/cli/browse.py +94 -73
- odxtools/cli/find.py +42 -59
- odxtools/cli/list.py +21 -17
- odxtools/cli/snoop.py +19 -18
- odxtools/communicationparameterref.py +6 -3
- odxtools/companydocinfo.py +2 -2
- odxtools/companyrevisioninfo.py +1 -1
- odxtools/comparamsubset.py +6 -6
- odxtools/complexcomparam.py +1 -1
- odxtools/compumethods/compumethod.py +6 -9
- odxtools/compumethods/createanycompumethod.py +11 -9
- odxtools/compumethods/identicalcompumethod.py +5 -4
- odxtools/compumethods/limit.py +9 -9
- odxtools/compumethods/linearcompumethod.py +25 -17
- odxtools/compumethods/scalelinearcompumethod.py +6 -5
- odxtools/compumethods/tabintpcompumethod.py +30 -9
- odxtools/compumethods/texttablecompumethod.py +22 -24
- odxtools/database.py +5 -5
- odxtools/dataobjectproperty.py +10 -23
- odxtools/decodestate.py +1 -1
- odxtools/determinenumberofitems.py +37 -8
- odxtools/diagcodedtype.py +14 -9
- odxtools/diagdatadictionaryspec.py +60 -37
- odxtools/diaglayer.py +30 -21
- odxtools/diaglayercontainer.py +40 -40
- odxtools/diaglayerraw.py +92 -63
- odxtools/diagnostictroublecode.py +2 -2
- odxtools/diagservice.py +53 -35
- odxtools/docrevision.py +1 -1
- odxtools/dopbase.py +14 -3
- odxtools/dtcdop.py +15 -9
- odxtools/dynamiclengthfield.py +6 -4
- odxtools/endofpdufield.py +22 -23
- odxtools/environmentdata.py +2 -5
- odxtools/environmentdatadescription.py +6 -4
- odxtools/field.py +3 -8
- odxtools/isotp_state_machine.py +52 -38
- odxtools/leadinglengthinfotype.py +9 -7
- odxtools/load_file.py +2 -1
- odxtools/load_odx_d_file.py +2 -5
- odxtools/load_pdx_file.py +2 -6
- odxtools/message.py +11 -3
- odxtools/minmaxlengthtype.py +107 -78
- odxtools/modification.py +2 -2
- odxtools/multiplexer.py +23 -21
- odxtools/multiplexerswitchkey.py +37 -8
- odxtools/nameditemlist.py +59 -58
- odxtools/odxlink.py +4 -2
- odxtools/odxtypes.py +4 -3
- odxtools/parameterinfo.py +6 -6
- odxtools/parameters/codedconstparameter.py +15 -25
- odxtools/parameters/createanyparameter.py +1 -1
- odxtools/parameters/dynamicparameter.py +6 -5
- odxtools/parameters/lengthkeyparameter.py +2 -1
- odxtools/parameters/matchingrequestparameter.py +8 -11
- odxtools/parameters/nrcconstparameter.py +11 -21
- odxtools/parameters/parameter.py +4 -18
- odxtools/parameters/parameterwithdop.py +14 -29
- odxtools/parameters/physicalconstantparameter.py +7 -9
- odxtools/parameters/reservedparameter.py +17 -38
- odxtools/parameters/systemparameter.py +6 -5
- odxtools/parameters/tableentryparameter.py +6 -5
- odxtools/parameters/tablekeyparameter.py +8 -15
- odxtools/parameters/tablestructparameter.py +11 -12
- odxtools/parameters/valueparameter.py +9 -24
- odxtools/paramlengthinfotype.py +11 -9
- odxtools/physicaldimension.py +1 -1
- odxtools/physicaltype.py +2 -2
- odxtools/response.py +7 -3
- odxtools/singleecujob.py +48 -22
- odxtools/standardlengthtype.py +11 -6
- odxtools/uds.py +1 -1
- odxtools/unit.py +5 -5
- odxtools/unitgroup.py +1 -1
- odxtools/unitspec.py +2 -2
- odxtools/version.py +13 -3
- odxtools/write_pdx_file.py +7 -4
- {odxtools-5.3.1.dist-info → odxtools-6.0.1.dist-info}/METADATA +7 -5
- {odxtools-5.3.1.dist-info → odxtools-6.0.1.dist-info}/RECORD +87 -88
- odxtools/positioneddataobjectproperty.py +0 -74
- {odxtools-5.3.1.dist-info → odxtools-6.0.1.dist-info}/LICENSE +0 -0
- {odxtools-5.3.1.dist-info → odxtools-6.0.1.dist-info}/WHEEL +0 -0
- {odxtools-5.3.1.dist-info → odxtools-6.0.1.dist-info}/entry_points.txt +0 -0
- {odxtools-5.3.1.dist-info → odxtools-6.0.1.dist-info}/top_level.txt +0 -0
@@ -111,6 +111,7 @@ class CommunicationParameterRef:
|
|
111
111
|
f"'{self.short_name}' are not specified "
|
112
112
|
f"correctly.",
|
113
113
|
OdxWarning,
|
114
|
+
stacklevel=1,
|
114
115
|
)
|
115
116
|
return None
|
116
117
|
|
@@ -122,6 +123,7 @@ class CommunicationParameterRef:
|
|
122
123
|
f"Communication parameter '{self.short_name}' "
|
123
124
|
f"does not specify a '{subparam_name}' sub-parameter.",
|
124
125
|
OdxWarning,
|
126
|
+
stacklevel=1,
|
125
127
|
)
|
126
128
|
return None
|
127
129
|
|
@@ -134,9 +136,10 @@ class CommunicationParameterRef:
|
|
134
136
|
return result
|
135
137
|
|
136
138
|
@property
|
137
|
-
def short_name(self):
|
139
|
+
def short_name(self) -> str:
|
138
140
|
if self.comparam:
|
139
141
|
return self.comparam.short_name
|
140
|
-
|
141
|
-
#
|
142
|
+
|
143
|
+
# ODXLINK IDs allow dots and hyphens, but short names do not.
|
144
|
+
# (This should not happen anyway in a correct PDX...)
|
142
145
|
return self.id_ref.ref_id.replace(".", "__").replace("-", "_")
|
odxtools/companydocinfo.py
CHANGED
@@ -54,7 +54,7 @@ class CompanyDocInfo:
|
|
54
54
|
|
55
55
|
return result
|
56
56
|
|
57
|
-
def _resolve_odxlinks(self, odxlinks: OdxLinkDatabase):
|
57
|
+
def _resolve_odxlinks(self, odxlinks: OdxLinkDatabase) -> None:
|
58
58
|
self._company_data = odxlinks.resolve(self.company_data_ref, CompanyData)
|
59
59
|
|
60
60
|
self._team_member: Optional[TeamMember] = None
|
@@ -64,6 +64,6 @@ class CompanyDocInfo:
|
|
64
64
|
for sdg in self.sdgs:
|
65
65
|
sdg._resolve_odxlinks(odxlinks)
|
66
66
|
|
67
|
-
def _resolve_snrefs(self, diag_layer: "DiagLayer"):
|
67
|
+
def _resolve_snrefs(self, diag_layer: "DiagLayer") -> None:
|
68
68
|
for sdg in self.sdgs:
|
69
69
|
sdg._resolve_snrefs(diag_layer)
|
odxtools/companyrevisioninfo.py
CHANGED
@@ -36,7 +36,7 @@ class CompanyRevisionInfo:
|
|
36
36
|
def _build_odxlinks(self) -> Dict[OdxLinkId, Any]:
|
37
37
|
return {}
|
38
38
|
|
39
|
-
def _resolve_odxlinks(self, odxlinks: OdxLinkDatabase):
|
39
|
+
def _resolve_odxlinks(self, odxlinks: OdxLinkDatabase) -> None:
|
40
40
|
self._company_data = odxlinks.resolve(self.company_data_ref, CompanyData)
|
41
41
|
|
42
42
|
def _resolve_snrefs(self, diag_layer: "DiagLayer") -> None:
|
odxtools/comparamsubset.py
CHANGED
@@ -87,8 +87,8 @@ class ComparamSubset(IdentifiableElement):
|
|
87
87
|
for comparam in self.comparams:
|
88
88
|
odxlinks.update(comparam._build_odxlinks())
|
89
89
|
|
90
|
-
for
|
91
|
-
odxlinks.update(
|
90
|
+
for ccomparam in self.complex_comparams:
|
91
|
+
odxlinks.update(ccomparam._build_odxlinks())
|
92
92
|
|
93
93
|
if self.unit_spec:
|
94
94
|
odxlinks.update(self.unit_spec._build_odxlinks())
|
@@ -112,8 +112,8 @@ class ComparamSubset(IdentifiableElement):
|
|
112
112
|
for comparam in self.comparams:
|
113
113
|
comparam._resolve_odxlinks(odxlinks)
|
114
114
|
|
115
|
-
for
|
116
|
-
|
115
|
+
for ccomparam in self.complex_comparams:
|
116
|
+
ccomparam._resolve_odxlinks(odxlinks)
|
117
117
|
|
118
118
|
if self.unit_spec:
|
119
119
|
self.unit_spec._resolve_odxlinks(odxlinks)
|
@@ -135,8 +135,8 @@ class ComparamSubset(IdentifiableElement):
|
|
135
135
|
for comparam in self.comparams:
|
136
136
|
comparam._resolve_snrefs(diag_layer)
|
137
137
|
|
138
|
-
for
|
139
|
-
|
138
|
+
for ccomparam in self.complex_comparams:
|
139
|
+
ccomparam._resolve_snrefs(diag_layer)
|
140
140
|
|
141
141
|
if self.unit_spec:
|
142
142
|
self.unit_spec._resolve_snrefs(diag_layer)
|
odxtools/complexcomparam.py
CHANGED
@@ -32,7 +32,7 @@ class ComplexComparam(BaseComparam):
|
|
32
32
|
|
33
33
|
@property
|
34
34
|
def allow_multiple_values(self) -> bool:
|
35
|
-
return self.allow_multiple_values_raw
|
35
|
+
return self.allow_multiple_values_raw is True
|
36
36
|
|
37
37
|
@staticmethod
|
38
38
|
def from_et(et_element: ElementTree.Element,
|
@@ -1,9 +1,9 @@
|
|
1
1
|
# SPDX-License-Identifier: MIT
|
2
2
|
import abc
|
3
3
|
from dataclasses import dataclass
|
4
|
-
from typing import Literal
|
4
|
+
from typing import Literal
|
5
5
|
|
6
|
-
from ..odxtypes import DataType
|
6
|
+
from ..odxtypes import AtomicOdxType, DataType
|
7
7
|
|
8
8
|
CompuMethodCategory = Literal[
|
9
9
|
"IDENTICAL",
|
@@ -24,17 +24,14 @@ class CompuMethod(abc.ABC):
|
|
24
24
|
def category(self) -> CompuMethodCategory:
|
25
25
|
pass
|
26
26
|
|
27
|
-
def convert_physical_to_internal(self, physical_value):
|
27
|
+
def convert_physical_to_internal(self, physical_value: AtomicOdxType) -> AtomicOdxType:
|
28
28
|
raise NotImplementedError()
|
29
29
|
|
30
|
-
def convert_internal_to_physical(self, internal_value) ->
|
30
|
+
def convert_internal_to_physical(self, internal_value: AtomicOdxType) -> AtomicOdxType:
|
31
31
|
raise NotImplementedError()
|
32
32
|
|
33
|
-
def is_valid_physical_value(self, physical_value):
|
33
|
+
def is_valid_physical_value(self, physical_value: AtomicOdxType) -> bool:
|
34
34
|
raise NotImplementedError()
|
35
35
|
|
36
|
-
def is_valid_internal_value(self, internal_value):
|
36
|
+
def is_valid_internal_value(self, internal_value: AtomicOdxType) -> bool:
|
37
37
|
raise NotImplementedError()
|
38
|
-
|
39
|
-
def get_valid_physical_values(self):
|
40
|
-
return None
|
@@ -19,12 +19,12 @@ from .texttablecompumethod import TexttableCompuMethod
|
|
19
19
|
|
20
20
|
def _parse_compu_scale_to_linear_compu_method(
|
21
21
|
*,
|
22
|
-
scale_element,
|
22
|
+
scale_element: ElementTree.Element,
|
23
23
|
internal_type: DataType,
|
24
24
|
physical_type: DataType,
|
25
|
-
is_scale_linear=False,
|
26
|
-
**kwargs,
|
27
|
-
):
|
25
|
+
is_scale_linear: bool = False,
|
26
|
+
**kwargs: Any,
|
27
|
+
) -> LinearCompuMethod:
|
28
28
|
odxassert(physical_type in [
|
29
29
|
DataType.A_FLOAT32,
|
30
30
|
DataType.A_FLOAT64,
|
@@ -47,18 +47,20 @@ def _parse_compu_scale_to_linear_compu_method(
|
|
47
47
|
kwargs["internal_type"] = internal_type
|
48
48
|
kwargs["physical_type"] = physical_type
|
49
49
|
|
50
|
-
coeffs = scale_element.find("COMPU-RATIONAL-COEFFS")
|
50
|
+
coeffs = odxrequire(scale_element.find("COMPU-RATIONAL-COEFFS"))
|
51
51
|
nums = coeffs.iterfind("COMPU-NUMERATOR/V")
|
52
52
|
|
53
|
-
offset = computation_python_type(next(nums).text)
|
53
|
+
offset = computation_python_type(odxrequire(next(nums).text))
|
54
54
|
factor_el = next(nums, None)
|
55
|
-
factor = computation_python_type(factor_el.text if factor_el is not None else "0")
|
55
|
+
factor = computation_python_type(odxrequire(factor_el.text) if factor_el is not None else "0")
|
56
56
|
denominator = 1.0
|
57
57
|
if (string := coeffs.findtext("COMPU-DENOMINATOR/V")) is not None:
|
58
58
|
denominator = float(string)
|
59
59
|
if denominator == 0:
|
60
|
-
warnings.warn(
|
61
|
-
|
60
|
+
warnings.warn(
|
61
|
+
"CompuMethod: A denominator of zero will lead to divisions by zero.",
|
62
|
+
OdxWarning,
|
63
|
+
stacklevel=1)
|
62
64
|
# Read lower limit
|
63
65
|
internal_lower_limit = Limit.from_et(
|
64
66
|
scale_element.find("LOWER-LIMIT"),
|
@@ -1,6 +1,7 @@
|
|
1
1
|
# SPDX-License-Identifier: MIT
|
2
2
|
from dataclasses import dataclass
|
3
3
|
|
4
|
+
from ..odxtypes import AtomicOdxType
|
4
5
|
from .compumethod import CompuMethod, CompuMethodCategory
|
5
6
|
|
6
7
|
|
@@ -11,14 +12,14 @@ class IdenticalCompuMethod(CompuMethod):
|
|
11
12
|
def category(self) -> CompuMethodCategory:
|
12
13
|
return "IDENTICAL"
|
13
14
|
|
14
|
-
def convert_physical_to_internal(self, physical_value):
|
15
|
+
def convert_physical_to_internal(self, physical_value: AtomicOdxType) -> AtomicOdxType:
|
15
16
|
return physical_value
|
16
17
|
|
17
|
-
def convert_internal_to_physical(self, internal_value):
|
18
|
+
def convert_internal_to_physical(self, internal_value: AtomicOdxType) -> AtomicOdxType:
|
18
19
|
return internal_value
|
19
20
|
|
20
|
-
def is_valid_physical_value(self, physical_value):
|
21
|
+
def is_valid_physical_value(self, physical_value: AtomicOdxType) -> bool:
|
21
22
|
return self.physical_type.isinstance(physical_value)
|
22
23
|
|
23
|
-
def is_valid_internal_value(self, internal_value):
|
24
|
+
def is_valid_internal_value(self, internal_value: AtomicOdxType) -> bool:
|
24
25
|
return self.internal_type.isinstance(internal_value)
|
odxtools/compumethods/limit.py
CHANGED
@@ -1,11 +1,11 @@
|
|
1
1
|
# SPDX-License-Identifier: MIT
|
2
2
|
from dataclasses import dataclass
|
3
3
|
from enum import Enum
|
4
|
-
from typing import Optional
|
4
|
+
from typing import Optional
|
5
5
|
from xml.etree import ElementTree
|
6
6
|
|
7
7
|
from ..exceptions import odxassert, odxraise, odxrequire
|
8
|
-
from ..odxtypes import DataType
|
8
|
+
from ..odxtypes import AtomicOdxType, DataType
|
9
9
|
|
10
10
|
|
11
11
|
class IntervalType(Enum):
|
@@ -16,7 +16,7 @@ class IntervalType(Enum):
|
|
16
16
|
|
17
17
|
@dataclass
|
18
18
|
class Limit:
|
19
|
-
value:
|
19
|
+
value: AtomicOdxType
|
20
20
|
interval_type: IntervalType = IntervalType.CLOSED
|
21
21
|
|
22
22
|
def __post_init__(self) -> None:
|
@@ -53,7 +53,7 @@ class Limit:
|
|
53
53
|
else:
|
54
54
|
return Limit(internal_type.from_string(odxrequire(et_element.text)), interval_type)
|
55
55
|
|
56
|
-
def complies_to_upper(self, value):
|
56
|
+
def complies_to_upper(self, value: AtomicOdxType) -> bool:
|
57
57
|
"""Checks if the value is in the range w.r.t. the upper limit.
|
58
58
|
|
59
59
|
* If the interval type is closed, return `value <= limit.value`.
|
@@ -61,13 +61,13 @@ class Limit:
|
|
61
61
|
* If the interval type is infinite, return `True`.
|
62
62
|
"""
|
63
63
|
if self.interval_type == IntervalType.CLOSED:
|
64
|
-
return value <= self.value
|
64
|
+
return value <= self.value # type: ignore[operator]
|
65
65
|
elif self.interval_type == IntervalType.OPEN:
|
66
|
-
return value < self.value
|
66
|
+
return value < self.value # type: ignore[operator]
|
67
67
|
elif self.interval_type == IntervalType.INFINITE:
|
68
68
|
return True
|
69
69
|
|
70
|
-
def complies_to_lower(self, value):
|
70
|
+
def complies_to_lower(self, value: AtomicOdxType) -> bool:
|
71
71
|
"""Checks if the value is in the range w.r.t. the lower limit.
|
72
72
|
|
73
73
|
* If the interval type is closed, return `limit.value <= value`.
|
@@ -75,8 +75,8 @@ class Limit:
|
|
75
75
|
* If the interval type is infinite, return `True`.
|
76
76
|
"""
|
77
77
|
if self.interval_type == IntervalType.CLOSED:
|
78
|
-
return self.value <= value
|
78
|
+
return self.value <= value # type: ignore[operator]
|
79
79
|
elif self.interval_type == IntervalType.OPEN:
|
80
|
-
return self.value < value
|
80
|
+
return self.value < value # type: ignore[operator]
|
81
81
|
elif self.interval_type == IntervalType.INFINITE:
|
82
82
|
return True
|
@@ -1,9 +1,9 @@
|
|
1
1
|
# SPDX-License-Identifier: MIT
|
2
2
|
from dataclasses import dataclass
|
3
|
-
from typing import
|
3
|
+
from typing import cast
|
4
4
|
|
5
|
-
from ..exceptions import odxassert
|
6
|
-
from ..odxtypes import DataType
|
5
|
+
from ..exceptions import DecodeError, EncodeError, odxassert
|
6
|
+
from ..odxtypes import AtomicOdxType, DataType
|
7
7
|
from .compumethod import CompuMethod, CompuMethodCategory
|
8
8
|
from .limit import IntervalType, Limit
|
9
9
|
|
@@ -73,20 +73,20 @@ class LinearCompuMethod(CompuMethod):
|
|
73
73
|
return "LINEAR"
|
74
74
|
|
75
75
|
@property
|
76
|
-
def physical_lower_limit(self):
|
76
|
+
def physical_lower_limit(self) -> Limit:
|
77
77
|
return self._physical_lower_limit
|
78
78
|
|
79
79
|
@property
|
80
|
-
def physical_upper_limit(self):
|
80
|
+
def physical_upper_limit(self) -> Limit:
|
81
81
|
return self._physical_upper_limit
|
82
82
|
|
83
|
-
def __compute_physical_limits(self):
|
83
|
+
def __compute_physical_limits(self) -> None:
|
84
84
|
"""Computes the physical limits and stores them in the properties
|
85
85
|
self._physical_lower_limit and self._physical_upper_limit.
|
86
86
|
This method is only called during the initialization of a LinearCompuMethod.
|
87
87
|
"""
|
88
88
|
|
89
|
-
def convert_to_limit_to_physical(limit: Limit, is_upper_limit: bool):
|
89
|
+
def convert_to_limit_to_physical(limit: Limit, is_upper_limit: bool) -> Limit:
|
90
90
|
"""Helper method
|
91
91
|
|
92
92
|
Parameters:
|
@@ -127,10 +127,14 @@ class LinearCompuMethod(CompuMethod):
|
|
127
127
|
if self.physical_type == DataType.A_UINT32:
|
128
128
|
# If the data type is unsigned, the physical lower limit should be at least 0.
|
129
129
|
if (self._physical_lower_limit.interval_type == IntervalType.INFINITE or
|
130
|
-
self._physical_lower_limit.value < 0):
|
130
|
+
cast(float, self._physical_lower_limit.value) < 0):
|
131
131
|
self._physical_lower_limit = Limit(value=0, interval_type=IntervalType.CLOSED)
|
132
132
|
|
133
|
-
def _convert_internal_to_physical(self, internal_value):
|
133
|
+
def _convert_internal_to_physical(self, internal_value: AtomicOdxType) -> AtomicOdxType:
|
134
|
+
if not isinstance(internal_value, (int, float)):
|
135
|
+
raise DecodeError("The type of internal values of linear compumethods must "
|
136
|
+
"either int or float")
|
137
|
+
|
134
138
|
if self.denominator is None:
|
135
139
|
result = self.offset + self.factor * internal_value
|
136
140
|
else:
|
@@ -143,11 +147,15 @@ class LinearCompuMethod(CompuMethod):
|
|
143
147
|
result = round(result)
|
144
148
|
return self.physical_type.make_from(result)
|
145
149
|
|
146
|
-
def convert_internal_to_physical(self, internal_value) ->
|
150
|
+
def convert_internal_to_physical(self, internal_value: AtomicOdxType) -> AtomicOdxType:
|
147
151
|
odxassert(self.is_valid_internal_value(internal_value))
|
148
152
|
return self._convert_internal_to_physical(internal_value)
|
149
153
|
|
150
|
-
def convert_physical_to_internal(self, physical_value):
|
154
|
+
def convert_physical_to_internal(self, physical_value: AtomicOdxType) -> AtomicOdxType:
|
155
|
+
if not isinstance(physical_value, (int, float)):
|
156
|
+
raise EncodeError("The type of physical values of linear compumethods must "
|
157
|
+
"either int or float")
|
158
|
+
|
151
159
|
odxassert(
|
152
160
|
self.is_valid_physical_value(physical_value),
|
153
161
|
f"physical value {physical_value} of type {type(physical_value)} "
|
@@ -165,12 +173,12 @@ class LinearCompuMethod(CompuMethod):
|
|
165
173
|
result = round(result)
|
166
174
|
return self.internal_type.make_from(result)
|
167
175
|
|
168
|
-
def is_valid_physical_value(self, physical_value):
|
176
|
+
def is_valid_physical_value(self, physical_value: AtomicOdxType) -> bool:
|
169
177
|
# Do type checks
|
170
178
|
expected_type = self.physical_type.as_python_type()
|
171
|
-
if expected_type == float and
|
179
|
+
if expected_type == float and not isinstance(physical_value, (int, float)):
|
172
180
|
return False
|
173
|
-
elif expected_type != float and
|
181
|
+
elif expected_type != float and not isinstance(physical_value, expected_type):
|
174
182
|
return False
|
175
183
|
|
176
184
|
# Compare to the limits
|
@@ -180,11 +188,11 @@ class LinearCompuMethod(CompuMethod):
|
|
180
188
|
return False
|
181
189
|
return True
|
182
190
|
|
183
|
-
def is_valid_internal_value(self, internal_value):
|
191
|
+
def is_valid_internal_value(self, internal_value: AtomicOdxType) -> bool:
|
184
192
|
expected_type = self.internal_type.as_python_type()
|
185
|
-
if expected_type == float and
|
193
|
+
if expected_type == float and not isinstance(internal_value, (int, float)):
|
186
194
|
return False
|
187
|
-
elif expected_type != float and
|
195
|
+
elif expected_type != float and not isinstance(internal_value, expected_type):
|
188
196
|
return False
|
189
197
|
|
190
198
|
if not self.internal_lower_limit.complies_to_lower(internal_value):
|
@@ -3,6 +3,7 @@ from dataclasses import dataclass
|
|
3
3
|
from typing import List
|
4
4
|
|
5
5
|
from ..exceptions import odxassert
|
6
|
+
from ..odxtypes import AtomicOdxType
|
6
7
|
from .compumethod import CompuMethod, CompuMethodCategory
|
7
8
|
from .linearcompumethod import LinearCompuMethod
|
8
9
|
|
@@ -15,24 +16,24 @@ class ScaleLinearCompuMethod(CompuMethod):
|
|
15
16
|
def category(self) -> CompuMethodCategory:
|
16
17
|
return "SCALE-LINEAR"
|
17
18
|
|
18
|
-
def convert_physical_to_internal(self, physical_value):
|
19
|
+
def convert_physical_to_internal(self, physical_value: AtomicOdxType) -> AtomicOdxType:
|
19
20
|
odxassert(
|
20
21
|
self.is_valid_physical_value(physical_value),
|
21
|
-
f"cannot convert the invalid physical value {physical_value} "
|
22
|
+
f"cannot convert the invalid physical value {physical_value!r} "
|
22
23
|
f"of type {type(physical_value)}")
|
23
24
|
lin_method = next(
|
24
25
|
scale for scale in self.linear_methods if scale.is_valid_physical_value(physical_value))
|
25
26
|
return lin_method.convert_physical_to_internal(physical_value)
|
26
27
|
|
27
|
-
def convert_internal_to_physical(self, internal_value):
|
28
|
+
def convert_internal_to_physical(self, internal_value: AtomicOdxType) -> AtomicOdxType:
|
28
29
|
lin_method = next(
|
29
30
|
scale for scale in self.linear_methods if scale.is_valid_internal_value(internal_value))
|
30
31
|
return lin_method.convert_internal_to_physical(internal_value)
|
31
32
|
|
32
|
-
def is_valid_physical_value(self, physical_value):
|
33
|
+
def is_valid_physical_value(self, physical_value: AtomicOdxType) -> bool:
|
33
34
|
return any(
|
34
35
|
True for scale in self.linear_methods if scale.is_valid_physical_value(physical_value))
|
35
36
|
|
36
|
-
def is_valid_internal_value(self, internal_value):
|
37
|
+
def is_valid_internal_value(self, internal_value: AtomicOdxType) -> bool:
|
37
38
|
return any(
|
38
39
|
True for scale in self.linear_methods if scale.is_valid_internal_value(internal_value))
|
@@ -3,7 +3,7 @@ from dataclasses import dataclass
|
|
3
3
|
from typing import List, Tuple, Union
|
4
4
|
|
5
5
|
from ..exceptions import DecodeError, EncodeError, odxassert, odxraise
|
6
|
-
from ..odxtypes import DataType
|
6
|
+
from ..odxtypes import AtomicOdxType, DataType
|
7
7
|
from .compumethod import CompuMethod, CompuMethodCategory
|
8
8
|
from .limit import IntervalType, Limit
|
9
9
|
|
@@ -115,34 +115,55 @@ class TabIntpCompuMethod(CompuMethod):
|
|
115
115
|
|
116
116
|
return None
|
117
117
|
|
118
|
-
def convert_physical_to_internal(self, physical_value:
|
118
|
+
def convert_physical_to_internal(self, physical_value: AtomicOdxType) -> AtomicOdxType:
|
119
|
+
if not isinstance(physical_value, (int, float)):
|
120
|
+
raise EncodeError("The type of values of tab-intp compumethods must "
|
121
|
+
"either int or float")
|
122
|
+
|
119
123
|
reference_points = list(zip(self.physical_points, self.internal_points))
|
120
|
-
|
124
|
+
odxassert(
|
125
|
+
isinstance(physical_value, (int, float)),
|
126
|
+
"Only integers and floats can be piecewise linearly interpolated")
|
127
|
+
result = self._piecewise_linear_interpolate(
|
128
|
+
physical_value, # type: ignore[arg-type]
|
129
|
+
reference_points)
|
121
130
|
|
122
131
|
if result is None:
|
123
|
-
raise EncodeError(f"Internal value {physical_value} must be inside the range"
|
132
|
+
raise EncodeError(f"Internal value {physical_value!r} must be inside the range"
|
124
133
|
f" [{min(self.physical_points)}, {max(self.physical_points)}]")
|
125
134
|
res = self.internal_type.make_from(result)
|
126
135
|
if not isinstance(res, (int, float)):
|
127
136
|
odxraise()
|
128
137
|
return res
|
129
138
|
|
130
|
-
def convert_internal_to_physical(self, internal_value:
|
139
|
+
def convert_internal_to_physical(self, internal_value: AtomicOdxType) -> AtomicOdxType:
|
140
|
+
if not isinstance(internal_value, (int, float)):
|
141
|
+
raise EncodeError("The internal type of values of tab-intp compumethods must "
|
142
|
+
"either int or float")
|
143
|
+
|
131
144
|
reference_points = list(zip(self.internal_points, self.physical_points))
|
132
|
-
result = self._piecewise_linear_interpolate(
|
145
|
+
result = self._piecewise_linear_interpolate(
|
146
|
+
internal_value, # type: ignore[arg-type]
|
147
|
+
reference_points)
|
133
148
|
|
134
149
|
if result is None:
|
135
|
-
raise DecodeError(f"Internal value {internal_value} must be inside the range"
|
150
|
+
raise DecodeError(f"Internal value {internal_value!r} must be inside the range"
|
136
151
|
f" [{min(self.internal_points)}, {max(self.internal_points)}]")
|
137
152
|
res = self.physical_type.make_from(result)
|
138
153
|
if not isinstance(res, (int, float)):
|
139
154
|
odxraise()
|
140
155
|
return res
|
141
156
|
|
142
|
-
def is_valid_physical_value(self, physical_value:
|
157
|
+
def is_valid_physical_value(self, physical_value: AtomicOdxType) -> bool:
|
158
|
+
if not isinstance(physical_value, (int, float)):
|
159
|
+
return False
|
160
|
+
|
143
161
|
return min(self.physical_points) <= physical_value and physical_value <= max(
|
144
162
|
self.physical_points)
|
145
163
|
|
146
|
-
def is_valid_internal_value(self, internal_value:
|
164
|
+
def is_valid_internal_value(self, internal_value: AtomicOdxType) -> bool:
|
165
|
+
if not isinstance(internal_value, (int, float)):
|
166
|
+
return False
|
167
|
+
|
147
168
|
return min(self.internal_points) <= internal_value and internal_value <= max(
|
148
169
|
self.internal_points)
|
@@ -3,7 +3,7 @@ from dataclasses import dataclass
|
|
3
3
|
from typing import List, Optional
|
4
4
|
|
5
5
|
from ..exceptions import DecodeError, EncodeError, odxassert
|
6
|
-
from ..odxtypes import DataType
|
6
|
+
from ..odxtypes import AtomicOdxType, DataType
|
7
7
|
from .compumethod import CompuMethod, CompuMethodCategory
|
8
8
|
from .compuscale import CompuScale
|
9
9
|
|
@@ -28,25 +28,26 @@ class TexttableCompuMethod(CompuMethod):
|
|
28
28
|
def category(self) -> CompuMethodCategory:
|
29
29
|
return "TEXTTABLE"
|
30
30
|
|
31
|
-
def
|
31
|
+
def get_scales(self) -> List[CompuScale]:
|
32
32
|
scales = list(self.internal_to_phys)
|
33
33
|
if self.compu_default_value:
|
34
34
|
# Default is last, since it's a fallback
|
35
35
|
scales.append(self.compu_default_value)
|
36
36
|
return scales
|
37
37
|
|
38
|
-
def convert_physical_to_internal(self, physical_value):
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
raise EncodeError(f"Texttable compu method could not encode '{physical_value}'.")
|
38
|
+
def convert_physical_to_internal(self, physical_value: AtomicOdxType) -> AtomicOdxType:
|
39
|
+
matching_scales = [x for x in self.get_scales() if x.compu_const == physical_value]
|
40
|
+
for scale in matching_scales:
|
41
|
+
if scale.compu_inverse_value is not None:
|
42
|
+
return scale.compu_inverse_value
|
43
|
+
elif scale.lower_limit is not None:
|
44
|
+
return scale.lower_limit.value
|
45
|
+
elif scale.upper_limit is not None:
|
46
|
+
return scale.upper_limit.value
|
48
47
|
|
49
|
-
|
48
|
+
raise EncodeError(f"Texttable compu method could not encode '{physical_value!r}'.")
|
49
|
+
|
50
|
+
def __is_internal_in_scale(self, internal_value: AtomicOdxType, scale: CompuScale) -> bool:
|
50
51
|
if scale == self.compu_default_value:
|
51
52
|
return True
|
52
53
|
if scale.lower_limit is not None and not scale.lower_limit.complies_to_lower(
|
@@ -60,23 +61,20 @@ class TexttableCompuMethod(CompuMethod):
|
|
60
61
|
# value complies to the defined limits
|
61
62
|
return True
|
62
63
|
|
63
|
-
def convert_internal_to_physical(self, internal_value):
|
64
|
+
def convert_internal_to_physical(self, internal_value: AtomicOdxType) -> AtomicOdxType:
|
64
65
|
scale = next(
|
65
66
|
filter(
|
66
67
|
lambda scale: self.__is_internal_in_scale(internal_value, scale),
|
67
|
-
self.
|
68
|
+
self.get_scales(),
|
68
69
|
), None)
|
69
|
-
if scale is None:
|
70
|
+
if scale is None or scale.compu_const is None:
|
70
71
|
raise DecodeError(
|
71
|
-
f"Texttable compu method could not decode {internal_value} to string.")
|
72
|
+
f"Texttable compu method could not decode {internal_value!r} to string.")
|
72
73
|
return scale.compu_const
|
73
74
|
|
74
|
-
def is_valid_physical_value(self, physical_value):
|
75
|
-
return physical_value in self.
|
75
|
+
def is_valid_physical_value(self, physical_value: AtomicOdxType) -> bool:
|
76
|
+
return any(x.compu_const == physical_value for x in self.get_scales())
|
76
77
|
|
77
|
-
def is_valid_internal_value(self, internal_value):
|
78
|
+
def is_valid_internal_value(self, internal_value: AtomicOdxType) -> bool:
|
78
79
|
return any(
|
79
|
-
self.__is_internal_in_scale(internal_value, scale) for scale in self.
|
80
|
-
|
81
|
-
def get_valid_physical_values(self):
|
82
|
-
return [x.compu_const for x in self._get_scales()]
|
80
|
+
self.__is_internal_in_scale(internal_value, scale) for scale in self.get_scales())
|
odxtools/database.py
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
# SPDX-License-Identifier: MIT
|
2
2
|
from itertools import chain
|
3
3
|
from pathlib import Path
|
4
|
-
from typing import List, Optional
|
4
|
+
from typing import List, Optional, Tuple
|
5
5
|
from xml.etree import ElementTree
|
6
6
|
from zipfile import ZipFile
|
7
7
|
|
@@ -13,7 +13,7 @@ from .nameditemlist import NamedItemList, short_name_as_key
|
|
13
13
|
from .odxlink import OdxLinkDatabase
|
14
14
|
|
15
15
|
|
16
|
-
def version(v: str):
|
16
|
+
def version(v: str) -> Tuple[int, ...]:
|
17
17
|
return tuple(map(int, (v.split("."))))
|
18
18
|
|
19
19
|
|
@@ -135,13 +135,13 @@ class Database:
|
|
135
135
|
return self._diag_layers
|
136
136
|
|
137
137
|
@property
|
138
|
-
def diag_layer_containers(self):
|
138
|
+
def diag_layer_containers(self) -> NamedItemList[DiagLayerContainer]:
|
139
139
|
return self._diag_layer_containers
|
140
140
|
|
141
141
|
@diag_layer_containers.setter
|
142
|
-
def diag_layer_containers(self, value):
|
142
|
+
def diag_layer_containers(self, value: NamedItemList[DiagLayerContainer]) -> None:
|
143
143
|
self._diag_layer_containers = value
|
144
144
|
|
145
145
|
@property
|
146
|
-
def comparam_subsets(self):
|
146
|
+
def comparam_subsets(self) -> NamedItemList[ComparamSubset]:
|
147
147
|
return self._comparam_subsets
|