#!/usr/bin/env bash # Replay a recorded session's CAN frames through the decoder, so displays react as # if the car were live. Runs on the car Orin (sudo: vcan and the decoder service). # # usage: scripts/replay-session.sh SESSION_DIR [SKIP_S] # SESSION_DIR e.g. /var/lib/yukart/recordings/000001-20261013T170050 # SKIP_S seconds of the recording to skip first (default 0) # # Frames go onto a virtual interface, vcan_replay, never can_pcan: the recorder only # watches can_pcan, so a replay is never recorded as a drive. The decoder service is # stopped for the replay (one MQTT client id) and a decoder runs on vcan_replay instead; # however the replay ends (finished, Ctrl-C, killed), the service comes back and the # interface goes. Values carry the replay's own timing; nothing marks them as replayed. set -euo pipefail SESSION="${1:?usage: replay-session.sh SESSION_DIR [SKIP_S]}" SKIP="${2:-0}" IF=vcan_replay ls "$SESSION"/can-*.log* >/dev/null cleanup() { [ -n "${DEC:-}" ] && kill "$DEC" 2>/dev/null || true sudo ip link del "$IF" 2>/dev/null || true sudo systemctl start yukart-decoder echo "replay: done; yukart-decoder back on can_pcan" } trap cleanup EXIT INT TERM sudo modprobe vcan sudo ip link add "$IF" type vcan 2>/dev/null || true sudo ip link set "$IF" up sudo systemctl stop yukart-decoder /usr/local/bin/yukart-decoder --can "$IF" & DEC=$! sleep 1 echo "replay: $SESSION from +${SKIP} s onto $IF" # candump -L lines: "(1791878450.945008) can_pcan 139#...". Skip by the first frame's time. zstdcat -f "$SESSION"/can-*.log* 2>/dev/null \ | awk -v skip="$SKIP" '{ t = substr($1, 2, length($1) - 2) + 0; if (NR == 1) t0 = t; if (t - t0 >= skip) print }' \ | canplayer "$IF=can_pcan"