119 lines
4.2 KiB
Python
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"
|
|
)
|