odxtools 6.6.1__py3-none-any.whl → 9.3.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 +7 -5
- odxtools/additionalaudience.py +3 -5
- odxtools/admindata.py +5 -7
- odxtools/audience.py +10 -13
- odxtools/basecomparam.py +3 -5
- odxtools/basicstructure.py +55 -241
- odxtools/cli/_parser_utils.py +16 -1
- odxtools/cli/_print_utils.py +169 -134
- odxtools/cli/browse.py +127 -103
- odxtools/cli/compare.py +114 -87
- odxtools/cli/decode.py +2 -1
- odxtools/cli/dummy_sub_parser.py +3 -1
- odxtools/cli/find.py +2 -1
- odxtools/cli/list.py +26 -16
- odxtools/cli/main.py +1 -0
- odxtools/cli/snoop.py +32 -6
- odxtools/codec.py +211 -0
- odxtools/commrelation.py +122 -0
- 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 +14 -14
- odxtools/comparamspec.py +16 -54
- odxtools/comparamsubset.py +22 -62
- odxtools/complexcomparam.py +5 -7
- odxtools/compumethods/compucodecompumethod.py +63 -0
- odxtools/compumethods/compuconst.py +31 -0
- odxtools/compumethods/compudefaultvalue.py +27 -0
- odxtools/compumethods/compuinternaltophys.py +56 -0
- odxtools/compumethods/compuinversevalue.py +7 -0
- odxtools/compumethods/compumethod.py +94 -15
- odxtools/compumethods/compuphystointernal.py +56 -0
- odxtools/compumethods/compurationalcoeffs.py +20 -9
- odxtools/compumethods/compuscale.py +67 -32
- odxtools/compumethods/createanycompumethod.py +31 -172
- odxtools/compumethods/identicalcompumethod.py +31 -6
- odxtools/compumethods/limit.py +70 -36
- odxtools/compumethods/linearcompumethod.py +70 -181
- odxtools/compumethods/linearsegment.py +190 -0
- odxtools/compumethods/ratfunccompumethod.py +106 -0
- odxtools/compumethods/ratfuncsegment.py +87 -0
- odxtools/compumethods/scalelinearcompumethod.py +132 -26
- odxtools/compumethods/scaleratfunccompumethod.py +113 -0
- odxtools/compumethods/tabintpcompumethod.py +123 -92
- odxtools/compumethods/texttablecompumethod.py +117 -57
- odxtools/createanydiagcodedtype.py +10 -67
- odxtools/database.py +167 -87
- odxtools/dataobjectproperty.py +25 -32
- odxtools/decodestate.py +14 -17
- odxtools/description.py +47 -0
- odxtools/determinenumberofitems.py +4 -5
- odxtools/diagcodedtype.py +37 -106
- odxtools/diagcomm.py +24 -12
- odxtools/diagdatadictionaryspec.py +120 -96
- odxtools/diaglayercontainer.py +46 -54
- odxtools/diaglayers/basevariant.py +128 -0
- odxtools/diaglayers/basevariantraw.py +123 -0
- odxtools/diaglayers/diaglayer.py +432 -0
- odxtools/{diaglayerraw.py → diaglayers/diaglayerraw.py} +105 -120
- odxtools/diaglayers/diaglayertype.py +42 -0
- odxtools/diaglayers/ecushareddata.py +96 -0
- odxtools/diaglayers/ecushareddataraw.py +87 -0
- odxtools/diaglayers/ecuvariant.py +124 -0
- odxtools/diaglayers/ecuvariantraw.py +129 -0
- odxtools/diaglayers/functionalgroup.py +110 -0
- odxtools/diaglayers/functionalgroupraw.py +106 -0
- odxtools/{diaglayer.py → diaglayers/hierarchyelement.py} +273 -472
- odxtools/diaglayers/hierarchyelementraw.py +58 -0
- odxtools/diaglayers/protocol.py +64 -0
- odxtools/diaglayers/protocolraw.py +91 -0
- odxtools/diagnostictroublecode.py +8 -9
- odxtools/diagservice.py +57 -44
- odxtools/diagvariable.py +113 -0
- odxtools/docrevision.py +5 -7
- odxtools/dopbase.py +15 -15
- odxtools/dtcdop.py +170 -50
- odxtools/dynamicendmarkerfield.py +134 -0
- odxtools/dynamiclengthfield.py +47 -42
- odxtools/dyndefinedspec.py +177 -0
- odxtools/dynenddopref.py +38 -0
- odxtools/ecuvariantmatcher.py +6 -7
- odxtools/element.py +13 -15
- odxtools/encodestate.py +199 -22
- odxtools/endofpdufield.py +31 -18
- odxtools/environmentdata.py +8 -1
- odxtools/environmentdatadescription.py +198 -36
- odxtools/exceptions.py +11 -2
- odxtools/field.py +10 -10
- odxtools/functionalclass.py +3 -5
- odxtools/inputparam.py +3 -12
- odxtools/internalconstr.py +14 -5
- odxtools/isotp_state_machine.py +14 -6
- odxtools/leadinglengthinfotype.py +37 -18
- odxtools/library.py +66 -0
- odxtools/loadfile.py +64 -0
- odxtools/matchingparameter.py +3 -3
- odxtools/message.py +0 -7
- odxtools/minmaxlengthtype.py +61 -33
- odxtools/modification.py +3 -5
- odxtools/multiplexer.py +135 -75
- odxtools/multiplexercase.py +39 -18
- odxtools/multiplexerdefaultcase.py +15 -12
- odxtools/multiplexerswitchkey.py +4 -5
- odxtools/nameditemlist.py +33 -8
- odxtools/negoutputparam.py +3 -5
- odxtools/odxcategory.py +83 -0
- odxtools/odxlink.py +62 -53
- odxtools/odxtypes.py +93 -8
- odxtools/outputparam.py +5 -16
- odxtools/parameterinfo.py +219 -61
- odxtools/parameters/codedconstparameter.py +45 -32
- odxtools/parameters/createanyparameter.py +19 -193
- odxtools/parameters/dynamicparameter.py +25 -4
- odxtools/parameters/lengthkeyparameter.py +83 -25
- odxtools/parameters/matchingrequestparameter.py +48 -18
- odxtools/parameters/nrcconstparameter.py +76 -54
- odxtools/parameters/parameter.py +97 -73
- odxtools/parameters/parameterwithdop.py +41 -38
- odxtools/parameters/physicalconstantparameter.py +41 -20
- odxtools/parameters/reservedparameter.py +36 -18
- odxtools/parameters/systemparameter.py +74 -7
- odxtools/parameters/tableentryparameter.py +47 -7
- odxtools/parameters/tablekeyparameter.py +142 -55
- odxtools/parameters/tablestructparameter.py +79 -58
- odxtools/parameters/valueparameter.py +39 -21
- odxtools/paramlengthinfotype.py +56 -33
- odxtools/parentref.py +20 -3
- odxtools/physicaldimension.py +3 -8
- odxtools/progcode.py +26 -11
- odxtools/protstack.py +3 -5
- odxtools/py.typed +0 -0
- odxtools/relateddoc.py +7 -9
- odxtools/request.py +120 -10
- odxtools/response.py +123 -23
- odxtools/scaleconstr.py +14 -8
- odxtools/servicebinner.py +1 -1
- odxtools/singleecujob.py +12 -10
- odxtools/snrefcontext.py +29 -0
- odxtools/specialdata.py +3 -5
- odxtools/specialdatagroup.py +7 -9
- odxtools/specialdatagroupcaption.py +3 -6
- odxtools/standardlengthtype.py +80 -14
- odxtools/state.py +3 -5
- odxtools/statechart.py +13 -19
- odxtools/statetransition.py +8 -18
- odxtools/staticfield.py +107 -0
- odxtools/subcomponent.py +288 -0
- odxtools/swvariable.py +21 -0
- odxtools/table.py +9 -9
- odxtools/tablerow.py +30 -15
- odxtools/teammember.py +3 -5
- odxtools/templates/comparam-spec.odx-c.xml.jinja2 +4 -24
- odxtools/templates/comparam-subset.odx-cs.xml.jinja2 +5 -26
- odxtools/templates/diag_layer_container.odx-d.xml.jinja2 +15 -31
- odxtools/templates/{index.xml.xml.jinja2 → index.xml.jinja2} +1 -1
- odxtools/templates/macros/printAudience.xml.jinja2 +1 -1
- odxtools/templates/macros/printBaseVariant.xml.jinja2 +53 -0
- odxtools/templates/macros/printCompanyData.xml.jinja2 +4 -7
- odxtools/templates/macros/printComparam.xml.jinja2 +6 -4
- odxtools/templates/macros/printComparamRef.xml.jinja2 +5 -12
- odxtools/templates/macros/printCompuMethod.xml.jinja2 +147 -0
- odxtools/templates/macros/printDOP.xml.jinja2 +27 -137
- odxtools/templates/macros/printDescription.xml.jinja2 +18 -0
- odxtools/templates/macros/printDiagComm.xml.jinja2 +1 -1
- odxtools/templates/macros/printDiagLayer.xml.jinja2 +222 -0
- odxtools/templates/macros/printDiagVariable.xml.jinja2 +66 -0
- odxtools/templates/macros/printDynDefinedSpec.xml.jinja2 +48 -0
- odxtools/templates/macros/printDynamicEndmarkerField.xml.jinja2 +16 -0
- odxtools/templates/macros/printDynamicLengthField.xml.jinja2 +1 -1
- odxtools/templates/macros/printEcuSharedData.xml.jinja2 +30 -0
- odxtools/templates/macros/printEcuVariant.xml.jinja2 +53 -0
- odxtools/templates/macros/printEcuVariantPattern.xml.jinja2 +1 -1
- odxtools/templates/macros/printElementId.xml.jinja2 +8 -3
- odxtools/templates/macros/printEndOfPdu.xml.jinja2 +1 -1
- odxtools/templates/macros/printEnvDataDesc.xml.jinja2 +1 -1
- odxtools/templates/macros/printFunctionalClass.xml.jinja2 +1 -1
- odxtools/templates/macros/printFunctionalGroup.xml.jinja2 +40 -0
- odxtools/templates/macros/printHierarchyElement.xml.jinja2 +24 -0
- odxtools/templates/macros/printLibrary.xml.jinja2 +21 -0
- odxtools/templates/macros/printMux.xml.jinja2 +5 -3
- odxtools/templates/macros/printOdxCategory.xml.jinja2 +28 -0
- odxtools/templates/macros/printParam.xml.jinja2 +18 -19
- odxtools/templates/macros/printProtStack.xml.jinja2 +1 -1
- odxtools/templates/macros/printProtocol.xml.jinja2 +30 -0
- odxtools/templates/macros/printRequest.xml.jinja2 +1 -1
- odxtools/templates/macros/printResponse.xml.jinja2 +1 -1
- odxtools/templates/macros/printService.xml.jinja2 +3 -2
- odxtools/templates/macros/printSingleEcuJob.xml.jinja2 +5 -26
- odxtools/templates/macros/printSpecialData.xml.jinja2 +1 -1
- odxtools/templates/macros/printState.xml.jinja2 +1 -1
- odxtools/templates/macros/printStateChart.xml.jinja2 +1 -1
- odxtools/templates/macros/printStateTransition.xml.jinja2 +1 -1
- odxtools/templates/macros/printStaticField.xml.jinja2 +15 -0
- odxtools/templates/macros/printStructure.xml.jinja2 +1 -1
- odxtools/templates/macros/printSubComponent.xml.jinja2 +104 -0
- odxtools/templates/macros/printTable.xml.jinja2 +4 -5
- odxtools/templates/macros/printUnitSpec.xml.jinja2 +3 -5
- odxtools/uds.py +2 -10
- odxtools/unit.py +4 -8
- odxtools/unitgroup.py +3 -5
- odxtools/unitspec.py +17 -17
- odxtools/utils.py +38 -20
- odxtools/variablegroup.py +32 -0
- odxtools/version.py +2 -2
- odxtools/{write_pdx_file.py → writepdxfile.py} +22 -12
- odxtools/xdoc.py +3 -5
- {odxtools-6.6.1.dist-info → odxtools-9.3.0.dist-info}/METADATA +44 -33
- odxtools-9.3.0.dist-info/RECORD +228 -0
- {odxtools-6.6.1.dist-info → odxtools-9.3.0.dist-info}/WHEEL +1 -1
- odxtools/createcompanydatas.py +0 -17
- odxtools/createsdgs.py +0 -19
- odxtools/diaglayertype.py +0 -30
- odxtools/load_file.py +0 -13
- odxtools/load_odx_d_file.py +0 -6
- odxtools/load_pdx_file.py +0 -8
- odxtools/templates/macros/printVariant.xml.jinja2 +0 -208
- odxtools-6.6.1.dist-info/RECORD +0 -180
- {odxtools-6.6.1.dist-info → odxtools-9.3.0.dist-info}/LICENSE +0 -0
- {odxtools-6.6.1.dist-info → odxtools-9.3.0.dist-info}/entry_points.txt +0 -0
- {odxtools-6.6.1.dist-info → odxtools-9.3.0.dist-info}/top_level.txt +0 -0
@@ -1,25 +1,36 @@
|
|
1
1
|
# SPDX-License-Identifier: MIT
|
2
2
|
from dataclasses import dataclass
|
3
|
-
from typing import List
|
3
|
+
from typing import List, Union, cast
|
4
4
|
from xml.etree import ElementTree
|
5
5
|
|
6
|
-
from ..exceptions import odxrequire
|
6
|
+
from ..exceptions import odxassert, odxrequire
|
7
7
|
from ..odxlink import OdxDocFragment
|
8
|
+
from ..odxtypes import DataType
|
8
9
|
|
9
10
|
|
10
11
|
@dataclass
|
11
12
|
class CompuRationalCoeffs:
|
12
|
-
|
13
|
-
|
13
|
+
value_type: DataType
|
14
|
+
|
15
|
+
numerators: List[Union[int, float]]
|
16
|
+
denominators: List[Union[int, float]]
|
14
17
|
|
15
18
|
@staticmethod
|
16
|
-
def
|
17
|
-
|
19
|
+
def coeffs_from_et(et_element: ElementTree.Element, doc_frags: List[OdxDocFragment], *,
|
20
|
+
value_type: DataType) -> "CompuRationalCoeffs":
|
21
|
+
odxassert(
|
22
|
+
value_type
|
23
|
+
in (DataType.A_UINT32, DataType.A_INT32, DataType.A_FLOAT32, DataType.A_FLOAT64),
|
24
|
+
"Rational coefficients must be of numeric type.")
|
25
|
+
|
18
26
|
numerators = [
|
19
|
-
float(odxrequire(elem.text))
|
27
|
+
cast(float, value_type.from_string(odxrequire(elem.text)))
|
28
|
+
for elem in et_element.iterfind("COMPU-NUMERATOR/V")
|
20
29
|
]
|
21
30
|
denominators = [
|
22
|
-
float(odxrequire(elem.text))
|
31
|
+
cast(float, value_type.from_string(odxrequire(elem.text)))
|
32
|
+
for elem in et_element.iterfind("COMPU-DENOMINATOR/V")
|
23
33
|
]
|
24
34
|
|
25
|
-
return CompuRationalCoeffs(
|
35
|
+
return CompuRationalCoeffs(
|
36
|
+
value_type=value_type, numerators=numerators, denominators=denominators)
|
@@ -3,9 +3,11 @@ from dataclasses import dataclass
|
|
3
3
|
from typing import List, Optional
|
4
4
|
from xml.etree import ElementTree
|
5
5
|
|
6
|
+
from ..description import Description
|
6
7
|
from ..odxlink import OdxDocFragment
|
7
8
|
from ..odxtypes import AtomicOdxType, DataType
|
8
|
-
from
|
9
|
+
from .compuconst import CompuConst
|
10
|
+
from .compuinversevalue import CompuInverseValue
|
9
11
|
from .compurationalcoeffs import CompuRationalCoeffs
|
10
12
|
from .limit import Limit
|
11
13
|
|
@@ -13,49 +15,51 @@ from .limit import Limit
|
|
13
15
|
@dataclass
|
14
16
|
class CompuScale:
|
15
17
|
"""A COMPU-SCALE represents one value range of a COMPU-METHOD.
|
16
|
-
|
17
|
-
Example:
|
18
|
-
|
19
|
-
For a TEXTTABLE compu method a compu scale within COMPU-INTERNAL-TO-PHYS
|
20
|
-
can be defined with
|
21
|
-
```
|
22
|
-
scale = CompuScale(
|
23
|
-
short_label="example_label", # optional: provide a label
|
24
|
-
description="<p>fancy description</p>", # optional: provide a description
|
25
|
-
lower_limit=Limit(0), # required: lower limit
|
26
|
-
upper_limit=Limit(3), # required: upper limit
|
27
|
-
compu_inverse_value=2, # required if lower_limit != upper_limit
|
28
|
-
compu_const="true", # required: physical value to be shown to the user
|
29
|
-
)
|
30
|
-
```
|
31
|
-
|
32
|
-
Almost all attributes are optional but there are compu-method-specific restrictions.
|
33
|
-
E.g., lower_limit must always be defined unless the COMPU-METHOD is of CATEGORY LINEAR or RAT-FUNC.
|
34
|
-
Either `compu_const` or `compu_rational_coeffs` must be defined but never both.
|
35
18
|
"""
|
36
19
|
|
37
20
|
short_label: Optional[str]
|
38
|
-
description: Optional[
|
21
|
+
description: Optional[Description]
|
39
22
|
lower_limit: Optional[Limit]
|
40
23
|
upper_limit: Optional[Limit]
|
41
|
-
compu_inverse_value: Optional[
|
42
|
-
compu_const: Optional[
|
24
|
+
compu_inverse_value: Optional[CompuInverseValue]
|
25
|
+
compu_const: Optional[CompuConst]
|
43
26
|
compu_rational_coeffs: Optional[CompuRationalCoeffs]
|
44
27
|
|
28
|
+
# the following two attributes are not specified for COMPU-SCALE
|
29
|
+
# tags in the XML, but they are required to do anything useful
|
30
|
+
# with compu scales: The domain type is the input set of the
|
31
|
+
# function associated with the compu scale object, whilst the
|
32
|
+
# range type represents the output set. IOW, for scales contained
|
33
|
+
# by the internal-to-physical mapping function, the domain type is
|
34
|
+
# the internal and the range type is the physical type of the
|
35
|
+
# compu method. (Vice versa for scales specified by the
|
36
|
+
# physical-to-internal mapping function.)
|
37
|
+
domain_type: DataType
|
38
|
+
range_type: DataType
|
39
|
+
|
45
40
|
@staticmethod
|
46
|
-
def
|
47
|
-
|
41
|
+
def compuscale_from_et(et_element: ElementTree.Element, doc_frags: List[OdxDocFragment], *,
|
42
|
+
domain_type: DataType, range_type: DataType) -> "CompuScale":
|
48
43
|
short_label = et_element.findtext("SHORT-LABEL")
|
49
|
-
description =
|
50
|
-
|
51
|
-
|
44
|
+
description = Description.from_et(et_element.find("DESC"), doc_frags)
|
45
|
+
|
46
|
+
lower_limit = Limit.limit_from_et(
|
47
|
+
et_element.find("LOWER-LIMIT"), doc_frags, value_type=domain_type)
|
48
|
+
upper_limit = Limit.limit_from_et(
|
49
|
+
et_element.find("UPPER-LIMIT"), doc_frags, value_type=domain_type)
|
52
50
|
|
53
|
-
compu_inverse_value =
|
54
|
-
|
51
|
+
compu_inverse_value = None
|
52
|
+
if (cive := et_element.find("COMPU-INVERSE-VALUE")) is not None:
|
53
|
+
compu_inverse_value = CompuInverseValue.compuvalue_from_et(cive, data_type=domain_type)
|
54
|
+
|
55
|
+
compu_const = None
|
56
|
+
if (cce := et_element.find("COMPU-CONST")) is not None:
|
57
|
+
compu_const = CompuConst.compuvalue_from_et(cce, data_type=range_type)
|
55
58
|
|
56
59
|
compu_rational_coeffs: Optional[CompuRationalCoeffs] = None
|
57
60
|
if (crc_elem := et_element.find("COMPU-RATIONAL-COEFFS")) is not None:
|
58
|
-
compu_rational_coeffs = CompuRationalCoeffs.
|
61
|
+
compu_rational_coeffs = CompuRationalCoeffs.coeffs_from_et(
|
62
|
+
crc_elem, doc_frags, value_type=range_type)
|
59
63
|
|
60
64
|
return CompuScale(
|
61
65
|
short_label=short_label,
|
@@ -64,4 +68,35 @@ class CompuScale:
|
|
64
68
|
upper_limit=upper_limit,
|
65
69
|
compu_inverse_value=compu_inverse_value,
|
66
70
|
compu_const=compu_const,
|
67
|
-
compu_rational_coeffs=compu_rational_coeffs
|
71
|
+
compu_rational_coeffs=compu_rational_coeffs,
|
72
|
+
domain_type=domain_type,
|
73
|
+
range_type=range_type)
|
74
|
+
|
75
|
+
def applies(self, internal_value: AtomicOdxType) -> bool:
|
76
|
+
|
77
|
+
if self.lower_limit is None and self.upper_limit is None:
|
78
|
+
# Everything is allowed: No limits have been specified
|
79
|
+
return True
|
80
|
+
elif self.upper_limit is None:
|
81
|
+
# no upper limit has been specified. the spec says that
|
82
|
+
# the value specified by the lower limit is the only one
|
83
|
+
# which is allowed (cf section 7.3.6.6.1)
|
84
|
+
assert self.lower_limit is not None
|
85
|
+
|
86
|
+
return internal_value == self.lower_limit.value
|
87
|
+
elif self.lower_limit is None:
|
88
|
+
# only the upper limit has been specified. the spec is
|
89
|
+
# ambiguous: it only says that if no upper limit is
|
90
|
+
# defined, the lower limit shall also be used as the upper
|
91
|
+
# limit and a closed interval type ought to be assumed,
|
92
|
+
# but it does not say what happens if the lower limit is
|
93
|
+
# not defined (which is allowed by the XSD). We thus
|
94
|
+
# assume that if only the upper limit is defined, is
|
95
|
+
# treated the same way as if only the lower limit is
|
96
|
+
# specified.
|
97
|
+
assert self.upper_limit is not None
|
98
|
+
|
99
|
+
return internal_value == self.upper_limit.value
|
100
|
+
|
101
|
+
return self.lower_limit.complies_to_lower(internal_value) and \
|
102
|
+
self.upper_limit.complies_to_upper(internal_value)
|
@@ -1,194 +1,53 @@
|
|
1
1
|
# SPDX-License-Identifier: MIT
|
2
|
-
import
|
3
|
-
from typing import Any, Dict, List, Optional
|
2
|
+
from typing import List
|
4
3
|
from xml.etree import ElementTree
|
5
4
|
|
6
|
-
from ..exceptions import
|
7
|
-
from ..globals import logger
|
5
|
+
from ..exceptions import odxraise, odxrequire
|
8
6
|
from ..odxlink import OdxDocFragment
|
9
7
|
from ..odxtypes import DataType
|
8
|
+
from .compucodecompumethod import CompuCodeCompuMethod
|
10
9
|
from .compumethod import CompuMethod
|
11
|
-
from .compuscale import CompuScale
|
12
10
|
from .identicalcompumethod import IdenticalCompuMethod
|
13
|
-
from .limit import IntervalType, Limit
|
14
11
|
from .linearcompumethod import LinearCompuMethod
|
12
|
+
from .ratfunccompumethod import RatFuncCompuMethod
|
15
13
|
from .scalelinearcompumethod import ScaleLinearCompuMethod
|
14
|
+
from .scaleratfunccompumethod import ScaleRatFuncCompuMethod
|
16
15
|
from .tabintpcompumethod import TabIntpCompuMethod
|
17
16
|
from .texttablecompumethod import TexttableCompuMethod
|
18
17
|
|
19
18
|
|
20
|
-
def _parse_compu_scale_to_linear_compu_method(
|
21
|
-
*,
|
22
|
-
scale_element: ElementTree.Element,
|
23
|
-
internal_type: DataType,
|
24
|
-
physical_type: DataType,
|
25
|
-
is_scale_linear: bool = False,
|
26
|
-
**kwargs: Any,
|
27
|
-
) -> LinearCompuMethod:
|
28
|
-
odxassert(physical_type in [
|
29
|
-
DataType.A_FLOAT32,
|
30
|
-
DataType.A_FLOAT64,
|
31
|
-
DataType.A_INT32,
|
32
|
-
DataType.A_UINT32,
|
33
|
-
])
|
34
|
-
odxassert(internal_type in [
|
35
|
-
DataType.A_FLOAT32,
|
36
|
-
DataType.A_FLOAT64,
|
37
|
-
DataType.A_INT32,
|
38
|
-
DataType.A_UINT32,
|
39
|
-
])
|
40
|
-
|
41
|
-
if physical_type.as_python_type() == float:
|
42
|
-
computation_python_type = physical_type.from_string
|
43
|
-
else:
|
44
|
-
computation_python_type = internal_type.from_string
|
45
|
-
|
46
|
-
kwargs = kwargs.copy()
|
47
|
-
kwargs["internal_type"] = internal_type
|
48
|
-
kwargs["physical_type"] = physical_type
|
49
|
-
|
50
|
-
coeffs = odxrequire(scale_element.find("COMPU-RATIONAL-COEFFS"))
|
51
|
-
nums = coeffs.iterfind("COMPU-NUMERATOR/V")
|
52
|
-
|
53
|
-
offset = computation_python_type(odxrequire(next(nums).text))
|
54
|
-
factor_el = next(nums, None)
|
55
|
-
factor = computation_python_type(odxrequire(factor_el.text) if factor_el is not None else "0")
|
56
|
-
denominator = 1.0
|
57
|
-
if (string := coeffs.findtext("COMPU-DENOMINATOR/V")) is not None:
|
58
|
-
denominator = float(string)
|
59
|
-
if denominator == 0:
|
60
|
-
warnings.warn(
|
61
|
-
"CompuMethod: A denominator of zero will lead to divisions by zero.",
|
62
|
-
OdxWarning,
|
63
|
-
stacklevel=1)
|
64
|
-
# Read lower limit
|
65
|
-
internal_lower_limit = Limit.from_et(
|
66
|
-
scale_element.find("LOWER-LIMIT"),
|
67
|
-
internal_type=internal_type,
|
68
|
-
)
|
69
|
-
if internal_lower_limit is None:
|
70
|
-
internal_lower_limit = Limit(0, IntervalType.INFINITE)
|
71
|
-
kwargs["internal_lower_limit"] = internal_lower_limit
|
72
|
-
|
73
|
-
# Read upper limit
|
74
|
-
internal_upper_limit = Limit.from_et(
|
75
|
-
scale_element.find("UPPER-LIMIT"),
|
76
|
-
internal_type=internal_type,
|
77
|
-
)
|
78
|
-
if internal_upper_limit is None:
|
79
|
-
if not is_scale_linear:
|
80
|
-
internal_upper_limit = Limit(0, IntervalType.INFINITE)
|
81
|
-
else:
|
82
|
-
odxassert(internal_lower_limit is not None and
|
83
|
-
internal_lower_limit.interval_type == IntervalType.CLOSED)
|
84
|
-
logger.info("Scale linear without UPPER-LIMIT")
|
85
|
-
internal_upper_limit = internal_lower_limit
|
86
|
-
kwargs["internal_upper_limit"] = internal_upper_limit
|
87
|
-
kwargs["denominator"] = denominator
|
88
|
-
kwargs["factor"] = factor
|
89
|
-
kwargs["offset"] = offset
|
90
|
-
|
91
|
-
return LinearCompuMethod(**kwargs)
|
92
|
-
|
93
|
-
|
94
|
-
def create_compu_default_value(et_element: Optional[ElementTree.Element],
|
95
|
-
doc_frags: List[OdxDocFragment], internal_type: DataType,
|
96
|
-
physical_type: DataType) -> Optional[CompuScale]:
|
97
|
-
if et_element is None:
|
98
|
-
return None
|
99
|
-
compu_const = physical_type.create_from_et(et_element)
|
100
|
-
scale = CompuScale.from_et(
|
101
|
-
et_element, doc_frags, internal_type=internal_type, physical_type=physical_type)
|
102
|
-
scale.compu_const = compu_const
|
103
|
-
return scale
|
104
|
-
|
105
|
-
|
106
19
|
def create_any_compu_method_from_et(et_element: ElementTree.Element,
|
107
|
-
doc_frags: List[OdxDocFragment], internal_type: DataType,
|
20
|
+
doc_frags: List[OdxDocFragment], *, internal_type: DataType,
|
108
21
|
physical_type: DataType) -> CompuMethod:
|
109
|
-
compu_category = et_element.findtext("CATEGORY")
|
110
|
-
odxassert(compu_category in [
|
111
|
-
"IDENTICAL",
|
112
|
-
"LINEAR",
|
113
|
-
"SCALE-LINEAR",
|
114
|
-
"TEXTTABLE",
|
115
|
-
"COMPUCODE",
|
116
|
-
"TAB-INTP",
|
117
|
-
"RAT-FUNC",
|
118
|
-
"SCALE-RAT-FUNC",
|
119
|
-
])
|
120
|
-
|
121
|
-
if et_element.find("COMPU-PHYS-TO-INTERNAL") is not None: # TODO: Is this never used?
|
122
|
-
raise NotImplementedError(f"Found COMPU-PHYS-TO-INTERNAL for category {compu_category}")
|
123
|
-
|
124
|
-
kwargs: Dict[str, Any] = {
|
125
|
-
"physical_type": physical_type,
|
126
|
-
"internal_type": internal_type,
|
127
|
-
}
|
22
|
+
compu_category = odxrequire(et_element.findtext("CATEGORY"))
|
128
23
|
|
129
24
|
if compu_category == "IDENTICAL":
|
130
|
-
|
131
|
-
internal_type
|
132
|
-
(internal_type in [DataType.A_ASCIISTRING, DataType.A_UTF8STRING] and
|
133
|
-
physical_type == DataType.A_UNICODE2STRING),
|
134
|
-
f"Internal type '{internal_type}' and physical type '{physical_type}'"
|
135
|
-
f" must be the same for compu methods of category '{compu_category}'")
|
136
|
-
return IdenticalCompuMethod(internal_type=internal_type, physical_type=physical_type)
|
137
|
-
|
138
|
-
if compu_category == "TEXTTABLE":
|
139
|
-
odxassert(physical_type == DataType.A_UNICODE2STRING)
|
140
|
-
compu_internal_to_phys = odxrequire(et_element.find("COMPU-INTERNAL-TO-PHYS"))
|
141
|
-
|
142
|
-
internal_to_phys: List[CompuScale] = []
|
143
|
-
for scale_elem in compu_internal_to_phys.iterfind("COMPU-SCALES/COMPU-SCALE"):
|
144
|
-
internal_to_phys.append(
|
145
|
-
CompuScale.from_et(
|
146
|
-
scale_elem, doc_frags, internal_type=internal_type,
|
147
|
-
physical_type=physical_type))
|
148
|
-
compu_default_value = create_compu_default_value(
|
149
|
-
et_element.find("COMPU-DEFAULT-VALUE"), doc_frags, **kwargs)
|
150
|
-
|
151
|
-
return TexttableCompuMethod(
|
152
|
-
internal_to_phys=internal_to_phys,
|
153
|
-
compu_default_value=compu_default_value,
|
154
|
-
**kwargs,
|
155
|
-
)
|
156
|
-
|
25
|
+
return IdenticalCompuMethod.compu_method_from_et(
|
26
|
+
et_element, doc_frags, internal_type=internal_type, physical_type=physical_type)
|
157
27
|
elif compu_category == "LINEAR":
|
158
|
-
|
159
|
-
|
160
|
-
scale_elem = odxrequire(et_element.find("COMPU-INTERNAL-TO-PHYS/COMPU-SCALES/COMPU-SCALE"))
|
161
|
-
return _parse_compu_scale_to_linear_compu_method(scale_element=scale_elem, **kwargs)
|
162
|
-
|
28
|
+
return LinearCompuMethod.compu_method_from_et(
|
29
|
+
et_element, doc_frags, internal_type=internal_type, physical_type=physical_type)
|
163
30
|
elif compu_category == "SCALE-LINEAR":
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
return
|
171
|
-
|
31
|
+
return ScaleLinearCompuMethod.compu_method_from_et(
|
32
|
+
et_element, doc_frags, internal_type=internal_type, physical_type=physical_type)
|
33
|
+
elif compu_category == "RAT-FUNC":
|
34
|
+
return RatFuncCompuMethod.compu_method_from_et(
|
35
|
+
et_element, doc_frags, internal_type=internal_type, physical_type=physical_type)
|
36
|
+
elif compu_category == "SCALE-RAT-FUNC":
|
37
|
+
return ScaleRatFuncCompuMethod.compu_method_from_et(
|
38
|
+
et_element, doc_frags, internal_type=internal_type, physical_type=physical_type)
|
39
|
+
elif compu_category == "TEXTTABLE":
|
40
|
+
return TexttableCompuMethod.compu_method_from_et(
|
41
|
+
et_element, doc_frags, internal_type=internal_type, physical_type=physical_type)
|
42
|
+
elif compu_category == "COMPUCODE":
|
43
|
+
return CompuCodeCompuMethod.compu_method_from_et(
|
44
|
+
et_element, doc_frags, internal_type=internal_type, physical_type=physical_type)
|
172
45
|
elif compu_category == "TAB-INTP":
|
173
|
-
|
174
|
-
|
175
|
-
for scale_elem in et_element.iterfind("COMPU-INTERNAL-TO-PHYS/COMPU-SCALES/COMPU-SCALE"):
|
176
|
-
internal_point = internal_type.from_string(
|
177
|
-
odxrequire(scale_elem.findtext("LOWER-LIMIT")))
|
178
|
-
physical_point = physical_type.create_from_et(
|
179
|
-
odxrequire(scale_elem.find("COMPU-CONST")))
|
46
|
+
return TabIntpCompuMethod.compu_method_from_et(
|
47
|
+
et_element, doc_frags, internal_type=internal_type, physical_type=physical_type)
|
180
48
|
|
181
|
-
|
182
|
-
|
183
|
-
if not isinstance(physical_point, (float, int)):
|
184
|
-
odxraise()
|
49
|
+
# TODO: Implement all categories (never instantiate the CompuMethod base class!)
|
50
|
+
odxraise(f"Warning: Computation category {compu_category} is not implemented!")
|
185
51
|
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
return TabIntpCompuMethod(
|
190
|
-
internal_points=internal_points, physical_points=physical_points, **kwargs)
|
191
|
-
|
192
|
-
# TODO: Implement other categories (never instantiate CompuMethod)
|
193
|
-
logger.warning(f"Warning: Computation category {compu_category} is not implemented!")
|
194
|
-
return IdenticalCompuMethod(internal_type=DataType.A_UINT32, physical_type=DataType.A_UINT32)
|
52
|
+
return IdenticalCompuMethod.compu_method_from_et(
|
53
|
+
et_element, doc_frags, internal_type=internal_type, physical_type=physical_type)
|
@@ -1,16 +1,41 @@
|
|
1
1
|
# SPDX-License-Identifier: MIT
|
2
2
|
from dataclasses import dataclass
|
3
|
+
from typing import List
|
4
|
+
from xml.etree import ElementTree
|
3
5
|
|
4
|
-
from ..
|
5
|
-
from
|
6
|
+
from ..exceptions import odxassert
|
7
|
+
from ..odxlink import OdxDocFragment
|
8
|
+
from ..odxtypes import AtomicOdxType, DataType
|
9
|
+
from ..utils import dataclass_fields_asdict
|
10
|
+
from .compumethod import CompuMethod
|
6
11
|
|
7
12
|
|
8
13
|
@dataclass
|
9
14
|
class IdenticalCompuMethod(CompuMethod):
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
15
|
+
"""Identical compu methods just pass through the internal value.
|
16
|
+
|
17
|
+
For details, refer to ASAM specification MCD-2 D (ODX), section 7.3.6.6.2.
|
18
|
+
"""
|
19
|
+
|
20
|
+
@staticmethod
|
21
|
+
def compu_method_from_et(et_element: ElementTree.Element, doc_frags: List[OdxDocFragment], *,
|
22
|
+
internal_type: DataType,
|
23
|
+
physical_type: DataType) -> "IdenticalCompuMethod":
|
24
|
+
cm = CompuMethod.compu_method_from_et(
|
25
|
+
et_element, doc_frags, internal_type=internal_type, physical_type=physical_type)
|
26
|
+
kwargs = dataclass_fields_asdict(cm)
|
27
|
+
|
28
|
+
odxassert(
|
29
|
+
internal_type == physical_type or
|
30
|
+
(internal_type
|
31
|
+
in [DataType.A_ASCIISTRING, DataType.A_UTF8STRING, DataType.A_UNICODE2STRING] and
|
32
|
+
physical_type
|
33
|
+
in [DataType.A_ASCIISTRING, DataType.A_UTF8STRING, DataType.A_UNICODE2STRING]),
|
34
|
+
f"Internal type and physical type must be the same for compu methods of category "
|
35
|
+
f"'{cm.category}' (internal type: '{internal_type.value}', physical type: "
|
36
|
+
f"'{physical_type.value}')")
|
37
|
+
|
38
|
+
return IdenticalCompuMethod(**kwargs)
|
14
39
|
|
15
40
|
def convert_physical_to_internal(self, physical_value: AtomicOdxType) -> AtomicOdxType:
|
16
41
|
return physical_value
|
odxtools/compumethods/limit.py
CHANGED
@@ -1,11 +1,12 @@
|
|
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 List, Optional, overload
|
5
5
|
from xml.etree import ElementTree
|
6
6
|
|
7
|
-
from ..exceptions import
|
8
|
-
from ..
|
7
|
+
from ..exceptions import odxraise
|
8
|
+
from ..odxlink import OdxDocFragment
|
9
|
+
from ..odxtypes import AtomicOdxType, DataType, compare_odx_values
|
9
10
|
|
10
11
|
|
11
12
|
class IntervalType(Enum):
|
@@ -16,42 +17,54 @@ class IntervalType(Enum):
|
|
16
17
|
|
17
18
|
@dataclass
|
18
19
|
class Limit:
|
19
|
-
|
20
|
-
|
20
|
+
value_raw: Optional[str]
|
21
|
+
value_type: Optional[DataType]
|
22
|
+
interval_type: Optional[IntervalType]
|
21
23
|
|
22
24
|
def __post_init__(self) -> None:
|
23
|
-
|
24
|
-
|
25
|
+
self._value: Optional[AtomicOdxType] = None
|
26
|
+
|
27
|
+
if self.value_type is not None:
|
28
|
+
self.set_value_type(self.value_type)
|
29
|
+
|
30
|
+
@staticmethod
|
31
|
+
@overload
|
32
|
+
def limit_from_et(et_element: ElementTree.Element, doc_frags: List[OdxDocFragment],
|
33
|
+
value_type: Optional[DataType]) -> "Limit":
|
34
|
+
...
|
35
|
+
|
36
|
+
@staticmethod
|
37
|
+
@overload
|
38
|
+
def limit_from_et(et_element: None, doc_frags: List[OdxDocFragment],
|
39
|
+
value_type: Optional[DataType]) -> None:
|
40
|
+
...
|
25
41
|
|
26
42
|
@staticmethod
|
27
|
-
def
|
28
|
-
|
43
|
+
def limit_from_et(et_element: Optional[ElementTree.Element], doc_frags: List[OdxDocFragment],
|
44
|
+
value_type: Optional[DataType]) -> Optional["Limit"]:
|
29
45
|
|
30
46
|
if et_element is None:
|
31
47
|
return None
|
32
48
|
|
49
|
+
interval_type = None
|
33
50
|
if (interval_type_str := et_element.get("INTERVAL-TYPE")) is not None:
|
34
51
|
try:
|
35
52
|
interval_type = IntervalType(interval_type_str)
|
36
53
|
except ValueError:
|
37
|
-
interval_type = IntervalType.CLOSED
|
38
54
|
odxraise(f"Encountered unknown interval type '{interval_type_str}'")
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
return Limit(bytes.fromhex(hex_text), interval_type)
|
53
|
-
else:
|
54
|
-
return Limit(internal_type.from_string(odxrequire(et_element.text)), interval_type)
|
55
|
+
|
56
|
+
value_raw = et_element.text
|
57
|
+
|
58
|
+
return Limit(value_raw=value_raw, interval_type=interval_type, value_type=value_type)
|
59
|
+
|
60
|
+
def set_value_type(self, value_type: DataType) -> None:
|
61
|
+
self.value_type = value_type
|
62
|
+
if self.value_raw is not None:
|
63
|
+
self._value = value_type.from_string(self.value_raw)
|
64
|
+
|
65
|
+
@property
|
66
|
+
def value(self) -> Optional[AtomicOdxType]:
|
67
|
+
return self._value
|
55
68
|
|
56
69
|
def complies_to_upper(self, value: AtomicOdxType) -> bool:
|
57
70
|
"""Checks if the value is in the range w.r.t. the upper limit.
|
@@ -60,13 +73,23 @@ class Limit:
|
|
60
73
|
* If the interval type is open, return `value < limit.value`.
|
61
74
|
* If the interval type is infinite, return `True`.
|
62
75
|
"""
|
63
|
-
if self.
|
64
|
-
|
65
|
-
|
66
|
-
return value < self.value # type: ignore[operator]
|
67
|
-
elif self.interval_type == IntervalType.INFINITE:
|
76
|
+
if self._value is None:
|
77
|
+
# if no value is specified, assume interval type INFINITE
|
78
|
+
# (what are we supposed to compare against?)
|
68
79
|
return True
|
69
80
|
|
81
|
+
if self.interval_type is None or self.interval_type == IntervalType.CLOSED:
|
82
|
+
# assume interval type CLOSED if a value was specified,
|
83
|
+
# but no interval type
|
84
|
+
return compare_odx_values(value, self._value) <= 0
|
85
|
+
elif self.interval_type == IntervalType.OPEN:
|
86
|
+
return compare_odx_values(value, self._value) < 0
|
87
|
+
|
88
|
+
if self.interval_type != IntervalType.INFINITE:
|
89
|
+
odxraise("Unhandled interval type {self.interval_type}")
|
90
|
+
|
91
|
+
return True
|
92
|
+
|
70
93
|
def complies_to_lower(self, value: AtomicOdxType) -> bool:
|
71
94
|
"""Checks if the value is in the range w.r.t. the lower limit.
|
72
95
|
|
@@ -74,9 +97,20 @@ class Limit:
|
|
74
97
|
* If the interval type is open, return `limit.value < value`.
|
75
98
|
* If the interval type is infinite, return `True`.
|
76
99
|
"""
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
elif self.interval_type == IntervalType.INFINITE:
|
100
|
+
|
101
|
+
if self._value is None:
|
102
|
+
# if no value is specified, assume interval type INFINITE
|
103
|
+
# (what are we supposed to compare against?)
|
82
104
|
return True
|
105
|
+
|
106
|
+
if self.interval_type is None or self.interval_type == IntervalType.CLOSED:
|
107
|
+
# assume interval type CLOSED if a value was specified,
|
108
|
+
# but no interval type
|
109
|
+
return compare_odx_values(value, self._value) >= 0
|
110
|
+
elif self.interval_type == IntervalType.OPEN:
|
111
|
+
return compare_odx_values(value, self._value) > 0
|
112
|
+
|
113
|
+
if self.interval_type != IntervalType.INFINITE:
|
114
|
+
odxraise("Unhandled interval type {self.interval_type}")
|
115
|
+
|
116
|
+
return True
|