Skip to content

Commit

Permalink
Merge pull request #374 from andlaus/retarget_snrefs
Browse files Browse the repository at this point in the history
Add a `retarget_snrefs()` utility function
  • Loading branch information
andlaus authored Jan 16, 2025
2 parents ebd9e9b + a8ba263 commit 30fb09b
Show file tree
Hide file tree
Showing 6 changed files with 252 additions and 23 deletions.
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",
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

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 @@ -218,7 +218,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

0 comments on commit 30fb09b

Please sign in to comment.