odxtools 7.1.1__py3-none-any.whl → 7.2.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/__init__.py +6 -4
- odxtools/additionalaudience.py +3 -5
- odxtools/admindata.py +5 -7
- odxtools/audience.py +3 -5
- odxtools/basecomparam.py +3 -5
- odxtools/basicstructure.py +10 -17
- odxtools/cli/_parser_utils.py +1 -1
- odxtools/cli/_print_utils.py +3 -2
- odxtools/cli/compare.py +1 -1
- odxtools/companydata.py +5 -7
- odxtools/companydocinfo.py +7 -8
- odxtools/companyrevisioninfo.py +3 -5
- odxtools/companyspecificinfo.py +8 -9
- odxtools/comparam.py +4 -6
- odxtools/comparaminstance.py +6 -8
- odxtools/comparamspec.py +14 -13
- odxtools/comparamsubset.py +17 -16
- odxtools/complexcomparam.py +5 -7
- odxtools/compumethods/compuconst.py +31 -0
- odxtools/compumethods/compudefaultvalue.py +27 -0
- odxtools/compumethods/compuinternaltophys.py +39 -0
- odxtools/compumethods/compuinversevalue.py +7 -0
- odxtools/compumethods/compumethod.py +67 -12
- odxtools/compumethods/compuphystointernal.py +39 -0
- odxtools/compumethods/compuscale.py +15 -26
- odxtools/compumethods/createanycompumethod.py +14 -160
- odxtools/compumethods/identicalcompumethod.py +31 -6
- odxtools/compumethods/linearcompumethod.py +69 -189
- odxtools/compumethods/linearsegment.py +193 -0
- odxtools/compumethods/scalelinearcompumethod.py +132 -26
- odxtools/compumethods/tabintpcompumethod.py +119 -99
- odxtools/compumethods/texttablecompumethod.py +107 -43
- odxtools/createanydiagcodedtype.py +10 -67
- odxtools/database.py +68 -62
- odxtools/dataobjectproperty.py +10 -19
- odxtools/description.py +47 -0
- odxtools/determinenumberofitems.py +4 -5
- odxtools/diagcodedtype.py +29 -12
- odxtools/diagcomm.py +10 -6
- odxtools/diagdatadictionaryspec.py +20 -21
- odxtools/diaglayer.py +34 -5
- odxtools/diaglayercontainer.py +17 -11
- odxtools/diaglayerraw.py +20 -21
- odxtools/diagnostictroublecode.py +7 -8
- odxtools/diagservice.py +9 -7
- odxtools/docrevision.py +5 -7
- odxtools/dopbase.py +7 -8
- odxtools/dtcdop.py +5 -8
- odxtools/dynamicendmarkerfield.py +22 -9
- odxtools/dynamiclengthfield.py +5 -11
- odxtools/element.py +4 -3
- odxtools/endofpdufield.py +0 -2
- odxtools/environmentdatadescription.py +4 -6
- odxtools/exceptions.py +1 -1
- odxtools/field.py +9 -9
- odxtools/functionalclass.py +3 -5
- odxtools/inputparam.py +3 -5
- odxtools/leadinglengthinfotype.py +15 -2
- odxtools/loadfile.py +64 -0
- odxtools/minmaxlengthtype.py +20 -2
- odxtools/modification.py +3 -5
- odxtools/multiplexer.py +7 -14
- odxtools/multiplexercase.py +4 -6
- odxtools/multiplexerdefaultcase.py +4 -6
- odxtools/multiplexerswitchkey.py +4 -5
- odxtools/negoutputparam.py +3 -5
- odxtools/outputparam.py +3 -5
- odxtools/parameterinfo.py +3 -3
- odxtools/parameters/codedconstparameter.py +2 -14
- odxtools/parameters/lengthkeyparameter.py +3 -17
- odxtools/parameters/nrcconstparameter.py +2 -14
- odxtools/parameters/parameter.py +22 -22
- odxtools/parameters/parameterwithdop.py +6 -8
- odxtools/parameters/physicalconstantparameter.py +5 -8
- odxtools/parameters/reservedparameter.py +4 -3
- odxtools/parameters/tablekeyparameter.py +6 -9
- odxtools/parameters/tablestructparameter.py +6 -8
- odxtools/parameters/valueparameter.py +5 -8
- odxtools/paramlengthinfotype.py +19 -6
- odxtools/parentref.py +15 -1
- odxtools/physicaldimension.py +3 -5
- odxtools/progcode.py +18 -7
- odxtools/protstack.py +3 -5
- odxtools/relateddoc.py +7 -9
- odxtools/request.py +8 -0
- odxtools/response.py +8 -0
- odxtools/scaleconstr.py +3 -3
- odxtools/singleecujob.py +12 -10
- odxtools/snrefcontext.py +29 -0
- odxtools/specialdata.py +3 -5
- odxtools/specialdatagroup.py +5 -7
- odxtools/specialdatagroupcaption.py +3 -6
- odxtools/standardlengthtype.py +27 -2
- odxtools/state.py +3 -5
- odxtools/statechart.py +9 -11
- odxtools/statetransition.py +4 -9
- odxtools/staticfield.py +4 -8
- odxtools/table.py +7 -8
- odxtools/tablerow.py +7 -6
- odxtools/teammember.py +3 -5
- odxtools/templates/comparam-spec.odx-c.xml.jinja2 +2 -5
- odxtools/templates/comparam-subset.odx-cs.xml.jinja2 +2 -5
- odxtools/templates/macros/printCompanyData.xml.jinja2 +2 -5
- odxtools/templates/macros/printComparamRef.xml.jinja2 +5 -12
- odxtools/templates/macros/printCompuMethod.xml.jinja2 +153 -0
- odxtools/templates/macros/printDOP.xml.jinja2 +10 -132
- odxtools/templates/macros/printDescription.xml.jinja2 +18 -0
- odxtools/templates/macros/printElementId.xml.jinja2 +3 -3
- odxtools/templates/macros/printMux.xml.jinja2 +3 -2
- odxtools/templates/macros/printTable.xml.jinja2 +2 -3
- odxtools/unit.py +3 -5
- odxtools/unitgroup.py +3 -5
- odxtools/unitspec.py +9 -10
- odxtools/utils.py +1 -26
- odxtools/version.py +2 -2
- odxtools/{write_pdx_file.py → writepdxfile.py} +19 -10
- odxtools/xdoc.py +3 -5
- {odxtools-7.1.1.dist-info → odxtools-7.2.0.dist-info}/METADATA +1 -1
- odxtools-7.2.0.dist-info/RECORD +192 -0
- {odxtools-7.1.1.dist-info → odxtools-7.2.0.dist-info}/WHEEL +1 -1
- odxtools/createcompanydatas.py +0 -17
- odxtools/createsdgs.py +0 -19
- odxtools/load_file.py +0 -13
- odxtools/load_odx_d_file.py +0 -6
- odxtools/load_pdx_file.py +0 -8
- odxtools-7.1.1.dist-info/RECORD +0 -186
- /odxtools/templates/{index.xml.xml.jinja2 → index.xml.jinja2} +0 -0
- {odxtools-7.1.1.dist-info → odxtools-7.2.0.dist-info}/LICENSE +0 -0
- {odxtools-7.1.1.dist-info → odxtools-7.2.0.dist-info}/entry_points.txt +0 -0
- {odxtools-7.1.1.dist-info → odxtools-7.2.0.dist-info}/top_level.txt +0 -0
@@ -1,107 +1,114 @@
|
|
1
1
|
# SPDX-License-Identifier: MIT
|
2
2
|
from dataclasses import dataclass
|
3
|
-
from typing import List,
|
3
|
+
from typing import List, Union
|
4
|
+
from xml.etree import ElementTree
|
4
5
|
|
5
|
-
from ..exceptions import DecodeError, EncodeError, odxassert, odxraise
|
6
|
+
from ..exceptions import DecodeError, EncodeError, odxassert, odxraise, odxrequire
|
7
|
+
from ..odxlink import OdxDocFragment
|
6
8
|
from ..odxtypes import AtomicOdxType, DataType
|
7
|
-
from
|
9
|
+
from ..utils import dataclass_fields_asdict
|
10
|
+
from .compumethod import CompuCategory, CompuMethod
|
8
11
|
from .limit import IntervalType, Limit
|
9
12
|
|
10
13
|
|
11
14
|
@dataclass
|
12
15
|
class TabIntpCompuMethod(CompuMethod):
|
13
|
-
"""
|
14
|
-
|
15
|
-
|
16
|
-
A `TabIntpCompuMethod` is defined by a set of points. Each point
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
method = TabIntpCompuMethod(
|
32
|
-
internal_type=DataType.A_UINT32,
|
33
|
-
physical_type=DataType.A_UINT32,
|
34
|
-
internal_points=[0, 10, 30],
|
35
|
-
physical_points=[-1, 1, 2]
|
36
|
-
)
|
37
|
-
|
38
|
-
Note that the points are given as two lists. The equivalent odx definition is::
|
39
|
-
|
40
|
-
<COMPU-METHOD>
|
41
|
-
<CATEGORY>TAB-INTP</CATEGORY>
|
42
|
-
<COMPU-INTERNAL-TO-PHYS>
|
43
|
-
<COMPU-SCALES>
|
44
|
-
<COMPU-SCALE>
|
45
|
-
<LOWER-LIMIT INTERVAL-TYPE = "CLOSED">0</LOWER-LIMIT>
|
46
|
-
<COMPU-CONST>
|
47
|
-
<V>-1</V>
|
48
|
-
</COMPU-CONST>
|
49
|
-
</COMPU-SCALE>
|
50
|
-
<COMPU-SCALE>
|
51
|
-
<LOWER-LIMIT INTERVAL-TYPE = "CLOSED">10</LOWER-LIMIT>
|
52
|
-
<COMPU-CONST>
|
53
|
-
<V>1</V>
|
54
|
-
</COMPU-CONST>
|
55
|
-
</COMPU-SCALE>
|
56
|
-
<COMPU-SCALE>
|
57
|
-
<LOWER-LIMIT INTERVAL-TYPE = "CLOSED">30</LOWER-LIMIT>
|
58
|
-
<COMPU-CONST>
|
59
|
-
<V>2</V>
|
60
|
-
</COMPU-CONST>
|
61
|
-
</COMPU-SCALE>
|
62
|
-
</COMPU-SCALES>
|
63
|
-
</COMPU-INTERNAL-TO-PHYS>
|
64
|
-
</COMPU-METHOD>
|
16
|
+
"""A table-based interpolated compu method provides a continuous
|
17
|
+
transfer function based on piecewise linear interpolation.
|
18
|
+
|
19
|
+
A `TabIntpCompuMethod` is defined by a set of points. Each point
|
20
|
+
is an (internal, physical) value pair. When converting from
|
21
|
+
internal to physical or vice-versa, the result is linearly
|
22
|
+
interpolated.
|
23
|
+
|
24
|
+
The function defined by a `TabIntpCompuMethod` is similar to the
|
25
|
+
one of a `ScaleLinearCompuMethod` with the following differences:
|
26
|
+
|
27
|
+
* `TabIntpCompuMethod`s are always continuous whereas
|
28
|
+
`ScaleLinearCompuMethod` might exhibit gaps
|
29
|
+
* `TabIntpCompuMethod`s are always invertible: Even if the linear
|
30
|
+
interpolation is not monotonic, the first matching interval is
|
31
|
+
used.
|
32
|
+
|
33
|
+
For details, refer to ASAM specification MCD-2 D (ODX), section 7.3.6.6.8.
|
65
34
|
|
66
35
|
"""
|
67
36
|
|
68
|
-
|
69
|
-
|
37
|
+
@property
|
38
|
+
def internal_points(self) -> List[Union[float, int]]:
|
39
|
+
return self._internal_points
|
40
|
+
|
41
|
+
@property
|
42
|
+
def physical_points(self) -> List[Union[float, int]]:
|
43
|
+
return self._physical_points
|
44
|
+
|
45
|
+
@property
|
46
|
+
def internal_lower_limit(self) -> Limit:
|
47
|
+
return self._internal_lower_limit
|
48
|
+
|
49
|
+
@property
|
50
|
+
def internal_upper_limit(self) -> Limit:
|
51
|
+
return self._internal_upper_limit
|
52
|
+
|
53
|
+
@property
|
54
|
+
def physical_lower_limit(self) -> Limit:
|
55
|
+
return self._physical_lower_limit
|
56
|
+
|
57
|
+
@property
|
58
|
+
def physical_upper_limit(self) -> Limit:
|
59
|
+
return self._physical_upper_limit
|
60
|
+
|
61
|
+
@staticmethod
|
62
|
+
def compu_method_from_et(et_element: ElementTree.Element, doc_frags: List[OdxDocFragment], *,
|
63
|
+
internal_type: DataType,
|
64
|
+
physical_type: DataType) -> "TabIntpCompuMethod":
|
65
|
+
cm = CompuMethod.compu_method_from_et(
|
66
|
+
et_element, doc_frags, internal_type=internal_type, physical_type=physical_type)
|
67
|
+
kwargs = dataclass_fields_asdict(cm)
|
68
|
+
|
69
|
+
return TabIntpCompuMethod(**kwargs)
|
70
70
|
|
71
71
|
def __post_init__(self) -> None:
|
72
|
+
odxassert(self.category == CompuCategory.TAB_INTP,
|
73
|
+
"TabIntpCompuMethod must exibit TAB-INTP category")
|
74
|
+
|
75
|
+
self._internal_points: List[Union[int, float]] = []
|
76
|
+
self._physical_points: List[Union[int, float]] = []
|
77
|
+
for scale in odxrequire(self.compu_internal_to_phys).compu_scales:
|
78
|
+
internal_point = odxrequire(scale.lower_limit).value
|
79
|
+
physical_point = odxrequire(scale.compu_const).value
|
80
|
+
|
81
|
+
if not isinstance(internal_point, (float, int)):
|
82
|
+
odxraise("The type of values of tab-intp compumethods must "
|
83
|
+
"either int or float")
|
84
|
+
if not isinstance(physical_point, (float, int)):
|
85
|
+
odxraise("The type of values of tab-intp compumethods must "
|
86
|
+
"either int or float")
|
87
|
+
|
88
|
+
self._internal_points.append(internal_point)
|
89
|
+
self._physical_points.append(physical_point)
|
90
|
+
|
72
91
|
self._physical_lower_limit = Limit(
|
73
|
-
value_raw=str(min(self.
|
92
|
+
value_raw=str(min(self._physical_points)),
|
74
93
|
value_type=self.physical_type,
|
75
94
|
interval_type=IntervalType.CLOSED)
|
76
95
|
self._physical_upper_limit = Limit(
|
77
|
-
value_raw=str(max(self.
|
96
|
+
value_raw=str(max(self._physical_points)),
|
78
97
|
value_type=self.physical_type,
|
79
98
|
interval_type=IntervalType.CLOSED)
|
80
99
|
|
81
100
|
self._internal_lower_limit = Limit(
|
82
|
-
value_raw=str(min(self.
|
101
|
+
value_raw=str(min(self._internal_points)),
|
83
102
|
value_type=self.internal_type,
|
84
103
|
interval_type=IntervalType.CLOSED)
|
85
104
|
self._internal_upper_limit = Limit(
|
86
|
-
value_raw=str(max(self.
|
105
|
+
value_raw=str(max(self._internal_points)),
|
87
106
|
value_type=self.internal_type,
|
88
107
|
interval_type=IntervalType.CLOSED)
|
89
108
|
|
90
|
-
self.
|
91
|
-
|
92
|
-
@property
|
93
|
-
def category(self) -> CompuMethodCategory:
|
94
|
-
return "TAB-INTP"
|
95
|
-
|
96
|
-
@property
|
97
|
-
def physical_lower_limit(self) -> Limit:
|
98
|
-
return self._physical_lower_limit
|
109
|
+
self.__assert_validity()
|
99
110
|
|
100
|
-
|
101
|
-
def physical_upper_limit(self) -> Limit:
|
102
|
-
return self._physical_upper_limit
|
103
|
-
|
104
|
-
def _assert_validity(self) -> None:
|
111
|
+
def __assert_validity(self) -> None:
|
105
112
|
odxassert(len(self.internal_points) == len(self.physical_points))
|
106
113
|
|
107
114
|
odxassert(
|
@@ -110,7 +117,7 @@ class TabIntpCompuMethod(CompuMethod):
|
|
110
117
|
DataType.A_UINT32,
|
111
118
|
DataType.A_FLOAT32,
|
112
119
|
DataType.A_FLOAT64,
|
113
|
-
], "Internal data type of
|
120
|
+
], "Internal data type of TAB-INTP compumethod must be one of"
|
114
121
|
" [A_INT32, A_UINT32, A_FLOAT32, A_FLOAT64]")
|
115
122
|
odxassert(
|
116
123
|
self.physical_type in [
|
@@ -118,51 +125,64 @@ class TabIntpCompuMethod(CompuMethod):
|
|
118
125
|
DataType.A_UINT32,
|
119
126
|
DataType.A_FLOAT32,
|
120
127
|
DataType.A_FLOAT64,
|
121
|
-
], "Physical data type of
|
128
|
+
], "Physical data type of TAB-INTP compumethod must be one of"
|
122
129
|
" [A_INT32, A_UINT32, A_FLOAT32, A_FLOAT64]")
|
123
130
|
|
124
|
-
def
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
131
|
+
def __piecewise_linear_interpolate(self, x: Union[int, float],
|
132
|
+
range_samples: List[Union[int, float]],
|
133
|
+
domain_samples: List[Union[int,
|
134
|
+
float]]) -> Union[float, None]:
|
135
|
+
for i in range(0, len(range_samples) - 1):
|
136
|
+
if (x0 := range_samples[i]) <= x and x <= (x1 := range_samples[i + 1]):
|
137
|
+
y0 = domain_samples[i]
|
138
|
+
y1 = domain_samples[i + 1]
|
129
139
|
return y0 + (x - x0) * (y1 - y0) / (x1 - x0)
|
130
140
|
|
131
141
|
return None
|
132
142
|
|
133
143
|
def convert_physical_to_internal(self, physical_value: AtomicOdxType) -> AtomicOdxType:
|
134
144
|
if not isinstance(physical_value, (int, float)):
|
135
|
-
|
136
|
-
|
145
|
+
odxraise("The type of values of tab-intp compumethods must "
|
146
|
+
"either int or float", EncodeError)
|
147
|
+
return None
|
137
148
|
|
138
|
-
reference_points = list(zip(self.physical_points, self.internal_points))
|
139
149
|
odxassert(
|
140
150
|
isinstance(physical_value, (int, float)),
|
141
|
-
"Only integers and floats can be piecewise linearly interpolated")
|
142
|
-
result = self.
|
151
|
+
"Only integers and floats can be piecewise linearly interpolated", EncodeError)
|
152
|
+
result = self.__piecewise_linear_interpolate(physical_value, self._physical_points,
|
153
|
+
self._internal_points)
|
143
154
|
|
144
155
|
if result is None:
|
145
|
-
|
146
|
-
|
156
|
+
odxraise(
|
157
|
+
f"Internal value {physical_value!r} must be inside the range"
|
158
|
+
f" [{min(self.physical_points)}, {max(self.physical_points)}]", EncodeError)
|
159
|
+
|
147
160
|
res = self.internal_type.make_from(result)
|
148
|
-
|
149
|
-
odxraise()
|
161
|
+
|
150
162
|
return res
|
151
163
|
|
152
164
|
def convert_internal_to_physical(self, internal_value: AtomicOdxType) -> AtomicOdxType:
|
153
165
|
if not isinstance(internal_value, (int, float)):
|
154
|
-
|
155
|
-
|
166
|
+
odxraise(
|
167
|
+
"The internal type of values of tab-intp compumethods must "
|
168
|
+
"either int or float", EncodeError)
|
169
|
+
return None
|
170
|
+
|
171
|
+
odxassert(
|
172
|
+
isinstance(internal_value, (int, float)),
|
173
|
+
"Only integers and floats can be piecewise linearly interpolated", DecodeError)
|
156
174
|
|
157
|
-
|
158
|
-
|
175
|
+
result = self.__piecewise_linear_interpolate(internal_value, self._internal_points,
|
176
|
+
self._physical_points)
|
159
177
|
|
160
178
|
if result is None:
|
161
|
-
|
162
|
-
|
179
|
+
odxraise(
|
180
|
+
f"Internal value {internal_value!r} must be inside the range"
|
181
|
+
f" [{min(self.internal_points)}, {max(self.internal_points)}]", DecodeError)
|
182
|
+
return None
|
183
|
+
|
163
184
|
res = self.physical_type.make_from(result)
|
164
|
-
|
165
|
-
odxraise()
|
185
|
+
|
166
186
|
return res
|
167
187
|
|
168
188
|
def is_valid_physical_value(self, physical_value: AtomicOdxType) -> bool:
|
@@ -1,76 +1,140 @@
|
|
1
1
|
# SPDX-License-Identifier: MIT
|
2
2
|
from dataclasses import dataclass
|
3
|
-
from typing import List,
|
3
|
+
from typing import List, cast
|
4
|
+
from xml.etree import ElementTree
|
4
5
|
|
5
|
-
from ..exceptions import DecodeError, EncodeError, odxassert, odxraise
|
6
|
+
from ..exceptions import DecodeError, EncodeError, odxassert, odxraise, odxrequire
|
7
|
+
from ..odxlink import OdxDocFragment
|
6
8
|
from ..odxtypes import AtomicOdxType, DataType
|
7
|
-
from
|
9
|
+
from ..utils import dataclass_fields_asdict
|
10
|
+
from .compumethod import CompuCategory, CompuMethod
|
8
11
|
from .compuscale import CompuScale
|
9
12
|
|
10
13
|
|
11
14
|
@dataclass
|
12
15
|
class TexttableCompuMethod(CompuMethod):
|
16
|
+
"""Text table compute methods translate numbers to human readable
|
17
|
+
textual descriptions.
|
13
18
|
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
19
|
+
For details, refer to ASAM specification MCD-2 D (ODX), section 7.3.6.6.7.
|
20
|
+
|
21
|
+
"""
|
22
|
+
|
23
|
+
@staticmethod
|
24
|
+
def compu_method_from_et(et_element: ElementTree.Element, doc_frags: List[OdxDocFragment], *,
|
25
|
+
internal_type: DataType,
|
26
|
+
physical_type: DataType) -> "TexttableCompuMethod":
|
27
|
+
cm = CompuMethod.compu_method_from_et(
|
28
|
+
et_element, doc_frags, internal_type=internal_type, physical_type=physical_type)
|
29
|
+
kwargs = dataclass_fields_asdict(cm)
|
30
|
+
|
31
|
+
return TexttableCompuMethod(**kwargs)
|
18
32
|
|
19
33
|
def __post_init__(self) -> None:
|
20
|
-
odxassert(self.
|
21
|
-
"
|
34
|
+
odxassert(self.category == CompuCategory.TEXTTABLE,
|
35
|
+
"TexttableCompuMethod must exhibit TEXTTABLE category")
|
36
|
+
|
37
|
+
# the spec says that the physical data type shall be
|
38
|
+
# A_UNICODE2STRING, but we are a bit more lenient and allow
|
39
|
+
# any kind of string...
|
40
|
+
odxassert(
|
41
|
+
self.physical_type
|
42
|
+
in [DataType.A_UNICODE2STRING, DataType.A_UTF8STRING, DataType.A_ASCIISTRING],
|
43
|
+
"TEXTTABLE must have string type as its physical datatype.")
|
44
|
+
|
45
|
+
if self.compu_internal_to_phys is None:
|
46
|
+
odxraise("TEXTTABLE compu methods must exhibit a COMPU-INTERNAL-TO-PHYS subtag.")
|
47
|
+
scales = []
|
48
|
+
else:
|
49
|
+
scales = self.compu_internal_to_phys.compu_scales
|
50
|
+
|
22
51
|
odxassert(
|
23
|
-
all(scale.lower_limit is not None or scale.upper_limit is not None
|
24
|
-
|
25
|
-
|
52
|
+
all(scale.lower_limit is not None or scale.upper_limit is not None for scale in scales),
|
53
|
+
"All scales of TEXTTABLE compu methods must provide limits!")
|
54
|
+
|
55
|
+
self._compu_physical_default_value = None
|
56
|
+
citp = odxrequire(self.compu_internal_to_phys)
|
57
|
+
if (cdv := citp.compu_default_value) is not None:
|
58
|
+
self._compu_physical_default_value = self.physical_type.from_string(odxrequire(cdv.vt))
|
26
59
|
|
27
|
-
|
28
|
-
|
29
|
-
|
60
|
+
self._compu_internal_default_value = None
|
61
|
+
cpti = self.compu_phys_to_internal
|
62
|
+
if cpti is not None and (cdv := cpti.compu_default_value) is not None:
|
63
|
+
self._compu_internal_default_value = self.internal_type.from_string(odxrequire(cdv.v))
|
30
64
|
|
31
65
|
def convert_physical_to_internal(self, physical_value: AtomicOdxType) -> AtomicOdxType:
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
if scale == self.compu_default_value:
|
48
|
-
return True
|
66
|
+
scales = []
|
67
|
+
if (citp := self.compu_internal_to_phys) is not None:
|
68
|
+
scales = citp.compu_scales
|
69
|
+
matching_scales = [
|
70
|
+
x for x in scales if x.compu_const is not None and x.compu_const.value == physical_value
|
71
|
+
]
|
72
|
+
|
73
|
+
if len(matching_scales) == 0:
|
74
|
+
if self._compu_internal_default_value is None:
|
75
|
+
odxraise(f"Texttable could not encode {physical_value!r}.", EncodeError)
|
76
|
+
return cast(None, AtomicOdxType)
|
77
|
+
|
78
|
+
return self._compu_internal_default_value
|
79
|
+
elif len(matching_scales) > 1:
|
80
|
+
odxraise(f"Texttable could not uniquely encode {physical_value!r}.", EncodeError)
|
49
81
|
|
50
|
-
|
82
|
+
scale = matching_scales[0]
|
83
|
+
if scale.compu_inverse_value is not None and (civ :=
|
84
|
+
scale.compu_inverse_value.value) is not None:
|
85
|
+
return civ
|
86
|
+
elif scale.lower_limit is not None and scale.lower_limit._value is not None:
|
87
|
+
return scale.lower_limit._value
|
88
|
+
elif scale.upper_limit is not None and scale.upper_limit._value is not None:
|
89
|
+
return scale.upper_limit._value
|
90
|
+
|
91
|
+
odxraise(f"Texttable compu method could not encode '{physical_value!r}'.", EncodeError)
|
51
92
|
|
52
93
|
def convert_internal_to_physical(self, internal_value: AtomicOdxType) -> AtomicOdxType:
|
53
|
-
|
94
|
+
scales = []
|
95
|
+
if (citp := self.compu_internal_to_phys) is not None:
|
96
|
+
scales = citp.compu_scales
|
97
|
+
matching_scales: List[CompuScale] = [x for x in scales if x.applies(internal_value)]
|
98
|
+
|
54
99
|
if len(matching_scales) == 0:
|
55
|
-
if self.
|
100
|
+
if self._compu_physical_default_value is None:
|
56
101
|
odxraise(f"Texttable could not decode {internal_value!r}.", DecodeError)
|
57
102
|
return cast(None, AtomicOdxType)
|
58
103
|
|
59
|
-
return self.
|
104
|
+
return self._compu_physical_default_value
|
60
105
|
|
61
|
-
if len(matching_scales)
|
62
|
-
odxraise(f"Texttable could not decode {internal_value!r}.", DecodeError)
|
106
|
+
if len(matching_scales) > 1:
|
107
|
+
odxraise(f"Texttable could not uniquely decode {internal_value!r}.", DecodeError)
|
63
108
|
|
64
|
-
|
109
|
+
scale = matching_scales[0]
|
110
|
+
|
111
|
+
if scale.compu_const is None:
|
112
|
+
odxraise(f"Encountered a COMPU-SCALE with no COMPU-CONST.")
|
113
|
+
return cast(None, AtomicOdxType)
|
114
|
+
|
115
|
+
if scale.compu_const.value is not None:
|
116
|
+
return scale.compu_const.value
|
117
|
+
|
118
|
+
odxraise(f"Texttable compu method could not decode '{internal_value!r}'.", EncodeError)
|
65
119
|
|
66
120
|
def is_valid_physical_value(self, physical_value: AtomicOdxType) -> bool:
|
67
|
-
if self.
|
121
|
+
if self._compu_physical_default_value is not None:
|
68
122
|
return True
|
69
123
|
|
70
|
-
|
124
|
+
scales = []
|
125
|
+
if (cpti := self.compu_internal_to_phys) is not None:
|
126
|
+
scales = cpti.compu_scales
|
127
|
+
|
128
|
+
return any(scale.compu_const.value == physical_value
|
129
|
+
for scale in scales
|
130
|
+
if scale.compu_const is not None)
|
71
131
|
|
72
132
|
def is_valid_internal_value(self, internal_value: AtomicOdxType) -> bool:
|
73
|
-
if self.
|
133
|
+
if self._compu_internal_default_value is not None:
|
74
134
|
return True
|
75
135
|
|
76
|
-
|
136
|
+
scales = []
|
137
|
+
if (citp := self.compu_internal_to_phys) is not None:
|
138
|
+
scales = citp.compu_scales
|
139
|
+
|
140
|
+
return any(scale.applies(internal_value) for scale in scales)
|
@@ -1,85 +1,28 @@
|
|
1
1
|
# SPDX-License-Identifier: MIT
|
2
|
-
from typing import List
|
2
|
+
from typing import List
|
3
3
|
from xml.etree import ElementTree
|
4
4
|
|
5
5
|
from .diagcodedtype import DiagCodedType
|
6
|
-
from .exceptions import odxraise
|
6
|
+
from .exceptions import odxraise
|
7
7
|
from .globals import xsi
|
8
8
|
from .leadinglengthinfotype import LeadingLengthInfoType
|
9
9
|
from .minmaxlengthtype import MinMaxLengthType
|
10
|
-
from .odxlink import OdxDocFragment
|
11
|
-
from .odxtypes import DataType, odxstr_to_bool
|
10
|
+
from .odxlink import OdxDocFragment
|
12
11
|
from .paramlengthinfotype import ParamLengthInfoType
|
13
12
|
from .standardlengthtype import StandardLengthType
|
14
13
|
|
15
14
|
|
16
15
|
def create_any_diag_coded_type_from_et(et_element: ElementTree.Element,
|
17
16
|
doc_frags: List[OdxDocFragment]) -> DiagCodedType:
|
18
|
-
base_type_encoding = et_element.get("BASE-TYPE-ENCODING")
|
19
|
-
|
20
|
-
base_data_type_str = odxrequire(et_element.get("BASE-DATA-TYPE"))
|
21
|
-
try:
|
22
|
-
base_data_type = DataType(base_data_type_str)
|
23
|
-
except ValueError:
|
24
|
-
base_data_type = cast(DataType, None)
|
25
|
-
odxraise(f"Unknown base data type {base_data_type_str}")
|
26
|
-
|
27
|
-
is_highlow_byte_order_raw = odxstr_to_bool(et_element.get("IS-HIGHLOW-BYTE-ORDER"))
|
28
|
-
|
29
17
|
dct_type = et_element.get(f"{xsi}type")
|
30
|
-
bit_length = None
|
31
18
|
if dct_type == "LEADING-LENGTH-INFO-TYPE":
|
32
|
-
|
33
|
-
return LeadingLengthInfoType(
|
34
|
-
base_data_type=base_data_type,
|
35
|
-
bit_length=bit_length,
|
36
|
-
base_type_encoding=base_type_encoding,
|
37
|
-
is_highlow_byte_order_raw=is_highlow_byte_order_raw,
|
38
|
-
)
|
19
|
+
return LeadingLengthInfoType.from_et(et_element, doc_frags)
|
39
20
|
elif dct_type == "MIN-MAX-LENGTH-TYPE":
|
40
|
-
|
41
|
-
max_length = None
|
42
|
-
if et_element.find("MAX-LENGTH") is not None:
|
43
|
-
max_length = int(odxrequire(et_element.findtext("MAX-LENGTH")))
|
44
|
-
termination = odxrequire(et_element.get("TERMINATION"))
|
45
|
-
|
46
|
-
return MinMaxLengthType(
|
47
|
-
base_data_type=base_data_type,
|
48
|
-
min_length=min_length,
|
49
|
-
max_length=max_length,
|
50
|
-
termination=termination,
|
51
|
-
base_type_encoding=base_type_encoding,
|
52
|
-
is_highlow_byte_order_raw=is_highlow_byte_order_raw,
|
53
|
-
)
|
21
|
+
return MinMaxLengthType.from_et(et_element, doc_frags)
|
54
22
|
elif dct_type == "PARAM-LENGTH-INFO-TYPE":
|
55
|
-
|
56
|
-
OdxLinkRef.from_et(et_element.find("LENGTH-KEY-REF"), doc_frags))
|
57
|
-
|
58
|
-
return ParamLengthInfoType(
|
59
|
-
base_data_type=base_data_type,
|
60
|
-
length_key_ref=length_key_ref,
|
61
|
-
base_type_encoding=base_type_encoding,
|
62
|
-
is_highlow_byte_order_raw=is_highlow_byte_order_raw,
|
63
|
-
)
|
23
|
+
return ParamLengthInfoType.from_et(et_element, doc_frags)
|
64
24
|
elif dct_type == "STANDARD-LENGTH-TYPE":
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
# xsd:hexBinary allows for leading/trailing whitespace, empty strings, and it only allows an even
|
70
|
-
# number of hex digits, while some of the examples shown in the ODX specification exhibit an
|
71
|
-
# odd number of hex digits.
|
72
|
-
# This causes a validation paradox, so we try to be flexible
|
73
|
-
bit_mask_str = bit_mask_str.strip()
|
74
|
-
if len(bit_mask_str):
|
75
|
-
bit_mask = int(bit_mask_str, 16)
|
76
|
-
is_condensed_raw = odxstr_to_bool(et_element.get("CONDENSED"))
|
77
|
-
return StandardLengthType(
|
78
|
-
base_data_type=base_data_type,
|
79
|
-
bit_length=bit_length,
|
80
|
-
bit_mask=bit_mask,
|
81
|
-
is_condensed_raw=is_condensed_raw,
|
82
|
-
base_type_encoding=base_type_encoding,
|
83
|
-
is_highlow_byte_order_raw=is_highlow_byte_order_raw,
|
84
|
-
)
|
85
|
-
raise NotImplementedError(f"I do not know the diag-coded-type {dct_type}")
|
25
|
+
return StandardLengthType.from_et(et_element, doc_frags)
|
26
|
+
|
27
|
+
odxraise(f"Unknown DIAG-CODED-TYPE {dct_type}", NotImplementedError)
|
28
|
+
return DiagCodedType.from_et(et_element, doc_frags)
|