CARLA ? C-Shenron based Simualtor for Sensor data generation.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 

138 lines
4.9 KiB

"""
dashboard/app.py
----------------
Flask backend for the BATL CARLA Orchestrator Dashboard.
Serves the HTML UI, exposes REST endpoints, and streams
real-time simulation output via Server-Sent Events (SSE).
NOTE: This file lives inside the /dashboard sub-folder.
PROJECT_ROOT is therefore TWO levels up: dashboard/app.py → dashboard/ → Fox/
"""
import os
import sys
from flask import Flask, render_template, request, jsonify, Response
import subprocess
# ------------------------------------------------------------------
# Path bootstrapping — make the project root importable regardless
# of which directory the user launches from.
# ------------------------------------------------------------------
PROJECT_ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
if PROJECT_ROOT not in sys.path:
sys.path.insert(0, PROJECT_ROOT)
import config
from src.scenario_loader import list_scenarios
# ------------------------------------------------------------------
# Flask app — templates and static assets are siblings of this file.
# ------------------------------------------------------------------
DASHBOARD_DIR = os.path.dirname(os.path.abspath(__file__))
app = Flask(
__name__,
template_folder=os.path.join(DASHBOARD_DIR, "templates"),
static_folder=os.path.join(DASHBOARD_DIR, "static"),
)
# ── Routes ────────────────────────────────────────────────────────
@app.route("/")
def index():
return render_template("index.html")
@app.route("/api/config", methods=["GET"])
def get_config():
scenarios = list_scenarios()
return jsonify({
"scenarios": scenarios,
"default_scenario": getattr(config, "DEFAULT_SCENARIO", "braking"),
"default_frames": getattr(config, "MAX_FRAMES", 200),
"default_weather": getattr(config, "DEFAULT_WEATHER", "ClearNoon"),
"weather_options": [
"ClearNoon", "CloudyNoon", "WetNoon",
"SoftRainNoon", "HardRainNoon", "ClearSunset", "Night"
],
})
@app.route("/api/scenario_params/<scenario_name>", methods=["GET"])
def get_scenario_params(scenario_name):
from src.scenario_loader import load_scenario
try:
scenario = load_scenario(scenario_name)
tunable_params = {}
# Convention: tunable constants are UPPERCASE class-level attributes
for attr in dir(scenario):
if attr.isupper() and not attr.startswith("_"):
val = getattr(scenario, attr)
if isinstance(val, (int, float, str, bool)):
tunable_params[attr] = val
return jsonify({"success": True, "params": tunable_params})
except Exception as e:
return jsonify({"success": False, "error": str(e)})
@app.route("/api/run", methods=["POST"])
def run_simulation():
data = request.json or {}
scenario = data.get("scenario") or "braking"
frames = data.get("frames") or ""
weather = data.get("weather") or ""
no_record = bool(data.get("no_record"))
params = data.get("params") or ""
# Build the command — run.bat lives in PROJECT_ROOT
cmd = ["cmd.exe", "/c", "run.bat", scenario]
if frames:
cmd.extend(["--frames", str(frames)])
if weather:
cmd.extend(["--weather", weather])
if no_record:
cmd.append("--no-record")
if params:
cmd.extend(["--params", f'"{params}"'])
def generate():
try:
# cwd = project root so that run.bat is reachable
env = os.environ.copy()
env["PYTHONUNBUFFERED"] = "1"
process = subprocess.Popen(
cmd,
stdout=subprocess.PIPE,
stderr=subprocess.STDOUT,
text=True,
bufsize=1,
cwd=PROJECT_ROOT,
env=env,
creationflags=subprocess.CREATE_NEW_PROCESS_GROUP,
)
yield f"data: [INFO] Starting command: {' '.join(cmd)}\n\n"
for line in iter(process.stdout.readline, ""):
if line:
yield f"data: {line.rstrip()}\n\n"
process.stdout.close()
return_code = process.wait()
yield f"data: [PROCESS_COMPLETED] Exit Code: {return_code}\n\n"
except Exception as e:
yield f"data: [ERROR] Failed to start process: {str(e)}\n\n"
return Response(generate(), mimetype="text/event-stream")
# ── Entry point ───────────────────────────────────────────────────
if __name__ == "__main__":
print("Starting BATL CARLA Dashboard orchestrator...")
print(f" Project root : {PROJECT_ROOT}")
print(f" Dashboard dir: {DASHBOARD_DIR}")
app.run(host="0.0.0.0", port=5000, debug=True)