ecu-tests/tests/hardware/test_e2e_mum_led_activate.py

119 lines
4.2 KiB
Python

"""End-to-end hardware test on the MUM (Melexis Universal Master).
Power the ECU via MUM's built-in power output, then activate the RGB LED via
the master-published ALM_Req_A frame (ID 0x0A) and verify the slave responds
on ALM_Status (ID 0x11).
Frame layout (from vendor/4SEVEN_color_lib_test.ldf, ALM_Req_A @ 0x0A, 8B):
byte 0 AmbLightColourRed (0..255)
byte 1 AmbLightColourGreen (0..255)
byte 2 AmbLightColourBlue (0..255)
byte 3 AmbLightIntensity (0..255)
byte 4 AmbLightUpdate (bits 0-1) | AmbLightMode (bits 2-7)
byte 5 AmbLightDuration
byte 6 AmbLightLIDFrom
byte 7 AmbLightLIDTo
The ECU answers ALM_Req_A only when AmbLightLIDFrom <= ALMNadNo <= LIDTo, so
we read the current NAD from ALM_Status first and target that NAD exactly.
"""
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]
ALM_REQ_A_ID = 0x0A
ALM_STATUS_ID = 0x11
DEFAULT_RGB = (0xFF, 0xFF, 0xFF)
DEFAULT_INTENSITY = 0xFF
def _build_alm_req_a_payload(
r: int, g: int, b: int,
intensity: int = DEFAULT_INTENSITY,
update: int = 0,
mode: int = 0,
duration: int = 0,
lid_from: int = 0x01,
lid_to: int = 0xFF,
) -> bytes:
"""Pack RGB+mode signals into the 8-byte ALM_Req_A payload."""
byte4 = (update & 0x03) | ((mode & 0x3F) << 2)
return bytes([
r & 0xFF, g & 0xFF, b & 0xFF,
intensity & 0xFF,
byte4 & 0xFF,
duration & 0xFF,
lid_from & 0xFF,
lid_to & 0xFF,
])
def test_mum_e2e_power_on_then_led_activate(config: EcuTestConfig, lin: LinInterface, 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.
Requirements: REQ-MUM-LED-ACTIVATE
Test Steps:
1. Skip unless interface.type == 'mum'
2. Read ALM_Status (0x11) and extract ALMNadNo (byte 0 lower 8 bits)
3. Build ALM_Req_A payload with RGB=(0xFF,0xFF,0xFF), intensity=0xFF,
targeting LIDFrom=LIDTo=current_nad
4. Publish ALM_Req_A via lin.send()
5. Re-read ALM_Status and assert it still returns a valid frame
Expected Result:
- First ALM_Status read returns a 4-byte frame with a NAD in 0x01..0xFE
- 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")
# Step 2: read current NAD from ALM_Status
status = lin.receive(id=ALM_STATUS_ID, timeout=1.0)
assert status is not None, "No ALM_Status received — check MUM/ECU wiring and power"
assert len(status.data) >= 1, f"ALM_Status too short: {status.data!r}"
current_nad = status.data[0]
rp("alm_status_data_hex", bytes(status.data).hex())
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
payload = _build_alm_req_a_payload(
*DEFAULT_RGB,
intensity=DEFAULT_INTENSITY,
lid_from=current_nad,
lid_to=current_nad,
)
rp("tx_id", f"0x{ALM_REQ_A_ID:02X}")
rp("tx_data_hex", payload.hex())
rp("rgb", list(DEFAULT_RGB))
rp("intensity", DEFAULT_INTENSITY)
lin.send(LinFrame(id=ALM_REQ_A_ID, data=payload))
# Step 5: confirm bus liveness after the activation frame
status_after = lin.receive(id=ALM_STATUS_ID, timeout=1.0)
rp("post_status_present", status_after is not None)
if status_after is not None:
rp("post_status_data_hex", bytes(status_after.data).hex())
assert status_after is not None, (
"ALM_Status not received after publishing ALM_Req_A — ECU may have reset"
)