odxtools 8.1.0__py3-none-any.whl → 8.2.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/audience.py +7 -8
- odxtools/compumethods/compucodecompumethod.py +63 -0
- odxtools/compumethods/compuinternaltophys.py +19 -2
- odxtools/compumethods/compumethod.py +28 -2
- odxtools/compumethods/compuphystointernal.py +19 -2
- odxtools/compumethods/createanycompumethod.py +12 -0
- odxtools/compumethods/linearcompumethod.py +2 -2
- odxtools/compumethods/ratfunccompumethod.py +106 -0
- odxtools/compumethods/ratfuncsegment.py +87 -0
- odxtools/compumethods/scaleratfunccompumethod.py +113 -0
- odxtools/dataobjectproperty.py +3 -0
- odxtools/diaglayers/basevariantraw.py +7 -1
- odxtools/diaglayers/diaglayer.py +5 -0
- odxtools/diaglayers/diaglayerraw.py +13 -1
- odxtools/diaglayers/ecushareddataraw.py +7 -1
- odxtools/diaglayers/ecuvariantraw.py +7 -1
- odxtools/diaglayers/functionalgroupraw.py +7 -1
- odxtools/diaglayers/hierarchyelementraw.py +7 -1
- odxtools/diaglayers/protocolraw.py +7 -1
- odxtools/diagservice.py +3 -1
- odxtools/dtcdop.py +6 -0
- odxtools/environmentdatadescription.py +11 -6
- odxtools/library.py +66 -0
- odxtools/minmaxlengthtype.py +1 -1
- odxtools/multiplexer.py +4 -4
- odxtools/multiplexercase.py +1 -1
- odxtools/odxtypes.py +14 -0
- odxtools/parameterinfo.py +132 -76
- odxtools/parameters/systemparameter.py +51 -8
- odxtools/progcode.py +8 -4
- odxtools/standardlengthtype.py +4 -1
- odxtools/templates/macros/printCompuMethod.xml.jinja2 +3 -2
- odxtools/templates/macros/printDiagLayer.xml.jinja2 +8 -0
- odxtools/templates/macros/printLibrary.xml.jinja2 +21 -0
- odxtools/templates/macros/printSingleEcuJob.xml.jinja2 +2 -23
- odxtools/unitspec.py +10 -9
- odxtools/version.py +2 -2
- {odxtools-8.1.0.dist-info → odxtools-8.2.1.dist-info}/METADATA +1 -1
- {odxtools-8.1.0.dist-info → odxtools-8.2.1.dist-info}/RECORD +43 -37
- {odxtools-8.1.0.dist-info → odxtools-8.2.1.dist-info}/WHEEL +1 -1
- {odxtools-8.1.0.dist-info → odxtools-8.2.1.dist-info}/LICENSE +0 -0
- {odxtools-8.1.0.dist-info → odxtools-8.2.1.dist-info}/entry_points.txt +0 -0
- {odxtools-8.1.0.dist-info → odxtools-8.2.1.dist-info}/top_level.txt +0 -0
@@ -13,6 +13,7 @@ from ..diagservice import DiagService
|
|
13
13
|
from ..element import IdentifiableElement
|
14
14
|
from ..exceptions import odxassert, odxraise, odxrequire
|
15
15
|
from ..functionalclass import FunctionalClass
|
16
|
+
from ..library import Library
|
16
17
|
from ..nameditemlist import NamedItemList
|
17
18
|
from ..odxlink import OdxDocFragment, OdxLinkDatabase, OdxLinkId, OdxLinkRef
|
18
19
|
from ..request import Request
|
@@ -47,7 +48,7 @@ class DiagLayerRaw(IdentifiableElement):
|
|
47
48
|
state_charts: NamedItemList[StateChart]
|
48
49
|
additional_audiences: NamedItemList[AdditionalAudience]
|
49
50
|
# sub_components: List[DiagLayer] # TODO
|
50
|
-
|
51
|
+
libraries: NamedItemList[Library]
|
51
52
|
sdgs: List[SpecialDataGroup]
|
52
53
|
|
53
54
|
@property
|
@@ -149,6 +150,10 @@ class DiagLayerRaw(IdentifiableElement):
|
|
149
150
|
for el in et_element.iterfind("ADDITIONAL-AUDIENCES/ADDITIONAL-AUDIENCE")
|
150
151
|
]
|
151
152
|
|
153
|
+
libraries = [
|
154
|
+
Library.from_et(el, doc_frags) for el in et_element.iterfind("LIBRARYS/LIBRARY")
|
155
|
+
]
|
156
|
+
|
152
157
|
sdgs = [
|
153
158
|
SpecialDataGroup.from_et(sdge, doc_frags) for sdge in et_element.iterfind("SDGS/SDG")
|
154
159
|
]
|
@@ -168,6 +173,7 @@ class DiagLayerRaw(IdentifiableElement):
|
|
168
173
|
import_refs=import_refs,
|
169
174
|
state_charts=NamedItemList(state_charts),
|
170
175
|
additional_audiences=NamedItemList(additional_audiences),
|
176
|
+
libraries=NamedItemList(libraries),
|
171
177
|
sdgs=sdgs,
|
172
178
|
**kwargs)
|
173
179
|
|
@@ -200,6 +206,8 @@ class DiagLayerRaw(IdentifiableElement):
|
|
200
206
|
odxlinks.update(state_chart._build_odxlinks())
|
201
207
|
for additional_audience in self.additional_audiences:
|
202
208
|
odxlinks.update(additional_audience._build_odxlinks())
|
209
|
+
for library in self.libraries:
|
210
|
+
odxlinks.update(library._build_odxlinks())
|
203
211
|
for sdg in self.sdgs:
|
204
212
|
odxlinks.update(sdg._build_odxlinks())
|
205
213
|
|
@@ -252,6 +260,8 @@ class DiagLayerRaw(IdentifiableElement):
|
|
252
260
|
state_chart._resolve_odxlinks(odxlinks)
|
253
261
|
for additional_audience in self.additional_audiences:
|
254
262
|
additional_audience._resolve_odxlinks(odxlinks)
|
263
|
+
for library in self.libraries:
|
264
|
+
library._resolve_odxlinks(odxlinks)
|
255
265
|
for sdg in self.sdgs:
|
256
266
|
sdg._resolve_odxlinks(odxlinks)
|
257
267
|
|
@@ -282,5 +292,7 @@ class DiagLayerRaw(IdentifiableElement):
|
|
282
292
|
state_chart._resolve_snrefs(context)
|
283
293
|
for additional_audience in self.additional_audiences:
|
284
294
|
additional_audience._resolve_snrefs(context)
|
295
|
+
for library in self.libraries:
|
296
|
+
library._resolve_snrefs(context)
|
285
297
|
for sdg in self.sdgs:
|
286
298
|
sdg._resolve_snrefs(context)
|
@@ -28,7 +28,13 @@ class EcuSharedDataRaw(DiagLayerRaw):
|
|
28
28
|
@staticmethod
|
29
29
|
def from_et(et_element: ElementTree.Element,
|
30
30
|
doc_frags: List[OdxDocFragment]) -> "EcuSharedDataRaw":
|
31
|
-
|
31
|
+
# objects contained by diagnostic layers exibit an additional
|
32
|
+
# document fragment for the diag layer, so we use the document
|
33
|
+
# fragments of the odx id of the diag layer for IDs of
|
34
|
+
# contained objects.
|
35
|
+
dlr = DiagLayerRaw.from_et(et_element, doc_frags)
|
36
|
+
kwargs = dataclass_fields_asdict(dlr)
|
37
|
+
doc_frags = dlr.odx_id.doc_fragments
|
32
38
|
|
33
39
|
diag_variables_raw: List[Union[DiagVariable, OdxLinkRef]] = []
|
34
40
|
if (dv_elems := et_element.find("DIAG-VARIABLES")) is not None:
|
@@ -31,7 +31,13 @@ class EcuVariantRaw(HierarchyElementRaw):
|
|
31
31
|
@staticmethod
|
32
32
|
def from_et(et_element: ElementTree.Element,
|
33
33
|
doc_frags: List[OdxDocFragment]) -> "EcuVariantRaw":
|
34
|
-
|
34
|
+
# objects contained by diagnostic layers exibit an additional
|
35
|
+
# document fragment for the diag layer, so we use the document
|
36
|
+
# fragments of the odx id of the diag layer for IDs of
|
37
|
+
# contained objects.
|
38
|
+
her = HierarchyElementRaw.from_et(et_element, doc_frags)
|
39
|
+
kwargs = dataclass_fields_asdict(her)
|
40
|
+
doc_frags = her.odx_id.doc_fragments
|
35
41
|
|
36
42
|
diag_variables_raw: List[Union[DiagVariable, OdxLinkRef]] = []
|
37
43
|
if (dv_elems := et_element.find("DIAG-VARIABLES")) is not None:
|
@@ -30,7 +30,13 @@ class FunctionalGroupRaw(HierarchyElementRaw):
|
|
30
30
|
@staticmethod
|
31
31
|
def from_et(et_element: ElementTree.Element,
|
32
32
|
doc_frags: List[OdxDocFragment]) -> "FunctionalGroupRaw":
|
33
|
-
|
33
|
+
# objects contained by diagnostic layers exibit an additional
|
34
|
+
# document fragment for the diag layer, so we use the document
|
35
|
+
# fragments of the odx id of the diag layer for IDs of
|
36
|
+
# contained objects.
|
37
|
+
her = HierarchyElementRaw.from_et(et_element, doc_frags)
|
38
|
+
kwargs = dataclass_fields_asdict(her)
|
39
|
+
doc_frags = her.odx_id.doc_fragments
|
34
40
|
|
35
41
|
diag_variables_raw: List[Union[DiagVariable, OdxLinkRef]] = []
|
36
42
|
if (dv_elems := et_element.find("DIAG-VARIABLES")) is not None:
|
@@ -22,7 +22,13 @@ class HierarchyElementRaw(DiagLayerRaw):
|
|
22
22
|
@staticmethod
|
23
23
|
def from_et(et_element: ElementTree.Element,
|
24
24
|
doc_frags: List[OdxDocFragment]) -> "HierarchyElementRaw":
|
25
|
-
|
25
|
+
# objects contained by diagnostic layers exibit an additional
|
26
|
+
# document fragment for the diag layer, so we use the document
|
27
|
+
# fragments of the odx id of the diag layer for IDs of
|
28
|
+
# contained objects.
|
29
|
+
dlr = DiagLayerRaw.from_et(et_element, doc_frags)
|
30
|
+
kwargs = dataclass_fields_asdict(dlr)
|
31
|
+
doc_frags = dlr.odx_id.doc_fragments
|
26
32
|
|
27
33
|
comparam_refs = [
|
28
34
|
ComparamInstance.from_et(el, doc_frags)
|
@@ -37,7 +37,13 @@ class ProtocolRaw(HierarchyElementRaw):
|
|
37
37
|
|
38
38
|
@staticmethod
|
39
39
|
def from_et(et_element: ElementTree.Element, doc_frags: List[OdxDocFragment]) -> "ProtocolRaw":
|
40
|
-
|
40
|
+
# objects contained by diagnostic layers exibit an additional
|
41
|
+
# document fragment for the diag layer, so we use the document
|
42
|
+
# fragments of the odx id of the diag layer for IDs of
|
43
|
+
# contained objects.
|
44
|
+
her = HierarchyElementRaw.from_et(et_element, doc_frags)
|
45
|
+
kwargs = dataclass_fields_asdict(her)
|
46
|
+
doc_frags = her.odx_id.doc_fragments
|
41
47
|
|
42
48
|
comparam_spec_ref = OdxLinkRef.from_et(
|
43
49
|
odxrequire(et_element.find("COMPARAM-SPEC-REF")), doc_frags)
|
odxtools/diagservice.py
CHANGED
@@ -42,7 +42,9 @@ class DiagService(DiagComm):
|
|
42
42
|
pos_response_refs: List[OdxLinkRef]
|
43
43
|
neg_response_refs: List[OdxLinkRef]
|
44
44
|
|
45
|
-
#
|
45
|
+
# note that the spec has a typo here: it calls the corresponding
|
46
|
+
# XML tag POS-RESPONSE-SUPPRESSABLE...
|
47
|
+
# TODO: pos_response_suppressible: Optional[PosResponseSuppressible]
|
46
48
|
|
47
49
|
is_cyclic_raw: Optional[bool]
|
48
50
|
is_multiple_raw: Optional[bool]
|
odxtools/dtcdop.py
CHANGED
@@ -178,6 +178,8 @@ class DtcDop(DopBase):
|
|
178
178
|
def _build_odxlinks(self) -> Dict[OdxLinkId, Any]:
|
179
179
|
odxlinks = super()._build_odxlinks()
|
180
180
|
|
181
|
+
odxlinks.update(self.compu_method._build_odxlinks())
|
182
|
+
|
181
183
|
for dtc_proxy in self.dtcs_raw:
|
182
184
|
if isinstance(dtc_proxy, DiagnosticTroubleCode):
|
183
185
|
odxlinks.update(dtc_proxy._build_odxlinks())
|
@@ -187,6 +189,8 @@ class DtcDop(DopBase):
|
|
187
189
|
def _resolve_odxlinks(self, odxlinks: OdxLinkDatabase) -> None:
|
188
190
|
super()._resolve_odxlinks(odxlinks)
|
189
191
|
|
192
|
+
self.compu_method._resolve_odxlinks(odxlinks)
|
193
|
+
|
190
194
|
self._dtcs = NamedItemList[DiagnosticTroubleCode]()
|
191
195
|
for dtc_proxy in self.dtcs_raw:
|
192
196
|
if isinstance(dtc_proxy, DiagnosticTroubleCode):
|
@@ -202,6 +206,8 @@ class DtcDop(DopBase):
|
|
202
206
|
def _resolve_snrefs(self, context: SnRefContext) -> None:
|
203
207
|
super()._resolve_snrefs(context)
|
204
208
|
|
209
|
+
self.compu_method._resolve_snrefs(context)
|
210
|
+
|
205
211
|
for dtc_proxy in self.dtcs_raw:
|
206
212
|
if isinstance(dtc_proxy, DiagnosticTroubleCode):
|
207
213
|
dtc_proxy._resolve_snrefs(context)
|
@@ -11,6 +11,7 @@ from .dtcdop import DtcDop
|
|
11
11
|
from .encodestate import EncodeState
|
12
12
|
from .environmentdata import EnvironmentData
|
13
13
|
from .exceptions import odxraise, odxrequire
|
14
|
+
from .nameditemlist import NamedItemList
|
14
15
|
from .odxlink import OdxDocFragment, OdxLinkDatabase, OdxLinkId, OdxLinkRef
|
15
16
|
from .odxtypes import ParameterValue, ParameterValueDict
|
16
17
|
from .parameters.parameter import Parameter
|
@@ -35,7 +36,7 @@ class EnvironmentDataDescription(ComplexDop):
|
|
35
36
|
# in ODX 2.0.0, ENV-DATAS seems to be a mandatory
|
36
37
|
# sub-element of ENV-DATA-DESC, in ODX 2.2 it is not
|
37
38
|
# present
|
38
|
-
env_datas:
|
39
|
+
env_datas: NamedItemList[EnvironmentData]
|
39
40
|
env_data_refs: List[OdxLinkRef]
|
40
41
|
|
41
42
|
@property
|
@@ -62,16 +63,20 @@ class EnvironmentDataDescription(ComplexDop):
|
|
62
63
|
param_snpathref = None
|
63
64
|
if (param_snpathref_elem := et_element.find("PARAM-SNPATHREF")) is not None:
|
64
65
|
param_snpathref = odxrequire(param_snpathref_elem.get("SHORT-NAME-PATH"))
|
66
|
+
|
67
|
+
# ODX 2.0 mandates ENV-DATA-DESC to contain a list of
|
68
|
+
# ENV-DATAS and no ENV-DATA-REFS while for ODX 2.2 the
|
69
|
+
# situation is reversed. This means that we will create one
|
70
|
+
# empty and one non-empty list here. (Which is which depends
|
71
|
+
# on the version of the standard used by the file.)
|
65
72
|
env_data_refs = [
|
66
73
|
odxrequire(OdxLinkRef.from_et(env_data_ref, doc_frags))
|
67
74
|
for env_data_ref in et_element.iterfind("ENV-DATA-REFS/ENV-DATA-REF")
|
68
75
|
]
|
69
|
-
|
70
|
-
# ODX 2.0.0 says ENV-DATA-DESC could contain a list of ENV-DATAS
|
71
|
-
env_datas = [
|
76
|
+
env_datas = NamedItemList([
|
72
77
|
EnvironmentData.from_et(env_data_elem, doc_frags)
|
73
78
|
for env_data_elem in et_element.iterfind("ENV-DATAS/ENV-DATA")
|
74
|
-
]
|
79
|
+
])
|
75
80
|
|
76
81
|
return EnvironmentDataDescription(
|
77
82
|
param_snref=param_snref,
|
@@ -93,7 +98,7 @@ class EnvironmentDataDescription(ComplexDop):
|
|
93
98
|
# ODX 2.0 specifies environment data objects here, ODX 2.2
|
94
99
|
# uses references
|
95
100
|
if self.env_data_refs:
|
96
|
-
self.env_datas = [odxlinks.resolve(x) for x in self.env_data_refs]
|
101
|
+
self.env_datas = NamedItemList([odxlinks.resolve(x) for x in self.env_data_refs])
|
97
102
|
else:
|
98
103
|
for ed in self.env_datas:
|
99
104
|
ed._resolve_odxlinks(odxlinks)
|
odxtools/library.py
ADDED
@@ -0,0 +1,66 @@
|
|
1
|
+
# SPDX-License-Identifier: MIT
|
2
|
+
from dataclasses import dataclass
|
3
|
+
from typing import Any, Dict, List, Optional, cast
|
4
|
+
from xml.etree import ElementTree
|
5
|
+
|
6
|
+
from .element import IdentifiableElement
|
7
|
+
from .exceptions import odxraise, odxrequire
|
8
|
+
from .odxlink import OdxDocFragment, OdxLinkDatabase, OdxLinkId
|
9
|
+
from .snrefcontext import SnRefContext
|
10
|
+
from .utils import dataclass_fields_asdict
|
11
|
+
|
12
|
+
|
13
|
+
@dataclass
|
14
|
+
class Library(IdentifiableElement):
|
15
|
+
"""
|
16
|
+
A library defines a shared library used for single ECU jobs etc.
|
17
|
+
|
18
|
+
It this is basically equivalent to ProgCode.
|
19
|
+
"""
|
20
|
+
|
21
|
+
code_file: str
|
22
|
+
encryption: Optional[str]
|
23
|
+
syntax: str
|
24
|
+
revision: str
|
25
|
+
entrypoint: Optional[str]
|
26
|
+
|
27
|
+
@property
|
28
|
+
def code(self) -> bytes:
|
29
|
+
return self._code
|
30
|
+
|
31
|
+
@staticmethod
|
32
|
+
def from_et(et_element: ElementTree.Element, doc_frags: List[OdxDocFragment]) -> "Library":
|
33
|
+
|
34
|
+
kwargs = dataclass_fields_asdict(IdentifiableElement.from_et(et_element, doc_frags))
|
35
|
+
|
36
|
+
code_file = odxrequire(et_element.findtext("CODE-FILE"))
|
37
|
+
encryption = et_element.findtext("ENCRYPTION")
|
38
|
+
syntax = odxrequire(et_element.findtext("SYNTAX"))
|
39
|
+
revision = odxrequire(et_element.findtext("REVISION"))
|
40
|
+
entrypoint = et_element.findtext("ENTRYPOINT")
|
41
|
+
|
42
|
+
return Library(
|
43
|
+
code_file=code_file,
|
44
|
+
encryption=encryption,
|
45
|
+
syntax=syntax,
|
46
|
+
revision=revision,
|
47
|
+
entrypoint=entrypoint,
|
48
|
+
**kwargs)
|
49
|
+
|
50
|
+
def _build_odxlinks(self) -> Dict[OdxLinkId, Any]:
|
51
|
+
return {self.odx_id: self}
|
52
|
+
|
53
|
+
def _resolve_odxlinks(self, odxlinks: OdxLinkDatabase) -> None:
|
54
|
+
pass
|
55
|
+
|
56
|
+
def _resolve_snrefs(self, context: SnRefContext) -> None:
|
57
|
+
aux_file = odxrequire(context.database).auxiliary_files.get(self.code_file)
|
58
|
+
|
59
|
+
if aux_file is None:
|
60
|
+
odxraise(f"Reference to auxiliary file '{self.code_file}' "
|
61
|
+
f"could not be resolved")
|
62
|
+
self._code: bytes = cast(bytes, None)
|
63
|
+
return
|
64
|
+
|
65
|
+
self._code = aux_file.read()
|
66
|
+
aux_file.seek(0)
|
odxtools/minmaxlengthtype.py
CHANGED
@@ -74,7 +74,7 @@ class MinMaxLengthType(DiagCodedType):
|
|
74
74
|
@override
|
75
75
|
def encode_into_pdu(self, internal_value: AtomicOdxType, encode_state: EncodeState) -> None:
|
76
76
|
|
77
|
-
if not isinstance(internal_value, (bytes, str)):
|
77
|
+
if not isinstance(internal_value, (bytes, str, bytearray)):
|
78
78
|
odxraise("MinMaxLengthType is currently only implemented for strings and byte arrays",
|
79
79
|
EncodeError)
|
80
80
|
|
odxtools/multiplexer.py
CHANGED
@@ -12,6 +12,7 @@ from .exceptions import DecodeError, EncodeError, odxassert, odxraise, odxrequir
|
|
12
12
|
from .multiplexercase import MultiplexerCase
|
13
13
|
from .multiplexerdefaultcase import MultiplexerDefaultCase
|
14
14
|
from .multiplexerswitchkey import MultiplexerSwitchKey
|
15
|
+
from .nameditemlist import NamedItemList
|
15
16
|
from .odxlink import OdxDocFragment, OdxLinkDatabase, OdxLinkId
|
16
17
|
from .odxtypes import AtomicOdxType, ParameterValue, odxstr_to_bool
|
17
18
|
from .snrefcontext import SnRefContext
|
@@ -30,7 +31,7 @@ class Multiplexer(ComplexDop):
|
|
30
31
|
byte_position: int
|
31
32
|
switch_key: MultiplexerSwitchKey
|
32
33
|
default_case: Optional[MultiplexerDefaultCase]
|
33
|
-
cases:
|
34
|
+
cases: NamedItemList[MultiplexerCase]
|
34
35
|
is_visible_raw: Optional[bool]
|
35
36
|
|
36
37
|
@staticmethod
|
@@ -48,9 +49,8 @@ class Multiplexer(ComplexDop):
|
|
48
49
|
if (dc_elem := et_element.find("DEFAULT-CASE")) is not None:
|
49
50
|
default_case = MultiplexerDefaultCase.from_et(dc_elem, doc_frags)
|
50
51
|
|
51
|
-
cases =
|
52
|
-
|
53
|
-
cases = [MultiplexerCase.from_et(el, doc_frags) for el in cases_elem.iterfind("CASE")]
|
52
|
+
cases = NamedItemList(
|
53
|
+
[MultiplexerCase.from_et(el, doc_frags) for el in et_element.iterfind("CASES/CASE")])
|
54
54
|
|
55
55
|
is_visible_raw = odxstr_to_bool(et_element.get("IS-VISIBLE"))
|
56
56
|
|
odxtools/multiplexercase.py
CHANGED
@@ -15,7 +15,7 @@ from .utils import dataclass_fields_asdict
|
|
15
15
|
|
16
16
|
@dataclass
|
17
17
|
class MultiplexerCase(NamedElement):
|
18
|
-
"""This class represents a
|
18
|
+
"""This class represents a case which represents a range of keys of a multiplexer."""
|
19
19
|
|
20
20
|
structure_ref: Optional[OdxLinkRef]
|
21
21
|
structure_snref: Optional[str]
|
odxtools/odxtypes.py
CHANGED
@@ -241,3 +241,17 @@ class DataType(Enum):
|
|
241
241
|
return True
|
242
242
|
else:
|
243
243
|
return False
|
244
|
+
|
245
|
+
def __str__(self) -> str:
|
246
|
+
if self == DataType.A_INT32:
|
247
|
+
return "int"
|
248
|
+
elif self == DataType.A_UINT32:
|
249
|
+
return "uint"
|
250
|
+
elif self in (DataType.A_FLOAT32, DataType.A_FLOAT64):
|
251
|
+
return "float"
|
252
|
+
elif self == DataType.A_BYTEFIELD:
|
253
|
+
return "bytefield"
|
254
|
+
elif self in (DataType.A_UNICODE2STRING, DataType.A_ASCIISTRING, DataType.A_UTF8STRING):
|
255
|
+
return "string"
|
256
|
+
else:
|
257
|
+
return f"<unknown type '{self.value}'>"
|
odxtools/parameterinfo.py
CHANGED
@@ -3,9 +3,15 @@ import textwrap
|
|
3
3
|
from io import StringIO
|
4
4
|
from typing import Iterable
|
5
5
|
|
6
|
+
from .compumethods.compucodecompumethod import CompuCodeCompuMethod
|
6
7
|
from .compumethods.identicalcompumethod import IdenticalCompuMethod
|
7
8
|
from .compumethods.limit import IntervalType
|
8
9
|
from .compumethods.linearcompumethod import LinearCompuMethod
|
10
|
+
from .compumethods.linearsegment import LinearSegment
|
11
|
+
from .compumethods.ratfunccompumethod import RatFuncCompuMethod
|
12
|
+
from .compumethods.ratfuncsegment import RatFuncSegment
|
13
|
+
from .compumethods.scalelinearcompumethod import ScaleLinearCompuMethod
|
14
|
+
from .compumethods.scaleratfunccompumethod import ScaleRatFuncCompuMethod
|
9
15
|
from .compumethods.texttablecompumethod import TexttableCompuMethod
|
10
16
|
from .dataobjectproperty import DataObjectProperty
|
11
17
|
from .dtcdop import DtcDop
|
@@ -13,19 +19,55 @@ from .dynamiclengthfield import DynamicLengthField
|
|
13
19
|
from .endofpdufield import EndOfPduField
|
14
20
|
from .exceptions import odxrequire
|
15
21
|
from .multiplexer import Multiplexer
|
16
|
-
from .odxtypes import DataType
|
17
22
|
from .parameters.codedconstparameter import CodedConstParameter
|
18
23
|
from .parameters.matchingrequestparameter import MatchingRequestParameter
|
19
24
|
from .parameters.nrcconstparameter import NrcConstParameter
|
20
25
|
from .parameters.parameter import Parameter
|
21
26
|
from .parameters.parameterwithdop import ParameterWithDOP
|
22
27
|
from .parameters.reservedparameter import ReservedParameter
|
28
|
+
from .parameters.systemparameter import SystemParameter
|
23
29
|
from .parameters.tablekeyparameter import TableKeyParameter
|
24
30
|
from .parameters.tablestructparameter import TableStructParameter
|
25
31
|
from .paramlengthinfotype import ParamLengthInfoType
|
26
32
|
from .staticfield import StaticField
|
27
33
|
|
28
34
|
|
35
|
+
def _get_linear_segment_info(segment: LinearSegment) -> str:
|
36
|
+
ll = segment.physical_lower_limit
|
37
|
+
ul = segment.physical_upper_limit
|
38
|
+
if ll is None or ll.interval_type == IntervalType.INFINITE:
|
39
|
+
ll_str = "(-inf"
|
40
|
+
else:
|
41
|
+
ll_delim = '(' if ll.interval_type == IntervalType.OPEN else '['
|
42
|
+
ll_str = f"{ll_delim}{ll._value!r}"
|
43
|
+
|
44
|
+
if ul is None or ul.interval_type == IntervalType.INFINITE:
|
45
|
+
ul_str = "inf)"
|
46
|
+
else:
|
47
|
+
ul_delim = ')' if ul.interval_type == IntervalType.OPEN else ']'
|
48
|
+
ul_str = f"{ul._value!r}{ul_delim}"
|
49
|
+
|
50
|
+
return f"{ll_str}, {ul_str}"
|
51
|
+
|
52
|
+
|
53
|
+
def _get_rat_func_segment_info(segment: RatFuncSegment) -> str:
|
54
|
+
ll = segment.lower_limit
|
55
|
+
ul = segment.upper_limit
|
56
|
+
if ll is None or ll.interval_type == IntervalType.INFINITE:
|
57
|
+
ll_str = "(-inf"
|
58
|
+
else:
|
59
|
+
ll_delim = '(' if ll.interval_type == IntervalType.OPEN else '['
|
60
|
+
ll_str = f"{ll_delim}{ll._value!r}"
|
61
|
+
|
62
|
+
if ul is None or ul.interval_type == IntervalType.INFINITE:
|
63
|
+
ul_str = "inf)"
|
64
|
+
else:
|
65
|
+
ul_delim = ')' if ul.interval_type == IntervalType.OPEN else ']'
|
66
|
+
ul_str = f"{ul._value!r}{ul_delim}"
|
67
|
+
|
68
|
+
return f"{ll_str}, {ul_str}"
|
69
|
+
|
70
|
+
|
29
71
|
def parameter_info(param_list: Iterable[Parameter], quoted_names: bool = False) -> str:
|
30
72
|
q = "'" if quoted_names else ""
|
31
73
|
of = StringIO()
|
@@ -42,6 +84,11 @@ def parameter_info(param_list: Iterable[Parameter], quoted_names: bool = False)
|
|
42
84
|
elif isinstance(param, ReservedParameter):
|
43
85
|
of.write(f"{q}{param.short_name}{q}: <reserved>\n")
|
44
86
|
continue
|
87
|
+
elif isinstance(param, SystemParameter):
|
88
|
+
of.write(
|
89
|
+
f"{q}{param.short_name}{q}: <system; kind = \"{param.sysparam}\">; required = {param.is_required}\n"
|
90
|
+
)
|
91
|
+
continue
|
45
92
|
elif isinstance(param, TableKeyParameter):
|
46
93
|
of.write(
|
47
94
|
f"{q}{param.short_name}{q}: <optional> table key; table = '{param.table.short_name}'; choices:\n"
|
@@ -69,7 +116,10 @@ def parameter_info(param_list: Iterable[Parameter], quoted_names: bool = False)
|
|
69
116
|
continue
|
70
117
|
|
71
118
|
dop = param.dop
|
72
|
-
if
|
119
|
+
if dop is None:
|
120
|
+
of.write("{q}{param.short_name}{q}: <no DOP>\n")
|
121
|
+
continue
|
122
|
+
elif isinstance(dop, EndOfPduField):
|
73
123
|
of.write(f"{q}{param.short_name}{q}: list({{\n")
|
74
124
|
of.write(textwrap.indent(parameter_info(dop.structure.parameters, True), " "))
|
75
125
|
of.write(f"}})\n")
|
@@ -112,89 +162,95 @@ def parameter_info(param_list: Iterable[Parameter], quoted_names: bool = False)
|
|
112
162
|
of.write(textwrap.indent(parameter_info(struc.parameters, True), " "))
|
113
163
|
of.write(f" }})\n")
|
114
164
|
continue
|
165
|
+
elif isinstance(dop, DataObjectProperty):
|
166
|
+
# a "simple" DOP
|
167
|
+
if (cm := dop.compu_method) is None:
|
168
|
+
of.write(f"{q}{param.short_name}{q}: <no compu method>\n")
|
169
|
+
continue
|
115
170
|
|
116
|
-
|
171
|
+
if isinstance(cm, TexttableCompuMethod):
|
172
|
+
of.write(f"{q}{param.short_name}{q}: enum; choices:\n")
|
173
|
+
for scale in odxrequire(cm.compu_internal_to_phys).compu_scales:
|
174
|
+
val_str = ""
|
175
|
+
if scale.lower_limit is not None:
|
176
|
+
val_str = f"({repr(scale.lower_limit.value)})"
|
117
177
|
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
178
|
+
if scale.compu_const is None:
|
179
|
+
of.write(f" <ERROR in ODX data: no value specified>\n")
|
180
|
+
else:
|
181
|
+
vt = scale.compu_const.vt
|
182
|
+
v = scale.compu_const.v
|
183
|
+
if vt is not None:
|
184
|
+
of.write(f" \"{vt}\" {val_str}\n")
|
185
|
+
else:
|
186
|
+
of.write(f" {v}\n")
|
124
187
|
|
125
|
-
|
126
|
-
|
127
|
-
continue
|
188
|
+
elif isinstance(cm, IdenticalCompuMethod):
|
189
|
+
of.write(f"{q}{param.short_name}{q}: {dop.physical_type.base_data_type}\n")
|
128
190
|
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
if scale.lower_limit is not None:
|
134
|
-
val_str = f"({repr(scale.lower_limit.value)})"
|
191
|
+
elif isinstance(cm, ScaleLinearCompuMethod):
|
192
|
+
of.write(f"{q}{param.short_name}{q}: {dop.physical_type.base_data_type}")
|
193
|
+
seg_list = [_get_linear_segment_info(x) for x in cm.segments]
|
194
|
+
of.write(f"; ranges = {{ {', '.join(seg_list)} }}")
|
135
195
|
|
136
|
-
|
137
|
-
|
196
|
+
unit = dop.unit
|
197
|
+
unit_str = unit.display_name if unit is not None else None
|
198
|
+
if unit_str is not None:
|
199
|
+
of.write(f"; unit: {unit_str}")
|
200
|
+
|
201
|
+
of.write("\n")
|
202
|
+
|
203
|
+
elif isinstance(cm, LinearCompuMethod):
|
204
|
+
of.write(f"{q}{param.short_name}{q}: {dop.physical_type.base_data_type}")
|
205
|
+
of.write(f"; range: {_get_linear_segment_info(cm.segment)}")
|
206
|
+
|
207
|
+
unit = dop.unit
|
208
|
+
unit_str = unit.display_name if unit is not None else None
|
209
|
+
if unit_str is not None:
|
210
|
+
of.write(f"; unit: {unit_str}")
|
211
|
+
|
212
|
+
of.write("\n")
|
213
|
+
|
214
|
+
elif isinstance(cm, ScaleRatFuncCompuMethod):
|
215
|
+
of.write(f"{q}{param.short_name}{q}: {dop.physical_type.base_data_type}")
|
216
|
+
if cm._phys_to_int_segments is None:
|
217
|
+
of.write("<NOT ENCODABLE>")
|
138
218
|
else:
|
139
|
-
|
140
|
-
|
141
|
-
if vt is not None:
|
142
|
-
of.write(f" \"{vt}\" {val_str}\n")
|
143
|
-
else:
|
144
|
-
of.write(f" {v}\n")
|
145
|
-
|
146
|
-
elif isinstance(cm, IdenticalCompuMethod):
|
147
|
-
bdt = dop.physical_type.base_data_type
|
148
|
-
if bdt in (DataType.A_UTF8STRING, DataType.A_UNICODE2STRING, DataType.A_ASCIISTRING):
|
149
|
-
of.write(f": str")
|
150
|
-
elif bdt == DataType.A_BYTEFIELD:
|
151
|
-
of.write(f": bytes")
|
152
|
-
elif bdt.name.startswith("A_FLOAT"):
|
153
|
-
of.write(f": float")
|
154
|
-
elif bdt.name.startswith("A_UINT"):
|
155
|
-
of.write(f": uint")
|
156
|
-
elif bdt.name.startswith("A_INT"):
|
157
|
-
of.write(f": int")
|
158
|
-
else:
|
159
|
-
of.write(f": <unknown type {{ bdt.name }}>")
|
160
|
-
|
161
|
-
of.write("\n")
|
162
|
-
|
163
|
-
elif isinstance(cm, LinearCompuMethod):
|
164
|
-
bdt = dop.physical_type.base_data_type
|
165
|
-
if bdt in (DataType.A_UTF8STRING, DataType.A_UNICODE2STRING, DataType.A_ASCIISTRING):
|
166
|
-
of.write(f": str")
|
167
|
-
elif bdt in (DataType.A_BYTEFIELD,):
|
168
|
-
of.write(f": bytes")
|
169
|
-
elif bdt.name.startswith("A_FLOAT"):
|
170
|
-
of.write(f": float")
|
171
|
-
elif bdt.name.startswith("A_UINT"):
|
172
|
-
of.write(f": uint")
|
173
|
-
elif bdt.name.startswith("A_INT"):
|
174
|
-
of.write(f": int")
|
175
|
-
else:
|
176
|
-
of.write(f": <unknown type>")
|
219
|
+
seg_list = [_get_rat_func_segment_info(x) for x in cm._phys_to_int_segments]
|
220
|
+
of.write(f"; ranges = {{ {', '.join(seg_list)} }}")
|
177
221
|
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
else:
|
183
|
-
ll_delim = '(' if ll.interval_type == IntervalType.OPEN else '['
|
184
|
-
ll_str = f"{ll_delim}{ll._value!r}"
|
222
|
+
unit = dop.unit
|
223
|
+
unit_str = unit.display_name if unit is not None else None
|
224
|
+
if unit_str is not None:
|
225
|
+
of.write(f"; unit: {unit_str}")
|
185
226
|
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
227
|
+
of.write("\n")
|
228
|
+
|
229
|
+
elif isinstance(cm, RatFuncCompuMethod):
|
230
|
+
of.write(f"{q}{param.short_name}{q}: {dop.physical_type.base_data_type}")
|
231
|
+
if cm._phys_to_int_segment is None:
|
232
|
+
of.write("<NOT ENCODABLE>")
|
233
|
+
else:
|
234
|
+
of.write(f"; range: {_get_rat_func_segment_info(cm._phys_to_int_segment)}")
|
235
|
+
|
236
|
+
unit = dop.unit
|
237
|
+
unit_str = unit.display_name if unit is not None else None
|
238
|
+
if unit_str is not None:
|
239
|
+
of.write(f"; unit: {unit_str}")
|
192
240
|
|
193
|
-
|
194
|
-
unit_str = unit.display_name if unit is not None else None
|
195
|
-
if unit_str is not None:
|
196
|
-
of.write(f"; unit: {unit_str}")
|
241
|
+
of.write("\n")
|
197
242
|
|
198
|
-
|
243
|
+
elif isinstance(cm, CompuCodeCompuMethod):
|
244
|
+
of.write(f"{q}{param.short_name}{q}: {dop.physical_type.base_data_type}")
|
245
|
+
of.write(f"; <programmatic translation>")
|
246
|
+
|
247
|
+
of.write("\n")
|
248
|
+
|
249
|
+
else:
|
250
|
+
of.write(
|
251
|
+
f"{q}{param.short_name}{q}: unknown compu method {type(dop.compu_method).__name__}\n"
|
252
|
+
)
|
253
|
+
else:
|
254
|
+
of.write(f"{q}{param.short_name}{q}: <unhandled DOP '{type(dop).__name__}'>\n")
|
199
255
|
|
200
256
|
return of.getvalue()
|