Skip to content

Commit

Permalink
DiagDataDictionarySpec: some janitorial work
Browse files Browse the repository at this point in the history
- add the missing `admin_data` attribute
- use the same order for all member attributes as the XSD uses for the
  corresponding subtags.

Signed-off-by: Andreas Lauser <[email protected]>
Signed-off-by: Michael Hahn <[email protected]>
  • Loading branch information
andlaus committed Mar 1, 2024
1 parent 7c5d0f8 commit bfcfd12
Show file tree
Hide file tree
Showing 7 changed files with 109 additions and 72 deletions.
2 changes: 2 additions & 0 deletions examples/somersaultecu.py
Original file line number Diff line number Diff line change
Expand Up @@ -2054,6 +2054,7 @@ class SomersaultSID(IntEnum):
]

somersault_diag_data_dictionary_spec = DiagDataDictionarySpec(
admin_data=None,
data_object_props=NamedItemList(somersault_dops.values()),
unit_spec=UnitSpec(
unit_groups=list(somersault_unit_groups.values()),
Expand Down Expand Up @@ -2326,6 +2327,7 @@ class SomersaultSID(IntEnum):
company_datas=NamedItemList(),
functional_classes=NamedItemList(),
diag_data_dictionary_spec=DiagDataDictionarySpec(
admin_data=None,
dtc_dops=NamedItemList(),
data_object_props=NamedItemList(),
static_fields=NamedItemList(),
Expand Down
168 changes: 96 additions & 72 deletions odxtools/diagdatadictionaryspec.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
from typing import TYPE_CHECKING, Any, Dict, List, Optional
from xml.etree import ElementTree

from .admindata import AdminData
from .basicstructure import BasicStructure
from .createsdgs import create_sdgs_from_et
from .dataobjectproperty import DataObjectProperty
Expand All @@ -29,37 +30,55 @@

@dataclass
class DiagDataDictionarySpec:
admin_data: Optional[AdminData]
dtc_dops: NamedItemList[DtcDop]
env_data_descs: NamedItemList[EnvironmentDataDescription]
data_object_props: NamedItemList[DataObjectProperty]
structures: NamedItemList[BasicStructure]
end_of_pdu_fields: NamedItemList[EndOfPduField]
static_fields: NamedItemList[StaticField]
dynamic_length_fields: NamedItemList[DynamicLengthField]
tables: NamedItemList[Table]
env_data_descs: NamedItemList[EnvironmentDataDescription]
env_datas: NamedItemList[EnvironmentData]
#dynamic_endmarker_fields: NamedItemList[DynamicEndmarkerField]
end_of_pdu_fields: NamedItemList[EndOfPduField]
muxs: NamedItemList[Multiplexer]
env_datas: NamedItemList[EnvironmentData]
unit_spec: Optional[UnitSpec]
tables: NamedItemList[Table]
sdgs: List[SpecialDataGroup]

def __post_init__(self) -> None:
self._all_data_object_properties: NamedItemList[DopBase] = NamedItemList(
chain(
self.dtc_dops,
self.env_data_descs,
self.data_object_props,
self.structures,
self.end_of_pdu_fields,
self.static_fields,
self.dynamic_length_fields,
self.env_data_descs,
self.env_datas,
#self.dynamic_endmarker_fields,
self.end_of_pdu_fields,
self.muxs,
self.env_datas,
))

@staticmethod
def from_et(et_element: ElementTree.Element,
doc_frags: List[OdxDocFragment]) -> "DiagDataDictionarySpec":
# Parse DOP-BASEs
admin_data = None
if (admin_data_elem := et_element.find("ADMIN-DATA")) is not None:
admin_data = AdminData.from_et(admin_data_elem, doc_frags)

dtc_dops = []
for dtc_dop_elem in et_element.iterfind("DTC-DOPS/DTC-DOP"):
dtc_dop = DtcDop.from_et(dtc_dop_elem, doc_frags)
if not isinstance(dtc_dop, DtcDop):
odxraise()
dtc_dops.append(dtc_dop)

env_data_descs = [
EnvironmentDataDescription.from_et(env_data_desc_element, doc_frags)
for env_data_desc_element in et_element.iterfind("ENV-DATA-DESCS/ENV-DATA-DESC")
]

data_object_props = [
DataObjectProperty.from_et(dop_element, doc_frags)
for dop_element in et_element.iterfind("DATA-OBJECT-PROPS/DATA-OBJECT-PROP")
Expand All @@ -70,11 +89,6 @@ def from_et(et_element: ElementTree.Element,
for structure_element in et_element.iterfind("STRUCTURES/STRUCTURE")
]

end_of_pdu_fields = [
EndOfPduField.from_et(eofp_element, doc_frags)
for eofp_element in et_element.iterfind("END-OF-PDU-FIELDS/END-OF-PDU-FIELD")
]

static_fields = [
StaticField.from_et(dl_element, doc_frags)
for dl_element in et_element.iterfind("STATIC-FIELDS/STATIC-FIELD")
Expand All @@ -85,21 +99,20 @@ def from_et(et_element: ElementTree.Element,
for dl_element in et_element.iterfind("DYNAMIC-LENGTH-FIELDS/DYNAMIC-LENGTH-FIELD")
]

dtc_dops = []
for dtc_dop_elem in et_element.iterfind("DTC-DOPS/DTC-DOP"):
dtc_dop = DtcDop.from_et(dtc_dop_elem, doc_frags)
if not isinstance(dtc_dop, DtcDop):
odxraise()
dtc_dops.append(dtc_dop)
# TODO: dynamic endmarker fields
#dynamic_endmarker_fields = [
# DynamicEndmarkerField.from_et(dl_element, doc_frags)
# for dl_element in et_element.iterfind("DYNAMIC-ENDMARKER-FIELDS/DYNAMIC-ENDMARKER-FIELD")
#]

tables = [
Table.from_et(table_element, doc_frags)
for table_element in et_element.iterfind("TABLES/TABLE")
end_of_pdu_fields = [
EndOfPduField.from_et(eofp_element, doc_frags)
for eofp_element in et_element.iterfind("END-OF-PDU-FIELDS/END-OF-PDU-FIELD")
]

env_data_descs = [
EnvironmentDataDescription.from_et(env_data_desc_element, doc_frags)
for env_data_desc_element in et_element.iterfind("ENV-DATA-DESCS/ENV-DATA-DESC")
muxs = [
Multiplexer.from_et(mux_element, doc_frags)
for mux_element in et_element.iterfind("MUXS/MUX")
]

env_data_elements = chain(
Expand All @@ -112,118 +125,129 @@ def from_et(et_element: ElementTree.Element,
for env_data_element in env_data_elements
]

muxs = [
Multiplexer.from_et(mux_element, doc_frags)
for mux_element in et_element.iterfind("MUXS/MUX")
]

if (spec_elem := et_element.find("UNIT-SPEC")) is not None:
unit_spec = UnitSpec.from_et(spec_elem, doc_frags)
else:
unit_spec = None

tables = [
Table.from_et(table_element, doc_frags)
for table_element in et_element.iterfind("TABLES/TABLE")
]

sdgs = create_sdgs_from_et(et_element.find("SDGS"), doc_frags)

return DiagDataDictionarySpec(
admin_data=admin_data,
dtc_dops=NamedItemList(dtc_dops),
env_data_descs=NamedItemList(env_data_descs),
data_object_props=NamedItemList(data_object_props),
structures=NamedItemList(structures),
end_of_pdu_fields=NamedItemList(end_of_pdu_fields),
static_fields=NamedItemList(static_fields),
dynamic_length_fields=NamedItemList(dynamic_length_fields),
dtc_dops=NamedItemList(dtc_dops),
#dynamic_endmarker_fields=NamedItemList(dynamic_endmarker_fields),
end_of_pdu_fields=NamedItemList(end_of_pdu_fields),
muxs=NamedItemList(muxs),
env_datas=NamedItemList(env_datas),
unit_spec=unit_spec,
tables=NamedItemList(tables),
env_data_descs=NamedItemList(env_data_descs),
env_datas=NamedItemList(env_datas),
muxs=NamedItemList(muxs),
sdgs=sdgs,
)

def _build_odxlinks(self) -> Dict[OdxLinkId, Any]:
# note that DataDictionarySpec objects do not exhibit an ODXLINK id.
odxlinks = {}

for data_object_prop in self.data_object_props:
odxlinks.update(data_object_prop._build_odxlinks())
if self.admin_data is not None:
odxlinks.update(self.admin_data._build_odxlinks())
for dtc_dop in self.dtc_dops:
odxlinks.update(dtc_dop._build_odxlinks())
for env_data_desc in self.env_data_descs:
odxlinks.update(env_data_desc._build_odxlinks())
for env_data in self.env_datas:
odxlinks.update(env_data._build_odxlinks())
for mux in self.muxs:
odxlinks.update(mux._build_odxlinks())
for sdg in self.sdgs:
odxlinks.update(sdg._build_odxlinks())
for data_object_prop in self.data_object_props:
odxlinks.update(data_object_prop._build_odxlinks())
for structure in self.structures:
odxlinks.update(structure._build_odxlinks())
for static_field in self.static_fields:
odxlinks.update(static_field._build_odxlinks())
for dynamic_length_field in self.dynamic_length_fields:
odxlinks.update(dynamic_length_field._build_odxlinks())
#for dynamic_endmarker_field in self.dynamic_endmarker_fields:
# odxlinks.update(dynamic_endmarker_field._build_odxlinks())
for end_of_pdu_field in self.end_of_pdu_fields:
odxlinks.update(end_of_pdu_field._build_odxlinks())
for table in self.tables:
odxlinks.update(table._build_odxlinks())

for mux in self.muxs:
odxlinks.update(mux._build_odxlinks())
for env_data in self.env_datas:
odxlinks.update(env_data._build_odxlinks())
if self.unit_spec is not None:
odxlinks.update(self.unit_spec._build_odxlinks())
for table in self.tables:
odxlinks.update(table._build_odxlinks())
for sdg in self.sdgs:
odxlinks.update(sdg._build_odxlinks())

return odxlinks

def _resolve_odxlinks(self, odxlinks: OdxLinkDatabase) -> None:
for data_object_prop in self.data_object_props:
data_object_prop._resolve_odxlinks(odxlinks)
if self.admin_data is not None:
self.admin_data._resolve_odxlinks(odxlinks)
for dtc_dop in self.dtc_dops:
dtc_dop._resolve_odxlinks(odxlinks)
for env_data_desc in self.env_data_descs:
env_data_desc._resolve_odxlinks(odxlinks)
for data_object_prop in self.data_object_props:
data_object_prop._resolve_odxlinks(odxlinks)
for structure in self.structures:
structure._resolve_odxlinks(odxlinks)
for static_field in self.static_fields:
static_field._resolve_odxlinks(odxlinks)
for dynamic_length_field in self.dynamic_length_fields:
dynamic_length_field._resolve_odxlinks(odxlinks)
#for dynamic_endmarker_field in self.dynamic_endmarker_fields:
# dynamic_endmarker_field._resolve_odxlinks(odxlinks)
for end_of_pdu_field in self.end_of_pdu_fields:
end_of_pdu_field._resolve_odxlinks(odxlinks)
for env_data_desc in self.env_data_descs:
env_data_desc._resolve_odxlinks(odxlinks)
for env_data in self.env_datas:
env_data._resolve_odxlinks(odxlinks)
for mux in self.muxs:
mux._resolve_odxlinks(odxlinks)
for sdg in self.sdgs:
sdg._resolve_odxlinks(odxlinks)
for structure in self.structures:
structure._resolve_odxlinks(odxlinks)
for table in self.tables:
table._resolve_odxlinks(odxlinks)

for env_data in self.env_datas:
env_data._resolve_odxlinks(odxlinks)
if self.unit_spec is not None:
self.unit_spec._resolve_odxlinks(odxlinks)
for table in self.tables:
table._resolve_odxlinks(odxlinks)
for sdg in self.sdgs:
sdg._resolve_odxlinks(odxlinks)

def _resolve_snrefs(self, diag_layer: "DiagLayer") -> None:
for data_object_prop in self.data_object_props:
data_object_prop._resolve_snrefs(diag_layer)
if self.admin_data is not None:
self.admin_data._resolve_snrefs(diag_layer)
for dtc_dop in self.dtc_dops:
dtc_dop._resolve_snrefs(diag_layer)
for env_data_desc in self.env_data_descs:
env_data_desc._resolve_snrefs(diag_layer)
for data_object_prop in self.data_object_props:
data_object_prop._resolve_snrefs(diag_layer)
for structure in self.structures:
structure._resolve_snrefs(diag_layer)
for static_field in self.static_fields:
static_field._resolve_snrefs(diag_layer)
for dynamic_length_field in self.dynamic_length_fields:
dynamic_length_field._resolve_snrefs(diag_layer)
#for dynamic_endmarker_field in self.dynamic_endmarker_fields:
# dynamic_endmarker_field._resolve_snrefs(diag_layer)
for end_of_pdu_field in self.end_of_pdu_fields:
end_of_pdu_field._resolve_snrefs(diag_layer)
for env_data_desc in self.env_data_descs:
env_data_desc._resolve_snrefs(diag_layer)
for env_data in self.env_datas:
env_data._resolve_snrefs(diag_layer)
for mux in self.muxs:
mux._resolve_snrefs(diag_layer)
for sdg in self.sdgs:
sdg._resolve_snrefs(diag_layer)
for structure in self.structures:
structure._resolve_snrefs(diag_layer)
for table in self.tables:
table._resolve_snrefs(diag_layer)

for env_data in self.env_datas:
env_data._resolve_snrefs(diag_layer)
if self.unit_spec is not None:
self.unit_spec._resolve_snrefs(diag_layer)
for table in self.tables:
table._resolve_snrefs(diag_layer)
for sdg in self.sdgs:
sdg._resolve_snrefs(diag_layer)

@property
def all_data_object_properties(self) -> NamedItemList[DopBase]:
Expand Down
1 change: 1 addition & 0 deletions odxtools/diaglayer.py
Original file line number Diff line number Diff line change
Expand Up @@ -192,6 +192,7 @@ def _finalize_init(self, odxlinks: OdxLinkDatabase) -> None:
# inherited objects. To me, this seems rather inelegant, but
# hey, it's described like this in the standard.
self._diag_data_dictionary_spec = DiagDataDictionarySpec(
admin_data=None,
data_object_props=dops,
dtc_dops=dtc_dops,
structures=structures,
Expand Down
5 changes: 5 additions & 0 deletions tests/test_decoding.py
Original file line number Diff line number Diff line change
Expand Up @@ -652,6 +652,7 @@ def test_decode_request_structure(self) -> None:
company_datas=NamedItemList(),
functional_classes=NamedItemList(),
diag_data_dictionary_spec=DiagDataDictionarySpec(
admin_data=None,
dtc_dops=NamedItemList(),
data_object_props=NamedItemList([dop]),
structures=NamedItemList([struct]),
Expand Down Expand Up @@ -853,6 +854,7 @@ def test_static_field_coding(self) -> None:
company_datas=NamedItemList(),
functional_classes=NamedItemList(),
diag_data_dictionary_spec=DiagDataDictionarySpec(
admin_data=None,
dtc_dops=NamedItemList(),
data_object_props=NamedItemList([dop]),
structures=NamedItemList([struct]),
Expand Down Expand Up @@ -1075,6 +1077,7 @@ def test_dynamic_length_field_coding(self) -> None:
company_datas=NamedItemList(),
functional_classes=NamedItemList(),
diag_data_dictionary_spec=DiagDataDictionarySpec(
admin_data=None,
dtc_dops=NamedItemList(),
data_object_props=NamedItemList([dop]),
structures=NamedItemList([struct]),
Expand Down Expand Up @@ -1299,6 +1302,7 @@ def test_decode_request_end_of_pdu_field(self) -> None:
company_datas=NamedItemList(),
functional_classes=NamedItemList(),
diag_data_dictionary_spec=DiagDataDictionarySpec(
admin_data=None,
dtc_dops=NamedItemList(),
data_object_props=NamedItemList([dop]),
structures=NamedItemList([struct]),
Expand Down Expand Up @@ -1461,6 +1465,7 @@ def test_decode_request_linear_compu_method(self) -> None:
company_datas=NamedItemList(),
functional_classes=NamedItemList(),
diag_data_dictionary_spec=DiagDataDictionarySpec(
admin_data=None,
dtc_dops=NamedItemList(),
data_object_props=NamedItemList([dop]),
structures=NamedItemList(),
Expand Down
3 changes: 3 additions & 0 deletions tests/test_diag_coded_types.py
Original file line number Diff line number Diff line change
Expand Up @@ -246,6 +246,7 @@ def test_end_to_end(self) -> None:
company_datas=NamedItemList(),
functional_classes=NamedItemList(),
diag_data_dictionary_spec=DiagDataDictionarySpec(
admin_data=None,
data_object_props=NamedItemList(dops.values()),
dtc_dops=NamedItemList(),
structures=NamedItemList(),
Expand Down Expand Up @@ -546,6 +547,7 @@ def test_end_to_end(self) -> None:
company_datas=NamedItemList(),
functional_classes=NamedItemList(),
diag_data_dictionary_spec=DiagDataDictionarySpec(
admin_data=None,
data_object_props=NamedItemList(dops.values()),
dtc_dops=NamedItemList(),
structures=NamedItemList(),
Expand Down Expand Up @@ -853,6 +855,7 @@ def test_end_to_end(self) -> None:
company_datas=NamedItemList(),
functional_classes=NamedItemList(),
diag_data_dictionary_spec=DiagDataDictionarySpec(
admin_data=None,
data_object_props=NamedItemList(dops.values()),
dtc_dops=NamedItemList(),
structures=NamedItemList(),
Expand Down
1 change: 1 addition & 0 deletions tests/test_diag_data_dictionary_spec.py
Original file line number Diff line number Diff line change
Expand Up @@ -268,6 +268,7 @@ def test_initialization(self) -> None:
)

ddds = DiagDataDictionarySpec(
admin_data=None,
dtc_dops=NamedItemList([dtc_dop]),
data_object_props=NamedItemList([dop_1, dop_2]),
tables=NamedItemList([table]),
Expand Down
1 change: 1 addition & 0 deletions tests/test_unit_spec.py
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,7 @@ def test_resolve_odxlinks(self) -> None:
company_datas=NamedItemList(),
functional_classes=NamedItemList(),
diag_data_dictionary_spec=DiagDataDictionarySpec(
admin_data=None,
data_object_props=NamedItemList([dop]),
unit_spec=UnitSpec(
units=[unit],
Expand Down

0 comments on commit bfcfd12

Please sign in to comment.