odxtools 10.1.1__py3-none-any.whl → 10.2.1__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/addrdeffilter.py +33 -0
- odxtools/addrdefphyssegment.py +33 -0
- odxtools/checksum.py +67 -0
- odxtools/checksumresult.py +7 -0
- odxtools/database.py +24 -4
- odxtools/datablock.py +153 -0
- odxtools/datafile.py +23 -0
- odxtools/dataformat.py +39 -0
- odxtools/dataformatselection.py +9 -0
- odxtools/description.py +2 -5
- odxtools/diagdatadictionaryspec.py +1 -3
- odxtools/diaglayers/diaglayer.py +31 -11
- odxtools/diaglayers/ecuvariant.py +12 -19
- odxtools/diaglayers/hierarchyelement.py +5 -5
- odxtools/diaglayers/protocol.py +14 -0
- odxtools/direction.py +7 -0
- odxtools/ecumem.py +71 -0
- odxtools/ecumemconnector.py +136 -0
- odxtools/encryptcompressmethod.py +39 -0
- odxtools/encryptcompressmethodtype.py +13 -0
- odxtools/expectedident.py +40 -0
- odxtools/externflashdata.py +34 -0
- odxtools/filter.py +32 -0
- odxtools/flash.py +88 -0
- odxtools/flashclass.py +32 -0
- odxtools/flashdata.py +70 -0
- odxtools/fwchecksum.py +7 -0
- odxtools/fwsignature.py +7 -0
- odxtools/identdesc.py +54 -0
- odxtools/identvalue.py +32 -0
- odxtools/identvaluetype.py +14 -0
- odxtools/internflashdata.py +33 -0
- odxtools/loadfile.py +1 -1
- odxtools/mem.py +80 -0
- odxtools/modification.py +3 -2
- odxtools/negoffset.py +21 -0
- odxtools/odxlink.py +4 -2
- odxtools/ownident.py +38 -0
- odxtools/physicaltype.py +12 -10
- odxtools/physmem.py +52 -0
- odxtools/physsegment.py +42 -0
- odxtools/posoffset.py +21 -0
- odxtools/security.py +42 -0
- odxtools/securitymethod.py +7 -0
- odxtools/segment.py +63 -0
- odxtools/session.py +88 -0
- odxtools/sessiondesc.py +101 -0
- odxtools/sessionsubelemtype.py +14 -0
- odxtools/sizedeffilter.py +33 -0
- odxtools/sizedefphyssegment.py +33 -0
- odxtools/specialdata.py +2 -1
- odxtools/subcomponentparamconnector.py +1 -1
- odxtools/targetaddroffset.py +13 -0
- odxtools/templates/comparam-spec.odx-c.xml.jinja2 +1 -0
- odxtools/templates/comparam-subset.odx-cs.xml.jinja2 +1 -0
- odxtools/templates/diag_layer_container.odx-d.xml.jinja2 +2 -1
- odxtools/templates/flash.odx-f.xml.jinja2 +42 -0
- odxtools/templates/macros/printAdminData.xml.jinja2 +4 -4
- odxtools/templates/macros/printAudience.xml.jinja2 +3 -3
- odxtools/templates/macros/printChecksum.xml.jinja2 +36 -0
- odxtools/templates/macros/printComparam.xml.jinja2 +1 -1
- odxtools/templates/macros/printComparamRef.xml.jinja2 +1 -3
- odxtools/templates/macros/printCompuMethod.xml.jinja2 +1 -1
- odxtools/templates/macros/printDOP.xml.jinja2 +3 -3
- odxtools/templates/macros/printDatablock.xml.jinja2 +78 -0
- odxtools/templates/macros/printDiagComm.xml.jinja2 +2 -2
- odxtools/templates/macros/printDiagLayer.xml.jinja2 +2 -1
- odxtools/templates/macros/printDiagVariable.xml.jinja2 +4 -4
- odxtools/templates/macros/printDynDefinedSpec.xml.jinja2 +3 -3
- odxtools/templates/macros/printDynamicEndmarkerField.xml.jinja2 +2 -2
- odxtools/templates/macros/printDynamicLengthField.xml.jinja2 +2 -2
- odxtools/templates/macros/printEcuMem.xml.jinja2 +24 -0
- odxtools/templates/macros/printEcuMemConnector.xml.jinja2 +58 -0
- odxtools/templates/macros/printEndOfPdu.xml.jinja2 +1 -1
- odxtools/templates/macros/printEnvDataDesc.xml.jinja2 +1 -1
- odxtools/templates/macros/printExpectedIdent.xml.jinja2 +21 -0
- odxtools/templates/macros/printFlashdata.xml.jinja2 +43 -0
- odxtools/templates/macros/printIdentDesc.xml.jinja2 +17 -0
- odxtools/templates/macros/printMem.xml.jinja2 +35 -0
- odxtools/templates/macros/printMux.xml.jinja2 +3 -3
- odxtools/templates/macros/printOdxCategory.xml.jinja2 +4 -4
- odxtools/templates/macros/printOwnIdent.xml.jinja2 +17 -0
- odxtools/templates/macros/printParam.xml.jinja2 +4 -4
- odxtools/templates/macros/printParentRef.xml.jinja2 +1 -5
- odxtools/templates/macros/printPhysMem.xml.jinja2 +20 -0
- odxtools/templates/macros/printPhysSegment.xml.jinja2 +33 -0
- odxtools/templates/macros/printPreConditionStateRef.xml.jinja2 +1 -1
- odxtools/templates/macros/printProtStack.xml.jinja2 +1 -1
- odxtools/templates/macros/printProtocol.xml.jinja2 +1 -1
- odxtools/templates/macros/printSecurity.xml.jinja2 +37 -0
- odxtools/templates/macros/printSegment.xml.jinja2 +31 -0
- odxtools/templates/macros/printService.xml.jinja2 +3 -3
- odxtools/templates/macros/printSession.xml.jinja2 +45 -0
- odxtools/templates/macros/printSessionDesc.xml.jinja2 +40 -0
- odxtools/templates/macros/printSingleEcuJob.xml.jinja2 +3 -3
- odxtools/templates/macros/printSpecialData.xml.jinja2 +2 -2
- odxtools/templates/macros/printStateTransitionRef.xml.jinja2 +1 -1
- odxtools/templates/macros/printStaticField.xml.jinja2 +1 -1
- odxtools/templates/macros/printSubComponent.xml.jinja2 +3 -3
- odxtools/templates/macros/printTable.xml.jinja2 +6 -6
- odxtools/templates/macros/printUnitSpec.xml.jinja2 +2 -2
- odxtools/text.py +2 -6
- odxtools/utils.py +22 -1
- odxtools/validityfor.py +30 -0
- odxtools/version.py +2 -2
- odxtools/writepdxfile.py +70 -21
- {odxtools-10.1.1.dist-info → odxtools-10.2.1.dist-info}/METADATA +1 -1
- {odxtools-10.1.1.dist-info → odxtools-10.2.1.dist-info}/RECORD +112 -55
- {odxtools-10.1.1.dist-info → odxtools-10.2.1.dist-info}/WHEEL +1 -1
- {odxtools-10.1.1.dist-info → odxtools-10.2.1.dist-info}/entry_points.txt +0 -0
- {odxtools-10.1.1.dist-info → odxtools-10.2.1.dist-info}/licenses/LICENSE +0 -0
- {odxtools-10.1.1.dist-info → odxtools-10.2.1.dist-info}/top_level.txt +0 -0
odxtools/identvalue.py
ADDED
@@ -0,0 +1,32 @@
|
|
1
|
+
# SPDX-License-Identifier: MIT
|
2
|
+
from dataclasses import dataclass
|
3
|
+
from xml.etree import ElementTree
|
4
|
+
|
5
|
+
from .exceptions import odxraise
|
6
|
+
from .identvaluetype import IdentValueType
|
7
|
+
from .odxdoccontext import OdxDocContext
|
8
|
+
|
9
|
+
|
10
|
+
@dataclass(kw_only=True)
|
11
|
+
class IdentValue:
|
12
|
+
"""
|
13
|
+
Corresponds to IDENT-VALUE.
|
14
|
+
"""
|
15
|
+
|
16
|
+
value: str
|
17
|
+
|
18
|
+
# note that the spec says this attribute is named "TYPE", but in
|
19
|
+
# python, "type" is a build-in function...
|
20
|
+
value_type: IdentValueType
|
21
|
+
|
22
|
+
@staticmethod
|
23
|
+
def from_et(et_element: ElementTree.Element, context: OdxDocContext) -> "IdentValue":
|
24
|
+
value = et_element.text or ""
|
25
|
+
|
26
|
+
try:
|
27
|
+
value_type = IdentValueType(et_element.attrib["TYPE"])
|
28
|
+
except Exception as e:
|
29
|
+
odxraise(f"Cannot parse IDENT-VALUE-TYPE: {e}")
|
30
|
+
value_type = None
|
31
|
+
|
32
|
+
return IdentValue(value=value, value_type=value_type)
|
@@ -0,0 +1,14 @@
|
|
1
|
+
# SPDX-License-Identifier: MIT
|
2
|
+
from enum import Enum
|
3
|
+
|
4
|
+
from .odxtypes import DataType
|
5
|
+
|
6
|
+
|
7
|
+
class IdentValueType(Enum):
|
8
|
+
A_UINT32 = "A_UINT32"
|
9
|
+
A_BYTEFIELD = "A_BYTEFIELD"
|
10
|
+
A_ASCIISTRING = "A_ASCIISTRING"
|
11
|
+
|
12
|
+
@property
|
13
|
+
def data_type(self) -> DataType:
|
14
|
+
return DataType(self.value)
|
@@ -0,0 +1,33 @@
|
|
1
|
+
# SPDX-License-Identifier: MIT
|
2
|
+
from dataclasses import dataclass
|
3
|
+
from typing import Any
|
4
|
+
from xml.etree import ElementTree
|
5
|
+
|
6
|
+
from .exceptions import odxrequire
|
7
|
+
from .flashdata import Flashdata
|
8
|
+
from .odxdoccontext import OdxDocContext
|
9
|
+
from .odxlink import OdxLinkDatabase, OdxLinkId
|
10
|
+
from .snrefcontext import SnRefContext
|
11
|
+
from .utils import dataclass_fields_asdict
|
12
|
+
|
13
|
+
|
14
|
+
@dataclass(kw_only=True)
|
15
|
+
class InternFlashdata(Flashdata):
|
16
|
+
data: str
|
17
|
+
|
18
|
+
@staticmethod
|
19
|
+
def from_et(et_element: ElementTree.Element, context: OdxDocContext) -> "InternFlashdata":
|
20
|
+
kwargs = dataclass_fields_asdict(Flashdata.from_et(et_element, context))
|
21
|
+
|
22
|
+
data = odxrequire(et_element.findtext("DATA"))
|
23
|
+
|
24
|
+
return InternFlashdata(data=data, **kwargs)
|
25
|
+
|
26
|
+
def _build_odxlinks(self) -> dict[OdxLinkId, Any]:
|
27
|
+
return super()._build_odxlinks()
|
28
|
+
|
29
|
+
def _resolve_odxlinks(self, odxlinks: OdxLinkDatabase) -> None:
|
30
|
+
super()._resolve_odxlinks(odxlinks)
|
31
|
+
|
32
|
+
def _resolve_snrefs(self, context: SnRefContext) -> None:
|
33
|
+
super()._resolve_snrefs(context)
|
odxtools/loadfile.py
CHANGED
@@ -23,7 +23,7 @@ def load_odx_d_file(odx_d_file_name: str | Path) -> Database:
|
|
23
23
|
def load_file(file_name: str | Path) -> Database:
|
24
24
|
if str(file_name).lower().endswith(".pdx"):
|
25
25
|
return load_pdx_file(str(file_name))
|
26
|
-
elif
|
26
|
+
elif Path(file_name).suffix.lower().startswith(".odx"):
|
27
27
|
return load_odx_d_file(str(file_name))
|
28
28
|
else:
|
29
29
|
raise RuntimeError(f"Could not guess the file format of file '{file_name}'!")
|
odxtools/mem.py
ADDED
@@ -0,0 +1,80 @@
|
|
1
|
+
# SPDX-License-Identifier: MIT
|
2
|
+
from dataclasses import dataclass
|
3
|
+
from typing import Any
|
4
|
+
from xml.etree import ElementTree
|
5
|
+
|
6
|
+
from .datablock import Datablock
|
7
|
+
from .exceptions import odxraise
|
8
|
+
from .externflashdata import ExternFlashdata
|
9
|
+
from .flashdata import Flashdata
|
10
|
+
from .globals import xsi
|
11
|
+
from .internflashdata import InternFlashdata
|
12
|
+
from .nameditemlist import NamedItemList
|
13
|
+
from .odxdoccontext import OdxDocContext
|
14
|
+
from .odxlink import OdxLinkDatabase, OdxLinkId
|
15
|
+
from .session import Session
|
16
|
+
from .snrefcontext import SnRefContext
|
17
|
+
|
18
|
+
|
19
|
+
@dataclass(kw_only=True)
|
20
|
+
class Mem:
|
21
|
+
sessions: NamedItemList[Session]
|
22
|
+
datablocks: NamedItemList[Datablock]
|
23
|
+
flashdatas: NamedItemList[Flashdata]
|
24
|
+
|
25
|
+
@staticmethod
|
26
|
+
def from_et(et_element: ElementTree.Element, context: OdxDocContext) -> "Mem":
|
27
|
+
sessions = NamedItemList([
|
28
|
+
Session.from_et(sess_elem, context)
|
29
|
+
for sess_elem in et_element.iterfind("SESSIONS/SESSION")
|
30
|
+
])
|
31
|
+
|
32
|
+
datablocks = NamedItemList([
|
33
|
+
Datablock.from_et(db_elem, context)
|
34
|
+
for db_elem in et_element.iterfind("DATABLOCKS/DATABLOCK")
|
35
|
+
])
|
36
|
+
|
37
|
+
flashdatas: NamedItemList[Flashdata] = NamedItemList()
|
38
|
+
for flashdata_elem in et_element.iterfind("FLASHDATAS/FLASHDATA"):
|
39
|
+
flashdata_type = flashdata_elem.attrib.get(f"{xsi}type")
|
40
|
+
if flashdata_type == "INTERN-FLASHDATA":
|
41
|
+
flashdatas.append(InternFlashdata.from_et(flashdata_elem, context))
|
42
|
+
elif flashdata_type == "EXTERN-FLASHDATA":
|
43
|
+
flashdatas.append(ExternFlashdata.from_et(flashdata_elem, context))
|
44
|
+
else:
|
45
|
+
odxraise(f"Encountered unknown flashdata type {flashdata_type}")
|
46
|
+
flashdatas.append(Flashdata.from_et(flashdata_elem, context))
|
47
|
+
|
48
|
+
return Mem(
|
49
|
+
sessions=sessions,
|
50
|
+
datablocks=datablocks,
|
51
|
+
flashdatas=flashdatas,
|
52
|
+
)
|
53
|
+
|
54
|
+
def _build_odxlinks(self) -> dict[OdxLinkId, Any]:
|
55
|
+
odxlinks = {}
|
56
|
+
|
57
|
+
for session in self.sessions:
|
58
|
+
odxlinks.update(session._build_odxlinks())
|
59
|
+
for datablock in self.datablocks:
|
60
|
+
odxlinks.update(datablock._build_odxlinks())
|
61
|
+
for flashdata in self.flashdatas:
|
62
|
+
odxlinks.update(flashdata._build_odxlinks())
|
63
|
+
|
64
|
+
return odxlinks
|
65
|
+
|
66
|
+
def _resolve_odxlinks(self, odxlinks: OdxLinkDatabase) -> None:
|
67
|
+
for session in self.sessions:
|
68
|
+
session._resolve_odxlinks(odxlinks)
|
69
|
+
for datablock in self.datablocks:
|
70
|
+
datablock._resolve_odxlinks(odxlinks)
|
71
|
+
for flashdata in self.flashdatas:
|
72
|
+
flashdata._resolve_odxlinks(odxlinks)
|
73
|
+
|
74
|
+
def _resolve_snrefs(self, context: SnRefContext) -> None:
|
75
|
+
for session in self.sessions:
|
76
|
+
session._resolve_snrefs(context)
|
77
|
+
for datablock in self.datablocks:
|
78
|
+
datablock._resolve_snrefs(context)
|
79
|
+
for flashdata in self.flashdatas:
|
80
|
+
flashdata._resolve_snrefs(context)
|
odxtools/modification.py
CHANGED
@@ -7,6 +7,7 @@ from .exceptions import odxrequire
|
|
7
7
|
from .odxdoccontext import OdxDocContext
|
8
8
|
from .odxlink import OdxLinkDatabase, OdxLinkId
|
9
9
|
from .snrefcontext import SnRefContext
|
10
|
+
from .utils import strip_indent
|
10
11
|
|
11
12
|
|
12
13
|
@dataclass(kw_only=True)
|
@@ -16,8 +17,8 @@ class Modification:
|
|
16
17
|
|
17
18
|
@staticmethod
|
18
19
|
def from_et(et_element: ElementTree.Element, context: OdxDocContext) -> "Modification":
|
19
|
-
change = odxrequire(et_element.findtext("CHANGE"))
|
20
|
-
reason = et_element.findtext("REASON")
|
20
|
+
change = odxrequire(strip_indent(et_element.findtext("CHANGE")))
|
21
|
+
reason = strip_indent(et_element.findtext("REASON"))
|
21
22
|
|
22
23
|
return Modification(change=change, reason=reason)
|
23
24
|
|
odxtools/negoffset.py
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
# SPDX-License-Identifier: MIT
|
2
|
+
from dataclasses import dataclass
|
3
|
+
from xml.etree import ElementTree
|
4
|
+
|
5
|
+
from .exceptions import odxrequire
|
6
|
+
from .odxdoccontext import OdxDocContext
|
7
|
+
from .targetaddroffset import TargetAddrOffset
|
8
|
+
from .utils import dataclass_fields_asdict, read_hex_binary
|
9
|
+
|
10
|
+
|
11
|
+
@dataclass(kw_only=True)
|
12
|
+
class NegOffset(TargetAddrOffset):
|
13
|
+
negative_offset: int
|
14
|
+
|
15
|
+
@staticmethod
|
16
|
+
def from_et(et_element: ElementTree.Element, context: OdxDocContext) -> "NegOffset":
|
17
|
+
kwargs = dataclass_fields_asdict(TargetAddrOffset.from_et(et_element, context))
|
18
|
+
|
19
|
+
negative_offset = odxrequire(read_hex_binary(et_element.find("NEGATIVE-OFFSET")))
|
20
|
+
|
21
|
+
return NegOffset(negative_offset=negative_offset, **kwargs)
|
odxtools/odxlink.py
CHANGED
@@ -204,8 +204,10 @@ class OdxLinkDatabase:
|
|
204
204
|
# locate an object exhibiting with the referenced local ID
|
205
205
|
# in the ID database for the document fragment
|
206
206
|
if (obj := doc_frag_db.get(ref.ref_id)) is not None:
|
207
|
-
if expected_type is not None:
|
208
|
-
|
207
|
+
if expected_type is not None and not isinstance(obj, expected_type):
|
208
|
+
odxraise(f"Referenced object for link {ref.ref_id} is of type "
|
209
|
+
f"{type(obj).__name__} which is not a subclass of expected "
|
210
|
+
f"type {expected_type.__name__}")
|
209
211
|
|
210
212
|
return obj
|
211
213
|
|
odxtools/ownident.py
ADDED
@@ -0,0 +1,38 @@
|
|
1
|
+
# SPDX-License-Identifier: MIT
|
2
|
+
from dataclasses import dataclass
|
3
|
+
from typing import Any
|
4
|
+
from xml.etree import ElementTree
|
5
|
+
|
6
|
+
from .element import IdentifiableElement
|
7
|
+
from .exceptions import odxrequire
|
8
|
+
from .identvalue import IdentValue
|
9
|
+
from .odxdoccontext import OdxDocContext
|
10
|
+
from .odxlink import OdxLinkDatabase, OdxLinkId
|
11
|
+
from .snrefcontext import SnRefContext
|
12
|
+
from .utils import dataclass_fields_asdict
|
13
|
+
|
14
|
+
|
15
|
+
@dataclass(kw_only=True)
|
16
|
+
class OwnIdent(IdentifiableElement):
|
17
|
+
"""
|
18
|
+
Corresponds to OWN-IDENT.
|
19
|
+
"""
|
20
|
+
|
21
|
+
ident_value: IdentValue
|
22
|
+
|
23
|
+
@staticmethod
|
24
|
+
def from_et(et_element: ElementTree.Element, context: OdxDocContext) -> "OwnIdent":
|
25
|
+
kwargs = dataclass_fields_asdict(IdentifiableElement.from_et(et_element, context))
|
26
|
+
|
27
|
+
ident_value = IdentValue.from_et(odxrequire(et_element.find("IDENT-VALUE")), context)
|
28
|
+
|
29
|
+
return OwnIdent(ident_value=ident_value, **kwargs)
|
30
|
+
|
31
|
+
def _build_odxlinks(self) -> dict[OdxLinkId, Any]:
|
32
|
+
return {self.odx_id: self}
|
33
|
+
|
34
|
+
def _resolve_odxlinks(self, odxlinks: OdxLinkDatabase) -> None:
|
35
|
+
pass
|
36
|
+
|
37
|
+
def _resolve_snrefs(self, context: SnRefContext) -> None:
|
38
|
+
pass
|
odxtools/physicaltype.py
CHANGED
@@ -2,7 +2,7 @@
|
|
2
2
|
from dataclasses import dataclass
|
3
3
|
from xml.etree import ElementTree
|
4
4
|
|
5
|
-
from .exceptions import odxraise
|
5
|
+
from .exceptions import odxraise, odxrequire
|
6
6
|
from .odxdoccontext import OdxDocContext
|
7
7
|
from .odxtypes import DataType
|
8
8
|
from .radix import Radix
|
@@ -31,27 +31,29 @@ class PhysicalType:
|
|
31
31
|
PhysicalType(DataType.A_FLOAT64, precision=2)
|
32
32
|
"""
|
33
33
|
|
34
|
+
#: Number of digits after the decimal point to display to the user
|
35
|
+
#: The precision is only applicable if the base data type is
|
36
|
+
#: A_FLOAT32 or A_FLOAT64.
|
34
37
|
precision: int | None = None
|
35
|
-
"""Number of digits after the decimal point to display to the user
|
36
|
-
The precision is only applicable if the base data type is A_FLOAT32 or A_FLOAT64.
|
37
|
-
"""
|
38
38
|
|
39
39
|
base_data_type: DataType
|
40
40
|
|
41
|
+
#: The display radix defines how integers are displayed to the
|
42
|
+
#: user. The display radix is only applicable if the base data type
|
43
|
+
#: is A_UINT32.
|
41
44
|
display_radix: Radix | None = None
|
42
|
-
"""The display radix defines how integers are displayed to the user.
|
43
|
-
The display radix is only applicable if the base data type is A_UINT32.
|
44
|
-
"""
|
45
45
|
|
46
46
|
@staticmethod
|
47
47
|
def from_et(et_element: ElementTree.Element, context: OdxDocContext) -> "PhysicalType":
|
48
48
|
precision_str = et_element.findtext("PRECISION")
|
49
49
|
precision = int(precision_str) if precision_str is not None else None
|
50
50
|
|
51
|
-
base_data_type_str = et_element.get("BASE-DATA-TYPE")
|
52
|
-
|
51
|
+
base_data_type_str = odxrequire(et_element.attrib.get("BASE-DATA-TYPE"))
|
52
|
+
try:
|
53
|
+
base_data_type = DataType(base_data_type_str)
|
54
|
+
except ValueError:
|
53
55
|
odxraise(f"Encountered unknown base data type '{base_data_type_str}'")
|
54
|
-
|
56
|
+
base_data_type = None
|
55
57
|
|
56
58
|
display_radix_str = et_element.get("DISPLAY-RADIX")
|
57
59
|
if display_radix_str is not None:
|
odxtools/physmem.py
ADDED
@@ -0,0 +1,52 @@
|
|
1
|
+
# SPDX-License-Identifier: MIT
|
2
|
+
from dataclasses import dataclass
|
3
|
+
from typing import Any
|
4
|
+
from xml.etree import ElementTree
|
5
|
+
|
6
|
+
from .addrdefphyssegment import AddrdefPhysSegment
|
7
|
+
from .element import IdentifiableElement
|
8
|
+
from .globals import xsi
|
9
|
+
from .nameditemlist import NamedItemList
|
10
|
+
from .odxdoccontext import OdxDocContext
|
11
|
+
from .odxlink import OdxLinkDatabase, OdxLinkId
|
12
|
+
from .physsegment import PhysSegment
|
13
|
+
from .sizedefphyssegment import SizedefPhysSegment
|
14
|
+
from .snrefcontext import SnRefContext
|
15
|
+
from .utils import dataclass_fields_asdict
|
16
|
+
|
17
|
+
|
18
|
+
@dataclass(kw_only=True)
|
19
|
+
class PhysMem(IdentifiableElement):
|
20
|
+
phys_segments: NamedItemList[PhysSegment]
|
21
|
+
|
22
|
+
@staticmethod
|
23
|
+
def from_et(et_element: ElementTree.Element, context: OdxDocContext) -> "PhysMem":
|
24
|
+
kwargs = dataclass_fields_asdict(IdentifiableElement.from_et(et_element, context))
|
25
|
+
|
26
|
+
phys_segments: NamedItemList[PhysSegment] = NamedItemList()
|
27
|
+
for phys_segment_elem in et_element.iterfind("PHYS-SEGMENTS/PHYS-SEGMENT"):
|
28
|
+
phys_segment_type = phys_segment_elem.attrib.get(f"{xsi}type")
|
29
|
+
if phys_segment_type == "ADDRDEF-PHYS-SEGMENT":
|
30
|
+
phys_segments.append(AddrdefPhysSegment.from_et(phys_segment_elem, context))
|
31
|
+
elif phys_segment_type == "SIZEDEF-PHYS-SEGMENT":
|
32
|
+
phys_segments.append(SizedefPhysSegment.from_et(phys_segment_elem, context))
|
33
|
+
else:
|
34
|
+
phys_segments.append(PhysSegment.from_et(phys_segment_elem, context))
|
35
|
+
|
36
|
+
return PhysMem(phys_segments=phys_segments, **kwargs)
|
37
|
+
|
38
|
+
def _build_odxlinks(self) -> dict[OdxLinkId, Any]:
|
39
|
+
odxlinks = {self.odx_id: self}
|
40
|
+
|
41
|
+
for phys_segment in self.phys_segments:
|
42
|
+
odxlinks.update(phys_segment._build_odxlinks())
|
43
|
+
|
44
|
+
return odxlinks
|
45
|
+
|
46
|
+
def _resolve_odxlinks(self, odxlinks: OdxLinkDatabase) -> None:
|
47
|
+
for phys_segment in self.phys_segments:
|
48
|
+
phys_segment._resolve_odxlinks(odxlinks)
|
49
|
+
|
50
|
+
def _resolve_snrefs(self, context: SnRefContext) -> None:
|
51
|
+
for phys_segment in self.phys_segments:
|
52
|
+
phys_segment._resolve_snrefs(context)
|
odxtools/physsegment.py
ADDED
@@ -0,0 +1,42 @@
|
|
1
|
+
# SPDX-License-Identifier: MIT
|
2
|
+
from dataclasses import dataclass
|
3
|
+
from typing import Any
|
4
|
+
from xml.etree import ElementTree
|
5
|
+
|
6
|
+
from .element import IdentifiableElement
|
7
|
+
from .exceptions import odxrequire
|
8
|
+
from .odxdoccontext import OdxDocContext
|
9
|
+
from .odxlink import OdxLinkDatabase, OdxLinkId
|
10
|
+
from .snrefcontext import SnRefContext
|
11
|
+
from .utils import dataclass_fields_asdict, read_hex_binary
|
12
|
+
|
13
|
+
|
14
|
+
@dataclass(kw_only=True)
|
15
|
+
class PhysSegment(IdentifiableElement):
|
16
|
+
fillbyte: int | None = None
|
17
|
+
block_size: int | None = None
|
18
|
+
start_address: int
|
19
|
+
|
20
|
+
@staticmethod
|
21
|
+
def from_et(et_element: ElementTree.Element, context: OdxDocContext) -> "PhysSegment":
|
22
|
+
kwargs = dataclass_fields_asdict(IdentifiableElement.from_et(et_element, context))
|
23
|
+
|
24
|
+
fillbyte = read_hex_binary(et_element.find("FILLBYTE"))
|
25
|
+
block_size = 0
|
26
|
+
if (bs_elem := et_element.find("BLOCK-SIZE")) is not None:
|
27
|
+
block_size = int(odxrequire(bs_elem.text) or "0")
|
28
|
+
start_address = odxrequire(read_hex_binary(et_element.find("START-ADDRESS")))
|
29
|
+
|
30
|
+
return PhysSegment(
|
31
|
+
fillbyte=fillbyte, block_size=block_size, start_address=start_address, **kwargs)
|
32
|
+
|
33
|
+
def _build_odxlinks(self) -> dict[OdxLinkId, Any]:
|
34
|
+
odxlinks = {self.odx_id: self}
|
35
|
+
|
36
|
+
return odxlinks
|
37
|
+
|
38
|
+
def _resolve_odxlinks(self, odxlinks: OdxLinkDatabase) -> None:
|
39
|
+
pass
|
40
|
+
|
41
|
+
def _resolve_snrefs(self, context: SnRefContext) -> None:
|
42
|
+
pass
|
odxtools/posoffset.py
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
# SPDX-License-Identifier: MIT
|
2
|
+
from dataclasses import dataclass
|
3
|
+
from xml.etree import ElementTree
|
4
|
+
|
5
|
+
from .exceptions import odxrequire
|
6
|
+
from .odxdoccontext import OdxDocContext
|
7
|
+
from .targetaddroffset import TargetAddrOffset
|
8
|
+
from .utils import dataclass_fields_asdict, read_hex_binary
|
9
|
+
|
10
|
+
|
11
|
+
@dataclass(kw_only=True)
|
12
|
+
class PosOffset(TargetAddrOffset):
|
13
|
+
positive_offset: int
|
14
|
+
|
15
|
+
@staticmethod
|
16
|
+
def from_et(et_element: ElementTree.Element, context: OdxDocContext) -> "PosOffset":
|
17
|
+
kwargs = dataclass_fields_asdict(TargetAddrOffset.from_et(et_element, context))
|
18
|
+
|
19
|
+
positive_offset = odxrequire(read_hex_binary(et_element.find("POSITIVE-OFFSET")))
|
20
|
+
|
21
|
+
return PosOffset(positive_offset=positive_offset, **kwargs)
|
odxtools/security.py
ADDED
@@ -0,0 +1,42 @@
|
|
1
|
+
# SPDX-License-Identifier: MIT
|
2
|
+
from dataclasses import dataclass
|
3
|
+
from xml.etree import ElementTree
|
4
|
+
|
5
|
+
from .fwchecksum import FwChecksum
|
6
|
+
from .fwsignature import FwSignature
|
7
|
+
from .odxdoccontext import OdxDocContext
|
8
|
+
from .securitymethod import SecurityMethod
|
9
|
+
from .validityfor import ValidityFor
|
10
|
+
|
11
|
+
|
12
|
+
@dataclass(kw_only=True)
|
13
|
+
class Security:
|
14
|
+
security_method: SecurityMethod | None = None
|
15
|
+
fw_signature: FwSignature | None = None
|
16
|
+
fw_checksum: FwChecksum | None = None
|
17
|
+
validity_for: ValidityFor | None = None
|
18
|
+
|
19
|
+
@staticmethod
|
20
|
+
def from_et(et_element: ElementTree.Element, context: OdxDocContext) -> "Security":
|
21
|
+
security_method = None
|
22
|
+
if (sm_elem := et_element.find("SECURITY-METHOD")) is not None:
|
23
|
+
security_method = SecurityMethod.from_et(sm_elem, context)
|
24
|
+
|
25
|
+
fw_signature = None
|
26
|
+
if (fws_elem := et_element.find("FW-SIGNATURE")) is not None:
|
27
|
+
fw_signature = FwSignature.from_et(fws_elem, context)
|
28
|
+
|
29
|
+
fw_checksum = None
|
30
|
+
if (fwcs_elem := et_element.find("FW-CHECKSUM")) is not None:
|
31
|
+
fw_checksum = FwChecksum.from_et(fwcs_elem, context)
|
32
|
+
|
33
|
+
validity_for = None
|
34
|
+
if (val_elem := et_element.find("VALIDITY-FOR")) is not None:
|
35
|
+
validity_for = ValidityFor.from_et(val_elem, context)
|
36
|
+
|
37
|
+
return Security(
|
38
|
+
security_method=security_method,
|
39
|
+
fw_signature=fw_signature,
|
40
|
+
fw_checksum=fw_checksum,
|
41
|
+
validity_for=validity_for,
|
42
|
+
)
|
@@ -0,0 +1,7 @@
|
|
1
|
+
# SPDX-License-Identifier: MIT
|
2
|
+
from .validityfor import ValidityFor
|
3
|
+
|
4
|
+
# Note that the ODX specification specifies a separate tag for this,
|
5
|
+
# but this tag is identical to VALIDITY-FOR, so let's use a type alias
|
6
|
+
# to reduce the amount of copy-and-pasted code
|
7
|
+
SecurityMethod = ValidityFor
|
odxtools/segment.py
ADDED
@@ -0,0 +1,63 @@
|
|
1
|
+
# SPDX-License-Identifier: MIT
|
2
|
+
from dataclasses import dataclass
|
3
|
+
from typing import Any
|
4
|
+
from xml.etree import ElementTree
|
5
|
+
|
6
|
+
from .element import IdentifiableElement
|
7
|
+
from .encryptcompressmethod import EncryptCompressMethod
|
8
|
+
from .exceptions import odxrequire
|
9
|
+
from .odxdoccontext import OdxDocContext
|
10
|
+
from .odxlink import OdxLinkDatabase, OdxLinkId
|
11
|
+
from .snrefcontext import SnRefContext
|
12
|
+
from .utils import dataclass_fields_asdict, read_hex_binary
|
13
|
+
|
14
|
+
|
15
|
+
@dataclass(kw_only=True)
|
16
|
+
class Segment(IdentifiableElement):
|
17
|
+
source_start_address: int
|
18
|
+
compressed_size: int | None = None
|
19
|
+
|
20
|
+
# exactly one of the two next fields must be not None
|
21
|
+
uncompressed_size: int | None = None
|
22
|
+
source_end_address: int | None = None
|
23
|
+
|
24
|
+
encrypt_compress_method: EncryptCompressMethod | None = None
|
25
|
+
|
26
|
+
@staticmethod
|
27
|
+
def from_et(et_element: ElementTree.Element, context: OdxDocContext) -> "Segment":
|
28
|
+
kwargs = dataclass_fields_asdict(IdentifiableElement.from_et(et_element, context))
|
29
|
+
|
30
|
+
source_start_address = odxrequire(read_hex_binary(et_element.find("SOURCE-START-ADDRESS")))
|
31
|
+
compressed_size = None
|
32
|
+
if (cs_elem := et_element.find("COMPRESSED-SIZE")) is not None:
|
33
|
+
compressed_size = int(odxrequire(cs_elem.text) or "0")
|
34
|
+
|
35
|
+
# exactly one of the two next fields must be not None
|
36
|
+
uncompressed_size = None
|
37
|
+
if (ucs_elem := et_element.find("UNCOMPRESSED-SIZE")) is not None:
|
38
|
+
uncompressed_size = int(odxrequire(ucs_elem.text) or "0")
|
39
|
+
source_end_address = read_hex_binary(et_element.find("SOURCE-END-ADDRESS"))
|
40
|
+
|
41
|
+
encrypt_compress_method = None
|
42
|
+
if (encrypt_compress_method_elem := et_element.find("ENCRYPT-COMPRESS-METHOD")) is not None:
|
43
|
+
encrypt_compress_method = EncryptCompressMethod.from_et(encrypt_compress_method_elem,
|
44
|
+
context)
|
45
|
+
|
46
|
+
return Segment(
|
47
|
+
source_start_address=source_start_address,
|
48
|
+
compressed_size=compressed_size,
|
49
|
+
uncompressed_size=uncompressed_size,
|
50
|
+
source_end_address=source_end_address,
|
51
|
+
encrypt_compress_method=encrypt_compress_method,
|
52
|
+
**kwargs)
|
53
|
+
|
54
|
+
def _build_odxlinks(self) -> dict[OdxLinkId, Any]:
|
55
|
+
odxlinks = {self.odx_id: self}
|
56
|
+
|
57
|
+
return odxlinks
|
58
|
+
|
59
|
+
def _resolve_odxlinks(self, odxlinks: OdxLinkDatabase) -> None:
|
60
|
+
pass
|
61
|
+
|
62
|
+
def _resolve_snrefs(self, context: SnRefContext) -> None:
|
63
|
+
pass
|
odxtools/session.py
ADDED
@@ -0,0 +1,88 @@
|
|
1
|
+
# SPDX-License-Identifier: MIT
|
2
|
+
from dataclasses import dataclass, field
|
3
|
+
from typing import Any
|
4
|
+
from xml.etree import ElementTree
|
5
|
+
|
6
|
+
from .checksum import Checksum
|
7
|
+
from .datablock import Datablock
|
8
|
+
from .element import IdentifiableElement
|
9
|
+
from .expectedident import ExpectedIdent
|
10
|
+
from .nameditemlist import NamedItemList
|
11
|
+
from .odxdoccontext import OdxDocContext
|
12
|
+
from .odxlink import OdxLinkDatabase, OdxLinkId, OdxLinkRef
|
13
|
+
from .security import Security
|
14
|
+
from .snrefcontext import SnRefContext
|
15
|
+
from .specialdatagroup import SpecialDataGroup
|
16
|
+
from .utils import dataclass_fields_asdict
|
17
|
+
|
18
|
+
|
19
|
+
@dataclass(kw_only=True)
|
20
|
+
class Session(IdentifiableElement):
|
21
|
+
expected_idents: NamedItemList[ExpectedIdent] = field(default_factory=NamedItemList)
|
22
|
+
checksums: NamedItemList[Checksum] = field(default_factory=NamedItemList)
|
23
|
+
securities: list[Security] = field(default_factory=list)
|
24
|
+
datablock_refs: list[OdxLinkRef] = field(default_factory=list)
|
25
|
+
sdgs: list[SpecialDataGroup] = field(default_factory=list)
|
26
|
+
|
27
|
+
@property
|
28
|
+
def datablocks(self) -> NamedItemList[Datablock]:
|
29
|
+
return self._datablocks
|
30
|
+
|
31
|
+
@staticmethod
|
32
|
+
def from_et(et_element: ElementTree.Element, context: OdxDocContext) -> "Session":
|
33
|
+
|
34
|
+
kwargs = dataclass_fields_asdict(IdentifiableElement.from_et(et_element, context))
|
35
|
+
|
36
|
+
expected_idents = NamedItemList([
|
37
|
+
ExpectedIdent.from_et(el, context)
|
38
|
+
for el in et_element.iterfind("EXPECTED-IDENTS/EXPECTED-IDENT")
|
39
|
+
])
|
40
|
+
checksums = NamedItemList(
|
41
|
+
[Checksum.from_et(el, context) for el in et_element.iterfind("CHECKSUMS/CHECKSUM")])
|
42
|
+
securities = [
|
43
|
+
Security.from_et(el, context) for el in et_element.iterfind("SECURITYS/SECURITY")
|
44
|
+
]
|
45
|
+
datablock_refs = [
|
46
|
+
OdxLinkRef.from_et(el, context)
|
47
|
+
for el in et_element.iterfind("DATABLOCK-REFS/DATABLOCK-REF")
|
48
|
+
]
|
49
|
+
sdgs = [SpecialDataGroup.from_et(sdge, context) for sdge in et_element.iterfind("SDGS/SDG")]
|
50
|
+
|
51
|
+
return Session(
|
52
|
+
expected_idents=expected_idents,
|
53
|
+
checksums=checksums,
|
54
|
+
securities=securities,
|
55
|
+
datablock_refs=datablock_refs,
|
56
|
+
sdgs=sdgs,
|
57
|
+
**kwargs)
|
58
|
+
|
59
|
+
def _build_odxlinks(self) -> dict[OdxLinkId, Any]:
|
60
|
+
odxlinks = {self.odx_id: self}
|
61
|
+
|
62
|
+
for ei in self.expected_idents:
|
63
|
+
odxlinks.update(ei._build_odxlinks())
|
64
|
+
for cs in self.checksums:
|
65
|
+
odxlinks.update(cs._build_odxlinks())
|
66
|
+
for sdg in self.sdgs:
|
67
|
+
odxlinks.update(sdg._build_odxlinks())
|
68
|
+
|
69
|
+
return odxlinks
|
70
|
+
|
71
|
+
def _resolve_odxlinks(self, odxlinks: OdxLinkDatabase) -> None:
|
72
|
+
self._datablocks = NamedItemList(
|
73
|
+
[odxlinks.resolve(ref, Datablock) for ref in self.datablock_refs])
|
74
|
+
|
75
|
+
for ei in self.expected_idents:
|
76
|
+
ei._resolve_odxlinks(odxlinks)
|
77
|
+
for cs in self.checksums:
|
78
|
+
cs._resolve_odxlinks(odxlinks)
|
79
|
+
for sdg in self.sdgs:
|
80
|
+
sdg._resolve_odxlinks(odxlinks)
|
81
|
+
|
82
|
+
def _resolve_snrefs(self, context: SnRefContext) -> None:
|
83
|
+
for ei in self.expected_idents:
|
84
|
+
ei._resolve_snrefs(context)
|
85
|
+
for cs in self.checksums:
|
86
|
+
cs._resolve_snrefs(context)
|
87
|
+
for sdg in self.sdgs:
|
88
|
+
sdg._resolve_snrefs(context)
|