odxtools 10.1.0__py3-none-any.whl → 10.2.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.
Files changed (107) hide show
  1. odxtools/addrdeffilter.py +33 -0
  2. odxtools/addrdefphyssegment.py +33 -0
  3. odxtools/checksum.py +67 -0
  4. odxtools/checksumresult.py +7 -0
  5. odxtools/cli/compare.py +143 -170
  6. odxtools/database.py +24 -4
  7. odxtools/datablock.py +153 -0
  8. odxtools/datafile.py +23 -0
  9. odxtools/dataformat.py +39 -0
  10. odxtools/dataformatselection.py +9 -0
  11. odxtools/description.py +2 -5
  12. odxtools/diagdatadictionaryspec.py +1 -3
  13. odxtools/direction.py +7 -0
  14. odxtools/ecumem.py +71 -0
  15. odxtools/ecumemconnector.py +136 -0
  16. odxtools/encryptcompressmethod.py +39 -0
  17. odxtools/encryptcompressmethodtype.py +13 -0
  18. odxtools/expectedident.py +40 -0
  19. odxtools/externflashdata.py +34 -0
  20. odxtools/filter.py +32 -0
  21. odxtools/flash.py +88 -0
  22. odxtools/flashclass.py +32 -0
  23. odxtools/flashdata.py +70 -0
  24. odxtools/fwchecksum.py +7 -0
  25. odxtools/fwsignature.py +7 -0
  26. odxtools/identdesc.py +54 -0
  27. odxtools/identvalue.py +32 -0
  28. odxtools/identvaluetype.py +14 -0
  29. odxtools/internflashdata.py +33 -0
  30. odxtools/loadfile.py +1 -1
  31. odxtools/mem.py +80 -0
  32. odxtools/modification.py +3 -2
  33. odxtools/negoffset.py +21 -0
  34. odxtools/ownident.py +38 -0
  35. odxtools/physicaltype.py +12 -10
  36. odxtools/physmem.py +52 -0
  37. odxtools/physsegment.py +42 -0
  38. odxtools/posoffset.py +21 -0
  39. odxtools/security.py +42 -0
  40. odxtools/securitymethod.py +7 -0
  41. odxtools/segment.py +63 -0
  42. odxtools/session.py +88 -0
  43. odxtools/sessiondesc.py +101 -0
  44. odxtools/sessionsubelemtype.py +14 -0
  45. odxtools/sizedeffilter.py +33 -0
  46. odxtools/sizedefphyssegment.py +33 -0
  47. odxtools/specialdata.py +2 -1
  48. odxtools/targetaddroffset.py +13 -0
  49. odxtools/templates/comparam-spec.odx-c.xml.jinja2 +1 -0
  50. odxtools/templates/comparam-subset.odx-cs.xml.jinja2 +1 -0
  51. odxtools/templates/diag_layer_container.odx-d.xml.jinja2 +2 -1
  52. odxtools/templates/flash.odx-f.xml.jinja2 +42 -0
  53. odxtools/templates/macros/printAdminData.xml.jinja2 +4 -4
  54. odxtools/templates/macros/printAudience.xml.jinja2 +3 -3
  55. odxtools/templates/macros/printChecksum.xml.jinja2 +36 -0
  56. odxtools/templates/macros/printComparam.xml.jinja2 +1 -1
  57. odxtools/templates/macros/printComparamRef.xml.jinja2 +1 -3
  58. odxtools/templates/macros/printCompuMethod.xml.jinja2 +1 -1
  59. odxtools/templates/macros/printDOP.xml.jinja2 +3 -3
  60. odxtools/templates/macros/printDatablock.xml.jinja2 +78 -0
  61. odxtools/templates/macros/printDiagComm.xml.jinja2 +2 -2
  62. odxtools/templates/macros/printDiagLayer.xml.jinja2 +2 -1
  63. odxtools/templates/macros/printDiagVariable.xml.jinja2 +4 -4
  64. odxtools/templates/macros/printDynDefinedSpec.xml.jinja2 +3 -3
  65. odxtools/templates/macros/printDynamicEndmarkerField.xml.jinja2 +2 -2
  66. odxtools/templates/macros/printDynamicLengthField.xml.jinja2 +2 -2
  67. odxtools/templates/macros/printEcuMem.xml.jinja2 +24 -0
  68. odxtools/templates/macros/printEcuMemConnector.xml.jinja2 +58 -0
  69. odxtools/templates/macros/printEndOfPdu.xml.jinja2 +1 -1
  70. odxtools/templates/macros/printEnvDataDesc.xml.jinja2 +1 -1
  71. odxtools/templates/macros/printExpectedIdent.xml.jinja2 +21 -0
  72. odxtools/templates/macros/printFlashdata.xml.jinja2 +43 -0
  73. odxtools/templates/macros/printIdentDesc.xml.jinja2 +17 -0
  74. odxtools/templates/macros/printMem.xml.jinja2 +35 -0
  75. odxtools/templates/macros/printMux.xml.jinja2 +3 -3
  76. odxtools/templates/macros/printOdxCategory.xml.jinja2 +4 -4
  77. odxtools/templates/macros/printOwnIdent.xml.jinja2 +17 -0
  78. odxtools/templates/macros/printParam.xml.jinja2 +4 -4
  79. odxtools/templates/macros/printParentRef.xml.jinja2 +1 -5
  80. odxtools/templates/macros/printPhysMem.xml.jinja2 +20 -0
  81. odxtools/templates/macros/printPhysSegment.xml.jinja2 +33 -0
  82. odxtools/templates/macros/printPreConditionStateRef.xml.jinja2 +1 -1
  83. odxtools/templates/macros/printProtStack.xml.jinja2 +1 -1
  84. odxtools/templates/macros/printProtocol.xml.jinja2 +1 -1
  85. odxtools/templates/macros/printSecurity.xml.jinja2 +37 -0
  86. odxtools/templates/macros/printSegment.xml.jinja2 +31 -0
  87. odxtools/templates/macros/printService.xml.jinja2 +3 -3
  88. odxtools/templates/macros/printSession.xml.jinja2 +45 -0
  89. odxtools/templates/macros/printSessionDesc.xml.jinja2 +40 -0
  90. odxtools/templates/macros/printSingleEcuJob.xml.jinja2 +3 -3
  91. odxtools/templates/macros/printSpecialData.xml.jinja2 +2 -2
  92. odxtools/templates/macros/printStateTransitionRef.xml.jinja2 +1 -1
  93. odxtools/templates/macros/printStaticField.xml.jinja2 +1 -1
  94. odxtools/templates/macros/printSubComponent.xml.jinja2 +3 -3
  95. odxtools/templates/macros/printTable.xml.jinja2 +6 -6
  96. odxtools/templates/macros/printUnitSpec.xml.jinja2 +2 -2
  97. odxtools/text.py +2 -6
  98. odxtools/utils.py +22 -1
  99. odxtools/validityfor.py +30 -0
  100. odxtools/version.py +2 -2
  101. odxtools/writepdxfile.py +70 -21
  102. {odxtools-10.1.0.dist-info → odxtools-10.2.0.dist-info}/METADATA +1 -1
  103. {odxtools-10.1.0.dist-info → odxtools-10.2.0.dist-info}/RECORD +107 -50
  104. {odxtools-10.1.0.dist-info → odxtools-10.2.0.dist-info}/WHEEL +1 -1
  105. {odxtools-10.1.0.dist-info → odxtools-10.2.0.dist-info}/entry_points.txt +0 -0
  106. {odxtools-10.1.0.dist-info → odxtools-10.2.0.dist-info}/licenses/LICENSE +0 -0
  107. {odxtools-10.1.0.dist-info → odxtools-10.2.0.dist-info}/top_level.txt +0 -0
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)
@@ -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)
@@ -0,0 +1,101 @@
1
+ # SPDX-License-Identifier: MIT
2
+ from dataclasses import dataclass, field
3
+ from typing import Any, cast
4
+ from xml.etree import ElementTree
5
+
6
+ from .audience import Audience
7
+ from .direction import Direction
8
+ from .element import NamedElement
9
+ from .exceptions import odxraise, odxrequire
10
+ from .flashclass import FlashClass
11
+ from .nameditemlist import NamedItemList
12
+ from .odxdoccontext import OdxDocContext
13
+ from .odxlink import OdxLinkDatabase, OdxLinkId, OdxLinkRef
14
+ from .ownident import OwnIdent
15
+ from .snrefcontext import SnRefContext
16
+ from .specialdatagroup import SpecialDataGroup
17
+ from .utils import dataclass_fields_asdict
18
+
19
+
20
+ @dataclass(kw_only=True)
21
+ class SessionDesc(NamedElement):
22
+ partnumber: str | None = None
23
+ priority: int | None = None
24
+ session_snref: str
25
+ diag_comm_snref: str | None = None
26
+ flash_class_refs: list[OdxLinkRef] = field(default_factory=list)
27
+ sdgs: list[SpecialDataGroup] = field(default_factory=list)
28
+ audience: Audience | None = None
29
+ own_ident: OwnIdent | None = None
30
+ oid: str | None = None
31
+ direction: Direction
32
+
33
+ @property
34
+ def flash_classes(self) -> NamedItemList[FlashClass]:
35
+ return self._flash_classes
36
+
37
+ @staticmethod
38
+ def from_et(et_element: ElementTree.Element, context: OdxDocContext) -> "SessionDesc":
39
+
40
+ kwargs = dataclass_fields_asdict(NamedElement.from_et(et_element, context))
41
+
42
+ partnumber = et_element.findtext("PARTNUMBER")
43
+ priority = None
44
+ if (priority_str := et_element.findtext("PRIORITY")) is not None:
45
+ try:
46
+ priority = int(priority_str)
47
+ except Exception as e:
48
+ odxraise(f"Cannot parse PRIORITY: {e}")
49
+ session_snref = None
50
+ if (session_snref_elem := odxrequire(et_element.find("SESSION-SNREF"))) is not None:
51
+ session_snref = odxrequire(session_snref_elem.attrib.get("SHORT-NAME"))
52
+ diag_comm_snref = None
53
+ if (diag_comm_snref_elem := et_element.find("DIAG-COMM-SNREF")) is not None:
54
+ diag_comm_snref = odxrequire(diag_comm_snref_elem.attrib.get("SHORT-NAME"))
55
+ flash_class_refs = [
56
+ OdxLinkRef.from_et(el, context)
57
+ for el in et_element.iterfind("FLASH-CLASS-REFS/FLASH-CLASS-REF")
58
+ ]
59
+ sdgs = [SpecialDataGroup.from_et(sdge, context) for sdge in et_element.iterfind("SDGS/SDG")]
60
+ audience = None
61
+ if (audience_elem := et_element.find("AUDIENCE")) is not None:
62
+ audience = Audience.from_et(audience_elem, context)
63
+ own_ident = None
64
+ if (own_ident_elem := et_element.find("OWN-IDENT")) is not None:
65
+ own_ident = OwnIdent.from_et(own_ident_elem, context)
66
+ direction_str = odxrequire(et_element.attrib.get("DIRECTION"))
67
+ try:
68
+ direction = Direction(direction_str)
69
+ except ValueError:
70
+ odxraise(f"Encountered unknown DIRECTION '{direction_str}'")
71
+ direction = cast(Direction, None)
72
+ oid = et_element.attrib.get("OID")
73
+
74
+ return SessionDesc(
75
+ partnumber=partnumber,
76
+ priority=priority,
77
+ session_snref=session_snref,
78
+ diag_comm_snref=diag_comm_snref,
79
+ flash_class_refs=flash_class_refs,
80
+ sdgs=sdgs,
81
+ audience=audience,
82
+ own_ident=own_ident,
83
+ direction=direction,
84
+ oid=oid,
85
+ **kwargs)
86
+
87
+ def _build_odxlinks(self) -> dict[OdxLinkId, Any]:
88
+ odxlinks: dict[OdxLinkId, Any] = {}
89
+
90
+ if self.own_ident is not None:
91
+ odxlinks.update(self.own_ident._build_odxlinks())
92
+
93
+ return odxlinks
94
+
95
+ def _resolve_odxlinks(self, odxlinks: OdxLinkDatabase) -> None:
96
+ self._flash_classes = NamedItemList(
97
+ [odxlinks.resolve(ref, FlashClass) for ref in self.flash_class_refs])
98
+
99
+ def _resolve_snrefs(self, context: SnRefContext) -> None:
100
+ # TODO: resolve session_snref and diag_comm_snref
101
+ pass
@@ -0,0 +1,14 @@
1
+ # SPDX-License-Identifier: MIT
2
+ from enum import Enum
3
+
4
+ from .odxtypes import DataType
5
+
6
+
7
+ class SessionSubElemType(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 .filter import Filter
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 SizedefFilter(Filter):
16
+ filter_size: int
17
+
18
+ @staticmethod
19
+ def from_et(et_element: ElementTree.Element, context: OdxDocContext) -> "SizedefFilter":
20
+ kwargs = dataclass_fields_asdict(Filter.from_et(et_element, context))
21
+
22
+ filter_size = int(odxrequire(et_element.findtext("FILTER-SIZE")) or "0")
23
+
24
+ return SizedefFilter(filter_size=filter_size, **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)
@@ -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 .odxdoccontext import OdxDocContext
8
+ from .odxlink import OdxLinkDatabase, OdxLinkId
9
+ from .physsegment import PhysSegment
10
+ from .snrefcontext import SnRefContext
11
+ from .utils import dataclass_fields_asdict
12
+
13
+
14
+ @dataclass(kw_only=True)
15
+ class SizedefPhysSegment(PhysSegment):
16
+ size: int
17
+
18
+ @staticmethod
19
+ def from_et(et_element: ElementTree.Element, context: OdxDocContext) -> "SizedefPhysSegment":
20
+ kwargs = dataclass_fields_asdict(PhysSegment.from_et(et_element, context))
21
+
22
+ size = int(odxrequire(et_element.findtext("SIZE")) or "0")
23
+
24
+ return SizedefPhysSegment(size=size, **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/specialdata.py CHANGED
@@ -6,6 +6,7 @@ from xml.etree import ElementTree
6
6
  from .odxdoccontext import OdxDocContext
7
7
  from .odxlink import OdxLinkDatabase, OdxLinkId
8
8
  from .snrefcontext import SnRefContext
9
+ from .utils import strip_indent
9
10
 
10
11
 
11
12
  @dataclass(kw_only=True)
@@ -19,7 +20,7 @@ class SpecialData:
19
20
  def from_et(et_element: ElementTree.Element, context: OdxDocContext) -> "SpecialData":
20
21
  semantic_info = et_element.get("SI")
21
22
  text_identifier = et_element.get("TI")
22
- value = et_element.text or ""
23
+ value = strip_indent(et_element.text) or ""
23
24
 
24
25
  return SpecialData(
25
26
  semantic_info=semantic_info, text_identifier=text_identifier, value=value)
@@ -0,0 +1,13 @@
1
+ # SPDX-License-Identifier: MIT
2
+ from dataclasses import dataclass
3
+ from xml.etree import ElementTree
4
+
5
+ from .odxdoccontext import OdxDocContext
6
+
7
+
8
+ @dataclass(kw_only=True)
9
+ class TargetAddrOffset:
10
+
11
+ @staticmethod
12
+ def from_et(et_element: ElementTree.Element, context: OdxDocContext) -> "TargetAddrOffset":
13
+ return TargetAddrOffset()
@@ -12,6 +12,7 @@
12
12
  <?xml version="1.0" encoding="UTF-8" standalone="no" ?>
13
13
  <!-- Written using odxtools {{odxtools_version}} -->
14
14
  <ODX MODEL-VERSION="2.2.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="odx.xsd">
15
+ {{- set_category_docfrag(comparam_spec.short_name, "COMPARAM-SPEC") }}
15
16
  <COMPARAM-SPEC {{- poc.printOdxCategoryAttribs(comparam_spec) }}>
16
17
  {{- poc.printOdxCategorySubtags(comparam_spec)|indent(3) }}
17
18
  {%- if comparam_spec.prot_stacks %}
@@ -15,6 +15,7 @@
15
15
  <?xml version="1.0" encoding="UTF-8" standalone="no" ?>
16
16
  <!-- Written using odxtools {{odxtools_version}} -->
17
17
  <ODX MODEL-VERSION="2.2.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="odx.xsd">
18
+ {{- set_category_docfrag(comparam_subset.short_name, "COMPARAM-SUBSET") }}
18
19
  <COMPARAM-SUBSET {{- poc.printOdxCategoryAttribs(comparam_subset) }} {{make_xml_attrib("CATEGORY", comparam_subset.category)}}>
19
20
  {{- poc.printOdxCategorySubtags(comparam_subset)|indent(3) }}
20
21
  {%- if comparam_subset.comparams %}
@@ -14,6 +14,7 @@
14
14
  <?xml version="1.0" encoding="UTF-8" standalone="no" ?>
15
15
  <!-- Written using odxtools {{odxtools_version}} -->
16
16
  <ODX MODEL-VERSION="2.2.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="odx.xsd">
17
+ {{- set_category_docfrag(dlc.short_name, "CONTAINER") }}
17
18
  <DIAG-LAYER-CONTAINER {{- poc.printOdxCategoryAttribs(dlc) }}>
18
19
  {{- poc.printOdxCategorySubtags(dlc)|indent(3) }}
19
20
  {%- if dlc.protocols %}
@@ -44,7 +45,7 @@
44
45
  {%- endfor %}
45
46
  </BASE-VARIANTS>
46
47
  {%- endif %}
47
- {%- if dlc.ecu_variants %}
48
+ {%- if dlc.ecu_variants %}
48
49
  <ECU-VARIANTS>
49
50
  {%- for dl in dlc.ecu_variants %}
50
51
  {{pecuv.printEcuVariant(dl)|indent(3)}}
@@ -0,0 +1,42 @@
1
+ {#- -*- mode: sgml; tab-width: 1; indent-tabs-mode: nil -*-
2
+ #
3
+ # SPDX-License-Identifier: MIT
4
+ #
5
+ # This template writes an .odx-cs file for a communication
6
+ # parameter subset.
7
+ -#}
8
+ {%- import('macros/printOdxCategory.xml.jinja2') as poc %}
9
+ {%- import('macros/printEcuMem.xml.jinja2') as pem %}
10
+ {%- import('macros/printEcuMemConnector.xml.jinja2') as pemc %}
11
+ {%- import('macros/printAudience.xml.jinja2') as paud %}
12
+ {#- -#}
13
+
14
+ <?xml version="1.0" encoding="UTF-8" standalone="no" ?>
15
+ <!-- Written using odxtools {{odxtools_version}} -->
16
+ <ODX MODEL-VERSION="2.2.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="odx.xsd">
17
+ {{- set_category_docfrag(flash.short_name, "FLASH") }}
18
+ <FLASH {{- poc.printOdxCategoryAttribs(flash) }}>
19
+ {{- poc.printOdxCategorySubtags(flash)|indent(4) }}
20
+ {%- if flash.ecu_mems %}
21
+ <ECU-MEMS>
22
+ {%- for ecu_mem in flash.ecu_mems %}
23
+ {{ pem.printEcuMem(ecu_mem) | indent(6) }}
24
+ {%- endfor %}
25
+ </ECU-MEMS>
26
+ {%- endif %}
27
+ {%- if flash.ecu_mem_connectors %}
28
+ <ECU-MEM-CONNECTORS>
29
+ {%- for ecu_mem_connector in flash.ecu_mem_connectors %}
30
+ {{ pemc.printEcuMemConnector(ecu_mem_connector) | indent(6) }}
31
+ {%- endfor %}
32
+ </ECU-MEM-CONNECTORS>
33
+ {%- endif %}
34
+ {%- if flash.additional_audiences %}
35
+ <ADDITIONAL-AUDIENCES>
36
+ {%- for audience in flash.additional_audiences %}
37
+ {{ paud.printAdditionalAudience(audience) | indent(6) }}
38
+ {%- endfor %}
39
+ </ADDITIONAL-AUDIENCES>
40
+ {%- endif %}
41
+ </FLASH>
42
+ </ODX>
@@ -14,9 +14,9 @@
14
14
  <COMPANY-DOC-INFOS>
15
15
  {%- for cdi in admin_data.company_doc_infos %}
16
16
  <COMPANY-DOC-INFO>
17
- <COMPANY-DATA-REF ID-REF="{{cdi.company_data_ref.ref_id}}" />
17
+ <COMPANY-DATA-REF {{make_ref_attribs(cdi.company_data_ref)}} />
18
18
  {%- if cdi.team_member_ref is not none %}
19
- <TEAM-MEMBER-REF ID-REF="{{cdi.team_member_ref.ref_id}}" />
19
+ <TEAM-MEMBER-REF {{make_ref_attribs(cdi.team_member_ref)}} />
20
20
  {%- endif %}
21
21
  {%- if cdi.doc_label is not none %}
22
22
  <DOC-LABEL>{{cdi.doc_label|e}}</DOC-LABEL>
@@ -31,7 +31,7 @@
31
31
  {%- for doc_revision in admin_data.doc_revisions %}
32
32
  <DOC-REVISION>
33
33
  {%- if doc_revision.team_member_ref is not none %}
34
- <TEAM-MEMBER-REF ID-REF="{{doc_revision.team_member_ref.ref_id}}" />
34
+ <TEAM-MEMBER-REF {{make_ref_attribs(doc_revision.team_member_ref)}} />
35
35
  {%- endif %}
36
36
  {%- if doc_revision.revision_label is not none %}
37
37
  <REVISION-LABEL>{{doc_revision.revision_label|e}}</REVISION-LABEL>
@@ -47,7 +47,7 @@
47
47
  <COMPANY-REVISION-INFOS>
48
48
  {%- for cri in doc_revision.company_revision_infos %}
49
49
  <COMPANY-REVISION-INFO>
50
- <COMPANY-DATA-REF ID-REF="{{cri.company_data_ref.ref_id}}" />
50
+ <COMPANY-DATA-REF {{make_ref_attribs(cri.company_data_ref)}} />
51
51
  {%- if cri.revision_label is not none %}
52
52
  <REVISION-LABEL>{{cri.revision_label|e}}</REVISION-LABEL>
53
53
  {%- endif %}
@@ -7,7 +7,7 @@
7
7
 
8
8
  {%- macro printAdditionalAudience(audience) -%}
9
9
  <ADDITIONAL-AUDIENCE {{-peid.printElementIdAttribs(audience)}}>
10
- {{ peid.printElementIdSubtags(audience)|indent(1) }}
10
+ {{ peid.printElementIdSubtags(audience)|indent(2) }}
11
11
  </ADDITIONAL-AUDIENCE>
12
12
  {%- endmacro -%}
13
13
 
@@ -20,14 +20,14 @@
20
20
  {%- if audience.enabled_audience_refs %}
21
21
  <ENABLED-AUDIENCE-REFS>
22
22
  {%- for ref in audience.enabled_audience_refs %}
23
- <ENABLED-AUDIENCE-REF ID-REF="{{ref.ref_id}}" />
23
+ <ENABLED-AUDIENCE-REF {{make_ref_attribs(ref)}} />
24
24
  {%- endfor %}
25
25
  </ENABLED-AUDIENCE-REFS>
26
26
  {%- endif%}
27
27
  {%- if audience.disabled_audience_refs %}
28
28
  <DISABLED-AUDIENCE-REFS>
29
29
  {%- for ref in audience.disabled_audience_refs %}
30
- <DISABLED-AUDIENCE-REF ID-REF="{{ref.ref_id}}" />
30
+ <DISABLED-AUDIENCE-REF {{make_ref_attribs(ref)}} />
31
31
  {%- endfor %}
32
32
  </DISABLED-AUDIENCE-REFS>
33
33
  {%- endif%}