#!/usr/bin/env python3 import argparse from flash_pair import flash_pair from hil_common import PairCapture, load_config, main_guard, new_artifact_dir, pair_ports from report import generate def main() -> int: parser = argparse.ArgumentParser(description="Build, flash, capture, and verify one DD3 HIL run") parser.add_argument("--yes-flash", action="store_true") parser.add_argument("--seconds", type=float) parser.add_argument("--fault", choices=["drop_chunk", "duplicate_chunk", "corrupt_chunk", "suppress_ack", "delay_ack", "wrong_ack_batch"]) parser.add_argument("--fault-after", type=float, default=40.0) args = parser.parse_args() config = load_config() ports = pair_ports(config, "flash") artifact = new_artifact_dir("baseline" if not args.fault else f"fault-{args.fault}") flash_pair(artifact, args.yes_flash) baud = int(config.get("hil", {}).get("baud", 115200)) seconds = args.seconds or float(config.get("hil", {}).get("capture_seconds", 180)) sent = False def tick(elapsed, capture): nonlocal sent if args.fault and not sent and elapsed >= args.fault_after: target = "sender" if args.fault.endswith("chunk") else "receiver" command = "delay_ack:1000" if args.fault == "delay_ack" else args.fault capture.send_fault(target, command) sent = True PairCapture(ports, baud, artifact).run(seconds, tick, reset_on_start=True) checks = generate(artifact, args.fault) print(f"HIL report: {artifact / 'report.md'}") return 1 if any(check.status == "fail" for check in checks) else 0 if __name__ == "__main__": main_guard(main)