Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add a retarget_snrefs() utility function #374

Merged
merged 4 commits into from
Jan 16, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file modified examples/somersault.pdx
Binary file not shown.
Binary file modified examples/somersault_modified.pdx
Binary file not shown.
183 changes: 162 additions & 21 deletions examples/somersaultecu.py
Original file line number Diff line number Diff line change
Expand Up @@ -302,6 +302,15 @@ class SomersaultSID(IntEnum):
is_condensed_raw=None,
is_highlow_byte_order_raw=None,
),
"int8":
StandardLengthType(
base_data_type=DataType.A_INT32,
bit_length=8,
bit_mask=None,
is_condensed_raw=None,
is_highlow_byte_order_raw=None,
base_type_encoding=None,
),
"uint8":
StandardLengthType(
base_data_type=DataType.A_UINT32,
Expand Down Expand Up @@ -423,6 +432,13 @@ class SomersaultSID(IntEnum):

# computation methods
somersault_compumethods: Dict[str, CompuMethod] = {
"int_passthrough":
IdenticalCompuMethod(
category=CompuCategory.IDENTICAL,
compu_internal_to_phys=None,
compu_phys_to_internal=None,
internal_type=DataType.A_INT32,
physical_type=DataType.A_INT32),
"uint_passthrough":
IdenticalCompuMethod(
category=CompuCategory.IDENTICAL,
Expand Down Expand Up @@ -655,6 +671,54 @@ class SomersaultSID(IntEnum):
internal_constr=None,
physical_constr=None,
),
"schroedinger_base":
DataObjectProperty(
odx_id=OdxLinkId("somersault.DOP.schroedinger_base", doc_frags),
oid=None,
short_name="schroedinger_dop",
long_name=None,
description=None,
admin_data=None,
diag_coded_type=somersault_diagcodedtypes["int8"],
physical_type=PhysicalType(DataType.A_INT32, display_radix=None, precision=None),
compu_method=somersault_compumethods["int_passthrough"],
unit_ref=None,
sdgs=[],
internal_constr=None,
physical_constr=None,
),
"schroedinger_lazy":
DataObjectProperty(
odx_id=OdxLinkId("somersault.DOP.schroedinger_lazy", doc_frags),
oid=None,
short_name="schroedinger_dop",
long_name=None,
description=None,
admin_data=None,
diag_coded_type=somersault_diagcodedtypes["uint8"],
physical_type=PhysicalType(DataType.A_UINT32, display_radix=None, precision=None),
compu_method=somersault_compumethods["uint_passthrough"],
unit_ref=None,
sdgs=[],
internal_constr=None,
physical_constr=None,
),
"schroedinger_assiduous":
DataObjectProperty(
odx_id=OdxLinkId("somersault.DOP.schroedinger_assiduous", doc_frags),
oid=None,
short_name="schroedinger_dop",
long_name=None,
description=None,
admin_data=None,
diag_coded_type=somersault_diagcodedtypes["float32"],
physical_type=PhysicalType(DataType.A_FLOAT32, display_radix=None, precision=None),
compu_method=somersault_compumethods["float_passthrough"],
unit_ref=None,
sdgs=[],
internal_constr=None,
physical_constr=None,
),
}

last_flip_details_table_id = OdxLinkId("somersault.table.last_flip_details", doc_frags)
Expand Down Expand Up @@ -1656,6 +1720,33 @@ class SomersaultSID(IntEnum):
),
],
),
"schroedinger":
Request(
odx_id=OdxLinkId("somersault.RQ.schroedinger", doc_frags),
oid=None,
short_name="schroedinger_request",
long_name=None,
description=None,
admin_data=None,
sdgs=[],
parameters=NamedItemList([
ValueParameter(
oid=None,
short_name="schroedinger_param",
long_name=None,
semantic=None,
description=Description.from_string(
"Parameter where the DOP changes dending on how you "
"look at the SNREF to it"),
physical_default_value_raw=None,
byte_position=0,
dop_ref=None,
dop_snref="schroedinger_dop",
bit_position=None,
sdgs=[],
),
]),
),
}

# services
Expand Down Expand Up @@ -1948,6 +2039,35 @@ class SomersaultSID(IntEnum):
],
sdgs=[],
),
"schroedinger":
DiagService(
odx_id=OdxLinkId("somersault.service.schroedinger", doc_frags),
oid=None,
short_name="schroedinger",
long_name=None,
description=None,
admin_data=None,
protocol_snrefs=[],
related_diag_comm_refs=[],
diagnostic_class=None,
is_mandatory_raw=None,
is_executable_raw=None,
is_final_raw=None,
comparam_refs=[],
is_cyclic_raw=None,
is_multiple_raw=None,
addressing_raw=None,
transmission_mode_raw=None,
audience=None,
pre_condition_state_refs=[],
state_transition_refs=[],
request_ref=OdxLinkRef.from_id(somersault_requests["schroedinger"].odx_id),
semantic="ROUTINE",
pos_response_refs=[],
neg_response_refs=[],
functional_class_refs=[],
sdgs=[],
),
}

