"""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" )