ecu-tests/docs/04_lin_interface_call_flow.md

4.4 KiB
Raw Blame History

LIN Interface Call Flow

This document explains how LIN operations flow through the abstraction for the Mock, MUM, and legacy BabyLIN adapters.

Contract (base)

File: ecu_framework/lin/base.py

  • connect() / disconnect()
  • send(frame: LinFrame)
  • receive(id: int | None = None, timeout: float = 1.0) -> LinFrame | None
  • request(id: int, length: int, timeout: float = 1.0) -> LinFrame | None
  • flush()

LinFrame validates:

  • ID is 0x000x3F (6-bit LIN ID)
  • Data length ≤ 8 bytes

Mock adapter flow

File: ecu_framework/lin/mock.py

  • connect(): initialize buffers and state
  • send(frame): enqueues the frame and (for echo behavior) schedules it for RX
  • receive(timeout): waits up to timeout for a frame in RX buffer
  • request(id, length, timeout): synthesizes a deterministic response of the given length for predictability
  • disconnect(): clears state

Use cases:

  • Fast local dev, deterministic responses, no hardware
  • Timeout and boundary behavior validation

MUM adapter flow (Melexis Universal Master)

File: ecu_framework/lin/mum.py

The MUM is a networked LIN master (default IP 192.168.7.2) with built-in power control on power_out0. It is master-driven: there is no passive listen — to read a slave-published frame, the master triggers a header on that frame ID. Diagnostic frames (BSM-SNPD, service ID 0xB5) require LIN 1.x Classic checksum and are sent through the transport layer's ld_put_raw, not the regular send_message.

  • connect(): lazy-imports pymumclient + pylin; opens MUM (MelexisUniversalMaster.open_all(host)), gets the LIN device (linmaster) and power device (power_control), runs linmaster.setup(), builds LinBusManager + LinDevice22, sets lin_dev.baudrate, fetches the transport layer (get_device("bus/transport_layer")), and finally power_control.power_up() followed by a boot_settle_seconds sleep
  • send(frame): lin_dev.send_message(master_to_slave=True, frame_id, data_length, data)
  • receive(id, timeout): lin_dev.send_message(master_to_slave=False, frame_id=id, data_length=frame_lengths.get(id, default_data_length)) — pylin returns the response bytes (or raises on timeout, which we treat as None). id=None raises NotImplementedError because the MUM cannot listen passively.
  • disconnect(): best-effort power_control.power_down() followed by linmaster.teardown()
  • MUM-only extras: send_raw(bytes) (Classic checksum via ld_put_raw), power_up(), power_down(), power_cycle(wait)

Configuration:

  • interface.host is required; interface.lin_device and interface.power_device default to MUM conventions
  • interface.bitrate is the actual LIN baudrate the MUM drives
  • interface.frame_lengths lets you map slave frame IDs to their fixed data lengths so receive(id) can fetch the correct number of bytes; built-in defaults cover ALM_Status (4) and ALM_Req_A (8)

BabyLIN adapter flow (SDK wrapper)

File: ecu_framework/lin/babylin.py

  • connect(): import SDK BabyLIN_library.py, discover ports, open first, optionally BLC_loadSDF, get channel handle, and BLC_sendCommand("start schedule N;")
  • send(frame): calls BLC_mon_set_xmit(channelHandle, frameId, data, slotTime=0)
  • receive(timeout): calls BLC_getNextFrameTimeout(channelHandle, timeout_ms) and converts returned BLC_FRAME to LinFrame
  • request(id, length, timeout): prefers BLC_sendRawMasterRequest(channel, id, length); falls back to (channel, id, bytes); if unavailable, sends a header and waits on receive()
  • disconnect(): calls BLC_closeAll()
  • Error handling: uses BLC_getDetailedErrorString (if available)

Configuration:

  • interface.sdf_path locates the SDF to load
  • interface.schedule_nr sets the schedule to start upon connect
  • interface.channel selects the channel index

Edge considerations

  • Ensure the correct architecture (x86/x64) of the DLL matches Python
  • Channel/bitrate must match your network configuration
  • Some SDKs require initialization/scheduling steps before transmit/receive
  • Time synchronization and timestamp units vary per SDK — convert as needed

Note on master requests:

  • Our mock wrapper returns a deterministic byte pattern when called with the length signature.
  • When only the bytes signature is available, zeros of the requested length are used in tests.