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
odxtools/multiplexercase.py
CHANGED
@@ -1,42 +1,50 @@
|
|
1
1
|
# SPDX-License-Identifier: MIT
|
2
2
|
from dataclasses import dataclass
|
3
|
-
from typing import
|
3
|
+
from typing import Any, Dict, List, Optional
|
4
4
|
from xml.etree import ElementTree
|
5
5
|
|
6
|
-
from .
|
6
|
+
from .compumethods.limit import Limit
|
7
7
|
from .element import NamedElement
|
8
8
|
from .exceptions import odxrequire
|
9
|
-
from .odxlink import OdxDocFragment, OdxLinkDatabase, OdxLinkId, OdxLinkRef
|
9
|
+
from .odxlink import OdxDocFragment, OdxLinkDatabase, OdxLinkId, OdxLinkRef, resolve_snref
|
10
|
+
from .odxtypes import AtomicOdxType, DataType
|
11
|
+
from .snrefcontext import SnRefContext
|
12
|
+
from .structure import Structure
|
10
13
|
from .utils import dataclass_fields_asdict
|
11
14
|
|
12
|
-
if TYPE_CHECKING:
|
13
|
-
from .diaglayer import DiagLayer
|
14
|
-
|
15
15
|
|
16
16
|
@dataclass
|
17
17
|
class MultiplexerCase(NamedElement):
|
18
|
-
"""This class represents a
|
18
|
+
"""This class represents a case which represents a range of keys of a multiplexer."""
|
19
19
|
|
20
20
|
structure_ref: Optional[OdxLinkRef]
|
21
21
|
structure_snref: Optional[str]
|
22
|
-
lower_limit:
|
23
|
-
upper_limit:
|
22
|
+
lower_limit: Limit
|
23
|
+
upper_limit: Limit
|
24
24
|
|
25
25
|
def __post_init__(self) -> None:
|
26
|
-
self._structure:
|
26
|
+
self._structure: Optional[Structure]
|
27
27
|
|
28
28
|
@staticmethod
|
29
29
|
def from_et(et_element: ElementTree.Element,
|
30
30
|
doc_frags: List[OdxDocFragment]) -> "MultiplexerCase":
|
31
|
-
"""Reads a
|
31
|
+
"""Reads a case for a Multiplexer."""
|
32
32
|
kwargs = dataclass_fields_asdict(NamedElement.from_et(et_element, doc_frags))
|
33
33
|
structure_ref = OdxLinkRef.from_et(et_element.find("STRUCTURE-REF"), doc_frags)
|
34
34
|
structure_snref = None
|
35
35
|
if (structure_snref_elem := et_element.find("STRUCTURE-SNREF")) is not None:
|
36
36
|
structure_snref = odxrequire(structure_snref_elem.get("SHORT-NAME"))
|
37
37
|
|
38
|
-
lower_limit =
|
39
|
-
|
38
|
+
lower_limit = Limit.limit_from_et(
|
39
|
+
odxrequire(et_element.find("LOWER-LIMIT")),
|
40
|
+
doc_frags,
|
41
|
+
value_type=None,
|
42
|
+
)
|
43
|
+
upper_limit = Limit.limit_from_et(
|
44
|
+
odxrequire(et_element.find("UPPER-LIMIT")),
|
45
|
+
doc_frags,
|
46
|
+
value_type=None,
|
47
|
+
)
|
40
48
|
|
41
49
|
return MultiplexerCase(
|
42
50
|
structure_ref=structure_ref,
|
@@ -49,14 +57,27 @@ class MultiplexerCase(NamedElement):
|
|
49
57
|
return {}
|
50
58
|
|
51
59
|
def _resolve_odxlinks(self, odxlinks: OdxLinkDatabase) -> None:
|
60
|
+
raise RuntimeError("Calling MultiplexerCase._resolve_odxlinks() is not allowed. "
|
61
|
+
"Use ._mux_case_resolve_odxlinks().")
|
62
|
+
|
63
|
+
def _mux_case_resolve_odxlinks(self, odxlinks: OdxLinkDatabase, *,
|
64
|
+
key_physical_type: DataType) -> None:
|
65
|
+
self._structure = None
|
52
66
|
if self.structure_ref:
|
53
|
-
self._structure = odxlinks.resolve(self.structure_ref)
|
67
|
+
self._structure = odxlinks.resolve(self.structure_ref, Structure)
|
54
68
|
|
55
|
-
|
69
|
+
self.lower_limit.set_value_type(key_physical_type)
|
70
|
+
self.upper_limit.set_value_type(key_physical_type)
|
71
|
+
|
72
|
+
def _resolve_snrefs(self, context: SnRefContext) -> None:
|
56
73
|
if self.structure_snref:
|
57
|
-
ddds = diag_layer.diag_data_dictionary_spec
|
58
|
-
self._structure =
|
74
|
+
ddds = odxrequire(context.diag_layer).diag_data_dictionary_spec
|
75
|
+
self._structure = resolve_snref(self.structure_snref, ddds.structures, Structure)
|
76
|
+
|
77
|
+
def applies(self, value: AtomicOdxType) -> bool:
|
78
|
+
return self.lower_limit.complies_to_lower(value) \
|
79
|
+
and self.upper_limit.complies_to_upper(value)
|
59
80
|
|
60
81
|
@property
|
61
|
-
def structure(self) ->
|
82
|
+
def structure(self) -> Optional[Structure]:
|
62
83
|
return self._structure
|
@@ -1,17 +1,15 @@
|
|
1
1
|
# SPDX-License-Identifier: MIT
|
2
2
|
from dataclasses import dataclass
|
3
|
-
from typing import
|
3
|
+
from typing import Any, Dict, List, Optional
|
4
4
|
from xml.etree import ElementTree
|
5
5
|
|
6
|
-
from .basicstructure import BasicStructure
|
7
6
|
from .element import NamedElement
|
8
7
|
from .exceptions import odxrequire
|
9
|
-
from .odxlink import OdxDocFragment, OdxLinkDatabase, OdxLinkId, OdxLinkRef
|
8
|
+
from .odxlink import OdxDocFragment, OdxLinkDatabase, OdxLinkId, OdxLinkRef, resolve_snref
|
9
|
+
from .snrefcontext import SnRefContext
|
10
|
+
from .structure import Structure
|
10
11
|
from .utils import dataclass_fields_asdict
|
11
12
|
|
12
|
-
if TYPE_CHECKING:
|
13
|
-
from .diaglayer import DiagLayer
|
14
|
-
|
15
13
|
|
16
14
|
@dataclass
|
17
15
|
class MultiplexerDefaultCase(NamedElement):
|
@@ -20,12 +18,12 @@ class MultiplexerDefaultCase(NamedElement):
|
|
20
18
|
structure_snref: Optional[str]
|
21
19
|
|
22
20
|
def __post_init__(self) -> None:
|
23
|
-
self._structure: Optional[
|
21
|
+
self._structure: Optional[Structure]
|
24
22
|
|
25
23
|
@staticmethod
|
26
24
|
def from_et(et_element: ElementTree.Element,
|
27
25
|
doc_frags: List[OdxDocFragment]) -> "MultiplexerDefaultCase":
|
28
|
-
"""Reads a
|
26
|
+
"""Reads a default case for a multiplexer."""
|
29
27
|
kwargs = dataclass_fields_asdict(NamedElement.from_et(et_element, doc_frags))
|
30
28
|
|
31
29
|
structure_ref = OdxLinkRef.from_et(et_element.find("STRUCTURE-REF"), doc_frags)
|
@@ -40,10 +38,15 @@ class MultiplexerDefaultCase(NamedElement):
|
|
40
38
|
return {}
|
41
39
|
|
42
40
|
def _resolve_odxlinks(self, odxlinks: OdxLinkDatabase) -> None:
|
41
|
+
self._structure = None
|
43
42
|
if self.structure_ref is not None:
|
44
|
-
self._structure = odxlinks.resolve(self.structure_ref)
|
43
|
+
self._structure = odxlinks.resolve(self.structure_ref, Structure)
|
45
44
|
|
46
|
-
def _resolve_snrefs(self,
|
45
|
+
def _resolve_snrefs(self, context: SnRefContext) -> None:
|
47
46
|
if self.structure_snref:
|
48
|
-
ddds = diag_layer.diag_data_dictionary_spec
|
49
|
-
self._structure =
|
47
|
+
ddds = odxrequire(context.diag_layer).diag_data_dictionary_spec
|
48
|
+
self._structure = resolve_snref(self.structure_snref, ddds.structures, Structure)
|
49
|
+
|
50
|
+
@property
|
51
|
+
def structure(self) -> Optional[Structure]:
|
52
|
+
return self._structure
|
odxtools/multiplexerswitchkey.py
CHANGED
@@ -1,13 +1,12 @@
|
|
1
|
+
# SPDX-License-Identifier: MIT
|
1
2
|
from dataclasses import dataclass
|
2
|
-
from typing import
|
3
|
+
from typing import Any, Dict, List, Optional
|
3
4
|
from xml.etree import ElementTree
|
4
5
|
|
5
6
|
from .dataobjectproperty import DataObjectProperty
|
6
7
|
from .exceptions import odxrequire
|
7
8
|
from .odxlink import OdxDocFragment, OdxLinkDatabase, OdxLinkId, OdxLinkRef
|
8
|
-
|
9
|
-
if TYPE_CHECKING:
|
10
|
-
from ..diaglayer import DiagLayer
|
9
|
+
from .snrefcontext import SnRefContext
|
11
10
|
|
12
11
|
|
13
12
|
@dataclass
|
@@ -39,7 +38,7 @@ class MultiplexerSwitchKey:
|
|
39
38
|
def _resolve_odxlinks(self, odxlinks: OdxLinkDatabase) -> None:
|
40
39
|
self._dop = odxlinks.resolve(self.dop_ref, DataObjectProperty)
|
41
40
|
|
42
|
-
def _resolve_snrefs(self,
|
41
|
+
def _resolve_snrefs(self, context: SnRefContext) -> None:
|
43
42
|
pass
|
44
43
|
|
45
44
|
@property
|
odxtools/nameditemlist.py
CHANGED
@@ -1,18 +1,20 @@
|
|
1
1
|
# SPDX-License-Identifier: MIT
|
2
2
|
import abc
|
3
|
+
import typing
|
4
|
+
from copy import deepcopy
|
3
5
|
from keyword import iskeyword
|
4
|
-
from typing import (Any, Collection, Dict, Iterable, List, Optional,
|
5
|
-
|
6
|
+
from typing import (Any, Collection, Dict, Iterable, List, Optional, SupportsIndex, Tuple, TypeVar,
|
7
|
+
Union, cast, overload, runtime_checkable)
|
6
8
|
|
7
9
|
from .exceptions import odxraise
|
8
10
|
|
9
11
|
|
10
12
|
@runtime_checkable
|
11
|
-
class OdxNamed(Protocol):
|
13
|
+
class OdxNamed(typing.Protocol):
|
12
14
|
|
13
15
|
@property
|
14
16
|
def short_name(self) -> str:
|
15
|
-
|
17
|
+
...
|
16
18
|
|
17
19
|
|
18
20
|
T = TypeVar("T")
|
@@ -127,7 +129,7 @@ class ItemAttributeList(List[T]):
|
|
127
129
|
result.update(self._item_dict)
|
128
130
|
return result
|
129
131
|
|
130
|
-
@overload
|
132
|
+
@overload
|
131
133
|
def __getitem__(self, key: SupportsIndex) -> T:
|
132
134
|
...
|
133
135
|
|
@@ -174,10 +176,33 @@ class ItemAttributeList(List[T]):
|
|
174
176
|
def __repr__(self) -> str:
|
175
177
|
return f"{type(self).__name__}([{', '.join([repr(x) for x in self])}])"
|
176
178
|
|
179
|
+
def __copy__(self) -> Any:
|
180
|
+
return self.__class__(list(self))
|
181
|
+
|
182
|
+
def __deepcopy__(self, memo: Dict[int, Any]) -> Any:
|
183
|
+
cls = self.__class__
|
184
|
+
result = cls.__new__(cls)
|
185
|
+
memo[id(self)] = result
|
186
|
+
result._item_dict = {}
|
187
|
+
for x in self:
|
188
|
+
result.append(deepcopy(x, memo))
|
189
|
+
|
190
|
+
return result
|
191
|
+
|
192
|
+
def __reduce__(self) -> Tuple[Any, ...]:
|
193
|
+
"""Support for Python's pickle protocol.
|
194
|
+
This method ensures that the object can be reconstructed with its current state,
|
195
|
+
using its class and the list of items it contains.
|
196
|
+
It returns a tuple containing the reconstruction function (the class)
|
197
|
+
and its arguments necessary to recreate the object.
|
198
|
+
|
199
|
+
"""
|
200
|
+
return self.__class__, (list(self),)
|
201
|
+
|
177
202
|
|
178
203
|
class NamedItemList(ItemAttributeList[T]):
|
179
204
|
|
180
|
-
def _get_item_key(self,
|
205
|
+
def _get_item_key(self, item: T) -> str:
|
181
206
|
"""Transform an object's `short_name` attribute into a valid
|
182
207
|
python identifier
|
183
208
|
|
@@ -187,9 +212,9 @@ class NamedItemList(ItemAttributeList[T]):
|
|
187
212
|
such short names.
|
188
213
|
|
189
214
|
"""
|
190
|
-
if not isinstance(
|
215
|
+
if not isinstance(item, OdxNamed):
|
191
216
|
odxraise()
|
192
|
-
sn =
|
217
|
+
sn = item.short_name
|
193
218
|
if not isinstance(sn, str):
|
194
219
|
odxraise()
|
195
220
|
|
odxtools/negoutputparam.py
CHANGED
@@ -1,17 +1,15 @@
|
|
1
1
|
# SPDX-License-Identifier: MIT
|
2
2
|
from dataclasses import dataclass
|
3
|
-
from typing import
|
3
|
+
from typing import Any, Dict, List, Optional
|
4
4
|
from xml.etree import ElementTree
|
5
5
|
|
6
6
|
from .dopbase import DopBase
|
7
7
|
from .element import NamedElement
|
8
8
|
from .exceptions import odxrequire
|
9
9
|
from .odxlink import OdxDocFragment, OdxLinkDatabase, OdxLinkId, OdxLinkRef
|
10
|
+
from .snrefcontext import SnRefContext
|
10
11
|
from .utils import dataclass_fields_asdict
|
11
12
|
|
12
|
-
if TYPE_CHECKING:
|
13
|
-
from .diaglayer import DiagLayer
|
14
|
-
|
15
13
|
|
16
14
|
@dataclass
|
17
15
|
class NegOutputParam(NamedElement):
|
@@ -35,7 +33,7 @@ class NegOutputParam(NamedElement):
|
|
35
33
|
def _resolve_odxlinks(self, odxlinks: OdxLinkDatabase) -> None:
|
36
34
|
self._dop = odxlinks.resolve(self.dop_base_ref)
|
37
35
|
|
38
|
-
def _resolve_snrefs(self,
|
36
|
+
def _resolve_snrefs(self, context: SnRefContext) -> None:
|
39
37
|
pass
|
40
38
|
|
41
39
|
@property
|
odxtools/odxcategory.py
ADDED
@@ -0,0 +1,83 @@
|
|
1
|
+
# SPDX-License-Identifier: MIT
|
2
|
+
from dataclasses import dataclass
|
3
|
+
from typing import TYPE_CHECKING, Any, Dict, List, Optional
|
4
|
+
from xml.etree import ElementTree
|
5
|
+
|
6
|
+
from .admindata import AdminData
|
7
|
+
from .companydata import CompanyData
|
8
|
+
from .element import IdentifiableElement
|
9
|
+
from .exceptions import odxrequire
|
10
|
+
from .nameditemlist import NamedItemList
|
11
|
+
from .odxlink import OdxDocFragment, OdxLinkDatabase, OdxLinkId
|
12
|
+
from .snrefcontext import SnRefContext
|
13
|
+
from .specialdatagroup import SpecialDataGroup
|
14
|
+
from .utils import dataclass_fields_asdict
|
15
|
+
|
16
|
+
if TYPE_CHECKING:
|
17
|
+
from .database import Database
|
18
|
+
|
19
|
+
|
20
|
+
@dataclass
|
21
|
+
class OdxCategory(IdentifiableElement):
|
22
|
+
"""This is the base class for all top-level container classes in ODX"""
|
23
|
+
|
24
|
+
admin_data: Optional[AdminData]
|
25
|
+
company_datas: NamedItemList[CompanyData]
|
26
|
+
sdgs: List[SpecialDataGroup]
|
27
|
+
|
28
|
+
@staticmethod
|
29
|
+
def from_et(et_element: ElementTree.Element, doc_frags: List[OdxDocFragment]) -> "OdxCategory":
|
30
|
+
raise Exception("Calling `._from_et()` is not allowed for OdxCategory. "
|
31
|
+
"Use `OdxCategory.category_from_et()`!")
|
32
|
+
|
33
|
+
@staticmethod
|
34
|
+
def category_from_et(et_element: ElementTree.Element, doc_frags: List[OdxDocFragment], *,
|
35
|
+
doc_type: str) -> "OdxCategory":
|
36
|
+
|
37
|
+
short_name = odxrequire(et_element.findtext("SHORT-NAME"))
|
38
|
+
# create the current ODX "document fragment" (description of the
|
39
|
+
# current document for references and IDs)
|
40
|
+
doc_frags = [OdxDocFragment(short_name, doc_type)]
|
41
|
+
kwargs = dataclass_fields_asdict(IdentifiableElement.from_et(et_element, doc_frags))
|
42
|
+
|
43
|
+
admin_data = AdminData.from_et(et_element.find("ADMIN-DATA"), doc_frags)
|
44
|
+
company_datas = NamedItemList([
|
45
|
+
CompanyData.from_et(cde, doc_frags)
|
46
|
+
for cde in et_element.iterfind("COMPANY-DATAS/COMPANY-DATA")
|
47
|
+
])
|
48
|
+
sdgs = [
|
49
|
+
SpecialDataGroup.from_et(sdge, doc_frags) for sdge in et_element.iterfind("SDGS/SDG")
|
50
|
+
]
|
51
|
+
|
52
|
+
return OdxCategory(admin_data=admin_data, company_datas=company_datas, sdgs=sdgs, **kwargs)
|
53
|
+
|
54
|
+
def _build_odxlinks(self) -> Dict[OdxLinkId, Any]:
|
55
|
+
result = {self.odx_id: self}
|
56
|
+
|
57
|
+
if self.admin_data is not None:
|
58
|
+
result.update(self.admin_data._build_odxlinks())
|
59
|
+
for cd in self.company_datas:
|
60
|
+
result.update(cd._build_odxlinks())
|
61
|
+
for sdg in self.sdgs:
|
62
|
+
result.update(sdg._build_odxlinks())
|
63
|
+
|
64
|
+
return result
|
65
|
+
|
66
|
+
def _resolve_odxlinks(self, odxlinks: OdxLinkDatabase) -> None:
|
67
|
+
if self.admin_data is not None:
|
68
|
+
self.admin_data._resolve_odxlinks(odxlinks)
|
69
|
+
for cd in self.company_datas:
|
70
|
+
cd._resolve_odxlinks(odxlinks)
|
71
|
+
for sdg in self.sdgs:
|
72
|
+
sdg._resolve_odxlinks(odxlinks)
|
73
|
+
|
74
|
+
def _finalize_init(self, database: "Database", odxlinks: OdxLinkDatabase) -> None:
|
75
|
+
pass
|
76
|
+
|
77
|
+
def _resolve_snrefs(self, context: SnRefContext) -> None:
|
78
|
+
if self.admin_data is not None:
|
79
|
+
self.admin_data._resolve_snrefs(context)
|
80
|
+
for cd in self.company_datas:
|
81
|
+
cd._resolve_snrefs(context)
|
82
|
+
for sdg in self.sdgs:
|
83
|
+
sdg._resolve_snrefs(context)
|
odxtools/odxlink.py
CHANGED
@@ -1,32 +1,17 @@
|
|
1
1
|
# SPDX-License-Identifier: MIT
|
2
2
|
import warnings
|
3
3
|
from dataclasses import dataclass
|
4
|
-
from typing import Any, Dict, List, Optional, Type, TypeVar, overload
|
4
|
+
from typing import Any, Dict, Iterable, List, Optional, Type, TypeVar, overload
|
5
5
|
from xml.etree import ElementTree
|
6
6
|
|
7
|
-
from .exceptions import OdxWarning, odxassert
|
7
|
+
from .exceptions import OdxWarning, odxassert, odxraise, odxrequire
|
8
|
+
from .nameditemlist import OdxNamed, TNamed
|
8
9
|
|
9
10
|
|
10
11
|
@dataclass(frozen=True)
|
11
12
|
class OdxDocFragment:
|
12
13
|
doc_name: str
|
13
|
-
doc_type:
|
14
|
-
|
15
|
-
def __eq__(self, other: Any) -> bool:
|
16
|
-
if other is None:
|
17
|
-
# if the other document fragment is not specified, we
|
18
|
-
# treat it as a wildcard...
|
19
|
-
return True
|
20
|
-
|
21
|
-
if not isinstance(other, OdxDocFragment):
|
22
|
-
return False
|
23
|
-
|
24
|
-
# the ODX spec says that the doctype can be ignored...
|
25
|
-
return self.doc_name == other.doc_name
|
26
|
-
|
27
|
-
def __hash__(self) -> int:
|
28
|
-
# only the document name is relevant for the hash value
|
29
|
-
return hash(self.doc_name) + hash(self.doc_type)
|
14
|
+
doc_type: str
|
30
15
|
|
31
16
|
|
32
17
|
@dataclass(frozen=True)
|
@@ -58,10 +43,12 @@ class OdxLinkId:
|
|
58
43
|
if not isinstance(other, OdxLinkId):
|
59
44
|
return False
|
60
45
|
|
61
|
-
#
|
62
|
-
|
63
|
-
|
64
|
-
|
46
|
+
# if the local ID is different, the whole id is different
|
47
|
+
if self.local_id != other.local_id:
|
48
|
+
return False
|
49
|
+
|
50
|
+
# the document fragments must be identical for the IDs to be identical
|
51
|
+
return self.doc_fragments == other.doc_fragments
|
65
52
|
|
66
53
|
def __str__(self) -> str:
|
67
54
|
return f"OdxLinkId('{self.local_id}')"
|
@@ -119,6 +106,7 @@ class OdxLinkRef:
|
|
119
106
|
|
120
107
|
id_ref = et.attrib.get("ID-REF")
|
121
108
|
if id_ref is None:
|
109
|
+
odxraise(f"Tag {et.tag} is not a ODXLINK reference")
|
122
110
|
return None
|
123
111
|
|
124
112
|
doc_ref = et.attrib.get("DOCREF")
|
@@ -132,7 +120,7 @@ class OdxLinkRef:
|
|
132
120
|
# reference, use it, else use the document fragment containing
|
133
121
|
# the reference.
|
134
122
|
if doc_ref is not None:
|
135
|
-
doc_frags = [OdxDocFragment(doc_ref, doc_type)]
|
123
|
+
doc_frags = [OdxDocFragment(doc_ref, odxrequire(doc_type))]
|
136
124
|
else:
|
137
125
|
doc_frags = source_doc_frags
|
138
126
|
|
@@ -146,32 +134,19 @@ class OdxLinkRef:
|
|
146
134
|
def __str__(self) -> str:
|
147
135
|
return f"OdxLinkRef('{self.ref_id}')"
|
148
136
|
|
149
|
-
def __contains__(self, odx_id: OdxLinkId) -> bool:
|
150
|
-
"""
|
151
|
-
Returns true iff a given OdxLinkId object is referenced.
|
152
|
-
"""
|
153
|
-
|
154
|
-
# we must reference at to at least of the ID's document
|
155
|
-
# fragments
|
156
|
-
if not any(ref_doc in odx_id.doc_fragments for ref_doc in self.ref_docs):
|
157
|
-
return False
|
158
|
-
|
159
|
-
# the local ID of the reference and the object ID must match
|
160
|
-
return odx_id.local_id == self.ref_id
|
161
|
-
|
162
137
|
|
163
138
|
T = TypeVar("T")
|
164
139
|
|
165
140
|
|
166
141
|
class OdxLinkDatabase:
|
167
142
|
"""
|
168
|
-
A database holding all objects which
|
143
|
+
A database holding all objects which exhibit OdxLinkIds
|
169
144
|
|
170
|
-
This can resolve references
|
145
|
+
This can resolve ODXLINK references.
|
171
146
|
"""
|
172
147
|
|
173
148
|
def __init__(self) -> None:
|
174
|
-
self._db: Dict[OdxDocFragment, Dict[
|
149
|
+
self._db: Dict[OdxDocFragment, Dict[str, Any]] = {}
|
175
150
|
|
176
151
|
@overload
|
177
152
|
def resolve(self, ref: OdxLinkRef, expected_type: None = None) -> Any:
|
@@ -181,20 +156,19 @@ class OdxLinkDatabase:
|
|
181
156
|
def resolve(self, ref: OdxLinkRef, expected_type: Type[T]) -> T:
|
182
157
|
...
|
183
158
|
|
184
|
-
def resolve(self, ref: OdxLinkRef, expected_type: Optional[
|
159
|
+
def resolve(self, ref: OdxLinkRef, expected_type: Optional[Any] = None) -> Any:
|
185
160
|
"""
|
186
161
|
Resolve a reference to an object
|
187
162
|
|
188
163
|
If the database does not contain any object which is referred to, a
|
189
164
|
KeyError exception is raised.
|
190
165
|
"""
|
191
|
-
odx_id = OdxLinkId(ref.ref_id, ref.ref_docs)
|
192
166
|
for ref_frag in reversed(ref.ref_docs):
|
193
167
|
doc_frag_db = self._db.get(ref_frag)
|
194
168
|
if doc_frag_db is None:
|
195
169
|
# No object featured by the database uses the document
|
196
170
|
# fragment mentioned by the reference. This should not
|
197
|
-
# happen for correct databases...
|
171
|
+
# happen for correct ODX databases...
|
198
172
|
warnings.warn(
|
199
173
|
f"Warning: Unknown document fragment {ref_frag} "
|
200
174
|
f"when resolving reference {ref}",
|
@@ -203,15 +177,18 @@ class OdxLinkDatabase:
|
|
203
177
|
)
|
204
178
|
continue
|
205
179
|
|
206
|
-
|
207
|
-
|
180
|
+
# locate an object exhibiting with the referenced local ID
|
181
|
+
# in the ID database for the document fragment
|
182
|
+
if (obj := doc_frag_db.get(ref.ref_id)) is not None:
|
208
183
|
if expected_type is not None:
|
209
184
|
odxassert(isinstance(obj, expected_type))
|
210
185
|
|
211
186
|
return obj
|
212
187
|
|
213
|
-
|
214
|
-
|
188
|
+
odxraise(
|
189
|
+
f"ODXLINK reference {ref} could not be resolved for any "
|
190
|
+
f"of the document fragments {ref.ref_docs}", KeyError)
|
191
|
+
return None
|
215
192
|
|
216
193
|
@overload
|
217
194
|
def resolve_lenient(self, ref: OdxLinkRef, expected_type: None = None) -> Any:
|
@@ -223,7 +200,7 @@ class OdxLinkDatabase:
|
|
223
200
|
|
224
201
|
def resolve_lenient(self,
|
225
202
|
ref: OdxLinkRef,
|
226
|
-
expected_type: Optional[
|
203
|
+
expected_type: Optional[Any] = None) -> Optional[Any]:
|
227
204
|
"""
|
228
205
|
Resolve a reference to an object
|
229
206
|
|
@@ -231,7 +208,6 @@ class OdxLinkDatabase:
|
|
231
208
|
is returned.
|
232
209
|
"""
|
233
210
|
|
234
|
-
odx_id = OdxLinkId(ref.ref_id, ref.ref_docs)
|
235
211
|
for ref_frag in reversed(ref.ref_docs):
|
236
212
|
doc_frag_db = self._db.get(ref_frag)
|
237
213
|
if doc_frag_db is None:
|
@@ -246,8 +222,7 @@ class OdxLinkDatabase:
|
|
246
222
|
)
|
247
223
|
continue
|
248
224
|
|
249
|
-
obj
|
250
|
-
if obj is not None:
|
225
|
+
if (obj := doc_frag_db.get(ref.ref_id)) is not None:
|
251
226
|
if expected_type is not None:
|
252
227
|
odxassert(isinstance(obj, expected_type))
|
253
228
|
|
@@ -255,7 +230,7 @@ class OdxLinkDatabase:
|
|
255
230
|
|
256
231
|
return None
|
257
232
|
|
258
|
-
def update(self, new_entries: Dict[OdxLinkId, Any]) -> None:
|
233
|
+
def update(self, new_entries: Dict[OdxLinkId, Any], *, overwrite: bool = True) -> None:
|
259
234
|
"""
|
260
235
|
Add a bunch of new objects to the ODXLINK database.
|
261
236
|
|
@@ -269,4 +244,38 @@ class OdxLinkDatabase:
|
|
269
244
|
if doc_frag not in self._db:
|
270
245
|
self._db[doc_frag] = {}
|
271
246
|
|
272
|
-
|
247
|
+
if overwrite:
|
248
|
+
self._db[doc_frag][odx_id.local_id] = obj
|
249
|
+
else:
|
250
|
+
self._db[doc_frag].setdefault(odx_id.local_id, obj)
|
251
|
+
|
252
|
+
|
253
|
+
@overload
|
254
|
+
def resolve_snref(target_short_name: str,
|
255
|
+
items: Iterable[OdxNamed],
|
256
|
+
expected_type: None = None) -> Any:
|
257
|
+
"""Resolve a short name reference given a sequence of candidate objects"""
|
258
|
+
...
|
259
|
+
|
260
|
+
|
261
|
+
@overload
|
262
|
+
def resolve_snref(target_short_name: str, items: Iterable[OdxNamed],
|
263
|
+
expected_type: Type[TNamed]) -> TNamed:
|
264
|
+
...
|
265
|
+
|
266
|
+
|
267
|
+
def resolve_snref(target_short_name: str,
|
268
|
+
items: Iterable[OdxNamed],
|
269
|
+
expected_type: Any = None) -> Any:
|
270
|
+
candidates = [x for x in items if x.short_name == target_short_name]
|
271
|
+
|
272
|
+
if not candidates:
|
273
|
+
odxraise(f"Cannot resolve short name reference to '{target_short_name}'")
|
274
|
+
return None
|
275
|
+
elif len(candidates) > 1:
|
276
|
+
odxraise(f"Cannot uniquely resolve short name reference to '{target_short_name}'")
|
277
|
+
elif expected_type is not None and not isinstance(candidates[0], expected_type):
|
278
|
+
odxraise(f"Reference '{target_short_name}' points to a {type(candidates[0]).__name__}"
|
279
|
+
f"object while expecting {expected_type.__name__}")
|
280
|
+
|
281
|
+
return candidates[0]
|