"""Entry point to start N local Motor-CAD task executors (P3-M3 / P5-M2). Configurable via executor_config.json (see executor_config.py). This entry is kept for back-compat with the original --instances/--interval/ --mock CLI and delegates to the shared config loader + executor builder in run_task_executor. Run in background: python scripts/run_task_executor_parallel.py --instances 3 --interval 5 NOTE: All strings must be ASCII only. """ import argparse import os import sys import time _SCRIPTS_DIR = os.path.dirname(os.path.abspath(__file__)) if _SCRIPTS_DIR not in sys.path: sys.path.insert(0, _SCRIPTS_DIR) import executor_config # noqa: E402 from run_task_executor import setup_logging, build_executors # noqa: E402 def main(): parser = argparse.ArgumentParser( description="Start N local Motor-CAD executors") parser.add_argument("--instances", type=int, default=None, help="number of parallel executor instances " "(overrides config)") parser.add_argument("--interval", type=int, default=None, help="poll interval in seconds (overrides config)") parser.add_argument("--mock", action="store_true", help="use mock solver (no Motor-CAD) for testing") parser.add_argument("--config", default=None, help="path to executor_config.json") args = parser.parse_args() cfg = executor_config.load_config(cli_path=args.config) if args.instances is not None: cfg["instances"] = args.instances if args.interval is not None: cfg["poll_interval"] = args.interval if args.mock: cfg["enable_mock"] = True executor_config.validate_config(cfg) if cfg["instances"] < 1: parser.error("--instances must be >= 1") log_file = setup_logging(cfg["log_dir"], cfg["log_level"]) print("N=%d executors. Model=%s" % (cfg["instances"], cfg["model_path"]), flush=True) print("Web base URL: %s" % cfg["web_base_url"], flush=True) print("Log: %s" % log_file, flush=True) print("Ctrl+C to stop.", flush=True) executors = build_executors(cfg) threads = [] for ex in executors: threads.append(ex.start_polling(interval=int(cfg["poll_interval"]))) print("Executor started: %s" % ex.executor_id, flush=True) try: while any(t.is_alive() for t in threads): time.sleep(1) except KeyboardInterrupt: for ex in executors: ex.stop() ex.cleanup() print("All executors stopped.", flush=True) if __name__ == "__main__": main()