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,10 +1,16 @@
|
|
1
1
|
# SPDX-License-Identifier: MIT
|
2
2
|
from dataclasses import dataclass
|
3
|
-
from typing import
|
3
|
+
from typing import List, Optional
|
4
|
+
from xml.etree import ElementTree
|
5
|
+
|
6
|
+
from typing_extensions import override
|
4
7
|
|
5
8
|
from ..decodestate import DecodeState
|
6
9
|
from ..encodestate import EncodeState
|
7
|
-
from ..
|
10
|
+
from ..exceptions import odxrequire
|
11
|
+
from ..odxlink import OdxDocFragment
|
12
|
+
from ..odxtypes import DataType, ParameterValue
|
13
|
+
from ..utils import dataclass_fields_asdict
|
8
14
|
from .parameter import Parameter, ParameterType
|
9
15
|
|
10
16
|
|
@@ -12,35 +18,47 @@ from .parameter import Parameter, ParameterType
|
|
12
18
|
class ReservedParameter(Parameter):
|
13
19
|
bit_length: int
|
14
20
|
|
21
|
+
@staticmethod
|
22
|
+
@override
|
23
|
+
def from_et(et_element: ElementTree.Element,
|
24
|
+
doc_frags: List[OdxDocFragment]) -> "ReservedParameter":
|
25
|
+
|
26
|
+
kwargs = dataclass_fields_asdict(Parameter.from_et(et_element, doc_frags))
|
27
|
+
|
28
|
+
bit_length = int(odxrequire(et_element.findtext("BIT-LENGTH")))
|
29
|
+
|
30
|
+
return ReservedParameter(bit_length=bit_length, **kwargs)
|
31
|
+
|
15
32
|
@property
|
33
|
+
@override
|
16
34
|
def parameter_type(self) -> ParameterType:
|
17
35
|
return "RESERVED"
|
18
36
|
|
19
37
|
@property
|
38
|
+
@override
|
20
39
|
def is_required(self) -> bool:
|
21
40
|
return False
|
22
41
|
|
23
42
|
@property
|
43
|
+
@override
|
24
44
|
def is_settable(self) -> bool:
|
25
45
|
return False
|
26
46
|
|
47
|
+
@override
|
27
48
|
def get_static_bit_length(self) -> Optional[int]:
|
28
49
|
return self.bit_length
|
29
50
|
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
if self.byte_position is not None:
|
38
|
-
decode_state.cursor_byte_position = decode_state.origin_byte_position + self.byte_position
|
39
|
-
|
40
|
-
decode_state.cursor_byte_position += ((self.bit_position or 0) + self.bit_length + 7) // 8
|
41
|
-
|
42
|
-
decode_state.cursor_byte_position = max(orig_cursor, decode_state.cursor_byte_position)
|
43
|
-
decode_state.cursor_bit_position = 0
|
51
|
+
@override
|
52
|
+
def _encode_positioned_into_pdu(self, physical_value: Optional[ParameterValue],
|
53
|
+
encode_state: EncodeState) -> None:
|
54
|
+
encode_state.cursor_byte_position += (encode_state.cursor_bit_position + self.bit_length +
|
55
|
+
7) // 8
|
56
|
+
encode_state.cursor_bit_position = 0
|
57
|
+
encode_state.emplace_bytes(b'', self.short_name)
|
44
58
|
|
45
|
-
|
46
|
-
|
59
|
+
@override
|
60
|
+
def _decode_positioned_from_pdu(self, decode_state: DecodeState) -> ParameterValue:
|
61
|
+
return decode_state.extract_atomic_value(
|
62
|
+
bit_length=self.bit_length,
|
63
|
+
base_data_type=DataType.A_UINT32,
|
64
|
+
is_highlow_byte_order=False)
|
@@ -1,31 +1,98 @@
|
|
1
1
|
# SPDX-License-Identifier: MIT
|
2
|
+
import getpass
|
2
3
|
from dataclasses import dataclass
|
4
|
+
from datetime import datetime
|
5
|
+
from typing import List, Optional
|
6
|
+
from xml.etree import ElementTree
|
7
|
+
|
8
|
+
from typing_extensions import override
|
3
9
|
|
4
|
-
from ..decodestate import DecodeState
|
5
10
|
from ..encodestate import EncodeState
|
11
|
+
from ..exceptions import odxraise, odxrequire
|
12
|
+
from ..odxlink import OdxDocFragment
|
6
13
|
from ..odxtypes import ParameterValue
|
14
|
+
from ..utils import dataclass_fields_asdict
|
7
15
|
from .parameter import ParameterType
|
8
16
|
from .parameterwithdop import ParameterWithDOP
|
9
17
|
|
18
|
+
# The SYSTEM parameter types mandated by the ODX 2.2 standard. Users
|
19
|
+
# are free to specify additional types, but these must be handled
|
20
|
+
# (cf. table 5 in section 7.3.5.4 of the ASAM ODX 2.2 specification
|
21
|
+
# document.)
|
22
|
+
PREDEFINED_SYSPARAM_VALUES = [
|
23
|
+
"TIMESTAMP", "SECOND", "MINUTE", "HOUR", "TIMEZONE", "DAY", "WEEK", "MONTH", "YEAR", "CENTURY",
|
24
|
+
"TESTERID", "USERID"
|
25
|
+
]
|
26
|
+
|
10
27
|
|
11
28
|
@dataclass
|
12
29
|
class SystemParameter(ParameterWithDOP):
|
13
30
|
sysparam: str
|
14
31
|
|
32
|
+
@staticmethod
|
33
|
+
@override
|
34
|
+
def from_et(et_element: ElementTree.Element,
|
35
|
+
doc_frags: List[OdxDocFragment]) -> "SystemParameter":
|
36
|
+
|
37
|
+
kwargs = dataclass_fields_asdict(ParameterWithDOP.from_et(et_element, doc_frags))
|
38
|
+
|
39
|
+
sysparam = odxrequire(et_element.get("SYSPARAM"))
|
40
|
+
|
41
|
+
return SystemParameter(sysparam=sysparam, **kwargs)
|
42
|
+
|
15
43
|
@property
|
44
|
+
@override
|
16
45
|
def parameter_type(self) -> ParameterType:
|
17
46
|
return "SYSTEM"
|
18
47
|
|
19
48
|
@property
|
49
|
+
@override
|
20
50
|
def is_required(self) -> bool:
|
21
|
-
|
51
|
+
# if a SYSTEM parameter is not specified explicitly, its value
|
52
|
+
# can be determined from the operating system if it is type is
|
53
|
+
# predefined
|
54
|
+
return self.sysparam not in PREDEFINED_SYSPARAM_VALUES
|
22
55
|
|
23
56
|
@property
|
57
|
+
@override
|
24
58
|
def is_settable(self) -> bool:
|
25
|
-
|
59
|
+
return True
|
26
60
|
|
27
|
-
|
28
|
-
|
61
|
+
@override
|
62
|
+
def _encode_positioned_into_pdu(self, physical_value: Optional[ParameterValue],
|
63
|
+
encode_state: EncodeState) -> None:
|
64
|
+
if physical_value is None:
|
65
|
+
# determine the value to be encoded automatically
|
66
|
+
now = datetime.now()
|
67
|
+
if self.sysparam == "TIMESTAMP":
|
68
|
+
physical_value = round(now.timestamp() * 1000).to_bytes(8, "big")
|
69
|
+
elif self.sysparam == "SECOND":
|
70
|
+
physical_value = now.second
|
71
|
+
elif self.sysparam == "MINUTE":
|
72
|
+
physical_value = now.minute
|
73
|
+
elif self.sysparam == "HOUR":
|
74
|
+
physical_value = now.hour
|
75
|
+
elif self.sysparam == "TIMEZONE":
|
76
|
+
if (utc_offset := now.astimezone().utcoffset()) is not None:
|
77
|
+
physical_value = utc_offset.seconds // 60
|
78
|
+
else:
|
79
|
+
physical_value = 0
|
80
|
+
elif self.sysparam == "DAY":
|
81
|
+
physical_value = now.day
|
82
|
+
elif self.sysparam == "WEEK":
|
83
|
+
physical_value = now.isocalendar()[1]
|
84
|
+
elif self.sysparam == "MONTH":
|
85
|
+
physical_value = now.month
|
86
|
+
elif self.sysparam == "YEAR":
|
87
|
+
physical_value = now.year
|
88
|
+
elif self.sysparam == "CENTURY":
|
89
|
+
physical_value = now.year // 100
|
90
|
+
elif self.sysparam == "TESTERID":
|
91
|
+
physical_value = "odxtools".encode("latin1")
|
92
|
+
elif self.sysparam == "USERID":
|
93
|
+
physical_value = getpass.getuser().encode("latin1")
|
94
|
+
else:
|
95
|
+
odxraise(f"Unknown system parameter type '{self.sysparam}'")
|
96
|
+
physical_value = 0
|
29
97
|
|
30
|
-
|
31
|
-
raise NotImplementedError("Decoding a SystemParameter is not implemented yet.")
|
98
|
+
self.dop.encode_into_pdu(physical_value, encode_state=encode_state)
|
@@ -1,32 +1,72 @@
|
|
1
1
|
# SPDX-License-Identifier: MIT
|
2
2
|
from dataclasses import dataclass
|
3
|
+
from typing import TYPE_CHECKING, List, Optional
|
4
|
+
from xml.etree import ElementTree
|
5
|
+
|
6
|
+
from typing_extensions import override
|
3
7
|
|
4
8
|
from ..decodestate import DecodeState
|
5
9
|
from ..encodestate import EncodeState
|
6
|
-
from ..
|
10
|
+
from ..exceptions import odxrequire
|
11
|
+
from ..odxlink import OdxDocFragment, OdxLinkDatabase, OdxLinkRef
|
7
12
|
from ..odxtypes import ParameterValue
|
13
|
+
from ..utils import dataclass_fields_asdict
|
8
14
|
from .parameter import Parameter, ParameterType
|
9
15
|
|
16
|
+
if TYPE_CHECKING:
|
17
|
+
from ..tablerow import TableRow
|
18
|
+
|
10
19
|
|
11
20
|
@dataclass
|
12
21
|
class TableEntryParameter(Parameter):
|
13
22
|
target: str
|
14
23
|
table_row_ref: OdxLinkRef
|
15
24
|
|
25
|
+
@staticmethod
|
26
|
+
@override
|
27
|
+
def from_et(et_element: ElementTree.Element,
|
28
|
+
doc_frags: List[OdxDocFragment]) -> "TableEntryParameter":
|
29
|
+
|
30
|
+
kwargs = dataclass_fields_asdict(Parameter.from_et(et_element, doc_frags))
|
31
|
+
|
32
|
+
target = odxrequire(et_element.findtext("TARGET"))
|
33
|
+
table_row_ref = odxrequire(OdxLinkRef.from_et(et_element.find("TABLE-ROW-REF"), doc_frags))
|
34
|
+
|
35
|
+
return TableEntryParameter(target=target, table_row_ref=table_row_ref, **kwargs)
|
36
|
+
|
37
|
+
@override
|
38
|
+
def _resolve_odxlinks(self, odxlinks: OdxLinkDatabase) -> None:
|
39
|
+
super()._resolve_odxlinks(odxlinks)
|
40
|
+
|
41
|
+
if TYPE_CHECKING:
|
42
|
+
self._table_row = odxlinks.resolve(self.table_row_ref, TableRow)
|
43
|
+
else:
|
44
|
+
self._table_row = odxlinks.resolve(self.table_row_ref)
|
45
|
+
|
16
46
|
@property
|
47
|
+
@override
|
17
48
|
def parameter_type(self) -> ParameterType:
|
18
49
|
return "TABLE-ENTRY"
|
19
50
|
|
20
51
|
@property
|
52
|
+
@override
|
21
53
|
def is_required(self) -> bool:
|
22
|
-
raise NotImplementedError("
|
54
|
+
raise NotImplementedError("TableEntryParameter.is_required is not implemented yet.")
|
23
55
|
|
24
56
|
@property
|
57
|
+
@override
|
25
58
|
def is_settable(self) -> bool:
|
26
|
-
raise NotImplementedError("
|
59
|
+
raise NotImplementedError("TableEntryParameter.is_settable is not implemented yet.")
|
27
60
|
|
28
|
-
|
29
|
-
|
61
|
+
@override
|
62
|
+
def _encode_positioned_into_pdu(self, physical_value: Optional[ParameterValue],
|
63
|
+
encode_state: EncodeState) -> None:
|
64
|
+
raise NotImplementedError("Encoding a TableEntryParameter is not implemented yet.")
|
65
|
+
|
66
|
+
@property
|
67
|
+
def table_row(self) -> "TableRow":
|
68
|
+
return self._table_row
|
30
69
|
|
31
|
-
|
32
|
-
|
70
|
+
@override
|
71
|
+
def _decode_positioned_from_pdu(self, decode_state: DecodeState) -> ParameterValue:
|
72
|
+
raise NotImplementedError("Decoding a TableEntryParameter is not implemented yet.")
|
@@ -1,16 +1,20 @@
|
|
1
1
|
# SPDX-License-Identifier: MIT
|
2
2
|
from dataclasses import dataclass
|
3
|
-
from typing import TYPE_CHECKING, Any, Dict, Optional
|
3
|
+
from typing import TYPE_CHECKING, Any, Dict, List, Optional
|
4
|
+
from xml.etree import ElementTree
|
5
|
+
|
6
|
+
from typing_extensions import final, override
|
4
7
|
|
5
8
|
from ..decodestate import DecodeState
|
6
9
|
from ..encodestate import EncodeState
|
7
10
|
from ..exceptions import DecodeError, EncodeError, odxraise, odxrequire
|
8
|
-
from ..odxlink import OdxLinkDatabase, OdxLinkId, OdxLinkRef
|
11
|
+
from ..odxlink import OdxDocFragment, OdxLinkDatabase, OdxLinkId, OdxLinkRef, resolve_snref
|
9
12
|
from ..odxtypes import ParameterValue
|
13
|
+
from ..snrefcontext import SnRefContext
|
14
|
+
from ..utils import dataclass_fields_asdict
|
10
15
|
from .parameter import Parameter, ParameterType
|
11
16
|
|
12
17
|
if TYPE_CHECKING:
|
13
|
-
from ..diaglayer import DiagLayer
|
14
18
|
from ..table import Table
|
15
19
|
from ..tablerow import TableRow
|
16
20
|
|
@@ -24,17 +28,46 @@ class TableKeyParameter(Parameter):
|
|
24
28
|
table_row_snref: Optional[str]
|
25
29
|
table_row_ref: Optional[OdxLinkRef]
|
26
30
|
|
31
|
+
@staticmethod
|
32
|
+
@override
|
33
|
+
def from_et(et_element: ElementTree.Element,
|
34
|
+
doc_frags: List[OdxDocFragment]) -> "TableKeyParameter":
|
35
|
+
|
36
|
+
kwargs = dataclass_fields_asdict(Parameter.from_et(et_element, doc_frags))
|
37
|
+
|
38
|
+
odx_id = odxrequire(OdxLinkId.from_et(et_element, doc_frags))
|
39
|
+
|
40
|
+
table_ref = OdxLinkRef.from_et(et_element.find("TABLE-REF"), doc_frags)
|
41
|
+
table_snref = None
|
42
|
+
if (table_snref_elem := et_element.find("TABLE-SNREF")) is not None:
|
43
|
+
table_snref = odxrequire(table_snref_elem.get("SHORT-NAME"))
|
44
|
+
|
45
|
+
table_row_ref = OdxLinkRef.from_et(et_element.find("TABLE-ROW-REF"), doc_frags)
|
46
|
+
table_row_snref = None
|
47
|
+
if (table_row_snref_elem := et_element.find("TABLE-ROW-SNREF")) is not None:
|
48
|
+
table_row_snref = odxrequire(table_row_snref_elem.get("SHORT-NAME"))
|
49
|
+
|
50
|
+
return TableKeyParameter(
|
51
|
+
odx_id=odx_id,
|
52
|
+
table_ref=table_ref,
|
53
|
+
table_snref=table_snref,
|
54
|
+
table_row_ref=table_row_ref,
|
55
|
+
table_row_snref=table_row_snref,
|
56
|
+
**kwargs)
|
57
|
+
|
27
58
|
def __post_init__(self) -> None:
|
28
|
-
self._table:
|
29
|
-
self._table_row: Optional[
|
59
|
+
self._table: Table
|
60
|
+
self._table_row: Optional[TableRow] = None
|
30
61
|
if self.table_ref is None and self.table_snref is None and \
|
31
62
|
self.table_row_ref is None and self.table_row_snref is None:
|
32
63
|
odxraise("Either a table or a table row must be defined.")
|
33
64
|
|
34
65
|
@property
|
66
|
+
@override
|
35
67
|
def parameter_type(self) -> ParameterType:
|
36
68
|
return "TABLE-KEY"
|
37
69
|
|
70
|
+
@override
|
38
71
|
def _build_odxlinks(self) -> Dict[OdxLinkId, Any]:
|
39
72
|
result = super()._build_odxlinks()
|
40
73
|
|
@@ -42,104 +75,161 @@ class TableKeyParameter(Parameter):
|
|
42
75
|
|
43
76
|
return result
|
44
77
|
|
78
|
+
@override
|
45
79
|
def _resolve_odxlinks(self, odxlinks: OdxLinkDatabase) -> None:
|
46
80
|
super()._resolve_odxlinks(odxlinks)
|
47
81
|
|
48
82
|
# Either table_ref or table_row_ref will be defined
|
49
|
-
if self.table_ref:
|
83
|
+
if self.table_ref is not None:
|
50
84
|
if TYPE_CHECKING:
|
51
85
|
self._table = odxlinks.resolve(self.table_ref, Table)
|
52
86
|
else:
|
53
87
|
self._table = odxlinks.resolve(self.table_ref)
|
54
88
|
|
55
|
-
if self.table_row_ref:
|
89
|
+
if self.table_row_ref is not None:
|
56
90
|
if TYPE_CHECKING:
|
57
91
|
self._table_row = odxlinks.resolve(self.table_row_ref, TableRow)
|
58
92
|
else:
|
59
93
|
self._table_row = odxlinks.resolve(self.table_row_ref)
|
60
|
-
self._table = self._table_row.table
|
61
94
|
|
62
|
-
|
63
|
-
|
95
|
+
if self.table_ref is None and self.table_snref is None:
|
96
|
+
if TYPE_CHECKING:
|
97
|
+
self._table = odxlinks.resolve(self._table_row.table_ref, Table)
|
98
|
+
else:
|
99
|
+
self._table = odxlinks.resolve(self._table_row.table_ref)
|
100
|
+
|
101
|
+
@override
|
102
|
+
def _resolve_snrefs(self, context: SnRefContext) -> None:
|
103
|
+
super()._resolve_snrefs(context)
|
64
104
|
|
65
105
|
if self.table_snref is not None:
|
66
|
-
|
67
|
-
|
106
|
+
tables = odxrequire(context.diag_layer).diag_data_dictionary_spec.tables
|
107
|
+
if TYPE_CHECKING:
|
108
|
+
self._table = resolve_snref(self.table_snref, tables, Table)
|
109
|
+
else:
|
110
|
+
self._table = resolve_snref(self.table_snref, tables)
|
68
111
|
if self.table_row_snref is not None:
|
69
112
|
# make sure that we know the table to which the table row
|
70
113
|
# SNREF is relative to.
|
71
114
|
table = odxrequire(
|
72
|
-
self._table, "If a table
|
73
|
-
"
|
74
|
-
|
115
|
+
self._table, "If a table row is referenced via short name, a table must "
|
116
|
+
"be referenced as well")
|
117
|
+
if TYPE_CHECKING:
|
118
|
+
self._table_row = resolve_snref(self.table_row_snref, table.table_rows, TableRow)
|
119
|
+
else:
|
120
|
+
self._table_row = resolve_snref(self.table_row_snref, table.table_rows)
|
75
121
|
|
76
122
|
@property
|
77
123
|
def table(self) -> "Table":
|
78
|
-
|
79
|
-
return self._table
|
80
|
-
if self._table_row is not None:
|
81
|
-
return self._table_row.table
|
82
|
-
odxraise(f'Could not resolve the table of {self.short_name}')
|
124
|
+
return self._table
|
83
125
|
|
84
126
|
@property
|
85
127
|
def table_row(self) -> Optional["TableRow"]:
|
86
128
|
return self._table_row
|
87
129
|
|
88
130
|
@property
|
131
|
+
@override
|
89
132
|
def is_required(self) -> bool:
|
90
133
|
# TABLE-KEY parameters can be implicitly determined from the
|
91
134
|
# corresponding TABLE-STRUCT
|
92
135
|
return False
|
93
136
|
|
94
137
|
@property
|
138
|
+
@override
|
95
139
|
def is_settable(self) -> bool:
|
96
140
|
return True
|
97
141
|
|
98
|
-
|
99
|
-
|
142
|
+
@override
|
143
|
+
@final
|
144
|
+
def _encode_positioned_into_pdu(self, physical_value: Optional[ParameterValue],
|
145
|
+
encode_state: EncodeState) -> None:
|
146
|
+
# if you get this exception, you ought to use
|
147
|
+
# `.encode_placeholder_into_pdu()` followed by (after the
|
148
|
+
# value of the table key has been determined)
|
149
|
+
# `.encode_value_into_pdu()`.
|
150
|
+
raise RuntimeError("_encode_positioned_into_pdu() cannot be called for table keys.")
|
100
151
|
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
# the real data...
|
152
|
+
def encode_placeholder_into_pdu(self, physical_value: Optional[ParameterValue],
|
153
|
+
encode_state: EncodeState) -> None:
|
154
|
+
|
155
|
+
if physical_value is not None:
|
106
156
|
key_dop = self.table.key_dop
|
107
157
|
if key_dop is None:
|
108
|
-
|
109
|
-
|
110
|
-
|
158
|
+
odxraise(
|
159
|
+
f"Table '{self.table.short_name}' does not define "
|
160
|
+
f"a KEY-DOP, but is used by TABLE-KEY parameter "
|
161
|
+
f"'{self.short_name}'", EncodeError)
|
162
|
+
return
|
163
|
+
|
164
|
+
if not isinstance(physical_value, str):
|
165
|
+
odxraise(f"Invalid type for for table key '{self.short_name}' specified. "
|
166
|
+
f"(expect name of table row.)")
|
167
|
+
|
168
|
+
tkv = encode_state.table_keys.get(self.short_name)
|
169
|
+
if tkv is not None and tkv != physical_value:
|
170
|
+
odxraise(f"Got conflicting values for table key {self.short_name}: "
|
171
|
+
f"{tkv} and {physical_value!r}")
|
172
|
+
|
173
|
+
encode_state.table_keys[self.short_name] = physical_value
|
111
174
|
|
112
|
-
|
113
|
-
|
114
|
-
|
175
|
+
pos = encode_state.cursor_byte_position
|
176
|
+
if self.byte_position is not None:
|
177
|
+
pos = encode_state.origin_byte_position + self.byte_position
|
178
|
+
encode_state.key_pos[self.short_name] = pos
|
179
|
+
encode_state.cursor_byte_position = pos
|
180
|
+
encode_state.cursor_bit_position = self.bit_position or 0
|
181
|
+
|
182
|
+
key_dop = self.table.key_dop
|
183
|
+
if key_dop is None:
|
184
|
+
odxraise(f"No KEY-DOP specified for table {self.table.short_name}")
|
185
|
+
return
|
186
|
+
|
187
|
+
sz = key_dop.get_static_bit_length()
|
188
|
+
if sz is None:
|
189
|
+
odxraise("The DOP of table key {self.short_name} must exhibit a fixed size.",
|
190
|
+
EncodeError)
|
191
|
+
return
|
192
|
+
|
193
|
+
# emplace a value of zero into the encode state, but pretend the bits not to be used
|
194
|
+
n = sz + encode_state.cursor_bit_position
|
195
|
+
tmp_val = b'\x00' * ((n + 7) // 8)
|
196
|
+
encode_state.emplace_bytes(tmp_val, obj_used_mask=tmp_val)
|
197
|
+
|
198
|
+
encode_state.cursor_bit_position = 0
|
199
|
+
|
200
|
+
def encode_value_into_pdu(self, encode_state: EncodeState) -> None:
|
115
201
|
|
116
|
-
|
202
|
+
key_dop = self.table.key_dop
|
203
|
+
if key_dop is None:
|
204
|
+
odxraise(
|
205
|
+
f"Table '{self.table.short_name}' does not define "
|
206
|
+
f"a KEY-DOP, but is used by TABLE-KEY parameter "
|
207
|
+
f"'{self.short_name}'", EncodeError)
|
208
|
+
return
|
209
|
+
|
210
|
+
if self.short_name not in encode_state.table_keys:
|
211
|
+
odxraise(f"Table key {self.short_name} has not been defined before "
|
212
|
+
f"it is required.", EncodeError)
|
213
|
+
return
|
214
|
+
else:
|
215
|
+
tr_short_name = encode_state.table_keys[self.short_name]
|
117
216
|
|
118
|
-
# the table key
|
119
|
-
# into the PDU.
|
217
|
+
# We need to encode the table key using the associated DOP into the PDU.
|
120
218
|
tr_candidates = [x for x in self.table.table_rows if x.short_name == tr_short_name]
|
121
219
|
if len(tr_candidates) == 0:
|
122
|
-
|
220
|
+
odxraise(f"No table row with short name '{tr_short_name}' found", EncodeError)
|
221
|
+
return
|
123
222
|
elif len(tr_candidates) > 1:
|
124
|
-
|
223
|
+
odxraise(f"Multiple rows exhibiting short name '{tr_short_name}'", EncodeError)
|
125
224
|
tr = tr_candidates[0]
|
126
225
|
|
127
|
-
|
128
|
-
|
129
|
-
raise EncodeError(f"Table '{self.table.short_name}' does not define "
|
130
|
-
f"a KEY-DOP, but is used in TABLE-KEY parameter "
|
131
|
-
f"'{self.short_name}'")
|
132
|
-
bit_position = 0 if self.bit_position is None else self.bit_position
|
133
|
-
return key_dop.convert_physical_to_bytes(tr.key, encode_state, bit_position=bit_position)
|
134
|
-
|
135
|
-
def encode_into_pdu(self, encode_state: EncodeState) -> bytes:
|
136
|
-
return super().encode_into_pdu(encode_state)
|
226
|
+
encode_state.cursor_byte_position = encode_state.key_pos[self.short_name]
|
227
|
+
encode_state.cursor_bit_position = self.bit_position or 0
|
137
228
|
|
138
|
-
|
139
|
-
orig_cursor = decode_state.cursor_byte_position
|
140
|
-
if self.byte_position is not None:
|
141
|
-
decode_state.cursor_byte_position = decode_state.origin_byte_position + self.byte_position
|
229
|
+
key_dop.encode_into_pdu(encode_state=encode_state, physical_value=odxrequire(tr.key))
|
142
230
|
|
231
|
+
@override
|
232
|
+
def _decode_positioned_from_pdu(self, decode_state: DecodeState) -> ParameterValue:
|
143
233
|
if self.table_row is not None:
|
144
234
|
# the table row to be used is statically specified -> no
|
145
235
|
# need to decode anything!
|
@@ -147,7 +237,6 @@ class TableKeyParameter(Parameter):
|
|
147
237
|
else:
|
148
238
|
# Use DOP to decode
|
149
239
|
key_dop = odxrequire(self.table.key_dop)
|
150
|
-
decode_state.cursor_bit_position = self.bit_position or 0
|
151
240
|
key_dop_val = key_dop.decode_from_pdu(decode_state)
|
152
241
|
|
153
242
|
table_row_candidates = [x for x in self.table.table_rows if x.key == key_dop_val]
|
@@ -162,6 +251,4 @@ class TableKeyParameter(Parameter):
|
|
162
251
|
# update the decode_state's table key
|
163
252
|
decode_state.table_keys[self.short_name] = table_row
|
164
253
|
|
165
|
-
decode_state.cursor_byte_position = max(decode_state.cursor_byte_position, orig_cursor)
|
166
|
-
|
167
254
|
return phys_val
|