odxtools 10.2.1__py3-none-any.whl → 10.4.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- odxtools/cli/browse.py +4 -2
- odxtools/cli/compare.py +3 -3
- odxtools/compositecodec.py +1 -1
- odxtools/configdata.py +70 -0
- odxtools/configdatadictionaryspec.py +57 -0
- odxtools/configiditem.py +18 -0
- odxtools/configitem.py +85 -0
- odxtools/configrecord.py +146 -0
- odxtools/database.py +40 -0
- odxtools/dataiditem.py +18 -0
- odxtools/datarecord.py +132 -0
- odxtools/decodestate.py +1 -1
- odxtools/diagcommdataconnector.py +61 -0
- odxtools/diaglayers/diaglayer.py +9 -14
- odxtools/diagservice.py +10 -10
- odxtools/ecuconfig.py +89 -0
- odxtools/encryptcompressmethod.py +16 -3
- odxtools/externflashdata.py +21 -2
- odxtools/flashdata.py +40 -2
- odxtools/identvalue.py +16 -3
- odxtools/internflashdata.py +4 -0
- odxtools/isotp_state_machine.py +11 -11
- odxtools/itemvalue.py +77 -0
- odxtools/multipleecujob.py +178 -0
- odxtools/multipleecujobspec.py +142 -0
- odxtools/nameditemlist.py +2 -2
- odxtools/optionitem.py +79 -0
- odxtools/outputparam.py +1 -1
- odxtools/parameters/codedconstparameter.py +2 -3
- odxtools/readdiagcommconnector.py +79 -0
- odxtools/readparamvalue.py +52 -0
- odxtools/request.py +3 -3
- odxtools/response.py +8 -4
- odxtools/snrefcontext.py +2 -0
- odxtools/statemachine.py +3 -2
- odxtools/systemitem.py +23 -0
- odxtools/templates/diag_layer_container.odx-d.xml.jinja2 +4 -0
- odxtools/templates/ecu_config.odx-e.xml.jinja2 +38 -0
- odxtools/templates/flash.odx-f.xml.jinja2 +2 -2
- odxtools/templates/macros/printAdminData.xml.jinja2 +1 -1
- odxtools/templates/macros/printAudience.xml.jinja2 +3 -3
- odxtools/templates/macros/printChecksum.xml.jinja2 +1 -1
- odxtools/templates/macros/printCompuMethod.xml.jinja2 +2 -2
- odxtools/templates/macros/printConfigData.xml.jinja2 +39 -0
- odxtools/templates/macros/printConfigDataDictionarySpec.xml.jinja2 +22 -0
- odxtools/templates/macros/printConfigItems.xml.jinja2 +66 -0
- odxtools/templates/macros/printConfigRecord.xml.jinja2 +73 -0
- odxtools/templates/macros/printDOP.xml.jinja2 +5 -6
- odxtools/templates/macros/printDataRecord.xml.jinja2 +35 -0
- odxtools/templates/macros/printDiagCommDataConnector.xml.jinja2 +66 -0
- odxtools/templates/macros/printDiagDataDictionarySpec.xml.jinja2 +107 -0
- odxtools/templates/macros/printDiagLayer.xml.jinja2 +11 -97
- odxtools/templates/macros/printElementId.xml.jinja2 +2 -0
- odxtools/templates/macros/printExpectedIdent.xml.jinja2 +1 -1
- odxtools/templates/macros/printFlashdata.xml.jinja2 +2 -2
- odxtools/templates/macros/printFunctionalClass.xml.jinja2 +4 -4
- odxtools/templates/macros/printItemValue.xml.jinja2 +31 -0
- odxtools/templates/macros/printMultipleEcuJob.xml.jinja2 +77 -0
- odxtools/templates/macros/printOwnIdent.xml.jinja2 +1 -1
- odxtools/templates/macros/printSecurity.xml.jinja2 +4 -4
- odxtools/templates/macros/printSegment.xml.jinja2 +1 -1
- odxtools/templates/multiple-ecu-job-spec.odx-m.xml.jinja2 +51 -0
- odxtools/validbasevariant.py +62 -0
- odxtools/validityfor.py +16 -3
- odxtools/variantmatcher.py +4 -4
- odxtools/version.py +2 -2
- odxtools/writediagcommconnector.py +77 -0
- odxtools/writepdxfile.py +58 -27
- {odxtools-10.2.1.dist-info → odxtools-10.4.0.dist-info}/METADATA +2 -1
- {odxtools-10.2.1.dist-info → odxtools-10.4.0.dist-info}/RECORD +74 -45
- {odxtools-10.2.1.dist-info → odxtools-10.4.0.dist-info}/WHEEL +1 -1
- {odxtools-10.2.1.dist-info → odxtools-10.4.0.dist-info}/entry_points.txt +0 -0
- {odxtools-10.2.1.dist-info → odxtools-10.4.0.dist-info}/licenses/LICENSE +0 -0
- {odxtools-10.2.1.dist-info → odxtools-10.4.0.dist-info}/top_level.txt +0 -0
odxtools/cli/browse.py
CHANGED
@@ -255,8 +255,10 @@ def encode_message_from_string_values(
|
|
255
255
|
print(f"The value specified for parameter {inner_param_sn} is not a string")
|
256
256
|
continue
|
257
257
|
|
258
|
-
|
259
|
-
|
258
|
+
if isinstance(inner_param,
|
259
|
+
ParameterWithDOP) and inner_param.physical_type is not None:
|
260
|
+
typed_dict[inner_param_sn] = _convert_string_to_odx_type(
|
261
|
+
inner_param_value, inner_param.physical_type.base_data_type)
|
260
262
|
parameter_values[parameter.short_name] = typed_dict
|
261
263
|
else:
|
262
264
|
if not isinstance(parameter_value, str):
|
odxtools/cli/compare.py
CHANGED
@@ -425,10 +425,10 @@ class Comparison(Display):
|
|
425
425
|
changed_parameters_of_service=services_with_param_changes)
|
426
426
|
dl1_service_names = [service.short_name for service in dl1.services]
|
427
427
|
|
428
|
-
dl1_request_prefixes: list[bytes | None] = [
|
428
|
+
dl1_request_prefixes: list[bytes | bytearray | None] = [
|
429
429
|
None if s.request is None else s.request.coded_const_prefix() for s in dl1.services
|
430
430
|
]
|
431
|
-
dl2_request_prefixes: list[bytes | None] = [
|
431
|
+
dl2_request_prefixes: list[bytes | bytearray | None] = [
|
432
432
|
None if s.request is None else s.request.coded_const_prefix() for s in dl2.services
|
433
433
|
]
|
434
434
|
|
@@ -436,7 +436,7 @@ class Comparison(Display):
|
|
436
436
|
for service1 in dl1.services:
|
437
437
|
|
438
438
|
# check for added diagnostic services
|
439
|
-
rq_prefix: bytes
|
439
|
+
rq_prefix: bytes | bytearray
|
440
440
|
if service1.request is not None:
|
441
441
|
rq_prefix = service1.request.coded_const_prefix()
|
442
442
|
|
odxtools/compositecodec.py
CHANGED
@@ -79,7 +79,7 @@ def composite_codec_get_free_parameters(codec: CompositeCodec) -> list[Parameter
|
|
79
79
|
|
80
80
|
|
81
81
|
def composite_codec_get_coded_const_prefix(codec: CompositeCodec,
|
82
|
-
request_prefix: bytes = b'') ->
|
82
|
+
request_prefix: bytes = b'') -> bytearray:
|
83
83
|
encode_state = EncodeState(coded_message=bytearray(), triggering_request=request_prefix)
|
84
84
|
|
85
85
|
for param in codec.parameters:
|
odxtools/configdata.py
ADDED
@@ -0,0 +1,70 @@
|
|
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 .configrecord import ConfigRecord
|
7
|
+
from .element import NamedElement
|
8
|
+
from .nameditemlist import NamedItemList
|
9
|
+
from .odxdoccontext import OdxDocContext
|
10
|
+
from .odxlink import OdxLinkDatabase, OdxLinkId
|
11
|
+
from .snrefcontext import SnRefContext
|
12
|
+
from .specialdatagroup import SpecialDataGroup
|
13
|
+
from .utils import dataclass_fields_asdict
|
14
|
+
from .validbasevariant import ValidBaseVariant
|
15
|
+
|
16
|
+
|
17
|
+
@dataclass(kw_only=True)
|
18
|
+
class ConfigData(NamedElement):
|
19
|
+
"""This class represents a CONFIG-DATA."""
|
20
|
+
valid_base_variants: list[ValidBaseVariant]
|
21
|
+
config_records: NamedItemList[ConfigRecord]
|
22
|
+
sdgs: list[SpecialDataGroup] = field(default_factory=list)
|
23
|
+
|
24
|
+
@staticmethod
|
25
|
+
def from_et(et_element: ElementTree.Element, context: OdxDocContext) -> "ConfigData":
|
26
|
+
kwargs = dataclass_fields_asdict(NamedElement.from_et(et_element, context))
|
27
|
+
|
28
|
+
valid_base_variants = [
|
29
|
+
ValidBaseVariant.from_et(vbv_elem, context)
|
30
|
+
for vbv_elem in et_element.iterfind("VALID-BASE-VARIANTS/VALID-BASE-VARIANT")
|
31
|
+
]
|
32
|
+
config_records = NamedItemList([
|
33
|
+
ConfigRecord.from_et(cr_elem, context)
|
34
|
+
for cr_elem in et_element.iterfind("CONFIG-RECORDS/CONFIG-RECORD")
|
35
|
+
])
|
36
|
+
sdgs = [SpecialDataGroup.from_et(sdge, context) for sdge in et_element.iterfind("SDGS/SDG")]
|
37
|
+
|
38
|
+
return ConfigData(
|
39
|
+
valid_base_variants=valid_base_variants,
|
40
|
+
config_records=config_records,
|
41
|
+
sdgs=sdgs,
|
42
|
+
**kwargs)
|
43
|
+
|
44
|
+
def _build_odxlinks(self) -> dict[OdxLinkId, Any]:
|
45
|
+
result = {}
|
46
|
+
|
47
|
+
for valid_base_variant in self.valid_base_variants:
|
48
|
+
result.update(valid_base_variant._build_odxlinks())
|
49
|
+
for config_record in self.config_records:
|
50
|
+
result.update(config_record._build_odxlinks())
|
51
|
+
for sdg in self.sdgs:
|
52
|
+
result.update(sdg._build_odxlinks())
|
53
|
+
|
54
|
+
return result
|
55
|
+
|
56
|
+
def _resolve_odxlinks(self, odxlinks: OdxLinkDatabase) -> None:
|
57
|
+
for valid_base_variant in self.valid_base_variants:
|
58
|
+
valid_base_variant._resolve_odxlinks(odxlinks)
|
59
|
+
for config_record in self.config_records:
|
60
|
+
config_record._resolve_odxlinks(odxlinks)
|
61
|
+
for sdg in self.sdgs:
|
62
|
+
sdg._resolve_odxlinks(odxlinks)
|
63
|
+
|
64
|
+
def _resolve_snrefs(self, context: SnRefContext) -> None:
|
65
|
+
for valid_base_variant in self.valid_base_variants:
|
66
|
+
valid_base_variant._resolve_snrefs(context)
|
67
|
+
for config_record in self.config_records:
|
68
|
+
config_record._resolve_snrefs(context)
|
69
|
+
for sdg in self.sdgs:
|
70
|
+
sdg._resolve_snrefs(context)
|
@@ -0,0 +1,57 @@
|
|
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 .dataobjectproperty import DataObjectProperty
|
7
|
+
from .nameditemlist import NamedItemList
|
8
|
+
from .odxdoccontext import OdxDocContext
|
9
|
+
from .odxlink import OdxLinkDatabase, OdxLinkId
|
10
|
+
from .snrefcontext import SnRefContext
|
11
|
+
from .unitspec import UnitSpec
|
12
|
+
|
13
|
+
|
14
|
+
@dataclass(kw_only=True)
|
15
|
+
class ConfigDataDictionarySpec:
|
16
|
+
data_object_props: NamedItemList[DataObjectProperty] = field(default_factory=NamedItemList)
|
17
|
+
unit_spec: UnitSpec | None = None
|
18
|
+
|
19
|
+
@staticmethod
|
20
|
+
def from_et(et_element: ElementTree.Element,
|
21
|
+
context: OdxDocContext) -> "ConfigDataDictionarySpec":
|
22
|
+
data_object_props = NamedItemList([
|
23
|
+
DataObjectProperty.from_et(dop_element, context)
|
24
|
+
for dop_element in et_element.iterfind("DATA-OBJECT-PROPS/DATA-OBJECT-PROP")
|
25
|
+
])
|
26
|
+
|
27
|
+
if (spec_elem := et_element.find("UNIT-SPEC")) is not None:
|
28
|
+
unit_spec = UnitSpec.from_et(spec_elem, context)
|
29
|
+
else:
|
30
|
+
unit_spec = None
|
31
|
+
|
32
|
+
return ConfigDataDictionarySpec(
|
33
|
+
data_object_props=data_object_props,
|
34
|
+
unit_spec=unit_spec,
|
35
|
+
)
|
36
|
+
|
37
|
+
def _build_odxlinks(self) -> dict[OdxLinkId, Any]:
|
38
|
+
odxlinks = {}
|
39
|
+
|
40
|
+
for data_object_prop in self.data_object_props:
|
41
|
+
odxlinks.update(data_object_prop._build_odxlinks())
|
42
|
+
if self.unit_spec is not None:
|
43
|
+
odxlinks.update(self.unit_spec._build_odxlinks())
|
44
|
+
|
45
|
+
return odxlinks
|
46
|
+
|
47
|
+
def _resolve_odxlinks(self, odxlinks: OdxLinkDatabase) -> None:
|
48
|
+
for data_object_prop in self.data_object_props:
|
49
|
+
data_object_prop._resolve_odxlinks(odxlinks)
|
50
|
+
if self.unit_spec is not None:
|
51
|
+
self.unit_spec._resolve_odxlinks(odxlinks)
|
52
|
+
|
53
|
+
def _resolve_snrefs(self, context: SnRefContext) -> None:
|
54
|
+
for data_object_prop in self.data_object_props:
|
55
|
+
data_object_prop._resolve_snrefs(context)
|
56
|
+
if self.unit_spec is not None:
|
57
|
+
self.unit_spec._resolve_snrefs(context)
|
odxtools/configiditem.py
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
# SPDX-License-Identifier: MIT
|
2
|
+
from dataclasses import dataclass
|
3
|
+
from xml.etree import ElementTree
|
4
|
+
|
5
|
+
from .configitem import ConfigItem
|
6
|
+
from .odxdoccontext import OdxDocContext
|
7
|
+
from .utils import dataclass_fields_asdict
|
8
|
+
|
9
|
+
|
10
|
+
@dataclass(kw_only=True)
|
11
|
+
class ConfigIdItem(ConfigItem):
|
12
|
+
"""This class represents a CONFIG-ID-ITEM."""
|
13
|
+
|
14
|
+
@staticmethod
|
15
|
+
def from_et(et_element: ElementTree.Element, context: OdxDocContext) -> "ConfigIdItem":
|
16
|
+
kwargs = dataclass_fields_asdict(ConfigItem.from_et(et_element, context))
|
17
|
+
|
18
|
+
return ConfigIdItem(**kwargs)
|
odxtools/configitem.py
ADDED
@@ -0,0 +1,85 @@
|
|
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 .dopbase import DopBase
|
7
|
+
from .element import NamedElement
|
8
|
+
from .exceptions import odxrequire
|
9
|
+
from .odxdoccontext import OdxDocContext
|
10
|
+
from .odxlink import OdxLinkDatabase, OdxLinkId, OdxLinkRef, resolve_snref
|
11
|
+
from .snrefcontext import SnRefContext
|
12
|
+
from .specialdatagroup import SpecialDataGroup
|
13
|
+
from .utils import dataclass_fields_asdict
|
14
|
+
|
15
|
+
|
16
|
+
@dataclass(kw_only=True)
|
17
|
+
class ConfigItem(NamedElement):
|
18
|
+
"""This class represents a CONFIG-ITEM.
|
19
|
+
|
20
|
+
CONFIG-ITEM is the base class for CONFIG-ID-ITEM, DATA-ID-ITEM,
|
21
|
+
OPTION-ITEM, and SYSTEM-ITEM.
|
22
|
+
"""
|
23
|
+
byte_position: int | None = None
|
24
|
+
bit_position: int | None = None
|
25
|
+
|
26
|
+
# according to the spec exactly one of the following two
|
27
|
+
# attributes is not None...x
|
28
|
+
data_object_prop_ref: OdxLinkRef | None = None
|
29
|
+
data_object_prop_snref: str | None = None
|
30
|
+
sdgs: list[SpecialDataGroup] = field(default_factory=list)
|
31
|
+
|
32
|
+
@property
|
33
|
+
def data_object_prop(self) -> DopBase:
|
34
|
+
return self._data_object_prop
|
35
|
+
|
36
|
+
@staticmethod
|
37
|
+
def from_et(et_element: ElementTree.Element, context: OdxDocContext) -> "ConfigItem":
|
38
|
+
kwargs = dataclass_fields_asdict(NamedElement.from_et(et_element, context))
|
39
|
+
|
40
|
+
byte_position = None
|
41
|
+
if (byte_pos_elem := et_element.findtext("BYTE-POSITION")) is not None:
|
42
|
+
byte_position = int(byte_pos_elem)
|
43
|
+
|
44
|
+
bit_position = None
|
45
|
+
if (bit_pos_elem := et_element.findtext("BIT-POSITION")) is not None:
|
46
|
+
bit_position = int(bit_pos_elem)
|
47
|
+
|
48
|
+
data_object_prop_ref = OdxLinkRef.from_et(et_element.find("DATA-OBJECT-PROP-REF"), context)
|
49
|
+
data_object_prop_snref = None
|
50
|
+
if (data_object_prop_snref_elem := et_element.find("DATA-OBJECT-PROP-SNREF")) is not None:
|
51
|
+
data_object_prop_snref = odxrequire(
|
52
|
+
data_object_prop_snref_elem.attrib.get("SHORT-NAME"))
|
53
|
+
sdgs = [SpecialDataGroup.from_et(sdge, context) for sdge in et_element.iterfind("SDGS/SDG")]
|
54
|
+
|
55
|
+
return ConfigItem(
|
56
|
+
byte_position=byte_position,
|
57
|
+
bit_position=bit_position,
|
58
|
+
data_object_prop_ref=data_object_prop_ref,
|
59
|
+
data_object_prop_snref=data_object_prop_snref,
|
60
|
+
sdgs=sdgs,
|
61
|
+
**kwargs)
|
62
|
+
|
63
|
+
def _build_odxlinks(self) -> dict[OdxLinkId, Any]:
|
64
|
+
result = {}
|
65
|
+
|
66
|
+
for sdg in self.sdgs:
|
67
|
+
result.update(sdg._build_odxlinks())
|
68
|
+
|
69
|
+
return result
|
70
|
+
|
71
|
+
def _resolve_odxlinks(self, odxlinks: OdxLinkDatabase) -> None:
|
72
|
+
if self.data_object_prop_ref is not None:
|
73
|
+
self._data_object_prop = odxlinks.resolve(self.data_object_prop_ref, DopBase)
|
74
|
+
|
75
|
+
for sdg in self.sdgs:
|
76
|
+
sdg._resolve_odxlinks(odxlinks)
|
77
|
+
|
78
|
+
def _resolve_snrefs(self, context: SnRefContext) -> None:
|
79
|
+
if self.data_object_prop_snref is not None:
|
80
|
+
ddds = odxrequire(context.diag_layer).diag_data_dictionary_spec
|
81
|
+
self._data_object_prop = resolve_snref(self.data_object_prop_snref,
|
82
|
+
ddds.all_data_object_properties, DopBase)
|
83
|
+
|
84
|
+
for sdg in self.sdgs:
|
85
|
+
sdg._resolve_snrefs(context)
|
odxtools/configrecord.py
ADDED
@@ -0,0 +1,146 @@
|
|
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 .audience import Audience
|
7
|
+
from .configiditem import ConfigIdItem
|
8
|
+
from .dataiditem import DataIdItem
|
9
|
+
from .datarecord import DataRecord
|
10
|
+
from .diagcommdataconnector import DiagCommDataConnector
|
11
|
+
from .element import NamedElement
|
12
|
+
from .exceptions import odxrequire
|
13
|
+
from .identvalue import IdentValue
|
14
|
+
from .nameditemlist import NamedItemList
|
15
|
+
from .odxdoccontext import OdxDocContext
|
16
|
+
from .odxlink import OdxLinkDatabase, OdxLinkId
|
17
|
+
from .optionitem import OptionItem
|
18
|
+
from .snrefcontext import SnRefContext
|
19
|
+
from .specialdatagroup import SpecialDataGroup
|
20
|
+
from .systemitem import SystemItem
|
21
|
+
from .utils import dataclass_fields_asdict
|
22
|
+
|
23
|
+
|
24
|
+
@dataclass(kw_only=True)
|
25
|
+
class ConfigRecord(NamedElement):
|
26
|
+
"""This class represents a CONFIG-RECORD."""
|
27
|
+
config_id_item: ConfigIdItem | None = None
|
28
|
+
diag_comm_data_connectors: list[DiagCommDataConnector] = field(default_factory=list)
|
29
|
+
config_id: IdentValue | None = None
|
30
|
+
data_records: NamedItemList[DataRecord] = field(default_factory=NamedItemList)
|
31
|
+
audience: Audience | None = None
|
32
|
+
system_items: NamedItemList[SystemItem] = field(default_factory=NamedItemList)
|
33
|
+
data_id_item: DataIdItem | None = None
|
34
|
+
option_items: NamedItemList[OptionItem] = field(default_factory=NamedItemList)
|
35
|
+
default_data_record_snref: str | None = None
|
36
|
+
sdgs: list[SpecialDataGroup] = field(default_factory=list)
|
37
|
+
|
38
|
+
@staticmethod
|
39
|
+
def from_et(et_element: ElementTree.Element, context: OdxDocContext) -> "ConfigRecord":
|
40
|
+
kwargs = dataclass_fields_asdict(NamedElement.from_et(et_element, context))
|
41
|
+
|
42
|
+
config_id_item = None
|
43
|
+
if (cid_elem := et_element.find("CONFIG-ID-ITEM")) is not None:
|
44
|
+
config_id_item = ConfigIdItem.from_et(cid_elem, context)
|
45
|
+
diag_comm_data_connectors = [
|
46
|
+
DiagCommDataConnector.from_et(dcdc_elem, context) for dcdc_elem in et_element.iterfind(
|
47
|
+
"DIAG-COMM-DATA-CONNECTORS/DIAG-COMM-DATA-CONNECTOR")
|
48
|
+
]
|
49
|
+
config_id = None
|
50
|
+
if (cid_elem := et_element.find("CONFIG-ID")) is not None:
|
51
|
+
config_id = IdentValue.from_et(cid_elem, context)
|
52
|
+
data_records = NamedItemList([
|
53
|
+
DataRecord.from_et(dr_elem, context)
|
54
|
+
for dr_elem in et_element.iterfind("DATA-RECORDS/DATA-RECORD")
|
55
|
+
])
|
56
|
+
audience = None
|
57
|
+
if (aud_elem := et_element.find("AUDIENCE")) is not None:
|
58
|
+
audience = Audience.from_et(aud_elem, context)
|
59
|
+
system_items = NamedItemList([
|
60
|
+
SystemItem.from_et(si_elem, context)
|
61
|
+
for si_elem in et_element.iterfind("SYSTEM-ITEMS/SYSTEM-ITEM")
|
62
|
+
])
|
63
|
+
data_id_item = None
|
64
|
+
if (dii_elem := et_element.find("DATA-ID-ITEM")) is not None:
|
65
|
+
data_id_item = DataIdItem.from_et(dii_elem, context)
|
66
|
+
option_items = NamedItemList([
|
67
|
+
OptionItem.from_et(si_elem, context)
|
68
|
+
for si_elem in et_element.iterfind("OPTION-ITEMS/OPTION-ITEM")
|
69
|
+
])
|
70
|
+
default_data_record_snref = None
|
71
|
+
if (default_data_record_snref_elem :=
|
72
|
+
et_element.find("DEFAULT-DATA-RECORD-SNREF")) is not None:
|
73
|
+
default_data_record_snref = odxrequire(
|
74
|
+
default_data_record_snref_elem.attrib.get("SHORT-NAME"))
|
75
|
+
sdgs = [SpecialDataGroup.from_et(sdge, context) for sdge in et_element.iterfind("SDGS/SDG")]
|
76
|
+
|
77
|
+
return ConfigRecord(
|
78
|
+
config_id_item=config_id_item,
|
79
|
+
diag_comm_data_connectors=diag_comm_data_connectors,
|
80
|
+
config_id=config_id,
|
81
|
+
data_records=data_records,
|
82
|
+
audience=audience,
|
83
|
+
system_items=system_items,
|
84
|
+
data_id_item=data_id_item,
|
85
|
+
option_items=option_items,
|
86
|
+
default_data_record_snref=default_data_record_snref,
|
87
|
+
sdgs=sdgs,
|
88
|
+
**kwargs)
|
89
|
+
|
90
|
+
def _build_odxlinks(self) -> dict[OdxLinkId, Any]:
|
91
|
+
result = {}
|
92
|
+
|
93
|
+
if self.config_id_item is not None:
|
94
|
+
result.update(self.config_id_item._build_odxlinks())
|
95
|
+
for diag_comm_data_connector in self.diag_comm_data_connectors:
|
96
|
+
result.update(diag_comm_data_connector._build_odxlinks())
|
97
|
+
for data_record in self.data_records:
|
98
|
+
result.update(data_record._build_odxlinks())
|
99
|
+
if self.audience is not None:
|
100
|
+
result.update(self.audience._build_odxlinks())
|
101
|
+
for system_item in self.system_items:
|
102
|
+
result.update(system_item._build_odxlinks())
|
103
|
+
if self.data_id_item is not None:
|
104
|
+
result.update(self.data_id_item._build_odxlinks())
|
105
|
+
for option_item in self.option_items:
|
106
|
+
result.update(option_item._build_odxlinks())
|
107
|
+
for sdg in self.sdgs:
|
108
|
+
result.update(sdg._build_odxlinks())
|
109
|
+
|
110
|
+
return result
|
111
|
+
|
112
|
+
def _resolve_odxlinks(self, odxlinks: OdxLinkDatabase) -> None:
|
113
|
+
if self.config_id_item is not None:
|
114
|
+
self.config_id_item._resolve_odxlinks(odxlinks)
|
115
|
+
for diag_comm_data_connector in self.diag_comm_data_connectors:
|
116
|
+
diag_comm_data_connector._resolve_odxlinks(odxlinks)
|
117
|
+
for data_record in self.data_records:
|
118
|
+
data_record._resolve_odxlinks(odxlinks)
|
119
|
+
if self.audience is not None:
|
120
|
+
self.audience._resolve_odxlinks(odxlinks)
|
121
|
+
for system_item in self.system_items:
|
122
|
+
system_item._resolve_odxlinks(odxlinks)
|
123
|
+
if self.data_id_item is not None:
|
124
|
+
self.data_id_item._resolve_odxlinks(odxlinks)
|
125
|
+
for option_item in self.option_items:
|
126
|
+
option_item._resolve_odxlinks(odxlinks)
|
127
|
+
for sdg in self.sdgs:
|
128
|
+
sdg._resolve_odxlinks(odxlinks)
|
129
|
+
|
130
|
+
def _resolve_snrefs(self, context: SnRefContext) -> None:
|
131
|
+
if self.config_id_item is not None:
|
132
|
+
self.config_id_item._resolve_snrefs(context)
|
133
|
+
for diag_comm_data_connector in self.diag_comm_data_connectors:
|
134
|
+
diag_comm_data_connector._resolve_snrefs(context)
|
135
|
+
for data_record in self.data_records:
|
136
|
+
data_record._resolve_snrefs(context)
|
137
|
+
if self.audience is not None:
|
138
|
+
self.audience._resolve_snrefs(context)
|
139
|
+
for system_item in self.system_items:
|
140
|
+
system_item._resolve_snrefs(context)
|
141
|
+
if self.data_id_item is not None:
|
142
|
+
self.data_id_item._resolve_snrefs(context)
|
143
|
+
for option_item in self.option_items:
|
144
|
+
option_item._resolve_snrefs(context)
|
145
|
+
for sdg in self.sdgs:
|
146
|
+
sdg._resolve_snrefs(context)
|
odxtools/database.py
CHANGED
@@ -18,8 +18,10 @@ from .diaglayers.ecushareddata import EcuSharedData
|
|
18
18
|
from .diaglayers.ecuvariant import EcuVariant
|
19
19
|
from .diaglayers.functionalgroup import FunctionalGroup
|
20
20
|
from .diaglayers.protocol import Protocol
|
21
|
+
from .ecuconfig import EcuConfig
|
21
22
|
from .exceptions import odxraise, odxrequire
|
22
23
|
from .flash import Flash
|
24
|
+
from .multipleecujobspec import MultipleEcuJobSpec
|
23
25
|
from .nameditemlist import NamedItemList
|
24
26
|
from .odxdoccontext import OdxDocContext
|
25
27
|
from .odxlink import DocType, OdxDocFragment, OdxLinkDatabase, OdxLinkId
|
@@ -40,7 +42,9 @@ class Database:
|
|
40
42
|
self._diag_layer_containers = NamedItemList[DiagLayerContainer]()
|
41
43
|
self._comparam_subsets = NamedItemList[ComparamSubset]()
|
42
44
|
self._comparam_specs = NamedItemList[ComparamSpec]()
|
45
|
+
self._ecu_configs = NamedItemList[EcuConfig]()
|
43
46
|
self._flashs = NamedItemList[Flash]()
|
47
|
+
self._multiple_ecu_job_specs = NamedItemList[MultipleEcuJobSpec]()
|
44
48
|
self._short_name = "odx_database"
|
45
49
|
|
46
50
|
def add_pdx_file(self, pdx_file: Union[str, "PathLike[Any]", IO[bytes], ZipFile]) -> None:
|
@@ -114,9 +118,17 @@ class Database:
|
|
114
118
|
self._comparam_subsets.append(ComparamSubset.from_et(category_et, context))
|
115
119
|
else:
|
116
120
|
self._comparam_specs.append(ComparamSpec.from_et(category_et, context))
|
121
|
+
elif category_tag == "ECU-CONFIG":
|
122
|
+
context = OdxDocContext(model_version,
|
123
|
+
(OdxDocFragment(category_sn, DocType.ECU_CONFIG),))
|
124
|
+
self._ecu_configs.append(EcuConfig.from_et(category_et, context))
|
117
125
|
elif category_tag == "FLASH":
|
118
126
|
context = OdxDocContext(model_version, (OdxDocFragment(category_sn, DocType.FLASH),))
|
119
127
|
self._flashs.append(Flash.from_et(category_et, context))
|
128
|
+
elif category_tag == "MULTIPLE-ECU-JOB-SPEC":
|
129
|
+
context = OdxDocContext(model_version,
|
130
|
+
(OdxDocFragment(category_sn, DocType.MULTIPLE_ECU_JOB_SPEC),))
|
131
|
+
self._multiple_ecu_job_specs.append(MultipleEcuJobSpec.from_et(category_et, context))
|
120
132
|
|
121
133
|
def refresh(self) -> None:
|
122
134
|
# Create wrapper objects
|
@@ -148,9 +160,15 @@ class Database:
|
|
148
160
|
for dlc in self.diag_layer_containers:
|
149
161
|
dlc._resolve_odxlinks(self._odxlinks)
|
150
162
|
|
163
|
+
for ecu_config in self.ecu_configs:
|
164
|
+
ecu_config._resolve_odxlinks(self._odxlinks)
|
165
|
+
|
151
166
|
for flash in self.flashs:
|
152
167
|
flash._resolve_odxlinks(self._odxlinks)
|
153
168
|
|
169
|
+
for multiple_ecu_job_spec in self.multiple_ecu_job_specs:
|
170
|
+
multiple_ecu_job_spec._resolve_odxlinks(self._odxlinks)
|
171
|
+
|
154
172
|
# resolve short name references for containers which do not do
|
155
173
|
# inheritance (we can call directly call _resolve_snrefs())
|
156
174
|
context = SnRefContext()
|
@@ -163,8 +181,12 @@ class Database:
|
|
163
181
|
spec._finalize_init(self, self._odxlinks)
|
164
182
|
for dlc in self.diag_layer_containers:
|
165
183
|
dlc._finalize_init(self, self._odxlinks)
|
184
|
+
for ecu_config in self.ecu_configs:
|
185
|
+
ecu_config._finalize_init(self, self._odxlinks)
|
166
186
|
for flash in self.flashs:
|
167
187
|
flash._finalize_init(self, self._odxlinks)
|
188
|
+
for multiple_ecu_job_spec in self.multiple_ecu_job_specs:
|
189
|
+
multiple_ecu_job_spec._finalize_init(self, self._odxlinks)
|
168
190
|
|
169
191
|
for subset in self.comparam_subsets:
|
170
192
|
subset._resolve_snrefs(context)
|
@@ -172,8 +194,12 @@ class Database:
|
|
172
194
|
spec._resolve_snrefs(context)
|
173
195
|
for dlc in self.diag_layer_containers:
|
174
196
|
dlc._resolve_snrefs(context)
|
197
|
+
for ecu_config in self.ecu_configs:
|
198
|
+
ecu_config._resolve_snrefs(context)
|
175
199
|
for flash in self.flashs:
|
176
200
|
flash._resolve_snrefs(context)
|
201
|
+
for multiple_ecu_job_spec in self.multiple_ecu_job_specs:
|
202
|
+
multiple_ecu_job_spec._resolve_snrefs(context)
|
177
203
|
|
178
204
|
def _build_odxlinks(self) -> dict[OdxLinkId, Any]:
|
179
205
|
result: dict[OdxLinkId, Any] = {}
|
@@ -187,8 +213,13 @@ class Database:
|
|
187
213
|
for dlc in self.diag_layer_containers:
|
188
214
|
result.update(dlc._build_odxlinks())
|
189
215
|
|
216
|
+
for ecu_config in self.ecu_configs:
|
217
|
+
result.update(ecu_config._build_odxlinks())
|
218
|
+
|
190
219
|
for flash in self.flashs:
|
191
220
|
result.update(flash._build_odxlinks())
|
221
|
+
for multiple_ecu_job_spec in self.multiple_ecu_job_specs:
|
222
|
+
result.update(multiple_ecu_job_spec._build_odxlinks())
|
192
223
|
|
193
224
|
return result
|
194
225
|
|
@@ -263,10 +294,18 @@ class Database:
|
|
263
294
|
def comparam_specs(self) -> NamedItemList[ComparamSpec]:
|
264
295
|
return self._comparam_specs
|
265
296
|
|
297
|
+
@property
|
298
|
+
def ecu_configs(self) -> NamedItemList[EcuConfig]:
|
299
|
+
return self._ecu_configs
|
300
|
+
|
266
301
|
@property
|
267
302
|
def flashs(self) -> NamedItemList[Flash]:
|
268
303
|
return self._flashs
|
269
304
|
|
305
|
+
@property
|
306
|
+
def multiple_ecu_job_specs(self) -> NamedItemList[MultipleEcuJobSpec]:
|
307
|
+
return self._multiple_ecu_job_specs
|
308
|
+
|
270
309
|
def __repr__(self) -> str:
|
271
310
|
return f"Database(model_version={self.model_version}, " \
|
272
311
|
f"protocols={[x.short_name for x in self.protocols]}, " \
|
@@ -274,4 +313,5 @@ class Database:
|
|
274
313
|
f"diag_layer_containers={repr(self.diag_layer_containers)}, " \
|
275
314
|
f"comparam_subsets={repr(self.comparam_subsets)}, " \
|
276
315
|
f"comparam_specs={repr(self.comparam_specs)}, " \
|
316
|
+
f"ecu_configs={repr(self.ecu_configs)}, " \
|
277
317
|
f"flashs={repr(self.flashs)})"
|
odxtools/dataiditem.py
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
# SPDX-License-Identifier: MIT
|
2
|
+
from dataclasses import dataclass
|
3
|
+
from xml.etree import ElementTree
|
4
|
+
|
5
|
+
from .configitem import ConfigItem
|
6
|
+
from .odxdoccontext import OdxDocContext
|
7
|
+
from .utils import dataclass_fields_asdict
|
8
|
+
|
9
|
+
|
10
|
+
@dataclass(kw_only=True)
|
11
|
+
class DataIdItem(ConfigItem):
|
12
|
+
"""This class represents a DATA-ID-ITEM."""
|
13
|
+
|
14
|
+
@staticmethod
|
15
|
+
def from_et(et_element: ElementTree.Element, context: OdxDocContext) -> "DataIdItem":
|
16
|
+
kwargs = dataclass_fields_asdict(ConfigItem.from_et(et_element, context))
|
17
|
+
|
18
|
+
return DataIdItem(**kwargs)
|