somersault_single_ecu_jobs = {
Expand Down Expand Up @@ -2113,9 +2233,11 @@ class SomersaultSID(IntEnum):
),
]

somersault_diag_data_dictionary_spec = DiagDataDictionarySpec(
somersault_base_diag_data_dictionary_spec = DiagDataDictionarySpec(
admin_data=None,
data_object_props=NamedItemList(somersault_dops.values()),
data_object_props=NamedItemList(
[x for x in somersault_dops.values() if x.short_name != "schroedinger_dop"] +
[somersault_dops["schroedinger_base"]]),
unit_spec=UnitSpec(
unit_groups=NamedItemList(somersault_unit_groups.values()),
units=NamedItemList(somersault_units.values()),
Expand All @@ -2135,6 +2257,40 @@ class SomersaultSID(IntEnum):
sdgs=[],
)

somersault_lazy_diag_data_dictionary_spec = DiagDataDictionarySpec(
admin_data=None,
data_object_props=NamedItemList([somersault_dops["schroedinger_lazy"]]),
unit_spec=None,
tables=NamedItemList(),
muxs=NamedItemList(),
env_datas=NamedItemList(),
env_data_descs=NamedItemList(),
dtc_dops=NamedItemList(),
structures=NamedItemList(),
static_fields=NamedItemList(),
end_of_pdu_fields=NamedItemList(),
dynamic_length_fields=NamedItemList(),
dynamic_endmarker_fields=NamedItemList(),
sdgs=[],
)

somersault_assiduous_diag_data_dictionary_spec = DiagDataDictionarySpec(
admin_data=None,
data_object_props=NamedItemList([somersault_dops["schroedinger_assiduous"]]),
unit_spec=None,
tables=NamedItemList(),
muxs=NamedItemList(),
env_datas=NamedItemList(),
env_data_descs=NamedItemList(),
dtc_dops=NamedItemList(),
structures=NamedItemList(),
static_fields=NamedItemList(),
end_of_pdu_fields=NamedItemList(),
dynamic_length_fields=NamedItemList(),
dynamic_endmarker_fields=NamedItemList(),
sdgs=[],
)

# diagnostics layer for the protocol
somersault_protocol_raw = ProtocolRaw(
variant_type=DiagLayerType.PROTOCOL,
Expand Down Expand Up @@ -2178,7 +2334,7 @@ class SomersaultSID(IntEnum):
admin_data=None,
company_datas=NamedItemList(),
functional_classes=NamedItemList(somersault_functional_classes.values()),
diag_data_dictionary_spec=somersault_diag_data_dictionary_spec,
diag_data_dictionary_spec=somersault_base_diag_data_dictionary_spec,
diag_comms_raw=[*somersault_services.values(), *somersault_single_ecu_jobs.values()],
requests=NamedItemList(somersault_requests.values()),
positive_responses=NamedItemList(somersault_positive_responses.values()),
Expand Down Expand Up @@ -2221,7 +2377,7 @@ class SomersaultSID(IntEnum):
admin_data=None,
company_datas=NamedItemList(),
functional_classes=NamedItemList(),
diag_data_dictionary_spec=None,
diag_data_dictionary_spec=somersault_lazy_diag_data_dictionary_spec,
diag_comms_raw=[],
requests=NamedItemList(),
positive_responses=NamedItemList(),
Expand Down Expand Up @@ -2445,22 +2601,7 @@ class SomersaultSID(IntEnum):
admin_data=None,
company_datas=NamedItemList(),
functional_classes=NamedItemList(),
diag_data_dictionary_spec=DiagDataDictionarySpec(
admin_data=None,
dtc_dops=NamedItemList(),
data_object_props=NamedItemList(),
static_fields=NamedItemList(),
structures=NamedItemList(),
end_of_pdu_fields=NamedItemList(),
dynamic_length_fields=NamedItemList(),
dynamic_endmarker_fields=NamedItemList(),
tables=NamedItemList(),
env_datas=NamedItemList(),
env_data_descs=NamedItemList(),
muxs=NamedItemList(),
unit_spec=None,
sdgs=[],
),
diag_data_dictionary_spec=somersault_assiduous_diag_data_dictionary_spec,
diag_comms_raw=list(somersault_assiduous_services.values()),
requests=NamedItemList(somersault_assiduous_requests.values()),
positive_responses=NamedItemList(somersault_assiduous_positive_responses.values()),
Expand All @@ -2476,7 +2617,7 @@ class SomersaultSID(IntEnum):
# this variant does everything which the base variant does
# and more
not_inherited_diag_comms=[],
not_inherited_dops=[],
not_inherited_dops=["schroedinger"],
not_inherited_variables=[],
not_inherited_tables=[],
not_inherited_global_neg_responses=[],
Expand Down
45 changes: 44 additions & 1 deletion odxtools/utils.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,50 @@
# SPDX-License-Identifier: MIT
import dataclasses
import re
from typing import Any, Dict
from typing import TYPE_CHECKING, Any, Dict, Optional

if TYPE_CHECKING:
from .database import Database
from .diaglayers.diaglayer import DiagLayer
from .snrefcontext import SnRefContext


def retarget_snrefs(database: "Database",
andlaus marked this conversation as resolved.
Show resolved Hide resolved
diag_layer: "DiagLayer",
context: Optional["SnRefContext"] = None) -> None:
"""Re-resolve the short name references reachable by a
DiagLayer to this DiagLayer

This implies that after the SNREFs have been retargeted, accessing
the resolved objects via a different diagnostic layer might not be
correct. E.g.: If the ECU variants "V1" and "V2" are derived from
the base variant "BV", BV defines a short name reference to a data
object property called "Foo" and V1 and V2 both define a "Foo"
DOP, the reference in the base variant to Foo ought to be resolved
differently depending on whether it is accessed via V1 or
V2. Since odxtools resolves all references ahead of time, a fixed
variant has to be chosen. This method allows to switch the variant
to another one.

"""
from .snrefcontext import SnRefContext

if context is None:
context = SnRefContext()

if context.database is None:
context.database = database
andlaus marked this conversation as resolved.
Show resolved Hide resolved

if context.diag_layer is None:
context.diag_layer = diag_layer

# retarget the objects "owned" by the layer itself
diag_layer._resolve_snrefs(context)

# retarget all parents of the current layer (if any)
if (parent_refs := getattr(diag_layer, "parent_refs", None)) is not None:
for pr in parent_refs:
retarget_snrefs(database, pr.layer, context)


def dataclass_fields_asdict(obj: Any) -> Dict[str, Any]:
Expand Down
2 changes: 1 addition & 1 deletion tests/test_odxtools.py
Original file line number Diff line number Diff line change
Expand Up @@ -207,7 +207,7 @@ def test_udsbinner(self) -> None:

service_groups = ecu.service_groups

self.assertEqual(len(service_groups._service_groups), 4)
self.assertEqual(len(service_groups._service_groups), 5)
self.assertEqual([s.short_name for s in service_groups[0x10]],
["session_start", "session_stop"])
self.assertEqual(service_groups[0x10].session_start.short_name, "session_start")
Expand Down
45 changes: 45 additions & 0 deletions tests/test_somersault.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@
from odxtools.exceptions import OdxError, odxrequire
from odxtools.loadfile import load_pdx_file
from odxtools.parameters.nrcconstparameter import NrcConstParameter
from odxtools.parameters.valueparameter import ValueParameter
from odxtools.utils import retarget_snrefs

odxdb = load_pdx_file("./examples/somersault.pdx")

Expand Down Expand Up @@ -126,6 +128,7 @@ def test_somersault_lazy(self) -> None:
"session_start",
"session_stop",
"tester_present",
"schroedinger",
},
)

Expand Down Expand Up @@ -352,6 +355,48 @@ def test_decode_response(self) -> None:
self.assertEqual(m.coding_object, pos_response)
self.assertEqual(m.param_dict, {"sid": 0xFA, "num_flips_done": 0x03, "sault_time": 255})

def test_retarget_snrefs(self) -> None:
base_variant = odxdb.base_variants.somersault
ecu_lazy = odxdb.ecu_variants.somersault_lazy
ecu_assiduous = odxdb.ecu_variants.somersault_assiduous

schroedinger_param_base = odxrequire(
base_variant.services.schroedinger.request).parameters.schroedinger_param
schroedinger_param_lazy = odxrequire(
ecu_lazy.services.schroedinger.request).parameters.schroedinger_param
schroedinger_param_assiduous = odxrequire(
ecu_assiduous.services.schroedinger.request).parameters.schroedinger_param

# the parameter object is the same regardless of how it has
# been accessed.
self.assertEqual(id(schroedinger_param_base), id(schroedinger_param_lazy))
self.assertEqual(id(schroedinger_param_base), id(schroedinger_param_assiduous))

# all Schroedinger parameters are the same, so let's skip the
# qualifier from here on out
schroedinger_param = schroedinger_param_base

# by default, the DOP referenced by the Schroedinger parameter
# is the one defined by the base variant
assert isinstance(schroedinger_param, ValueParameter)
self.assertEqual(schroedinger_param.dop.odx_id.local_id, "somersault.DOP.schroedinger_base")

# if we retarget all short name references towards the lazy or
# assiduous ECUs, the schroedinger parameter's DOP will change
# accordingly (note that the parameter always references a DOP
# called "schroedinger_dop" via SNREF but each diag layer
# redefines this DOP)
retarget_snrefs(odxdb, ecu_lazy)
self.assertEqual(schroedinger_param.dop.odx_id.local_id, "somersault.DOP.schroedinger_lazy")

retarget_snrefs(odxdb, ecu_assiduous)
self.assertEqual(schroedinger_param.dop.odx_id.local_id,
"somersault.DOP.schroedinger_assiduous")

# after the database is refreshed, it is back to the original state
odxdb.refresh()
self.assertEqual(schroedinger_param.dop.odx_id.local_id, "somersault.DOP.schroedinger_base")


class TestNavigation(unittest.TestCase):

Expand Down
Loading