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/database.py CHANGED
@@ -19,6 +19,7 @@ from .diaglayers.ecuvariant import EcuVariant
19
19
  from .diaglayers.functionalgroup import FunctionalGroup
20
20
  from .diaglayers.protocol import Protocol
21
21
  from .exceptions import odxraise, odxrequire
22
+ from .flash import Flash
22
23
  from .nameditemlist import NamedItemList
23
24
  from .odxdoccontext import OdxDocContext
24
25
  from .odxlink import DocType, OdxDocFragment, OdxLinkDatabase, OdxLinkId
@@ -39,6 +40,7 @@ class Database:
39
40
  self._diag_layer_containers = NamedItemList[DiagLayerContainer]()
40
41
  self._comparam_subsets = NamedItemList[ComparamSubset]()
41
42
  self._comparam_specs = NamedItemList[ComparamSpec]()
43
+ self._flashs = NamedItemList[Flash]()
42
44
  self._short_name = "odx_database"
43
45
 
44
46
  def add_pdx_file(self, pdx_file: Union[str, "PathLike[Any]", IO[bytes], ZipFile]) -> None:
@@ -57,7 +59,7 @@ class Database:
57
59
  p = Path(zip_member)
58
60
  if p.suffix.lower().startswith(".odx"):
59
61
  root = ElementTree.parse(pdx_zip.open(zip_member)).getroot()
60
- self._process_xml_tree(root)
62
+ self.add_xml_tree(root)
61
63
  elif p.name.lower() == "index.xml":
62
64
  root = ElementTree.parse(pdx_zip.open(zip_member)).getroot()
63
65
  db_short_name = odxrequire(root.findtext("SHORT-NAME"))
@@ -66,7 +68,7 @@ class Database:
66
68
  self.add_auxiliary_file(zip_member, pdx_zip.open(zip_member))
67
69
 
68
70
  def add_odx_file(self, odx_file_name: Union[str, "PathLike[Any]"]) -> None:
69
- self._process_xml_tree(ElementTree.parse(odx_file_name).getroot())
71
+ self.add_xml_tree(ElementTree.parse(odx_file_name).getroot())
70
72
 
