ecu-tests/vendor/automated_lin_test/test_led_control.py

231 lines
7.3 KiB
Python

#!/usr/bin/env python3
"""
LIN LED Control Test
This test verifies LIN communication by controlling the LED on the board.
It will fade through different colors (Red, Green, Blue) to verify that
frames are being received correctly.
Frame structure (ALM_Req_A, ID=0x0A, 8 bytes):
- Byte 0: AmbLightColourRed (0-255)
- Byte 1: AmbLightColourGreen (0-255)
- Byte 2: AmbLightColourBlue (0-255)
- Byte 3: AmbLightIntensity (0-255)
- Byte 4: AmbLightUpdate[1:0] | (AmbLightMode[5:0] << 2)
- Byte 5: AmbLightDuration (0-255)
- Byte 6: AmbLightLIDFrom (NAD range start — set equal to LIDTo to target one node)
- Byte 7: AmbLightLIDTo (NAD range end)
"""
import argparse
import logging
import time
import math
from pylin import LinBusManager, LinDevice22
from pymumclient import MelexisUniversalMaster
from config import *
logging.basicConfig(
level=logging.INFO,
format='%(asctime)-15s %(levelname)-8s %(message)s'
)
logger = logging.getLogger(__name__)
def read_alm_status(lin_dev):
"""Read ALM_Status frame and return (ALMNadNo, raw_bytes)."""
try:
response = lin_dev.send_message(
master_to_slave=False,
frame_id=ALM_STATUS_FRAME['frame_id'],
data_length=ALM_STATUS_FRAME['length'],
data=None
)
if response and len(response) >= ALM_STATUS_FRAME['length']:
parsed = unpack_frame(ALM_STATUS_FRAME, response)
return parsed['ALMNadNo'], response
return None, None
except Exception as e:
logger.error(f"Failed to read ALM_Status: {e}")
return None, None
def set_led_color(lin_dev, nad, red, green, blue, intensity,
update=0, mode=0, duration=0):
"""
Set LED color and intensity via ALM_Req_A frame.
The node responds only if AmbLightLIDFrom <= ALMNadNo <= AmbLightLIDTo.
Setting both to the same NAD targets a single node.
"""
try:
data = pack_frame(ALM_REQ_A_FRAME,
AmbLightColourRed=red,
AmbLightColourGreen=green,
AmbLightColourBlue=blue,
AmbLightIntensity=intensity,
AmbLightUpdate=update,
AmbLightMode=mode,
AmbLightDuration=duration,
AmbLightLIDFrom=nad,
AmbLightLIDTo=nad,
)
lin_dev.send_message(
master_to_slave=True,
frame_id=ALM_REQ_A_FRAME['frame_id'],
data_length=ALM_REQ_A_FRAME['length'],
data=data,
)
return True
except Exception as e:
logger.error(f"Failed to set LED color: {e}")
return False
def fade_test(lin_dev, nad, duration_per_color=5.0):
"""
Fade through Red, Green, and Blue colors.
Args:
lin_dev: LinDevice22 instance
nad: Node address
duration_per_color: How long to fade each color (seconds)
"""
colors = [
("Red", 255, 0, 0),
("Green", 0, 255, 0),
("Blue", 0, 0, 255),
]
steps = 50 # Number of fade steps
delay = duration_per_color / steps
for color_name, r_max, g_max, b_max in colors:
logger.info(f"Fading {color_name}...")
# Fade in
for step in range(steps + 1):
progress = step / steps
# Use sine wave for smoother fade
brightness = math.sin(progress * math.pi / 2)
red = int(r_max * brightness)
green = int(g_max * brightness)
blue = int(b_max * brightness)
intensity = int(100 * brightness)
set_led_color(lin_dev, nad, red, green, blue, intensity)
time.sleep(delay)
# Fade out
for step in range(steps, -1, -1):
progress = step / steps
brightness = math.sin(progress * math.pi / 2)
red = int(r_max * brightness)
green = int(g_max * brightness)
blue = int(b_max * brightness)
intensity = int(100 * brightness)
set_led_color(lin_dev, nad, red, green, blue, intensity)
time.sleep(delay)
def main():
parser = argparse.ArgumentParser(description='LIN LED Control Test')
parser.add_argument('--host', default=MUM_HOST,
help=f'MUM IP address (default: {MUM_HOST})')
parser.add_argument('--nad', type=lambda x: int(x,0), default=LED_DEFAULT_NAD,
help=f'Node address to control (default: 0x{LED_DEFAULT_NAD:02X})')
parser.add_argument('--cycles', type=int, default=3,
help='Number of fade cycles (default: 3)')
parser.add_argument('--duration', type=float, default=3.0,
help='Duration per color in seconds (default: 3.0)')
args = parser.parse_args()
try:
logger.info(f"Connecting to MUM at {args.host}...")
# Setup MUM and LIN
mum = MelexisUniversalMaster()
mum.open_all(args.host)
power_control = mum.get_device(MUM_POWER_DEVICE)
linmaster = mum.get_device(MUM_LIN_DEVICE)
linmaster.setup()
lin_bus = LinBusManager(linmaster)
lin_dev = LinDevice22(lin_bus)
lin_dev.baudrate = LIN_BAUDRATE
lin_dev.nad = args.nad
power_control.power_up()
time.sleep(0.5)
logger.info("MUM connected and LIN bus ready")
logger.info("=" * 70)
# Read current NAD
logger.info("Reading current NAD from ALM_Status...")
current_nad, status_data = read_alm_status(lin_dev)
if current_nad is not None:
data_hex = ' '.join(f'{b:02X}' for b in status_data)
logger.info(f"Current NAD: 0x{current_nad:02X}")
logger.info(f"Full status data: {data_hex}")
else:
logger.warning("Could not read NAD, using command-line NAD")
current_nad = args.nad
logger.info("=" * 70)
logger.info(f"LED FADE TEST")
logger.info(f"Controlling NAD: 0x{current_nad:02X}")
logger.info(f"LIDFrom: 0x{current_nad:02X}, LIDTo: 0x{current_nad:02X}")
logger.info(f"Fade cycles: {args.cycles}")
logger.info(f"Duration per color: {args.duration}s")
logger.info("=" * 70)
# Turn LED off initially
logger.info("Turning LED off...")
set_led_color(lin_dev, current_nad, 0, 0, 0, 0)
time.sleep(1.0)
# Run fade test
for cycle in range(1, args.cycles + 1):
logger.info(f"\nCycle {cycle}/{args.cycles}")
fade_test(lin_dev, current_nad, args.duration)
if cycle < args.cycles:
logger.info("Pausing between cycles...")
time.sleep(1.0)
# Turn LED off at the end
logger.info("\nTurning LED off...")
set_led_color(lin_dev, current_nad, 0, 0, 0, 0)
logger.info("=" * 70)
logger.info("✓ LED TEST COMPLETED")
logger.info("=" * 70)
logger.info("Tearing down...")
linmaster.teardown()
logger.info("Done (ECU still powered)")
except KeyboardInterrupt:
logger.info("")
logger.info("Interrupted by user")
logger.info("Turning LED off...")
try:
set_led_color(lin_dev, args.nad, 0, 0, 0, 0)
linmaster.teardown()
except:
pass
except Exception as e:
logger.error(f"Error: {e}", exc_info=True)
if __name__ == "__main__":
main()