""" src/main.py ----------- Entry point for the Fox CARLA ADAS simulation pipeline. This file is now a thin CLI wrapper around the PipelineManager. It parses arguments, builds a PipelineContext, and executes the stage sequence: Simulation → Shenron → MCAP. Usage ----- python src/main.py --scenario braking python src/main.py --scenario cutin --frames 120 python src/main.py --list-scenarios python src/main.py --only-mcap --session data/braking_20260423_093000 python src/main.py --skip-shenron This file never changes when new scenarios are added. All scenario logic lives in scenarios/.py. """ import sys import os from pathlib import Path import argparse # Add project root to sys.path sys.path.append(str(Path(__file__).resolve().parents[1])) import config from scenario_loader import list_scenarios # ----------------------------------------------------------------------- # CLI # ----------------------------------------------------------------------- def parse_args(): parser = argparse.ArgumentParser( description="CARLA ADAS Simulation — stage-based pipeline runner" ) parser.add_argument( "--scenario", "-s", type=str, default=getattr(config, "DEFAULT_SCENARIO", "braking"), help="Scenario name to run (e.g. braking, cutin, obstacle)" ) parser.add_argument( "--frames", "-f", type=int, default=None, help="Override config.MAX_FRAMES for this run" ) parser.add_argument( "--spawn-point", "-p", type=int, default=None, help="Index of the spawn point to use for ego vehicle" ) parser.add_argument( "--no-record", action="store_true", help="Disable image and metadata recording for faster iteration" ) parser.add_argument( "--weather", "-w", type=str, default=None, help="Weather preset (ClearNoon, Rain, etc.). " "If omitted, scenario or config default is used." ) parser.add_argument( "--params", type=str, default=None, help="Scenario parameters as key=val pairs, " "e.g. 'BRAKE_FRAME=100,SPEED=50'" ) parser.add_argument( "--list-scenarios", "-l", action="store_true", help="Print available scenarios and exit" ) # --- Pipeline Stage Control --- parser.add_argument( "--skip-sim", action="store_true", help="Skip the simulation stage (requires --session)" ) parser.add_argument( "--skip-shenron", action="store_true", help="Skip the Shenron radar synthesis stage" ) parser.add_argument( "--skip-mcap", action="store_true", help="Skip the MCAP conversion stage" ) parser.add_argument( "--only-mcap", action="store_true", help="Run only the MCAP stage (requires --session)" ) parser.add_argument( "--only-shenron", action="store_true", help="Run only the Shenron stage (requires --session)" ) parser.add_argument( "--session", type=str, default=None, help="Path to an existing session folder. Required when " "skipping the simulation stage." ) return parser.parse_args() # ----------------------------------------------------------------------- # Main # ----------------------------------------------------------------------- def main(): args = parse_args() # ------------------------------------------------------------------ # 0. Clean up stale stop flags # ------------------------------------------------------------------ flag_path = os.path.join(os.path.dirname(__file__), "..", "tmp", "stop.flag") if os.path.exists(flag_path): try: os.remove(flag_path) print("[INFO] Cleaned up stale stop.flag") except Exception: pass if args.list_scenarios: print("Available scenarios:") for s in list_scenarios(): print(f" - {s}") return # ------------------------------------------------------------------ # 1. Build PipelineContext # ------------------------------------------------------------------ from pipeline.base import PipelineContext ctx = PipelineContext( scenario_name=args.scenario, args=args, ) # Populate session_path if provided via CLI (for --skip-sim modes) if args.session: session = Path(args.session) if session.exists(): ctx.session_path = session print(f"[INFO] Using existing session: {session}") else: print(f"[ERROR] Session path does not exist: {args.session}") return # Determine which stages to skip if args.only_mcap: ctx.skip_stages = ["simulation", "shenron"] elif args.only_shenron: ctx.skip_stages = ["simulation", "mcap"] else: if args.skip_sim: ctx.skip_stages.append("simulation") if args.skip_shenron: ctx.skip_stages.append("shenron") if args.skip_mcap: ctx.skip_stages.append("mcap") # Validate: if sim is skipped, session_path must be provided if "simulation" in ctx.skip_stages and ctx.session_path is None: print("[ERROR] --session is required when skipping the simulation " "stage (--skip-sim, --only-mcap, --only-shenron).") return # ------------------------------------------------------------------ # 2. Build & Execute Pipeline # ------------------------------------------------------------------ from pipeline.manager import PipelineManager from pipeline.stages.sim_stage import SimulationStage from pipeline.stages.shenron_stage import ShenronStage from pipeline.stages.mcap_stage import McapStage from pipeline.stages.video_stage import VideoStage manager = PipelineManager([ SimulationStage(), ShenronStage(), McapStage(), VideoStage(), ]) manager.execute(ctx) print("[INFO] Done") if __name__ == "__main__": main()