"""Convert a gateway session log (t, direction, arbitration_id, is_extended, dlc, data_hex, relayed) into a plain capture CSV (timestamp_ms, arbitration_id, is_extended, dlc, data_hex) that replay_source.py / gateway/replay_to_bus.py can play back. Usage: python tools/gateway_log_to_capture.py captures/gateway_20260829_164540.csv \\ --direction car->eps --out captures/replay_car_to_eps.csv Only rows that were actually relayed are kept by default (--include-blocked to keep everything, e.g. to inspect what was withheld). """ import argparse import csv import sys from pathlib import Path def main() -> int: parser = argparse.ArgumentParser(description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter) parser.add_argument("gateway_log", help="Gateway session CSV to convert") parser.add_argument("--direction", default="car->eps", choices=["car->eps", "eps->car"]) parser.add_argument("--out", required=True, help="Output capture CSV path") parser.add_argument("--include-blocked", action="store_true", help="Also keep frames that were NOT relayed") args = parser.parse_args() src = Path(args.gateway_log) out = Path(args.out) written = 0 with open(src, newline="") as fin, open(out, "w", newline="") as fout: reader = csv.DictReader(fin) writer = csv.writer(fout) writer.writerow(["timestamp_ms", "arbitration_id", "is_extended", "dlc", "data_hex"]) for row in reader: if row["direction"] != args.direction: continue if not args.include_blocked and row["relayed"] != "1": continue writer.writerow( [ round(float(row["t"]) * 1000), row["arbitration_id"], row["is_extended"], row["dlc"], row["data_hex"], ] ) written += 1 print(f"Wrote {written} frames ({args.direction}) to {out}", file=sys.stderr) return 0 if __name__ == "__main__": raise SystemExit(main())