ecu-tests/tests/hardware/test_e2e_mum_led_activate.py

94 lines
3.7 KiB
Python

"""End-to-end hardware test on the MUM (Melexis Universal Master).
Powers the ECU via MUM's built-in power output, reads ALM_Status to discover
the slave's NAD, then activates the RGB LED via the master-published
ALM_Req_A frame targeting that NAD with full white at full intensity. Frame
layouts are taken from the LDF at runtime via the `ldf` fixture, so signal
names and bit positions stay in sync with `vendor/4SEVEN_color_lib_test.ldf`
without manual byte building.
"""
from __future__ import annotations
import pytest
from ecu_framework.config import EcuTestConfig
from ecu_framework.lin.base import LinFrame, LinInterface
pytestmark = [pytest.mark.hardware, pytest.mark.mum]
def test_mum_e2e_power_on_then_led_activate(
config: EcuTestConfig, lin: LinInterface, ldf, rp
):
"""
Title: MUM E2E - Power ECU, Read NAD, Activate RGB LED
Description:
Drives the full hardware path through the Melexis Universal Master:
the `lin` fixture has already powered the ECU via power_out0 and set
up the LIN bus. This test reads ALM_Status to discover the slave's
NAD, publishes ALM_Req_A targeting that NAD with full white at full
intensity, and re-reads ALM_Status to confirm the bus is alive.
Frame layouts come from the LDF database, not hand-coded byte
positions.
Requirements: REQ-MUM-LED-ACTIVATE
Test Steps:
1. Skip unless interface.type == 'mum'
2. Read ALM_Status; decode signals via the LDF; extract ALMNadNo
3. Build the ALM_Req_A payload via ldf.frame("ALM_Req_A").pack(...),
targeting LIDFrom=LIDTo=current_nad with full-white RGB
4. Publish ALM_Req_A via lin.send()
5. Re-read ALM_Status and confirm the bus still returns a valid frame
Expected Result:
- First ALM_Status decode yields ALMNadNo in 0x01..0xFE
- lin.send() of the LDF-packed frame succeeds
- Second ALM_Status read returns a frame (bus still alive after Tx)
"""
if config.interface.type != "mum":
pytest.skip("interface.type must be 'mum' for this test")
req_a = ldf.frame("ALM_Req_A")
status = ldf.frame("ALM_Status")
rp("ldf_path", str(ldf.path))
rp("req_a_id", f"0x{req_a.id:02X}")
rp("status_id", f"0x{status.id:02X}")
# Step 2: read ALM_Status and decode it via the LDF.
rx = lin.receive(id=status.id, timeout=1.0)
assert rx is not None, "No ALM_Status received — check MUM/ECU wiring and power"
decoded = status.unpack(bytes(rx.data))
current_nad = int(decoded["ALMNadNo"])
rp("alm_status_decoded", decoded)
rp("current_nad", f"0x{current_nad:02X}")
assert 0x01 <= current_nad <= 0xFE, (
f"ALMNadNo {current_nad:#x} is out of valid range; ECU may be unconfigured"
)
# Step 3 + 4: target the discovered NAD with full white at full intensity.
payload = req_a.pack(
AmbLightColourRed=0xFF,
AmbLightColourGreen=0xFF,
AmbLightColourBlue=0xFF,
AmbLightIntensity=0xFF,
AmbLightUpdate=0, # 0 = Immediate color update
AmbLightMode=0, # 0 = Immediate Setpoint
AmbLightDuration=0,
AmbLightLIDFrom=current_nad,
AmbLightLIDTo=current_nad,
)
rp("tx_data_hex", payload.hex())
lin.send(LinFrame(id=req_a.id, data=payload))
# Step 5: confirm bus liveness after the activation frame.
rx_after = lin.receive(id=status.id, timeout=1.0)
rp("post_status_present", rx_after is not None)
if rx_after is not None:
rp("post_status_decoded", status.unpack(bytes(rx_after.data)))
assert rx_after is not None, (
"ALM_Status not received after publishing ALM_Req_A — ECU may have reset"
)