odxtools 9.7.0__py3-none-any.whl → 10.1.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/additionalaudience.py +7 -7
- odxtools/admindata.py +14 -13
- odxtools/audience.py +17 -17
- odxtools/basecomparam.py +9 -8
- odxtools/basevariantpattern.py +9 -10
- odxtools/basicstructure.py +15 -15
- odxtools/cli/_print_utils.py +34 -22
- odxtools/cli/browse.py +8 -8
- odxtools/cli/compare.py +24 -24
- odxtools/cli/decode.py +3 -4
- odxtools/cli/find.py +4 -5
- odxtools/cli/list.py +6 -6
- odxtools/cli/main.py +2 -2
- odxtools/cli/snoop.py +3 -3
- odxtools/codec.py +3 -3
- odxtools/commrelation.py +18 -17
- odxtools/companydata.py +13 -13
- odxtools/companydocinfo.py +15 -17
- odxtools/companyrevisioninfo.py +9 -9
- odxtools/companyspecificinfo.py +11 -13
- odxtools/comparam.py +8 -7
- odxtools/comparaminstance.py +14 -14
- odxtools/comparamspec.py +10 -11
- odxtools/comparamsubset.py +17 -25
- odxtools/complexcomparam.py +14 -14
- odxtools/complexdop.py +1 -1
- odxtools/compositecodec.py +8 -8
- odxtools/compumethods/compucodecompumethod.py +7 -7
- odxtools/compumethods/compuconst.py +5 -6
- odxtools/compumethods/compudefaultvalue.py +2 -3
- odxtools/compumethods/compuinternaltophys.py +13 -12
- odxtools/compumethods/compumethod.py +10 -9
- odxtools/compumethods/compuphystointernal.py +13 -12
- odxtools/compumethods/compurationalcoeffs.py +7 -7
- odxtools/compumethods/compuscale.py +15 -16
- odxtools/compumethods/createanycompumethod.py +12 -13
- odxtools/compumethods/identicalcompumethod.py +4 -5
- odxtools/compumethods/limit.py +14 -14
- odxtools/compumethods/linearcompumethod.py +5 -5
- odxtools/compumethods/linearsegment.py +10 -11
- odxtools/compumethods/ratfunccompumethod.py +6 -6
- odxtools/compumethods/ratfuncsegment.py +7 -8
- odxtools/compumethods/scalelinearcompumethod.py +9 -9
- odxtools/compumethods/scaleratfunccompumethod.py +7 -7
- odxtools/compumethods/tabintpcompumethod.py +10 -13
- odxtools/compumethods/texttablecompumethod.py +6 -6
- odxtools/createanycomparam.py +5 -7
- odxtools/createanydiagcodedtype.py +7 -8
- odxtools/database.py +34 -31
- odxtools/dataobjectproperty.py +19 -20
- odxtools/decodestate.py +5 -5
- odxtools/description.py +9 -9
- odxtools/determinenumberofitems.py +8 -7
- odxtools/diagcodedtype.py +10 -10
- odxtools/diagcomm.py +29 -30
- odxtools/diagdatadictionaryspec.py +36 -36
- odxtools/diaglayercontainer.py +35 -34
- odxtools/diaglayers/basevariant.py +14 -12
- odxtools/diaglayers/basevariantraw.py +22 -23
- odxtools/diaglayers/diaglayer.py +24 -22
- odxtools/diaglayers/diaglayerraw.py +43 -52
- odxtools/diaglayers/diaglayertype.py +1 -2
- odxtools/diaglayers/ecushareddata.py +9 -9
- odxtools/diaglayers/ecushareddataraw.py +15 -16
- odxtools/diaglayers/ecuvariant.py +15 -13
- odxtools/diaglayers/ecuvariantraw.py +21 -22
- odxtools/diaglayers/functionalgroup.py +12 -11
- odxtools/diaglayers/functionalgroupraw.py +17 -18
- odxtools/diaglayers/hierarchyelement.py +48 -54
- odxtools/diaglayers/hierarchyelementraw.py +10 -11
- odxtools/diaglayers/protocol.py +7 -7
- odxtools/diaglayers/protocolraw.py +13 -14
- odxtools/diagnostictroublecode.py +15 -17
- odxtools/diagservice.py +28 -27
- odxtools/diagvariable.py +24 -25
- odxtools/docrevision.py +18 -17
- odxtools/dopbase.py +13 -14
- odxtools/dtcconnector.py +8 -7
- odxtools/dtcdop.py +24 -20
- odxtools/dynamicendmarkerfield.py +10 -9
- odxtools/dynamiclengthfield.py +10 -9
- odxtools/dyndefinedspec.py +10 -10
- odxtools/dynenddopref.py +9 -9
- odxtools/dyniddefmodeinfo.py +21 -21
- odxtools/ecuvariantpattern.py +8 -10
- odxtools/element.py +12 -13
- odxtools/encodestate.py +11 -11
- odxtools/encoding.py +2 -3
- odxtools/endofpdufield.py +9 -10
- odxtools/envdataconnector.py +8 -8
- odxtools/environmentdata.py +7 -9
- odxtools/environmentdatadescription.py +18 -17
- odxtools/exceptions.py +5 -5
- odxtools/externalaccessmethod.py +4 -6
- odxtools/externaldoc.py +6 -6
- odxtools/field.py +15 -15
- odxtools/functionalclass.py +9 -9
- odxtools/inputparam.py +11 -10
- odxtools/internalconstr.py +10 -11
- odxtools/isotp_state_machine.py +12 -11
- odxtools/leadinglengthinfotype.py +4 -6
- odxtools/library.py +9 -8
- odxtools/linkeddtcdop.py +9 -8
- odxtools/loadfile.py +5 -6
- odxtools/matchingbasevariantparameter.py +5 -6
- odxtools/matchingparameter.py +10 -10
- odxtools/message.py +1 -1
- odxtools/minmaxlengthtype.py +6 -7
- odxtools/modification.py +7 -6
- odxtools/multiplexer.py +54 -18
- odxtools/multiplexercase.py +13 -13
- odxtools/multiplexerdefaultcase.py +11 -10
- odxtools/multiplexerswitchkey.py +8 -8
- odxtools/nameditemlist.py +13 -13
- odxtools/negoutputparam.py +8 -8
- odxtools/obd.py +1 -2
- odxtools/odxcategory.py +14 -26
- odxtools/odxdoccontext.py +16 -0
- odxtools/odxlink.py +23 -25
- odxtools/odxtypes.py +18 -15
- odxtools/outputparam.py +9 -8
- odxtools/parameterinfo.py +1 -1
- odxtools/parameters/codedconstparameter.py +10 -10
- odxtools/parameters/createanyparameter.py +15 -16
- odxtools/parameters/dynamicparameter.py +5 -7
- odxtools/parameters/lengthkeyparameter.py +10 -10
- odxtools/parameters/matchingrequestparameter.py +6 -7
- odxtools/parameters/nrcconstparameter.py +13 -13
- odxtools/parameters/parameter.py +17 -18
- odxtools/parameters/parameterwithdop.py +13 -13
- odxtools/parameters/physicalconstantparameter.py +8 -7
- odxtools/parameters/reservedparameter.py +6 -8
- odxtools/parameters/systemparameter.py +5 -7
- odxtools/parameters/tableentryparameter.py +8 -8
- odxtools/parameters/tablekeyparameter.py +17 -17
- odxtools/parameters/tablestructparameter.py +11 -11
- odxtools/parameters/valueparameter.py +11 -11
- odxtools/paramlengthinfotype.py +10 -9
- odxtools/parentref.py +15 -13
- odxtools/physicaldimension.py +15 -15
- odxtools/physicaltype.py +5 -6
- odxtools/posresponsesuppressible.py +11 -12
- odxtools/preconditionstateref.py +11 -11
- odxtools/progcode.py +11 -10
- odxtools/protstack.py +10 -9
- odxtools/relateddiagcommref.py +5 -6
- odxtools/relateddoc.py +11 -10
- odxtools/request.py +18 -19
- odxtools/response.py +19 -20
- odxtools/scaleconstr.py +8 -9
- odxtools/servicebinner.py +5 -5
- odxtools/singleecujob.py +16 -15
- odxtools/snrefcontext.py +3 -3
- odxtools/specialdata.py +8 -7
- odxtools/specialdatagroup.py +17 -17
- odxtools/specialdatagroupcaption.py +7 -6
- odxtools/standardlengthtype.py +14 -22
- odxtools/state.py +7 -6
- odxtools/statechart.py +12 -11
- odxtools/statemachine.py +4 -3
- odxtools/statetransition.py +9 -9
- odxtools/statetransitionref.py +19 -19
- odxtools/staticfield.py +9 -7
- odxtools/structure.py +5 -6
- odxtools/subcomponent.py +20 -18
- odxtools/subcomponentparamconnector.py +10 -9
- odxtools/subcomponentpattern.py +9 -9
- odxtools/swvariable.py +6 -7
- odxtools/table.py +25 -26
- odxtools/tablediagcommconnector.py +9 -8
- odxtools/tablerow.py +64 -43
- odxtools/tablerowconnector.py +8 -8
- odxtools/teammember.py +16 -15
- odxtools/templates/macros/printParentRef.xml.jinja2 +3 -1
- odxtools/text.py +4 -5
- odxtools/uds.py +2 -3
- odxtools/unit.py +14 -13
- odxtools/unitgroup.py +11 -10
- odxtools/unitspec.py +18 -19
- odxtools/utils.py +3 -3
- odxtools/variablegroup.py +5 -6
- odxtools/variantmatcher.py +10 -10
- odxtools/variantpattern.py +5 -6
- odxtools/version.py +2 -2
- odxtools/writepdxfile.py +5 -24
- odxtools/xdoc.py +13 -12
- {odxtools-9.7.0.dist-info → odxtools-10.1.0.dist-info}/METADATA +4 -5
- odxtools-10.1.0.dist-info/RECORD +265 -0
- {odxtools-9.7.0.dist-info → odxtools-10.1.0.dist-info}/WHEEL +1 -1
- odxtools-9.7.0.dist-info/RECORD +0 -264
- {odxtools-9.7.0.dist-info → odxtools-10.1.0.dist-info}/entry_points.txt +0 -0
- {odxtools-9.7.0.dist-info → odxtools-10.1.0.dist-info}/licenses/LICENSE +0 -0
- {odxtools-9.7.0.dist-info → odxtools-10.1.0.dist-info}/top_level.txt +0 -0
odxtools/loadfile.py
CHANGED
@@ -1,19 +1,18 @@
|
|
1
1
|
# SPDX-License-Identifier: MIT
|
2
2
|
import os
|
3
3
|
from pathlib import Path
|
4
|
-
from typing import Union
|
5
4
|
|
6
5
|
from .database import Database
|
7
6
|
|
8
7
|
|
9
|
-
def load_pdx_file(pdx_file:
|
8
|
+
def load_pdx_file(pdx_file: str | Path) -> Database:
|
10
9
|
db = Database()
|
11
10
|
db.add_pdx_file(str(pdx_file))
|
12
11
|
db.refresh()
|
13
12
|
return db
|
14
13
|
|
15
14
|
|
16
|
-
def load_odx_d_file(odx_d_file_name:
|
15
|
+
def load_odx_d_file(odx_d_file_name: str | Path) -> Database:
|
17
16
|
db = Database()
|
18
17
|
db.add_odx_file(str(odx_d_file_name))
|
19
18
|
db.refresh()
|
@@ -21,7 +20,7 @@ def load_odx_d_file(odx_d_file_name: Union[str, Path]) -> Database:
|
|
21
20
|
return db
|
22
21
|
|
23
22
|
|
24
|
-
def load_file(file_name:
|
23
|
+
def load_file(file_name: str | Path) -> Database:
|
25
24
|
if str(file_name).lower().endswith(".pdx"):
|
26
25
|
return load_pdx_file(str(file_name))
|
27
26
|
elif str(file_name).lower().endswith(".odx-d"):
|
@@ -30,7 +29,7 @@ def load_file(file_name: Union[str, Path]) -> Database:
|
|
30
29
|
raise RuntimeError(f"Could not guess the file format of file '{file_name}'!")
|
31
30
|
|
32
31
|
|
33
|
-
def load_files(*file_names:
|
32
|
+
def load_files(*file_names: str | Path) -> Database:
|
34
33
|
db = Database()
|
35
34
|
for file_name in file_names:
|
36
35
|
p = Path(file_name)
|
@@ -45,7 +44,7 @@ def load_files(*file_names: Union[str, Path]) -> Database:
|
|
45
44
|
return db
|
46
45
|
|
47
46
|
|
48
|
-
def load_directory(dir_name:
|
47
|
+
def load_directory(dir_name: str | Path) -> Database:
|
49
48
|
db = Database()
|
50
49
|
for file_name in os.listdir(str(dir_name)):
|
51
50
|
p = Path(dir_name) / file_name
|
@@ -1,15 +1,14 @@
|
|
1
1
|
# SPDX-License-Identifier: MIT
|
2
2
|
from dataclasses import dataclass
|
3
|
-
from typing import List, Optional
|
4
3
|
from xml.etree import ElementTree
|
5
4
|
|
6
5
|
from .matchingparameter import MatchingParameter
|
7
|
-
from .
|
6
|
+
from .odxdoccontext import OdxDocContext
|
8
7
|
from .odxtypes import odxstr_to_bool
|
9
8
|
from .utils import dataclass_fields_asdict
|
10
9
|
|
11
10
|
|
12
|
-
@dataclass
|
11
|
+
@dataclass(kw_only=True)
|
13
12
|
class MatchingBaseVariantParameter(MatchingParameter):
|
14
13
|
"""A description of a parameter used for base variant matching.
|
15
14
|
|
@@ -18,7 +17,7 @@ class MatchingBaseVariantParameter(MatchingParameter):
|
|
18
17
|
additional subtag `USE-PHYSICAL-ADDRESSING`.
|
19
18
|
"""
|
20
19
|
|
21
|
-
use_physical_addressing_raw:
|
20
|
+
use_physical_addressing_raw: bool | None = None
|
22
21
|
|
23
22
|
@property
|
24
23
|
def use_physical_addressing(self) -> bool:
|
@@ -26,9 +25,9 @@ class MatchingBaseVariantParameter(MatchingParameter):
|
|
26
25
|
|
27
26
|
@staticmethod
|
28
27
|
def from_et(et_element: ElementTree.Element,
|
29
|
-
|
28
|
+
context: OdxDocContext) -> "MatchingBaseVariantParameter":
|
30
29
|
|
31
|
-
kwargs = dataclass_fields_asdict(MatchingParameter.from_et(et_element,
|
30
|
+
kwargs = dataclass_fields_asdict(MatchingParameter.from_et(et_element, context))
|
32
31
|
|
33
32
|
use_physical_addressing_raw = odxstr_to_bool(et_element.findtext("USE-PHYSICAL-ADDRESSING"))
|
34
33
|
|
odxtools/matchingparameter.py
CHANGED
@@ -1,18 +1,19 @@
|
|
1
1
|
# SPDX-License-Identifier: MIT
|
2
2
|
from dataclasses import dataclass
|
3
|
-
from typing import Any,
|
3
|
+
from typing import Any, cast
|
4
4
|
from xml.etree import ElementTree
|
5
5
|
|
6
6
|
from .diaglayers.diaglayer import DiagLayer
|
7
7
|
from .diagnostictroublecode import DiagnosticTroubleCode
|
8
8
|
from .diagservice import DiagService
|
9
9
|
from .exceptions import odxraise, odxrequire
|
10
|
-
from .
|
10
|
+
from .odxdoccontext import OdxDocContext
|
11
|
+
from .odxlink import OdxLinkDatabase, OdxLinkId, resolve_snref
|
11
12
|
from .odxtypes import BytesTypes, ParameterValue, ParameterValueDict
|
12
13
|
from .snrefcontext import SnRefContext
|
13
14
|
|
14
15
|
|
15
|
-
@dataclass
|
16
|
+
@dataclass(kw_only=True)
|
16
17
|
class MatchingParameter:
|
17
18
|
"""According to ISO 22901, a MatchingParameter contains a string
|
18
19
|
value identifying the active ECU or base variant. Moreover, it
|
@@ -34,12 +35,11 @@ class MatchingParameter:
|
|
34
35
|
# or negative response. What it probably actually wants to say is
|
35
36
|
# that any response that can possibly be received shall exhibit
|
36
37
|
# the referenced parameter.
|
37
|
-
out_param_if_snref:
|
38
|
-
out_param_if_snpathref:
|
38
|
+
out_param_if_snref: str | None = None
|
39
|
+
out_param_if_snpathref: str | None = None
|
39
40
|
|
40
41
|
@staticmethod
|
41
|
-
def from_et(et_element: ElementTree.Element,
|
42
|
-
doc_frags: List[OdxDocFragment]) -> "MatchingParameter":
|
42
|
+
def from_et(et_element: ElementTree.Element, context: OdxDocContext) -> "MatchingParameter":
|
43
43
|
|
44
44
|
expected_value = odxrequire(et_element.findtext("EXPECTED-VALUE"))
|
45
45
|
diag_comm_snref = odxrequire(
|
@@ -61,7 +61,7 @@ class MatchingParameter:
|
|
61
61
|
out_param_if_snpathref=out_param_if_snpathref,
|
62
62
|
)
|
63
63
|
|
64
|
-
def _build_odxlinks(self) ->
|
64
|
+
def _build_odxlinks(self) -> dict[OdxLinkId, Any]:
|
65
65
|
return {}
|
66
66
|
|
67
67
|
def _resolve_odxlinks(self, odxlinks: OdxLinkDatabase) -> None:
|
@@ -93,7 +93,7 @@ class MatchingParameter:
|
|
93
93
|
|
94
94
|
return self.__matches(param_dict, snpath_chunks)
|
95
95
|
|
96
|
-
def __matches(self, param_dict: ParameterValue, snpath_chunks:
|
96
|
+
def __matches(self, param_dict: ParameterValue, snpath_chunks: list[str]) -> bool:
|
97
97
|
if len(snpath_chunks) == 0:
|
98
98
|
parameter_value = param_dict
|
99
99
|
if isinstance(parameter_value, dict):
|
@@ -105,7 +105,7 @@ class MatchingParameter:
|
|
105
105
|
# floating point
|
106
106
|
return abs(float(self.expected_value) - parameter_value) < 1e-8
|
107
107
|
elif isinstance(parameter_value, BytesTypes):
|
108
|
-
return parameter_value.hex().upper() == self.expected_value.upper()
|
108
|
+
return bytes(parameter_value).hex().upper() == self.expected_value.upper()
|
109
109
|
elif isinstance(parameter_value, DiagnosticTroubleCode):
|
110
110
|
# TODO: what happens if non-numerical DTCs like
|
111
111
|
# "U123456" are specified? Is specifying DTCs even
|
odxtools/message.py
CHANGED
odxtools/minmaxlengthtype.py
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
# SPDX-License-Identifier: MIT
|
2
2
|
from dataclasses import dataclass
|
3
|
-
from typing import
|
3
|
+
from typing import cast
|
4
4
|
from xml.etree import ElementTree
|
5
5
|
|
6
6
|
from typing_extensions import override
|
@@ -10,15 +10,15 @@ from .diagcodedtype import DctType, DiagCodedType
|
|
10
10
|
from .encodestate import EncodeState
|
11
11
|
from .encoding import get_string_encoding
|
12
12
|
from .exceptions import DecodeError, EncodeError, odxassert, odxraise, odxrequire
|
13
|
-
from .
|
13
|
+
from .odxdoccontext import OdxDocContext
|
14
14
|
from .odxtypes import AtomicOdxType, BytesTypes, DataType
|
15
15
|
from .termination import Termination
|
16
16
|
from .utils import dataclass_fields_asdict
|
17
17
|
|
18
18
|
|
19
|
-
@dataclass
|
19
|
+
@dataclass(kw_only=True)
|
20
20
|
class MinMaxLengthType(DiagCodedType):
|
21
|
-
max_length:
|
21
|
+
max_length: int | None = None
|
22
22
|
min_length: int
|
23
23
|
termination: Termination
|
24
24
|
|
@@ -28,9 +28,8 @@ class MinMaxLengthType(DiagCodedType):
|
|
28
28
|
|
29
29
|
@staticmethod
|
30
30
|
@override
|
31
|
-
def from_et(et_element: ElementTree.Element,
|
32
|
-
|
33
|
-
kwargs = dataclass_fields_asdict(DiagCodedType.from_et(et_element, doc_frags))
|
31
|
+
def from_et(et_element: ElementTree.Element, context: OdxDocContext) -> "MinMaxLengthType":
|
32
|
+
kwargs = dataclass_fields_asdict(DiagCodedType.from_et(et_element, context))
|
34
33
|
|
35
34
|
max_length = None
|
36
35
|
if et_element.find("MAX-LENGTH") is not None:
|
odxtools/modification.py
CHANGED
@@ -1,26 +1,27 @@
|
|
1
1
|
# SPDX-License-Identifier: MIT
|
2
2
|
from dataclasses import dataclass
|
3
|
-
from typing import Any
|
3
|
+
from typing import Any
|
4
4
|
from xml.etree import ElementTree
|
5
5
|
|
6
6
|
from .exceptions import odxrequire
|
7
|
-
from .
|
7
|
+
from .odxdoccontext import OdxDocContext
|
8
|
+
from .odxlink import OdxLinkDatabase, OdxLinkId
|
8
9
|
from .snrefcontext import SnRefContext
|
9
10
|
|
10
11
|
|
11
|
-
@dataclass
|
12
|
+
@dataclass(kw_only=True)
|
12
13
|
class Modification:
|
13
14
|
change: str
|
14
|
-
reason:
|
15
|
+
reason: str | None = None
|
15
16
|
|
16
17
|
@staticmethod
|
17
|
-
def from_et(et_element: ElementTree.Element,
|
18
|
+
def from_et(et_element: ElementTree.Element, context: OdxDocContext) -> "Modification":
|
18
19
|
change = odxrequire(et_element.findtext("CHANGE"))
|
19
20
|
reason = et_element.findtext("REASON")
|
20
21
|
|
21
22
|
return Modification(change=change, reason=reason)
|
22
23
|
|
23
|
-
def _build_odxlinks(self) ->
|
24
|
+
def _build_odxlinks(self) -> dict[OdxLinkId, Any]:
|
24
25
|
return {}
|
25
26
|
|
26
27
|
def _resolve_odxlinks(self, odxlinks: OdxLinkDatabase) -> None:
|
odxtools/multiplexer.py
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
# SPDX-License-Identifier: MIT
|
2
|
-
from dataclasses import dataclass
|
3
|
-
from typing import Any,
|
2
|
+
from dataclasses import dataclass, field
|
3
|
+
from typing import Any, cast
|
4
4
|
from xml.etree import ElementTree
|
5
5
|
|
6
6
|
from typing_extensions import override
|
@@ -13,13 +13,14 @@ from .multiplexercase import MultiplexerCase
|
|
13
13
|
from .multiplexerdefaultcase import MultiplexerDefaultCase
|
14
14
|
from .multiplexerswitchkey import MultiplexerSwitchKey
|
15
15
|
from .nameditemlist import NamedItemList
|
16
|
-
from .
|
16
|
+
from .odxdoccontext import OdxDocContext
|
17
|
+
from .odxlink import OdxLinkDatabase, OdxLinkId
|
17
18
|
from .odxtypes import AtomicOdxType, ParameterValue, odxstr_to_bool
|
18
19
|
from .snrefcontext import SnRefContext
|
19
20
|
from .utils import dataclass_fields_asdict
|
20
21
|
|
21
22
|
|
22
|
-
@dataclass
|
23
|
+
@dataclass(kw_only=True)
|
23
24
|
class Multiplexer(ComplexDop):
|
24
25
|
"""This class represents a Multiplexer (MUX)
|
25
26
|
|
@@ -30,9 +31,9 @@ class Multiplexer(ComplexDop):
|
|
30
31
|
|
31
32
|
byte_position: int
|
32
33
|
switch_key: MultiplexerSwitchKey
|
33
|
-
default_case:
|
34
|
-
cases: NamedItemList[MultiplexerCase]
|
35
|
-
is_visible_raw:
|
34
|
+
default_case: MultiplexerDefaultCase | None = None
|
35
|
+
cases: NamedItemList[MultiplexerCase] = field(default_factory=NamedItemList)
|
36
|
+
is_visible_raw: bool | None = None
|
36
37
|
|
37
38
|
@property
|
38
39
|
def is_visible(self) -> bool:
|
@@ -40,20 +41,20 @@ class Multiplexer(ComplexDop):
|
|
40
41
|
|
41
42
|
@staticmethod
|
42
43
|
@override
|
43
|
-
def from_et(et_element: ElementTree.Element,
|
44
|
+
def from_et(et_element: ElementTree.Element, context: OdxDocContext) -> "Multiplexer":
|
44
45
|
"""Reads a Multiplexer from Diag Layer."""
|
45
|
-
kwargs = dataclass_fields_asdict(ComplexDop.from_et(et_element,
|
46
|
+
kwargs = dataclass_fields_asdict(ComplexDop.from_et(et_element, context))
|
46
47
|
|
47
48
|
byte_position = int(et_element.findtext("BYTE-POSITION", "0"))
|
48
49
|
switch_key = MultiplexerSwitchKey.from_et(
|
49
|
-
odxrequire(et_element.find("SWITCH-KEY")),
|
50
|
+
odxrequire(et_element.find("SWITCH-KEY")), context)
|
50
51
|
|
51
52
|
default_case = None
|
52
53
|
if (dc_elem := et_element.find("DEFAULT-CASE")) is not None:
|
53
|
-
default_case = MultiplexerDefaultCase.from_et(dc_elem,
|
54
|
+
default_case = MultiplexerDefaultCase.from_et(dc_elem, context)
|
54
55
|
|
55
56
|
cases = NamedItemList(
|
56
|
-
[MultiplexerCase.from_et(el,
|
57
|
+
[MultiplexerCase.from_et(el, context) for el in et_element.iterfind("CASES/CASE")])
|
57
58
|
|
58
59
|
is_visible_raw = odxstr_to_bool(et_element.get("IS-VISIBLE"))
|
59
60
|
|
@@ -66,7 +67,7 @@ class Multiplexer(ComplexDop):
|
|
66
67
|
**kwargs)
|
67
68
|
|
68
69
|
@override
|
69
|
-
def _build_odxlinks(self) ->
|
70
|
+
def _build_odxlinks(self) -> dict[OdxLinkId, Any]:
|
70
71
|
odxlinks = super()._build_odxlinks()
|
71
72
|
|
72
73
|
odxlinks.update(self.switch_key._build_odxlinks())
|
@@ -98,7 +99,7 @@ class Multiplexer(ComplexDop):
|
|
98
99
|
for mux_case in self.cases:
|
99
100
|
mux_case._resolve_snrefs(context)
|
100
101
|
|
101
|
-
def _get_case_limits(self, case: MultiplexerCase) ->
|
102
|
+
def _get_case_limits(self, case: MultiplexerCase) -> tuple[AtomicOdxType, AtomicOdxType]:
|
102
103
|
key_type = self.switch_key.dop.physical_type.base_data_type
|
103
104
|
lower_limit = key_type.make_from(case.lower_limit.value)
|
104
105
|
upper_limit = key_type.make_from(case.upper_limit.value)
|
@@ -127,8 +128,8 @@ class Multiplexer(ComplexDop):
|
|
127
128
|
f"Values of multiplexer parameters must be defined as a "
|
128
129
|
f"(case_name, content_value) tuple instead of as '{physical_value!r}'")
|
129
130
|
|
130
|
-
mux_case:
|
131
|
-
applicable_cases:
|
131
|
+
mux_case: MultiplexerCase | MultiplexerDefaultCase
|
132
|
+
applicable_cases: list[MultiplexerCase | MultiplexerDefaultCase]
|
132
133
|
|
133
134
|
if isinstance(case_spec, str):
|
134
135
|
applicable_cases = [x for x in self.cases if x.short_name == case_spec]
|
@@ -147,7 +148,7 @@ class Multiplexer(ComplexDop):
|
|
147
148
|
elif isinstance(case_spec, int):
|
148
149
|
applicable_cases = []
|
149
150
|
for x in self.cases:
|
150
|
-
lower, upper = cast(
|
151
|
+
lower, upper = cast(tuple[int, int], self._get_case_limits(x))
|
151
152
|
if lower <= case_spec and case_spec <= upper:
|
152
153
|
applicable_cases.append(x)
|
153
154
|
|
@@ -208,7 +209,7 @@ class Multiplexer(ComplexDop):
|
|
208
209
|
# relatively to the byte position of the MUX."
|
209
210
|
decode_state.cursor_byte_position = decode_state.origin_byte_position + self.byte_position
|
210
211
|
|
211
|
-
applicable_case:
|
212
|
+
applicable_case: MultiplexerCase | MultiplexerDefaultCase | None = None
|
212
213
|
for mux_case in self.cases:
|
213
214
|
lower, upper = self._get_case_limits(mux_case)
|
214
215
|
if lower <= key_value and key_value <= upper: # type: ignore[operator]
|
@@ -235,3 +236,38 @@ class Multiplexer(ComplexDop):
|
|
235
236
|
decode_state.origin_byte_position = orig_origin
|
236
237
|
|
237
238
|
return result
|
239
|
+
|
240
|
+
@override
|
241
|
+
def get_static_bit_length(self) -> int | None:
|
242
|
+
"""
|
243
|
+
Returns the static bit length of the multiplexer structure, if determinable.
|
244
|
+
|
245
|
+
If all cases (including the default, if present) have the same static bit length,
|
246
|
+
the codec length is considered static and is returned.
|
247
|
+
Otherwise, returns None to indicate that the size is dynamic.
|
248
|
+
"""
|
249
|
+
reference_case = self.default_case if self.default_case else self.cases[0]
|
250
|
+
|
251
|
+
case_bit_length: int | None
|
252
|
+
if reference_case.structure is None:
|
253
|
+
case_bit_length = 0
|
254
|
+
else:
|
255
|
+
case_bit_length = reference_case.structure.get_static_bit_length()
|
256
|
+
if case_bit_length is None:
|
257
|
+
return None
|
258
|
+
case_size: int | None
|
259
|
+
for mux_case in self.cases:
|
260
|
+
if mux_case.structure is None:
|
261
|
+
case_size = 0
|
262
|
+
else:
|
263
|
+
case_size = mux_case.structure.get_static_bit_length()
|
264
|
+
if case_size != case_bit_length:
|
265
|
+
return None # Found a case with a different or unknown size
|
266
|
+
|
267
|
+
switch_key_size = self.switch_key.dop.get_static_bit_length()
|
268
|
+
if switch_key_size is None:
|
269
|
+
return None
|
270
|
+
|
271
|
+
return max(
|
272
|
+
switch_key_size + self.switch_key.byte_position * 8 +
|
273
|
+
(self.switch_key.bit_position or 0), case_bit_length + self.byte_position * 8)
|
odxtools/multiplexercase.py
CHANGED
@@ -1,49 +1,49 @@
|
|
1
1
|
# SPDX-License-Identifier: MIT
|
2
2
|
from dataclasses import dataclass
|
3
|
-
from typing import Any
|
3
|
+
from typing import Any
|
4
4
|
from xml.etree import ElementTree
|
5
5
|
|
6
6
|
from .compumethods.limit import Limit
|
7
7
|
from .element import NamedElement
|
8
8
|
from .exceptions import odxrequire
|
9
|
-
from .
|
9
|
+
from .odxdoccontext import OdxDocContext
|
10
|
+
from .odxlink import OdxLinkDatabase, OdxLinkId, OdxLinkRef, resolve_snref
|
10
11
|
from .odxtypes import AtomicOdxType, DataType
|
11
12
|
from .snrefcontext import SnRefContext
|
12
13
|
from .structure import Structure
|
13
14
|
from .utils import dataclass_fields_asdict
|
14
15
|
|
15
16
|
|
16
|
-
@dataclass
|
17
|
+
@dataclass(kw_only=True)
|
17
18
|
class MultiplexerCase(NamedElement):
|
18
19
|
"""This class represents a case which represents a range of keys of a multiplexer."""
|
19
20
|
|
20
|
-
structure_ref:
|
21
|
-
structure_snref:
|
21
|
+
structure_ref: OdxLinkRef | None = None
|
22
|
+
structure_snref: str | None = None
|
22
23
|
lower_limit: Limit
|
23
24
|
upper_limit: Limit
|
24
25
|
|
25
26
|
@property
|
26
|
-
def structure(self) ->
|
27
|
+
def structure(self) -> Structure | None:
|
27
28
|
return self._structure
|
28
29
|
|
29
30
|
@staticmethod
|
30
|
-
def from_et(et_element: ElementTree.Element,
|
31
|
-
doc_frags: List[OdxDocFragment]) -> "MultiplexerCase":
|
31
|
+
def from_et(et_element: ElementTree.Element, context: OdxDocContext) -> "MultiplexerCase":
|
32
32
|
"""Reads a case for a Multiplexer."""
|
33
|
-
kwargs = dataclass_fields_asdict(NamedElement.from_et(et_element,
|
34
|
-
structure_ref = OdxLinkRef.from_et(et_element.find("STRUCTURE-REF"),
|
33
|
+
kwargs = dataclass_fields_asdict(NamedElement.from_et(et_element, context))
|
34
|
+
structure_ref = OdxLinkRef.from_et(et_element.find("STRUCTURE-REF"), context)
|
35
35
|
structure_snref = None
|
36
36
|
if (structure_snref_elem := et_element.find("STRUCTURE-SNREF")) is not None:
|
37
37
|
structure_snref = odxrequire(structure_snref_elem.get("SHORT-NAME"))
|
38
38
|
|
39
39
|
lower_limit = Limit.limit_from_et(
|
40
40
|
odxrequire(et_element.find("LOWER-LIMIT")),
|
41
|
-
|
41
|
+
context,
|
42
42
|
value_type=None,
|
43
43
|
)
|
44
44
|
upper_limit = Limit.limit_from_et(
|
45
45
|
odxrequire(et_element.find("UPPER-LIMIT")),
|
46
|
-
|
46
|
+
context,
|
47
47
|
value_type=None,
|
48
48
|
)
|
49
49
|
|
@@ -54,7 +54,7 @@ class MultiplexerCase(NamedElement):
|
|
54
54
|
upper_limit=upper_limit,
|
55
55
|
**kwargs)
|
56
56
|
|
57
|
-
def _build_odxlinks(self) ->
|
57
|
+
def _build_odxlinks(self) -> dict[OdxLinkId, Any]:
|
58
58
|
return {}
|
59
59
|
|
60
60
|
def _resolve_odxlinks(self, odxlinks: OdxLinkDatabase) -> None:
|
@@ -1,33 +1,34 @@
|
|
1
1
|
# SPDX-License-Identifier: MIT
|
2
2
|
from dataclasses import dataclass
|
3
|
-
from typing import Any
|
3
|
+
from typing import Any
|
4
4
|
from xml.etree import ElementTree
|
5
5
|
|
6
6
|
from .element import NamedElement
|
7
7
|
from .exceptions import odxrequire
|
8
|
-
from .
|
8
|
+
from .odxdoccontext import OdxDocContext
|
9
|
+
from .odxlink import OdxLinkDatabase, OdxLinkId, OdxLinkRef, resolve_snref
|
9
10
|
from .snrefcontext import SnRefContext
|
10
11
|
from .structure import Structure
|
11
12
|
from .utils import dataclass_fields_asdict
|
12
13
|
|
13
14
|
|
14
|
-
@dataclass
|
15
|
+
@dataclass(kw_only=True)
|
15
16
|
class MultiplexerDefaultCase(NamedElement):
|
16
17
|
"""This class represents a Default Case, which is selected when there are no cases defined in the Multiplexer."""
|
17
|
-
structure_ref:
|
18
|
-
structure_snref:
|
18
|
+
structure_ref: OdxLinkRef | None = None
|
19
|
+
structure_snref: str | None = None
|
19
20
|
|
20
21
|
@property
|
21
|
-
def structure(self) ->
|
22
|
+
def structure(self) -> Structure | None:
|
22
23
|
return self._structure
|
23
24
|
|
24
25
|
@staticmethod
|
25
26
|
def from_et(et_element: ElementTree.Element,
|
26
|
-
|
27
|
+
context: OdxDocContext) -> "MultiplexerDefaultCase":
|
27
28
|
"""Reads a default case for a multiplexer."""
|
28
|
-
kwargs = dataclass_fields_asdict(NamedElement.from_et(et_element,
|
29
|
+
kwargs = dataclass_fields_asdict(NamedElement.from_et(et_element, context))
|
29
30
|
|
30
|
-
structure_ref = OdxLinkRef.from_et(et_element.find("STRUCTURE-REF"),
|
31
|
+
structure_ref = OdxLinkRef.from_et(et_element.find("STRUCTURE-REF"), context)
|
31
32
|
structure_snref = None
|
32
33
|
if (structure_snref_elem := et_element.find("STRUCTURE-SNREF")) is not None:
|
33
34
|
structure_snref = odxrequire(structure_snref_elem.get("SHORT-NAME"))
|
@@ -35,7 +36,7 @@ class MultiplexerDefaultCase(NamedElement):
|
|
35
36
|
return MultiplexerDefaultCase(
|
36
37
|
structure_ref=structure_ref, structure_snref=structure_snref, **kwargs)
|
37
38
|
|
38
|
-
def _build_odxlinks(self) ->
|
39
|
+
def _build_odxlinks(self) -> dict[OdxLinkId, Any]:
|
39
40
|
return {}
|
40
41
|
|
41
42
|
def _resolve_odxlinks(self, odxlinks: OdxLinkDatabase) -> None:
|
odxtools/multiplexerswitchkey.py
CHANGED
@@ -1,21 +1,22 @@
|
|
1
1
|
# SPDX-License-Identifier: MIT
|
2
2
|
from dataclasses import dataclass
|
3
|
-
from typing import Any
|
3
|
+
from typing import Any
|
4
4
|
from xml.etree import ElementTree
|
5
5
|
|
6
6
|
from .dataobjectproperty import DataObjectProperty
|
7
7
|
from .exceptions import odxrequire
|
8
|
-
from .
|
8
|
+
from .odxdoccontext import OdxDocContext
|
9
|
+
from .odxlink import OdxLinkDatabase, OdxLinkId, OdxLinkRef
|
9
10
|
from .snrefcontext import SnRefContext
|
10
11
|
|
11
12
|
|
12
|
-
@dataclass
|
13
|
+
@dataclass(kw_only=True)
|
13
14
|
class MultiplexerSwitchKey:
|
14
15
|
"""
|
15
16
|
The object that determines the case to be used by a multiplexer
|
16
17
|
"""
|
17
18
|
byte_position: int
|
18
|
-
bit_position:
|
19
|
+
bit_position: int | None = None
|
19
20
|
dop_ref: OdxLinkRef
|
20
21
|
|
21
22
|
@property
|
@@ -23,12 +24,11 @@ class MultiplexerSwitchKey:
|
|
23
24
|
return self._dop
|
24
25
|
|
25
26
|
@staticmethod
|
26
|
-
def from_et(et_element: ElementTree.Element,
|
27
|
-
doc_frags: List[OdxDocFragment]) -> "MultiplexerSwitchKey":
|
27
|
+
def from_et(et_element: ElementTree.Element, context: OdxDocContext) -> "MultiplexerSwitchKey":
|
28
28
|
byte_position = int(odxrequire(et_element.findtext("BYTE-POSITION")))
|
29
29
|
bit_position_str = et_element.findtext("BIT-POSITION")
|
30
30
|
bit_position = int(bit_position_str) if bit_position_str is not None else None
|
31
|
-
dop_ref = odxrequire(OdxLinkRef.from_et(et_element.find("DATA-OBJECT-PROP-REF"),
|
31
|
+
dop_ref = odxrequire(OdxLinkRef.from_et(et_element.find("DATA-OBJECT-PROP-REF"), context))
|
32
32
|
|
33
33
|
return MultiplexerSwitchKey(
|
34
34
|
byte_position=byte_position,
|
@@ -36,7 +36,7 @@ class MultiplexerSwitchKey:
|
|
36
36
|
dop_ref=dop_ref,
|
37
37
|
)
|
38
38
|
|
39
|
-
def _build_odxlinks(self) ->
|
39
|
+
def _build_odxlinks(self) -> dict[OdxLinkId, Any]:
|
40
40
|
return {}
|
41
41
|
|
42
42
|
def _resolve_odxlinks(self, odxlinks: OdxLinkDatabase) -> None:
|
odxtools/nameditemlist.py
CHANGED
@@ -1,10 +1,10 @@
|
|
1
1
|
# SPDX-License-Identifier: MIT
|
2
2
|
import abc
|
3
3
|
import typing
|
4
|
+
from collections.abc import Collection, Iterable
|
4
5
|
from copy import deepcopy
|
5
6
|
from keyword import iskeyword
|
6
|
-
from typing import
|
7
|
-
Union, cast, overload, runtime_checkable)
|
7
|
+
from typing import Any, SupportsIndex, TypeVar, cast, overload, runtime_checkable
|
8
8
|
|
9
9
|
from .exceptions import odxraise
|
10
10
|
|
@@ -21,7 +21,7 @@ T = TypeVar("T")
|
|
21
21
|
TNamed = TypeVar("TNamed", bound=OdxNamed)
|
22
22
|
|
23
23
|
|
24
|
-
class ItemAttributeList(
|
24
|
+
class ItemAttributeList(list[T]):
|
25
25
|
"""A list that provides direct access to its items as named attributes.
|
26
26
|
|
27
27
|
This is a hybrid between a list and a user-defined object: One can
|
@@ -35,8 +35,8 @@ class ItemAttributeList(List[T]):
|
|
35
35
|
returned by the item-to-name function are valid identifiers in python.
|
36
36
|
"""
|
37
37
|
|
38
|
-
def __init__(self, input_list:
|
39
|
-
self._item_dict:
|
38
|
+
def __init__(self, input_list: Iterable[T] | None = None) -> None:
|
39
|
+
self._item_dict: dict[str, T] = {}
|
40
40
|
|
41
41
|
if input_list is not None:
|
42
42
|
for item in input_list:
|
@@ -121,10 +121,10 @@ class ItemAttributeList(List[T]):
|
|
121
121
|
def values(self) -> Collection[T]:
|
122
122
|
return self._item_dict.values()
|
123
123
|
|
124
|
-
def items(self) -> Collection[
|
124
|
+
def items(self) -> Collection[tuple[str, T]]:
|
125
125
|
return self._item_dict.items()
|
126
126
|
|
127
|
-
def __dir__(self) ->
|
127
|
+
def __dir__(self) -> dict[str, Any]:
|
128
128
|
result = dict(self.__dict__)
|
129
129
|
result.update(self._item_dict)
|
130
130
|
return result
|
@@ -138,10 +138,10 @@ class ItemAttributeList(List[T]):
|
|
138
138
|
...
|
139
139
|
|
140
140
|
@overload
|
141
|
-
def __getitem__(self, key: slice) ->
|
141
|
+
def __getitem__(self, key: slice) -> list[T]:
|
142
142
|
...
|
143
143
|
|
144
|
-
def __getitem__(self, key:
|
144
|
+
def __getitem__(self, key: SupportsIndex | str | slice) -> T | list[T]:
|
145
145
|
if isinstance(key, (SupportsIndex, slice)):
|
146
146
|
return super().__getitem__(key)
|
147
147
|
else:
|
@@ -153,13 +153,13 @@ class ItemAttributeList(List[T]):
|
|
153
153
|
|
154
154
|
return self._item_dict[key]
|
155
155
|
|
156
|
-
def get(self, key:
|
156
|
+
def get(self, key: int | str, default: T | None = None) -> T | None:
|
157
157
|
if isinstance(key, int):
|
158
158
|
if 0 <= key and key < len(self):
|
159
159
|
return super().__getitem__(key)
|
160
160
|
return default
|
161
161
|
else:
|
162
|
-
return cast(
|
162
|
+
return cast(T | None, self._item_dict.get(key, default))
|
163
163
|
|
164
164
|
def __eq__(self, other: object) -> bool:
|
165
165
|
"""
|
@@ -179,7 +179,7 @@ class ItemAttributeList(List[T]):
|
|
179
179
|
def __copy__(self) -> Any:
|
180
180
|
return self.__class__(list(self))
|
181
181
|
|
182
|
-
def __deepcopy__(self, memo:
|
182
|
+
def __deepcopy__(self, memo: dict[int, Any]) -> Any:
|
183
183
|
cls = self.__class__
|
184
184
|
result = cls.__new__(cls)
|
185
185
|
memo[id(self)] = result
|
@@ -189,7 +189,7 @@ class ItemAttributeList(List[T]):
|
|
189
189
|
|
190
190
|
return result
|
191
191
|
|
192
|
-
def __reduce__(self) ->
|
192
|
+
def __reduce__(self) -> tuple[Any, ...]:
|
193
193
|
"""Support for Python's pickle protocol.
|
194
194
|
This method ensures that the object can be reconstructed with its current state,
|
195
195
|
using its class and the list of items it contains.
|