71
73
  def add_auxiliary_file(self,
72
74
  aux_file_name: Union[str, "PathLike[Any]"],
@@ -76,7 +78,7 @@ class Database:
76
78
 
77
79
  self.auxiliary_files[str(aux_file_name)] = aux_file_obj
78
80
 
79
- def _process_xml_tree(self, root: ElementTree.Element) -> None:
81
+ def add_xml_tree(self, root: ElementTree.Element) -> None:
80
82
  # ODX spec version
81
83
  model_version = Version(root.attrib.get("MODEL-VERSION", "2.0"))
82
84
  if self.model_version is not None and self.model_version != model_version:
@@ -112,6 +114,9 @@ class Database:
112
114
  self._comparam_subsets.append(ComparamSubset.from_et(category_et, context))
113
115
  else:
114
116
  self._comparam_specs.append(ComparamSpec.from_et(category_et, context))
117
+ elif category_tag == "FLASH":
118
+ context = OdxDocContext(model_version, (OdxDocFragment(category_sn, DocType.FLASH),))
119
+ self._flashs.append(Flash.from_et(category_et, context))
115
120
 
116
121
  def refresh(self) -> None:
117
122
  # Create wrapper objects
@@ -143,6 +148,9 @@ class Database:
143
148
  for dlc in self.diag_layer_containers:
144
149
  dlc._resolve_odxlinks(self._odxlinks)
145
150
 
151
+ for flash in self.flashs:
152
+ flash._resolve_odxlinks(self._odxlinks)
153
+
146
154
  # resolve short name references for containers which do not do
147
155
  # inheritance (we can call directly call _resolve_snrefs())
148
156
  context = SnRefContext()
@@ -155,6 +163,8 @@ class Database:
155
163
  spec._finalize_init(self, self._odxlinks)
156
164
  for dlc in self.diag_layer_containers:
157
165
  dlc._finalize_init(self, self._odxlinks)
166
+ for flash in self.flashs:
167
+ flash._finalize_init(self, self._odxlinks)
158
168
 
159
169
  for subset in self.comparam_subsets:
160
170
  subset._resolve_snrefs(context)
@@ -162,6 +172,8 @@ class Database:
162
172
  spec._resolve_snrefs(context)
163
173
  for dlc in self.diag_layer_containers:
164
174
  dlc._resolve_snrefs(context)
175
+ for flash in self.flashs:
176
+ flash._resolve_snrefs(context)
165
177
 
166
178
  def _build_odxlinks(self) -> dict[OdxLinkId, Any]:
167
179
  result: dict[OdxLinkId, Any] = {}
@@ -175,6 +187,9 @@ class Database:
175
187
  for dlc in self.diag_layer_containers:
176
188
  result.update(dlc._build_odxlinks())
177
189
 
190
+ for flash in self.flashs:
191
+ result.update(flash._build_odxlinks())
192
+
178
193
  return result
179
194
 
180
195
  @property
@@ -248,10 +263,15 @@ class Database:
248
263
  def comparam_specs(self) -> NamedItemList[ComparamSpec]:
249
264
  return self._comparam_specs
250
265
 
266
+ @property
267
+ def flashs(self) -> NamedItemList[Flash]:
268
+ return self._flashs
269
+
251
270
  def __repr__(self) -> str:
252
271
  return f"Database(model_version={self.model_version}, " \
253
272
  f"protocols={[x.short_name for x in self.protocols]}, " \
254
273
  f"ecus={[x.short_name for x in self.ecus]}, " \
255
274
  f"diag_layer_containers={repr(self.diag_layer_containers)}, " \
256
275
  f"comparam_subsets={repr(self.comparam_subsets)}, " \
257
- f"comparam_specs={repr(self.comparam_specs)})"
276
+ f"comparam_specs={repr(self.comparam_specs)}, " \
277
+ f"flashs={repr(self.flashs)})"
odxtools/datablock.py ADDED
@@ -0,0 +1,153 @@
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 .addrdeffilter import AddrdefFilter
7
+ from .audience import Audience
8
+ from .element import IdentifiableElement
9
+ from .exceptions import odxraise, odxrequire
10
+ from .filter import Filter
11
+ from .flashdata import Flashdata
12
+ from .globals import xsi
13
+ from .nameditemlist import NamedItemList
14
+ from .negoffset import NegOffset
15
+ from .odxdoccontext import OdxDocContext
16
+ from .odxlink import OdxLinkDatabase, OdxLinkId, OdxLinkRef
17
+ from .ownident import OwnIdent
18
+ from .posoffset import PosOffset
19
+ from .security import Security
20
+ from .segment import Segment
21
+ from .sizedeffilter import SizedefFilter
22
+ from .snrefcontext import SnRefContext
23
+ from .specialdatagroup import SpecialDataGroup
24
+ from .targetaddroffset import TargetAddrOffset
25
+ from .utils import dataclass_fields_asdict, read_hex_binary
26
+
27
+
28
+ @dataclass(kw_only=True)
29
+ class Datablock(IdentifiableElement):
30
+ logical_block_index: int | None = None
31
+ flashdata_ref: OdxLinkRef | None = None
32
+ filters: list[Filter] = field(default_factory=list)
33
+ segments: NamedItemList[Segment] = field(default_factory=NamedItemList)
34
+
35
+ # the specification does not define the content of
36
+ # TARGET-ADDR-OFFSET, i.e., if it is defined, it must be one of
37
+ # its specializations
38
+ target_addr_offset: TargetAddrOffset | None = None
39
+
40
+ own_idents: NamedItemList[OwnIdent] = field(default_factory=NamedItemList)
41
+ securities: list[Security] = field(default_factory=list)
42
+ sdgs: list[SpecialDataGroup] = field(default_factory=list)
43
+ audience: Audience | None = None
44
+
45
+ # note that the spec says this attribute is named "TYPE", but in
46
+ # python, "type" is a build-in function...
47
+ data_type: str
48
+
49
+ @property
50
+ def flashdata(self) -> Flashdata | None:
51
+ return self._flashdata
52
+
53
+ @staticmethod
54
+ def from_et(et_element: ElementTree.Element, context: OdxDocContext) -> "Datablock":
55
+
56
+ kwargs = dataclass_fields_asdict(IdentifiableElement.from_et(et_element, context))
57
+
58
+ logical_block_index = read_hex_binary(et_element.find("LOGICAL-BLOCK-INDEX"))
59
+ flashdata_ref = OdxLinkRef.from_et(et_element.find("FLASHDATA-REF"), context)
60
+ filters: list[Filter] = []
61
+ for filter_elem in et_element.iterfind("FILTERS/FILTER"):
62
+ filter_type = filter_elem.attrib.get(f"{xsi}type")
63
+ if filter_type == "ADDRDEF-FILTER":
64
+ filters.append(AddrdefFilter.from_et(filter_elem, context))
65
+ elif filter_type == "SIZEDEF-FILTER":
66
+ filters.append(SizedefFilter.from_et(filter_elem, context))
67
+ else:
68
+ odxraise(f"Encountered filter of illegal type {filter_type}")
69
+ filters.append(Filter.from_et(filter_elem, context))
70
+ segments = NamedItemList([
71
+ Segment.from_et(segment_elem, context)
72
+ for segment_elem in et_element.iterfind("SEGMENTS/SEGMENT")
73
+ ])
74
+ target_addr_offset: TargetAddrOffset | None = None
75
+ if (tao_elem := et_element.find("TARGET-ADDR-OFFSET")) is not None:
76
+ tao_type = tao_elem.attrib.get(f"{xsi}type")
77
+ if tao_type == "POS-OFFSET":
78
+ target_addr_offset = PosOffset.from_et(tao_elem, context)
79
+ elif tao_type == "NEG-OFFSET":
80
+ target_addr_offset = NegOffset.from_et(tao_elem, context)
81
+ else:
82
+ odxraise(f"Unknown TARGET-ADDR-OFFSET type '{tao_type}'")
83
+
84
+ own_idents = NamedItemList([
85
+ OwnIdent.from_et(own_ident_elem, context)
86
+ for own_ident_elem in et_element.iterfind("OWN-IDENTS/OWN-IDENT")
87
+ ])
88
+ securities = [
89
+ Security.from_et(security_elem, context)
90
+ for security_elem in et_element.iterfind("SECURITYS/SECURITY")
91
+ ]
92
+ sdgs = [SpecialDataGroup.from_et(sdge, context) for sdge in et_element.iterfind("SDGS/SDG")]
93
+ audience = None
94
+ if (audience_elem := et_element.find("AUDIENCE")) is not None:
95
+ audience = Audience.from_et(audience_elem, context)
96
+ data_type = odxrequire(et_element.attrib.get("TYPE"))
97
+
98
+ return Datablock(
99
+ logical_block_index=logical_block_index,
100
+ flashdata_ref=flashdata_ref,
101
+ filters=filters,
102
+ segments=segments,
103
+ own_idents=own_idents,
104
+ securities=securities,
105
+ target_addr_offset=target_addr_offset,
106
+ sdgs=sdgs,
107
+ audience=audience,
108
+ data_type=data_type,
109
+ **kwargs)
110
+
111
+ def _build_odxlinks(self) -> dict[OdxLinkId, Any]:
112
+ odxlinks = {self.odx_id: self}
113
+
114
+ for odxfilter in self.filters:
115
+ odxlinks.update(odxfilter._build_odxlinks())
116
+ for segment in self.segments:
117
+ odxlinks.update(segment._build_odxlinks())
118
+ for own_indent in self.own_idents:
119
+ odxlinks.update(own_indent._build_odxlinks())
120
+ for sdg in self.sdgs:
121
+ odxlinks.update(sdg._build_odxlinks())
122
+ if self.audience is not None:
123
+ odxlinks.update(self.audience._build_odxlinks())
124
+
125
+ return odxlinks
126
+
127
+ def _resolve_odxlinks(self, odxlinks: OdxLinkDatabase) -> None:
128
+ self._flashdata = None
129
+ if self.flashdata_ref is not None:
130
+ self._flashdata = odxlinks.resolve(self.flashdata_ref, Flashdata)
131
+
132
+ for odxfilter in self.filters:
133
+ odxfilter._resolve_odxlinks(odxlinks)
134
+ for segment in self.segments:
135
+ segment._resolve_odxlinks(odxlinks)
136
+ for own_indent in self.own_idents:
137
+ own_indent._resolve_odxlinks(odxlinks)
138
+ for sdg in self.sdgs:
139
+ sdg._resolve_odxlinks(odxlinks)
140
+ if self.audience is not None:
141
+ self.audience._resolve_odxlinks(odxlinks)
142
+
143
+ def _resolve_snrefs(self, context: SnRefContext) -> None:
144
+ for odxfilter in self.filters:
145
+ odxfilter._resolve_snrefs(context)
146
+ for segment in self.segments:
147
+ segment._resolve_snrefs(context)
148
+ for own_indent in self.own_idents:
149
+ own_indent._resolve_snrefs(context)
150
+ for sdg in self.sdgs:
151
+ sdg._resolve_snrefs(context)
152
+ if self.audience is not None:
153
+ self.audience._resolve_snrefs(context)
odxtools/datafile.py ADDED
@@ -0,0 +1,23 @@
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 .odxtypes import odxstr_to_bool
8
+
9
+
10
+ @dataclass(kw_only=True)
11
+ class Datafile:
12
+ value: str
13
+ latebound_datafile: bool
14
+
15
+ @staticmethod
16
+ def from_et(et_element: ElementTree.Element, context: OdxDocContext) -> "Datafile":
17
+ value = et_element.text or ""
18
+ latebound_datafile = odxrequire(odxstr_to_bool(et_element.attrib.get("LATEBOUND-DATAFILE")))
19
+
20
+ return Datafile(
21
+ value=value,
22
+ latebound_datafile=latebound_datafile,
23
+ )
odxtools/dataformat.py ADDED
@@ -0,0 +1,39 @@
1
+ # SPDX-License-Identifier: MIT
2
+ from dataclasses import dataclass
3
+ from typing import Any, cast
4
+ from xml.etree import ElementTree
5
+
6
+ from .dataformatselection import DataformatSelection
7
+ from .exceptions import odxraise, odxrequire
8
+ from .odxdoccontext import OdxDocContext
9
+ from .odxlink import OdxLinkDatabase, OdxLinkId
10
+ from .snrefcontext import SnRefContext
11
+
12
+
13
+ @dataclass(kw_only=True)
14
+ class Dataformat:
15
+ selection: DataformatSelection
16
+ user_selection: str | None = None
17
+
18
+ @staticmethod
19
+ def from_et(et_element: ElementTree.Element, context: OdxDocContext) -> "Dataformat":
20
+ selection_str = odxrequire(et_element.attrib.get("SELECTION"))
21
+ try:
22
+ selection = DataformatSelection(selection_str)
23
+ except ValueError:
24
+ selection = cast(DataformatSelection, None)
25
+ odxraise(f"Encountered unknown data format selection '{selection_str}'")
26
+ user_selection = et_element.attrib.get("USER-SELECTION")
27
+
28
+ return Dataformat(selection=selection, user_selection=user_selection)
29
+
30
+ def _build_odxlinks(self) -> dict[OdxLinkId, Any]:
31
+ odxlinks: dict[OdxLinkId, Any] = {}
32
+
33
+ return odxlinks
34
+
35
+ def _resolve_odxlinks(self, odxlinks: OdxLinkDatabase) -> None:
36
+ pass
37
+
38
+ def _resolve_snrefs(self, context: SnRefContext) -> None:
39
+ pass
@@ -0,0 +1,9 @@
1
+ # SPDX-License-Identifier: MIT
2
+ from enum import Enum
3
+
4
+
5
+ class DataformatSelection(Enum):
6
+ INTEL_HEX = "INTEL-HEX"
7
+ MOTOROLA_S = "MOTOROLA-S"
8
+ BINARY = "BINARY"
9
+ USER_DEFINED = "USER-DEFINED"
odxtools/description.py CHANGED
@@ -5,6 +5,7 @@ from xml.etree import ElementTree
5
5
  from .exceptions import odxrequire
6
6
  from .externaldoc import ExternalDoc
7
7
  from .odxdoccontext import OdxDocContext
8
+ from .utils import strip_indent
8
9
 
9
10
 
10
11
  @dataclass(kw_only=True)
@@ -27,11 +28,7 @@ class Description:
27
28
  break
28
29
  raw_string += ElementTree.tostring(e, encoding="unicode")
29
30
 
30
- # remove white spaces at the beginning and at the end of all
31
- # extracted lines
32
- stripped_lines = [x.strip() for x in raw_string.split("\n")]
33
-
34
- text = "\n".join(stripped_lines).strip()
31
+ text = strip_indent(raw_string)
35
32
 
36
33
  external_docs = \
37
34
  [
@@ -46,9 +46,7 @@ class DiagDataDictionarySpec:
46
46
  @staticmethod
47
47
  def from_et(et_element: ElementTree.Element,
48
48
  context: OdxDocContext) -> "DiagDataDictionarySpec":
49
- admin_data = None
50
- if (admin_data_elem := et_element.find("ADMIN-DATA")) is not None:
51
- admin_data = AdminData.from_et(admin_data_elem, context)
49
+ admin_data = AdminData.from_et(et_element.find("ADMIN-DATA"), context)
52
50
 
53
51
  dtc_dops = NamedItemList([
54
52
  DtcDop.from_et(dtc_dop_elem, context)
odxtools/direction.py ADDED
@@ -0,0 +1,7 @@
1
+ # SPDX-License-Identifier: MIT
2
+ from enum import Enum
3
+
4
+
5
+ class Direction(Enum):
6
+ UPLOAD = "UPLOAD"
7
+ DOWNLOAD = "DOWNLOAD"
odxtools/ecumem.py ADDED
@@ -0,0 +1,71 @@
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 .admindata import AdminData
7
+ from .element import IdentifiableElement
8
+ from .exceptions import odxrequire
9
+ from .mem import Mem
10
+ from .odxdoccontext import OdxDocContext
11
+ from .odxlink import OdxLinkDatabase, OdxLinkId
12
+ from .physmem import PhysMem
13
+ from .snrefcontext import SnRefContext
14
+ from .specialdatagroup import SpecialDataGroup
15
+ from .utils import dataclass_fields_asdict
16
+
17
+
18
+ @dataclass(kw_only=True)
19
+ class EcuMem(IdentifiableElement):
20
+ admin_data: AdminData | None = None
21
+ mem: Mem
22
+ phys_mem: PhysMem | None = None
23
+ sdgs: list[SpecialDataGroup] = field(default_factory=list)
24
+
25
+ @staticmethod
26
+ def from_et(et_element: ElementTree.Element, context: OdxDocContext) -> "EcuMem":
27
+
28
+ kwargs = dataclass_fields_asdict(IdentifiableElement.from_et(et_element, context))
29
+
30
+ admin_data = AdminData.from_et(et_element.find("ADMIN-DATA"), context)
31
+ mem = Mem.from_et(odxrequire(et_element.find("MEM")), context)
32
+ phys_mem = None
33
+ if (phys_mem_elem := et_element.find("PHYS-MEM")) is not None:
34
+ phys_mem = PhysMem.from_et(phys_mem_elem, context)
35
+ sdgs = [SpecialDataGroup.from_et(sdge, context) for sdge in et_element.iterfind("SDGS/SDG")]
36
+
37
+ return EcuMem(admin_data=admin_data, mem=mem, phys_mem=phys_mem, sdgs=sdgs, **kwargs)
38
+
39
+ def _build_odxlinks(self) -> dict[OdxLinkId, Any]:
40
+ odxlinks = {self.odx_id: self}
41
+
42
+ if self.admin_data is not None:
43
+ odxlinks.update(self.admin_data._build_odxlinks())
44
+
45
+ odxlinks.update(self.mem._build_odxlinks())
46
+ if self.phys_mem is not None:
47
+ odxlinks.update(self.phys_mem._build_odxlinks())
48
+ for sdg in self.sdgs:
49
+ odxlinks.update(sdg._build_odxlinks())
50
+
51
+ return odxlinks
52
+
53
+ def _resolve_odxlinks(self, odxlinks: OdxLinkDatabase) -> None:
54
+ if self.admin_data is not None:
55
+ self.admin_data._resolve_odxlinks(odxlinks)
56
+
57
+ self.mem._resolve_odxlinks(odxlinks)
58
+ if self.phys_mem is not None:
59
+ self.phys_mem._resolve_odxlinks(odxlinks)
60
+ for sdg in self.sdgs:
61
+ sdg._resolve_odxlinks(odxlinks)
62
+
63
+ def _resolve_snrefs(self, context: SnRefContext) -> None:
64
+ if self.admin_data is not None:
65
+ self.admin_data._resolve_snrefs(context)
66
+
67
+ self.mem._resolve_snrefs(context)
68
+ if self.phys_mem is not None:
69
+ self.phys_mem._resolve_snrefs(context)
70
+ for sdg in self.sdgs:
71
+ sdg._resolve_snrefs(context)
@@ -0,0 +1,136 @@
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 .admindata import AdminData
7
+ from .diaglayers.basevariant import BaseVariant
8
+ from .diaglayers.ecuvariant import EcuVariant
9
+ from .ecumem import EcuMem
10
+ from .element import IdentifiableElement
11
+ from .exceptions import odxraise, odxrequire
12
+ from .flashclass import FlashClass
13
+ from .identdesc import IdentDesc
14
+ from .nameditemlist import NamedItemList
15
+ from .odxdoccontext import OdxDocContext
16
+ from .odxlink import OdxLinkDatabase, OdxLinkId, OdxLinkRef
17
+ from .sessiondesc import SessionDesc
18
+ from .snrefcontext import SnRefContext
19
+ from .specialdatagroup import SpecialDataGroup
20
+ from .utils import dataclass_fields_asdict
21
+
22
+
23
+ @dataclass(kw_only=True)
24
+ class EcuMemConnector(IdentifiableElement):
25
+ admin_data: AdminData | None = None
26
+ flash_classes: NamedItemList[FlashClass]
27
+ session_descs: NamedItemList[SessionDesc] = field(default_factory=NamedItemList)
28
+ ident_descs: list[IdentDesc] = field(default_factory=list)
29
+ ecu_mem_ref: OdxLinkRef
30
+ layer_refs: list[OdxLinkRef]
31
+ all_variant_refs: list[OdxLinkRef]
32
+ sdgs: list[SpecialDataGroup] = field(default_factory=list)
33
+
34
+ @property
35
+ def ecu_mem(self) -> EcuMem:
36
+ return self._ecu_mem
37
+
38
+ @property
39
+ def layers(self) -> NamedItemList[EcuVariant | BaseVariant]:
40
+ return self._layers
41
+
42
+ @property
43
+ def all_variants(self) -> NamedItemList[BaseVariant]:
44
+ return self._all_variants
45
+
46
+ @staticmethod
47
+ def from_et(et_element: ElementTree.Element, context: OdxDocContext) -> "EcuMemConnector":
48
+
49
+ kwargs = dataclass_fields_asdict(IdentifiableElement.from_et(et_element, context))
50
+
51
+ admin_data = AdminData.from_et(et_element.find("ADMIN-DATA"), context)
52
+ flash_classes = NamedItemList([
53
+ FlashClass.from_et(el, context)
54
+ for el in et_element.iterfind("FLASH-CLASSS/FLASH-CLASS")
55
+ ])
56
+ session_descs = NamedItemList([
57
+ SessionDesc.from_et(el, context)
58
+ for el in et_element.iterfind("SESSION-DESCS/SESSION-DESC")
59
+ ])
60
+ ident_descs = [
61
+ IdentDesc.from_et(el, context) for el in et_element.iterfind("IDENT-DESCS/IDENT-DESC")
62
+ ]
63
+ ecu_mem_ref = OdxLinkRef.from_et(odxrequire(et_element.find("ECU-MEM-REF")), context)
64
+ layer_refs = [
65
+ OdxLinkRef.from_et(el, context) for el in et_element.iterfind("LAYER-REFS/LAYER-REF")
66
+ ]
67
+ all_variant_refs = [
68
+ OdxLinkRef.from_et(el, context)
69
+ for el in et_element.iterfind("ALL-VARIANT-REFS/ALL-VARIANT-REF")
70
+ ]
71
+ sdgs = [SpecialDataGroup.from_et(sdge, context) for sdge in et_element.iterfind("SDGS/SDG")]
72
+
73
+ return EcuMemConnector(
74
+ admin_data=admin_data,
75
+ flash_classes=flash_classes,
76
+ session_descs=session_descs,
77
+ ident_descs=ident_descs,
78
+ ecu_mem_ref=ecu_mem_ref,
79
+ layer_refs=layer_refs,
80
+ all_variant_refs=all_variant_refs,
81
+ sdgs=sdgs,
82
+ **kwargs)
83
+
84
+ def _build_odxlinks(self) -> dict[OdxLinkId, Any]:
85
+ odxlinks = {self.odx_id: self}
86
+
87
+ if self.admin_data is not None:
88
+ odxlinks.update(self.admin_data._build_odxlinks())
89
+ for flash_class in self.flash_classes:
90
+ odxlinks.update(flash_class._build_odxlinks())
91
+ for session_desc in self.session_descs:
92
+ odxlinks.update(session_desc._build_odxlinks())
93
+ for ident_desc in self.ident_descs:
94
+ odxlinks.update(ident_desc._build_odxlinks())
95
+ for sdg in self.sdgs:
96
+ odxlinks.update(sdg._build_odxlinks())
97
+
98
+ return odxlinks
99
+
100
+ def _resolve_odxlinks(self, odxlinks: OdxLinkDatabase) -> None:
101
+ self._ecu_mem = odxlinks.resolve(self.ecu_mem_ref, EcuMem)
102
+
103
+ tmp = []
104
+ for ref in self.layer_refs:
105
+ x = odxlinks.resolve(ref)
106
+ if not isinstance(x, (BaseVariant, EcuVariant)):
107
+ odxraise("Invalid type of referenced object")
108
+ tmp.append(x)
109
+ self._layers = NamedItemList(tmp)
110
+
111
+ self._all_variants = NamedItemList(
112
+ [odxlinks.resolve(ref, BaseVariant) for ref in self.all_variant_refs])
113
+ self._ecu_mem = odxlinks.resolve(self.ecu_mem_ref, EcuMem)
114
+
115
+ if self.admin_data is not None:
116
+ self.admin_data._resolve_odxlinks(odxlinks)
117
+ for flash_class in self.flash_classes:
118
+ flash_class._resolve_odxlinks(odxlinks)
119
+ for session_desc in self.session_descs:
120
+ session_desc._resolve_odxlinks(odxlinks)
121
+ for ident_desc in self.ident_descs:
122
+ ident_desc._resolve_odxlinks(odxlinks)
123
+ for sdg in self.sdgs:
124
+ sdg._resolve_odxlinks(odxlinks)
125
+
126
+ def _resolve_snrefs(self, context: SnRefContext) -> None:
127
+ if self.admin_data is not None:
128
+ self.admin_data._resolve_snrefs(context)
129
+ for flash_class in self.flash_classes:
130
+ flash_class._resolve_snrefs(context)
131
+ for session_desc in self.session_descs:
132
+ session_desc._resolve_snrefs(context)
133
+ for ident_desc in self.ident_descs:
134
+ ident_desc._resolve_snrefs(context)
135
+ for sdg in self.sdgs:
136
+ sdg._resolve_snrefs(context)
@@ -0,0 +1,39 @@
1
+ from dataclasses import dataclass
2
+ from typing import Any, cast
3
+ from xml.etree import ElementTree
4
+
5
+ from .encryptcompressmethodtype import EncryptCompressMethodType
6
+ from .exceptions import odxraise, odxrequire
7
+ from .odxdoccontext import OdxDocContext
8
+ from .odxlink import OdxLinkDatabase, OdxLinkId
9
+ from .snrefcontext import SnRefContext
10
+
11
+
12
+ @dataclass(kw_only=True)
13
+ class EncryptCompressMethod:
14
+ value: str
15
+ value_type: EncryptCompressMethodType
16
+
17
+ @staticmethod
18
+ def from_et(et_element: ElementTree.Element, context: OdxDocContext) -> "EncryptCompressMethod":
19
+ value = et_element.text or ""
20
+
21
+ value_type_str = odxrequire(et_element.attrib.get("TYPE"))
22
+ try:
23
+ value_type = EncryptCompressMethodType(value_type_str)
24
+ except ValueError:
25
+ value_type = cast(EncryptCompressMethodType, None)
26
+ odxraise(f"Encountered unknown addressing type '{value_type_str}'")
27
+
28
+ return EncryptCompressMethod(value=value, value_type=value_type)
29
+
30
+ def _build_odxlinks(self) -> dict[OdxLinkId, Any]:
31
+ odxlinks: dict[OdxLinkId, Any] = {}
32
+
33
+ return odxlinks
34
+
35
+ def _resolve_odxlinks(self, odxlinks: OdxLinkDatabase) -> None:
36
+ pass
37
+
38
+ def _resolve_snrefs(self, context: SnRefContext) -> None:
39
+ pass
@@ -0,0 +1,13 @@
1
+ # SPDX-License-Identifier: MIT
2
+ from enum import Enum
3
+
4
+ from .odxtypes import DataType
5
+
6
+
7
+ class EncryptCompressMethodType(Enum):
8
+ A_UINT32 = "A_UINT32"
9
+ A_BYTEFIELD = "A_BYTEFIELD"
10
+
11
+ @property
12
+ def data_type(self) -> DataType:
13
+ return DataType(self.value)
@@ -0,0 +1,40 @@
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 .identvalue import IdentValue
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 ExpectedIdent(IdentifiableElement):
16
+ """
17
+ Corresponds to EXPECTED-IDENT.
18
+ """
19
+
20
+ ident_values: list[IdentValue]
21
+
22
+ @staticmethod
23
+ def from_et(et_element: ElementTree.Element, context: OdxDocContext) -> "ExpectedIdent":
24
+ kwargs = dataclass_fields_asdict(IdentifiableElement.from_et(et_element, context))
25
+
26
+ ident_values = [
27
+ IdentValue.from_et(ive, context)
28
+ for ive in et_element.iterfind("IDENT-VALUES/IDENT-VALUE")
29
+ ]
30
+
31
+ return ExpectedIdent(ident_values=ident_values, **kwargs)
32
+
33
+ def _build_odxlinks(self) -> dict[OdxLinkId, Any]:
34
+ return {self.odx_id: self}
35
+
36
+ def _resolve_odxlinks(self, odxlinks: OdxLinkDatabase) -> None:
37
+ pass
38
+
39
+ def _resolve_snrefs(self, context: SnRefContext) -> None:
40
+ pass