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/standardlengthtype.py
CHANGED
@@ -1,12 +1,17 @@
|
|
1
1
|
# SPDX-License-Identifier: MIT
|
2
2
|
from dataclasses import dataclass
|
3
|
-
from typing import Optional
|
3
|
+
from typing import List, Literal, 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 .diagcodedtype import DctType, DiagCodedType
|
7
10
|
from .encodestate import EncodeState
|
8
|
-
from .exceptions import odxassert, odxraise
|
9
|
-
from .
|
11
|
+
from .exceptions import odxassert, odxraise, odxrequire
|
12
|
+
from .odxlink import OdxDocFragment
|
13
|
+
from .odxtypes import AtomicOdxType, DataType, odxstr_to_bool
|
14
|
+
from .utils import dataclass_fields_asdict
|
10
15
|
|
11
16
|
|
12
17
|
@dataclass
|
@@ -16,10 +21,36 @@ class StandardLengthType(DiagCodedType):
|
|
16
21
|
bit_mask: Optional[int]
|
17
22
|
is_condensed_raw: Optional[bool]
|
18
23
|
|
24
|
+
@staticmethod
|
25
|
+
@override
|
26
|
+
def from_et(et_element: ElementTree.Element,
|
27
|
+
doc_frags: List[OdxDocFragment]) -> "StandardLengthType":
|
28
|
+
kwargs = dataclass_fields_asdict(DiagCodedType.from_et(et_element, doc_frags))
|
29
|
+
|
30
|
+
bit_length = int(odxrequire(et_element.findtext("BIT-LENGTH")))
|
31
|
+
bit_mask = None
|
32
|
+
if (bit_mask_str := et_element.findtext("BIT-MASK")) is not None:
|
33
|
+
# The XSD uses the type xsd:hexBinary
|
34
|
+
# xsd:hexBinary allows for leading/trailing whitespace, empty strings, and it only allows an even
|
35
|
+
# number of hex digits, while some of the examples shown in the ODX specification exhibit an
|
36
|
+
# odd number of hex digits.
|
37
|
+
# This causes a validation paradox, so we try to be flexible
|
38
|
+
bit_mask_str = bit_mask_str.strip()
|
39
|
+
if len(bit_mask_str):
|
40
|
+
bit_mask = int(bit_mask_str, 16)
|
41
|
+
is_condensed_raw = odxstr_to_bool(et_element.get("IS-CONDENSED"))
|
42
|
+
|
43
|
+
return StandardLengthType(
|
44
|
+
bit_length=bit_length, bit_mask=bit_mask, is_condensed_raw=is_condensed_raw, **kwargs)
|
45
|
+
|
19
46
|
@property
|
20
47
|
def dct_type(self) -> DctType:
|
21
48
|
return "STANDARD-LENGTH-TYPE"
|
22
49
|
|
50
|
+
@property
|
51
|
+
def is_condensed(self) -> bool:
|
52
|
+
return self.is_condensed_raw is True
|
53
|
+
|
23
54
|
def __post_init__(self) -> None:
|
24
55
|
if self.bit_mask is not None:
|
25
56
|
maskable_types = (DataType.A_UINT32, DataType.A_INT32, DataType.A_BYTEFIELD)
|
@@ -28,11 +59,46 @@ class StandardLengthType(DiagCodedType):
|
|
28
59
|
'Can not apply a bit_mask on a value of type {self.base_data_type}',
|
29
60
|
)
|
30
61
|
|
62
|
+
def __get_raw_mask(self, internal_value: AtomicOdxType) -> Optional[bytes]:
|
63
|
+
"""Returns a byte field where all bits that are used by the
|
64
|
+
DiagCoded type are set and all unused ones are not set.
|
65
|
+
|
66
|
+
If `None` is returned, all bits are used.
|
67
|
+
"""
|
68
|
+
if self.bit_mask is None:
|
69
|
+
return None
|
70
|
+
|
71
|
+
if self.is_condensed:
|
72
|
+
odxraise("Condensed bit masks are not yet supported", NotImplementedError)
|
73
|
+
return
|
74
|
+
|
75
|
+
endianness: Literal["little", "big"] = "big"
|
76
|
+
if not self.is_highlow_byte_order and self.base_data_type in [
|
77
|
+
DataType.A_INT32, DataType.A_UINT32, DataType.A_FLOAT32, DataType.A_FLOAT64
|
78
|
+
]:
|
79
|
+
# TODO (?): Technically, little endian A_UNICODE2STRING
|
80
|
+
# objects require a byte swap for each 16 bit letter, and
|
81
|
+
# thus also for the mask. I somehow doubt that this has
|
82
|
+
# been anticipated by the standard, though...
|
83
|
+
endianness = "little"
|
84
|
+
|
85
|
+
sz: int
|
86
|
+
if isinstance(internal_value, (bytes, bytearray)):
|
87
|
+
sz = len(internal_value)
|
88
|
+
else:
|
89
|
+
sz = (odxrequire(self.get_static_bit_length()) + 7) // 8
|
90
|
+
|
91
|
+
max_value = (1 << (sz * 8)) - 1
|
92
|
+
bit_mask = self.bit_mask & max_value
|
93
|
+
|
94
|
+
return bit_mask.to_bytes(sz, endianness)
|
95
|
+
|
31
96
|
def __apply_mask(self, internal_value: AtomicOdxType) -> AtomicOdxType:
|
32
97
|
if self.bit_mask is None:
|
33
98
|
return internal_value
|
34
|
-
if self.
|
35
|
-
|
99
|
+
if self.is_condensed:
|
100
|
+
odxraise("Serialization of condensed bit mask is not supported", NotImplementedError)
|
101
|
+
return
|
36
102
|
if isinstance(internal_value, int):
|
37
103
|
return internal_value & self.bit_mask
|
38
104
|
if isinstance(internal_value, bytes):
|
@@ -46,16 +112,16 @@ class StandardLengthType(DiagCodedType):
|
|
46
112
|
def get_static_bit_length(self) -> Optional[int]:
|
47
113
|
return self.bit_length
|
48
114
|
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
self.__apply_mask(internal_value),
|
53
|
-
|
54
|
-
self.bit_length,
|
55
|
-
self.base_data_type,
|
56
|
-
is_highlow_byte_order=self.is_highlow_byte_order
|
57
|
-
)
|
115
|
+
@override
|
116
|
+
def encode_into_pdu(self, internal_value: AtomicOdxType, encode_state: EncodeState) -> None:
|
117
|
+
encode_state.emplace_atomic_value(
|
118
|
+
internal_value=self.__apply_mask(internal_value),
|
119
|
+
used_mask=self.__get_raw_mask(internal_value),
|
120
|
+
bit_length=self.bit_length,
|
121
|
+
base_data_type=self.base_data_type,
|
122
|
+
is_highlow_byte_order=self.is_highlow_byte_order)
|
58
123
|
|
124
|
+
@override
|
59
125
|
def decode_from_pdu(self, decode_state: DecodeState) -> AtomicOdxType:
|
60
126
|
internal_value = decode_state.extract_atomic_value(
|
61
127
|
self.bit_length,
|
odxtools/state.py
CHANGED
@@ -1,15 +1,13 @@
|
|
1
1
|
# SPDX-License-Identifier: MIT
|
2
2
|
from dataclasses import dataclass
|
3
|
-
from typing import
|
3
|
+
from typing import Any, Dict, List
|
4
4
|
from xml.etree import ElementTree
|
5
5
|
|
6
6
|
from .element import IdentifiableElement
|
7
7
|
from .odxlink import OdxDocFragment, OdxLinkDatabase, OdxLinkId
|
8
|
+
from .snrefcontext import SnRefContext
|
8
9
|
from .utils import dataclass_fields_asdict
|
9
10
|
|
10
|
-
if TYPE_CHECKING:
|
11
|
-
from .diaglayer import DiagLayer
|
12
|
-
|
13
11
|
|
14
12
|
@dataclass
|
15
13
|
class State(IdentifiableElement):
|
@@ -29,5 +27,5 @@ class State(IdentifiableElement):
|
|
29
27
|
def _resolve_odxlinks(self, odxlinks: OdxLinkDatabase) -> None:
|
30
28
|
pass
|
31
29
|
|
32
|
-
def _resolve_snrefs(self,
|
30
|
+
def _resolve_snrefs(self, context: SnRefContext) -> None:
|
33
31
|
pass
|
odxtools/statechart.py
CHANGED
@@ -1,19 +1,17 @@
|
|
1
1
|
# SPDX-License-Identifier: MIT
|
2
2
|
from dataclasses import dataclass
|
3
|
-
from typing import
|
3
|
+
from typing import Any, Dict, List
|
4
4
|
from xml.etree import ElementTree
|
5
5
|
|
6
6
|
from .element import IdentifiableElement
|
7
7
|
from .exceptions import odxrequire
|
8
8
|
from .nameditemlist import NamedItemList
|
9
|
-
from .odxlink import OdxDocFragment, OdxLinkDatabase, OdxLinkId
|
9
|
+
from .odxlink import OdxDocFragment, OdxLinkDatabase, OdxLinkId, resolve_snref
|
10
|
+
from .snrefcontext import SnRefContext
|
10
11
|
from .state import State
|
11
12
|
from .statetransition import StateTransition
|
12
13
|
from .utils import dataclass_fields_asdict
|
13
14
|
|
14
|
-
if TYPE_CHECKING:
|
15
|
-
from .diaglayer import DiagLayer
|
16
|
-
|
17
15
|
|
18
16
|
@dataclass
|
19
17
|
class StateChart(IdentifiableElement):
|
@@ -68,25 +66,21 @@ class StateChart(IdentifiableElement):
|
|
68
66
|
for st in self.states:
|
69
67
|
st._resolve_odxlinks(odxlinks)
|
70
68
|
|
71
|
-
|
72
|
-
|
69
|
+
def _resolve_snrefs(self, context: SnRefContext) -> None:
|
70
|
+
context.state_chart = self
|
71
|
+
|
72
|
+
# For now, we assume that the start state short name reference
|
73
|
+
# points to a local state of the state chart. TODO: The XSD
|
73
74
|
# allows to define state charts without any states, yet the
|
74
75
|
# start state SNREF is mandatory. Is this a gap in the spec or
|
75
76
|
# does it allow "foreign" start states? If the latter, what
|
76
77
|
# does that mean?
|
77
|
-
self._start_state
|
78
|
-
for st in self.states:
|
79
|
-
if st.short_name == self.start_state_snref:
|
80
|
-
self._start_state = st
|
81
|
-
break
|
78
|
+
self._start_state = resolve_snref(self.start_state_snref, self.states, State)
|
82
79
|
|
83
|
-
def _resolve_snrefs(self, diag_layer: "DiagLayer") -> None:
|
84
80
|
for st in self.states:
|
85
|
-
st._resolve_snrefs(
|
81
|
+
st._resolve_snrefs(context)
|
86
82
|
|
87
83
|
for strans in self.state_transitions:
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
# whole diag layer...
|
92
|
-
strans._resolve_snrefs(diag_layer, states=self.states)
|
84
|
+
strans._resolve_snrefs(context)
|
85
|
+
|
86
|
+
context.state_chart = None
|
odxtools/statetransition.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
|
4
4
|
from xml.etree import ElementTree
|
5
5
|
|
6
6
|
from .element import IdentifiableElement
|
7
7
|
from .exceptions import odxrequire
|
8
|
-
from .odxlink import OdxDocFragment, OdxLinkDatabase, OdxLinkId
|
8
|
+
from .odxlink import OdxDocFragment, OdxLinkDatabase, OdxLinkId, resolve_snref
|
9
|
+
from .snrefcontext import SnRefContext
|
9
10
|
from .state import State
|
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 StateTransition(IdentifiableElement):
|
@@ -20,7 +18,7 @@ class StateTransition(IdentifiableElement):
|
|
20
18
|
"""
|
21
19
|
source_snref: str
|
22
20
|
target_snref: str
|
23
|
-
#external_access_method: Optional[ExternalAccessMethod] # TODO
|
21
|
+
# external_access_method: Optional[ExternalAccessMethod] # TODO
|
24
22
|
|
25
23
|
@property
|
26
24
|
def source_state(self) -> State:
|
@@ -50,15 +48,7 @@ class StateTransition(IdentifiableElement):
|
|
50
48
|
def _resolve_odxlinks(self, odxlinks: OdxLinkDatabase) -> None:
|
51
49
|
pass
|
52
50
|
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
def _resolve_snrefs(self, diag_layer: "DiagLayer", *, states: Iterable[State]) -> None:
|
58
|
-
self._source_state: State
|
59
|
-
self._target_state: State
|
60
|
-
for st in states:
|
61
|
-
if st.short_name == self.source_snref:
|
62
|
-
self._source_state = st
|
63
|
-
if st.short_name == self.target_snref:
|
64
|
-
self._target_state = st
|
51
|
+
def _resolve_snrefs(self, context: SnRefContext) -> None:
|
52
|
+
states = odxrequire(context.state_chart).states
|
53
|
+
self._source_state = resolve_snref(self.source_snref, states, State)
|
54
|
+
self._target_state = resolve_snref(self.target_snref, states, State)
|
odxtools/staticfield.py
ADDED
@@ -0,0 +1,107 @@
|
|
1
|
+
# SPDX-License-Identifier: MIT
|
2
|
+
from dataclasses import dataclass
|
3
|
+
from typing import Any, Dict, List, Sequence
|
4
|
+
from xml.etree import ElementTree
|
5
|
+
|
6
|
+
from typing_extensions import override
|
7
|
+
|
8
|
+
from .decodestate import DecodeState
|
9
|
+
from .encodestate import EncodeState
|
10
|
+
from .exceptions import odxassert, odxraise, odxrequire
|
11
|
+
from .field import Field
|
12
|
+
from .odxlink import OdxDocFragment, OdxLinkDatabase, OdxLinkId
|
13
|
+
from .odxtypes import ParameterValue
|
14
|
+
from .snrefcontext import SnRefContext
|
15
|
+
from .utils import dataclass_fields_asdict
|
16
|
+
|
17
|
+
|
18
|
+
@dataclass
|
19
|
+
class StaticField(Field):
|
20
|
+
"""Array of a fixed number of structure objects"""
|
21
|
+
fixed_number_of_items: int
|
22
|
+
item_byte_size: int
|
23
|
+
|
24
|
+
@staticmethod
|
25
|
+
@override
|
26
|
+
def from_et(et_element: ElementTree.Element, doc_frags: List[OdxDocFragment]) -> "StaticField":
|
27
|
+
kwargs = dataclass_fields_asdict(Field.from_et(et_element, doc_frags))
|
28
|
+
|
29
|
+
fixed_number_of_items = int(odxrequire(et_element.findtext('FIXED-NUMBER-OF-ITEMS')))
|
30
|
+
item_byte_size = int(odxrequire(et_element.findtext('ITEM-BYTE-SIZE')))
|
31
|
+
|
32
|
+
return StaticField(
|
33
|
+
fixed_number_of_items=fixed_number_of_items, item_byte_size=item_byte_size, **kwargs)
|
34
|
+
|
35
|
+
@override
|
36
|
+
def _build_odxlinks(self) -> Dict[OdxLinkId, Any]:
|
37
|
+
odxlinks = super()._build_odxlinks()
|
38
|
+
return odxlinks
|
39
|
+
|
40
|
+
@override
|
41
|
+
def _resolve_odxlinks(self, odxlinks: OdxLinkDatabase) -> None:
|
42
|
+
super()._resolve_odxlinks(odxlinks)
|
43
|
+
|
44
|
+
@override
|
45
|
+
def _resolve_snrefs(self, context: SnRefContext) -> None:
|
46
|
+
super()._resolve_snrefs(context)
|
47
|
+
|
48
|
+
@override
|
49
|
+
def encode_into_pdu(self, physical_value: ParameterValue, encode_state: EncodeState) -> None:
|
50
|
+
|
51
|
+
if not isinstance(physical_value,
|
52
|
+
Sequence) or len(physical_value) != self.fixed_number_of_items:
|
53
|
+
odxraise(f"Value for static field '{self.short_name}' "
|
54
|
+
f"must be a list of size {self.fixed_number_of_items}")
|
55
|
+
|
56
|
+
orig_is_end_of_pdu = encode_state.is_end_of_pdu
|
57
|
+
encode_state.is_end_of_pdu = False
|
58
|
+
for i, val in enumerate(physical_value):
|
59
|
+
if not isinstance(val, dict):
|
60
|
+
odxraise(f"The individual parameter values for static field '{self.short_name}' "
|
61
|
+
f"must be dictionaries for structure '{self.structure.short_name}'")
|
62
|
+
|
63
|
+
if i == len(physical_value) - 1:
|
64
|
+
encode_state.is_end_of_pdu = orig_is_end_of_pdu
|
65
|
+
|
66
|
+
pos_before = encode_state.cursor_byte_position
|
67
|
+
self.structure.encode_into_pdu(val, encode_state)
|
68
|
+
pos_after = encode_state.cursor_byte_position
|
69
|
+
|
70
|
+
if pos_after - pos_before > self.item_byte_size:
|
71
|
+
odxraise(
|
72
|
+
f"Insufficient item byte size for static field {self.short_name}: "
|
73
|
+
f"Is {self.item_byte_size} bytes, but need at least {pos_after - pos_before} bytes"
|
74
|
+
)
|
75
|
+
encode_state.cursor_byte_position = pos_before + self.item_byte_size
|
76
|
+
elif pos_after - pos_before < self.item_byte_size:
|
77
|
+
# add some padding bytes
|
78
|
+
encode_state.emplace_bytes(b'\x00' * (self.item_byte_size -
|
79
|
+
(pos_after - pos_before)))
|
80
|
+
|
81
|
+
encode_state.is_end_of_pdu = orig_is_end_of_pdu
|
82
|
+
|
83
|
+
@override
|
84
|
+
def decode_from_pdu(self, decode_state: DecodeState) -> ParameterValue:
|
85
|
+
|
86
|
+
odxassert(decode_state.cursor_bit_position == 0,
|
87
|
+
"No bit position can be specified for static length fields!")
|
88
|
+
|
89
|
+
orig_origin = decode_state.origin_byte_position
|
90
|
+
decode_state.origin_byte_position = decode_state.cursor_byte_position
|
91
|
+
|
92
|
+
result: List[ParameterValue] = []
|
93
|
+
for _ in range(self.fixed_number_of_items):
|
94
|
+
orig_cursor = decode_state.cursor_byte_position
|
95
|
+
|
96
|
+
if decode_state.cursor_byte_position - orig_cursor > self.item_byte_size:
|
97
|
+
odxraise(f"Insufficient item byte size for static field {self.short_name}: "
|
98
|
+
f"Is {self.item_byte_size} bytes, but need at least "
|
99
|
+
f"{decode_state.cursor_byte_position - orig_cursor} bytes")
|
100
|
+
|
101
|
+
result.append(self.structure.decode_from_pdu(decode_state))
|
102
|
+
|
103
|
+
decode_state.cursor_byte_position = orig_cursor + self.item_byte_size
|
104
|
+
|
105
|
+
decode_state.origin_byte_position = orig_origin
|
106
|
+
|
107
|
+
return result
|
odxtools/subcomponent.py
ADDED
@@ -0,0 +1,288 @@
|
|
1
|
+
# SPDX-License-Identifier: MIT
|
2
|
+
from dataclasses import dataclass
|
3
|
+
from typing import Any, Dict, List, Optional
|
4
|
+
from xml.etree import ElementTree
|
5
|
+
|
6
|
+
from .diagnostictroublecode import DiagnosticTroubleCode
|
7
|
+
from .diagservice import DiagService
|
8
|
+
from .dtcdop import DtcDop
|
9
|
+
from .element import IdentifiableElement, NamedElement
|
10
|
+
from .environmentdata import EnvironmentData
|
11
|
+
from .environmentdatadescription import EnvironmentDataDescription
|
12
|
+
from .exceptions import odxraise, odxrequire
|
13
|
+
from .matchingparameter import MatchingParameter
|
14
|
+
from .nameditemlist import NamedItemList
|
15
|
+
from .odxlink import OdxDocFragment, OdxLinkDatabase, OdxLinkId, OdxLinkRef, resolve_snref
|
16
|
+
from .parameters.parameter import Parameter
|
17
|
+
from .snrefcontext import SnRefContext
|
18
|
+
from .table import Table
|
19
|
+
from .tablerow import TableRow
|
20
|
+
from .utils import dataclass_fields_asdict
|
21
|
+
|
22
|
+
|
23
|
+
@dataclass
|
24
|
+
class SubComponentPattern:
|
25
|
+
matching_parameters: List[MatchingParameter]
|
26
|
+
|
27
|
+
@staticmethod
|
28
|
+
def from_et(et_element: ElementTree.Element,
|
29
|
+
doc_frags: List[OdxDocFragment]) -> "SubComponentPattern":
|
30
|
+
|
31
|
+
matching_parameters = [
|
32
|
+
MatchingParameter.from_et(el, doc_frags)
|
33
|
+
for el in et_element.iterfind("MATCHING-PARAMETERS/MATCHING-PARAMETER")
|
34
|
+
]
|
35
|
+
|
36
|
+
return SubComponentPattern(matching_parameters=matching_parameters)
|
37
|
+
|
38
|
+
|
39
|
+
@dataclass
|
40
|
+
class SubComponentParamConnector(IdentifiableElement):
|
41
|
+
diag_comm_snref: str
|
42
|
+
|
43
|
+
# TODO: we currently only support SNREFs, not SNPATHREFs
|
44
|
+
out_param_if_refs: List[str]
|
45
|
+
in_param_if_refs: List[str]
|
46
|
+
|
47
|
+
@property
|
48
|
+
def service(self) -> DiagService:
|
49
|
+
return self._service
|
50
|
+
|
51
|
+
@property
|
52
|
+
def out_param_ifs(self) -> NamedItemList[Parameter]:
|
53
|
+
return self._out_param_ifs
|
54
|
+
|
55
|
+
@property
|
56
|
+
def in_param_ifs(self) -> NamedItemList[Parameter]:
|
57
|
+
return self._in_param_ifs
|
58
|
+
|
59
|
+
@staticmethod
|
60
|
+
def from_et(et_element: ElementTree.Element,
|
61
|
+
doc_frags: List[OdxDocFragment]) -> "SubComponentParamConnector":
|
62
|
+
kwargs = dataclass_fields_asdict(IdentifiableElement.from_et(et_element, doc_frags))
|
63
|
+
|
64
|
+
diag_comm_snref = odxrequire(
|
65
|
+
odxrequire(et_element.find("DIAG-COMM-SNREF")).get("SHORT-NAME"))
|
66
|
+
|
67
|
+
out_param_if_refs = []
|
68
|
+
for elem in et_element.find("OUT-PARAM-IF-REFS") or []:
|
69
|
+
if elem.tag != "OUT-PARAM-IF-SNREF":
|
70
|
+
odxraise("Currently, only SNREFS are supported for OUT-PARAM-IF-REFS")
|
71
|
+
continue
|
72
|
+
|
73
|
+
out_param_if_refs.append(odxrequire(elem.get("SHORT-NAME")))
|
74
|
+
|
75
|
+
in_param_if_refs = []
|
76
|
+
for elem in et_element.find("IN-PARAM-IF-REFS") or []:
|
77
|
+
if elem.tag != "IN-PARAM-IF-SNREF":
|
78
|
+
odxraise("Currently, only SNREFS are supported for IN-PARAM-IF-REFS")
|
79
|
+
continue
|
80
|
+
|
81
|
+
in_param_if_refs.append(odxrequire(elem.get("SHORT-NAME")))
|
82
|
+
|
83
|
+
return SubComponentParamConnector(
|
84
|
+
diag_comm_snref=diag_comm_snref,
|
85
|
+
out_param_if_refs=out_param_if_refs,
|
86
|
+
in_param_if_refs=in_param_if_refs,
|
87
|
+
**kwargs)
|
88
|
+
|
89
|
+
def _build_odxlinks(self) -> Dict[OdxLinkId, Any]:
|
90
|
+
return {}
|
91
|
+
|
92
|
+
def _resolve_odxlinks(self, odxlinks: OdxLinkDatabase) -> None:
|
93
|
+
pass
|
94
|
+
|
95
|
+
def _resolve_snrefs(self, context: SnRefContext) -> None:
|
96
|
+
service = resolve_snref(self.diag_comm_snref,
|
97
|
+
odxrequire(context.diag_layer).diag_comms, DiagService)
|
98
|
+
self._service = service
|
99
|
+
|
100
|
+
if self._service.request is not None:
|
101
|
+
odxraise()
|
102
|
+
return
|
103
|
+
if not self._service.positive_responses:
|
104
|
+
odxraise()
|
105
|
+
return
|
106
|
+
request = odxrequire(service.request)
|
107
|
+
response = service.positive_responses[0]
|
108
|
+
|
109
|
+
in_param_ifs = []
|
110
|
+
for x in self.in_param_if_refs:
|
111
|
+
in_param_ifs.append(resolve_snref(x, request.parameters, Parameter))
|
112
|
+
|
113
|
+
out_param_ifs = []
|
114
|
+
for x in self.out_param_if_refs:
|
115
|
+
out_param_ifs.append(resolve_snref(x, response.parameters, Parameter))
|
116
|
+
|
117
|
+
self._in_param_ifs = NamedItemList(in_param_ifs)
|
118
|
+
self._out_param_ifs = NamedItemList(out_param_ifs)
|
119
|
+
|
120
|
+
|
121
|
+
@dataclass
|
122
|
+
class TableRowConnector(NamedElement):
|
123
|
+
table_ref: OdxLinkRef
|
124
|
+
table_row_snref: str
|
125
|
+
|
126
|
+
@property
|
127
|
+
def table(self) -> Table:
|
128
|
+
return self._table
|
129
|
+
|
130
|
+
@property
|
131
|
+
def table_row(self) -> TableRow:
|
132
|
+
return self._table_row
|
133
|
+
|
134
|
+
@staticmethod
|
135
|
+
def from_et(et_element: ElementTree.Element,
|
136
|
+
doc_frags: List[OdxDocFragment]) -> "TableRowConnector":
|
137
|
+
kwargs = dataclass_fields_asdict(NamedElement.from_et(et_element, doc_frags))
|
138
|
+
|
139
|
+
table_ref = odxrequire(OdxLinkRef.from_et(et_element.find("TABLE-REF"), doc_frags))
|
140
|
+
table_row_snref_el = odxrequire(et_element.find("TABLE-ROW-SNREF"))
|
141
|
+
table_row_snref = odxrequire(table_row_snref_el.get("SHORT-NAME"))
|
142
|
+
|
143
|
+
return TableRowConnector(table_ref=table_ref, table_row_snref=table_row_snref, **kwargs)
|
144
|
+
|
145
|
+
def _build_odxlinks(self) -> Dict[OdxLinkId, Any]:
|
146
|
+
return {}
|
147
|
+
|
148
|
+
def _resolve_odxlinks(self, odxlinks: OdxLinkDatabase) -> None:
|
149
|
+
self._table = odxlinks.resolve(self.table_ref, Table)
|
150
|
+
|
151
|
+
def _resolve_snrefs(self, context: SnRefContext) -> None:
|
152
|
+
self._table_row = resolve_snref(self.table_row_snref, self._table.table_rows, TableRow)
|
153
|
+
|
154
|
+
|
155
|
+
@dataclass
|
156
|
+
class DtcConnector(NamedElement):
|
157
|
+
dtc_dop_ref: OdxLinkRef
|
158
|
+
dtc_snref: str
|
159
|
+
|
160
|
+
@property
|
161
|
+
def dtc_dop(self) -> DtcDop:
|
162
|
+
return self._dtc_dop
|
163
|
+
|
164
|
+
@property
|
165
|
+
def dtc(self) -> DiagnosticTroubleCode:
|
166
|
+
return self._dtc
|
167
|
+
|
168
|
+
@staticmethod
|
169
|
+
def from_et(et_element: ElementTree.Element, doc_frags: List[OdxDocFragment]) -> "DtcConnector":
|
170
|
+
kwargs = dataclass_fields_asdict(NamedElement.from_et(et_element, doc_frags))
|
171
|
+
|
172
|
+
dtc_dop_ref = odxrequire(OdxLinkRef.from_et(et_element.find("DTC-DOP-REF"), doc_frags))
|
173
|
+
dtc_snref_el = odxrequire(et_element.find("DTC-SNREF"))
|
174
|
+
dtc_snref = odxrequire(dtc_snref_el.get("SHORT-NAME"))
|
175
|
+
|
176
|
+
return DtcConnector(dtc_dop_ref=dtc_dop_ref, dtc_snref=dtc_snref, **kwargs)
|
177
|
+
|
178
|
+
def _build_odxlinks(self) -> Dict[OdxLinkId, Any]:
|
179
|
+
return {}
|
180
|
+
|
181
|
+
def _resolve_odxlinks(self, odxlinks: OdxLinkDatabase) -> None:
|
182
|
+
self._dtc_dop = odxlinks.resolve(self.dtc_dop_ref, DtcDop)
|
183
|
+
|
184
|
+
def _resolve_snrefs(self, context: SnRefContext) -> None:
|
185
|
+
self._dtc = resolve_snref(self.dtc_snref, self._dtc_dop.dtcs, DiagnosticTroubleCode)
|
186
|
+
|
187
|
+
|
188
|
+
@dataclass
|
189
|
+
class EnvDataConnector(NamedElement):
|
190
|
+
env_data_desc_ref: OdxLinkRef
|
191
|
+
env_data_snref: str
|
192
|
+
|
193
|
+
@property
|
194
|
+
def env_data_desc(self) -> EnvironmentDataDescription:
|
195
|
+
return self._env_data_desc
|
196
|
+
|
197
|
+
@property
|
198
|
+
def env_data(self) -> EnvironmentData:
|
199
|
+
return self._env_data
|
200
|
+
|
201
|
+
@staticmethod
|
202
|
+
def from_et(et_element: ElementTree.Element,
|
203
|
+
doc_frags: List[OdxDocFragment]) -> "EnvDataConnector":
|
204
|
+
kwargs = dataclass_fields_asdict(NamedElement.from_et(et_element, doc_frags))
|
205
|
+
|
206
|
+
env_data_desc_ref = odxrequire(
|
207
|
+
OdxLinkRef.from_et(et_element.find("ENV-DATA-DESC-REF"), doc_frags))
|
208
|
+
env_data_snref_el = odxrequire(et_element.find("ENV-DATA-SNREF"))
|
209
|
+
env_data_snref = odxrequire(env_data_snref_el.get("SHORT-NAME"))
|
210
|
+
|
211
|
+
return EnvDataConnector(
|
212
|
+
env_data_desc_ref=env_data_desc_ref, env_data_snref=env_data_snref, **kwargs)
|
213
|
+
|
214
|
+
def _build_odxlinks(self) -> Dict[OdxLinkId, Any]:
|
215
|
+
return {}
|
216
|
+
|
217
|
+
def _resolve_odxlinks(self, odxlinks: OdxLinkDatabase) -> None:
|
218
|
+
self._env_data_desc = odxlinks.resolve(self.env_data_desc_ref, EnvironmentDataDescription)
|
219
|
+
|
220
|
+
def _resolve_snrefs(self, context: SnRefContext) -> None:
|
221
|
+
self._env_data = resolve_snref(self.env_data_snref, self._env_data_desc.env_datas,
|
222
|
+
EnvironmentData)
|
223
|
+
|
224
|
+
|
225
|
+
@dataclass
|
226
|
+
class SubComponent(IdentifiableElement):
|
227
|
+
"""Sub-components describe collections of related diagnostic variables
|
228
|
+
|
229
|
+
Note that the communication paradigm via diagnostic variables is
|
230
|
+
somewhat uncommon. If your ECU does not define any, there's no
|
231
|
+
need for it to define sub-components.
|
232
|
+
|
233
|
+
"""
|
234
|
+
|
235
|
+
#sub_component_patterns: NamedItemList[SubComponentPattern]
|
236
|
+
sub_component_param_connectors: NamedItemList[SubComponentParamConnector]
|
237
|
+
table_row_connectors: NamedItemList[TableRowConnector]
|
238
|
+
env_data_connectors: NamedItemList[EnvDataConnector]
|
239
|
+
dtc_connectors: NamedItemList[DtcConnector]
|
240
|
+
|
241
|
+
semantic: Optional[str]
|
242
|
+
|
243
|
+
@staticmethod
|
244
|
+
def from_et(et_element: ElementTree.Element, doc_frags: List[OdxDocFragment]) -> "SubComponent":
|
245
|
+
kwargs = dataclass_fields_asdict(IdentifiableElement.from_et(et_element, doc_frags))
|
246
|
+
|
247
|
+
semantic = et_element.get("SEMANTIC")
|
248
|
+
|
249
|
+
sub_component_param_connectors = [
|
250
|
+
SubComponentParamConnector.from_et(el, doc_frags) for el in et_element.iterfind(
|
251
|
+
"SUB-COMPONENT-PARAM-CONNECTORS/SUB-COMPONENT-PARAM-CONNECTOR")
|
252
|
+
]
|
253
|
+
table_row_connectors = [
|
254
|
+
TableRowConnector.from_et(el, doc_frags)
|
255
|
+
for el in et_element.iterfind("TABLE-ROW-CONNECTORS/TABLE-ROW-CONNECTOR")
|
256
|
+
]
|
257
|
+
env_data_connectors = [
|
258
|
+
EnvDataConnector.from_et(el, doc_frags)
|
259
|
+
for el in et_element.iterfind("ENV-DATA-CONNECTORS/ENV-DATA-CONNECTOR")
|
260
|
+
]
|
261
|
+
dtc_connectors = [
|
262
|
+
DtcConnector.from_et(el, doc_frags)
|
263
|
+
for el in et_element.iterfind("DTC-CONNECTORS/DTC-CONNECTOR")
|
264
|
+
]
|
265
|
+
|
266
|
+
return SubComponent(
|
267
|
+
semantic=semantic,
|
268
|
+
sub_component_param_connectors=NamedItemList(sub_component_param_connectors),
|
269
|
+
table_row_connectors=NamedItemList(table_row_connectors),
|
270
|
+
env_data_connectors=NamedItemList(env_data_connectors),
|
271
|
+
dtc_connectors=NamedItemList(dtc_connectors),
|
272
|
+
**kwargs)
|
273
|
+
|
274
|
+
def _build_odxlinks(self) -> Dict[OdxLinkId, Any]:
|
275
|
+
result = {}
|
276
|
+
|
277
|
+
for dtc_conn in self.dtc_connectors:
|
278
|
+
result.update(dtc_conn._build_odxlinks())
|
279
|
+
|
280
|
+
return result
|
281
|
+
|
282
|
+
def _resolve_odxlinks(self, odxlinks: OdxLinkDatabase) -> None:
|
283
|
+
for dtc_conn in self.dtc_connectors:
|
284
|
+
dtc_conn._resolve_odxlinks(odxlinks)
|
285
|
+
|
286
|
+
def _resolve_snrefs(self, context: SnRefContext) -> None:
|
287
|
+
for dtc_conn in self.dtc_connectors:
|
288
|
+
dtc_conn._resolve_snrefs(context)
|
odxtools/swvariable.py
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
# SPDX-License-Identifier: MIT
|
2
|
+
from dataclasses import dataclass
|
3
|
+
from typing import List, Optional
|
4
|
+
from xml.etree import ElementTree
|
5
|
+
|
6
|
+
from .element import NamedElement
|
7
|
+
from .odxlink import OdxDocFragment
|
8
|
+
from .utils import dataclass_fields_asdict
|
9
|
+
|
10
|
+
|
11
|
+
@dataclass
|
12
|
+
class SwVariable(NamedElement):
|
13
|
+
origin: Optional[str]
|
14
|
+
|
15
|
+
@staticmethod
|
16
|
+
def from_et(et_element: ElementTree.Element, doc_frags: List[OdxDocFragment]) -> "SwVariable":
|
17
|
+
kwargs = dataclass_fields_asdict(NamedElement.from_et(et_element, doc_frags))
|
18
|
+
|
19
|
+
origin = et_element.findtext("ORIGIN")
|
20
|
+
|
21
|
+
return SwVariable(origin=origin, **kwargs)
|