ScenarioManager is a web editor and execution service for connected autonomous vehicle scenarios. It combines a React editor, a FastAPI backend, PostgreSQL, CARLA, and the bundled OpenCDA runtime.
| Editor | Object settings |
|---|---|
![]() |
![]() |
| Simulation settings | Export menu |
![]() |
![]() |
- Edit roads, vehicles, RSUs, obstacles, weather, and attack scenarios.
- Validate scenarios before export or simulation.
- Export OpenCDA YAML, OpenSCENARIO, OpenDRIVE, and CARLA Python scripts.
- Start and monitor OpenCDA simulations through REST and WebSocket APIs.
- Store scenarios in PostgreSQL and expose run artifacts through the backend.
- Evaluate routes, collisions, localization, communication, and attack effects.
| Area | Technologies |
|---|---|
| Frontend | React, TypeScript, Vite, Zustand, MapLibre GL, Three.js |
| Backend | Python, FastAPI, Pydantic, SQLAlchemy, Alembic |
| Simulation | CARLA 0.9.16, OpenCDA, SUMO integration |
| Storage | PostgreSQL 17 |
| Tooling | Docker Compose, uv, Yarn, Vitest, Playwright, pytest, Ruff |
- Docker Engine with Docker Compose.
- CARLA 0.9.16 for running simulations.
- Python 3.11 or 3.12 and uv for local backend work.
- Node.js 22 with Corepack for local frontend work.
-
Create the environment file and set a real database password:
cp .env.example .env
-
Prepare the evaluation directory for the backend container user:
mkdir -p evaluation_outputs sudo chown -R 10001:10001 evaluation_outputs sudo chmod -R a+rX evaluation_outputs
The backend runs as UID/GID
10001and needs write access. Artifacts must remain readable by all users because the rootless frontend container mounts the same directory read-only. -
Start CARLA on the host configured by
CARLA_HOSTandCARLA_PORT. -
Build and start the complete system:
docker compose --profile prod up --build -d
-
Check service status:
docker compose ps
Open the frontend at http://localhost, the API at http://localhost:8000, and the OpenAPI documentation at http://localhost:8000/docs.
The backend waits for PostgreSQL and applies Alembic migrations automatically
because Compose sets RUN_MIGRATIONS=1.
| Command | Description |
|---|---|
docker compose --profile prod up --build -d |
Build and start frontend, backend, and PostgreSQL. |
docker compose up --build backend |
Start backend and PostgreSQL in the foreground. |
docker compose ps |
Show service and health status. |
docker compose logs -f backend |
Follow backend and simulation process logs. |
docker compose logs -f db |
Follow PostgreSQL logs. |
docker compose restart backend |
Restart only the backend service. |
docker compose exec backend alembic current |
Show the applied database revision. |
docker compose exec backend alembic upgrade head |
Apply all pending migrations manually. |
docker build --target test --tag scenario-manager-backend-test . |
Build the isolated backend test image used by CI. |
docker run --rm scenario-manager-backend-test |
Run the backend pytest suite in that image. |
docker compose stop |
Stop services without removing containers. |
docker compose down |
Stop and remove service containers. |
docker compose down -v |
Also delete database and evaluation volumes. |
Use curl http://localhost:8000/health for a direct backend health check.
Install the core dependencies:
uv syncInclude CARLA and the full simulation stack when needed:
uv sync --extra simulationInstall the repository hook once after dependency installation:
uv run pre-commit installIt formats and checks only staged Python, TypeScript, and SCSS files. Run the
same checks for the whole repository with uv run pre-commit run --all-files.
| Command | Description |
|---|---|
uv run pytest tests/backend -q |
Run backend tests. |
uv run ruff check app tests migrations main.py |
Run Python lint checks. |
uv run alembic upgrade head |
Apply migrations from the local environment. |
uv run alembic current |
Show the current database revision. |
make dev |
Shortcut for starting backend and PostgreSQL with Compose. |
make test |
Shortcut for backend tests. |
Local Alembic commands require a database reachable with the values from
.env. The standard Compose database is intentionally available only inside
the Compose network, so migration execution normally happens in the backend
container.
Install dependencies and start Vite:
corepack enable
yarn --cwd frontend install --frozen-lockfile
yarn --cwd frontend devThe development UI is available at http://localhost:5173. Set
VITE_API_URL in frontend/.env.development when the backend is not available
at its default address.
| Command | Description |
|---|---|
yarn --cwd frontend build |
Build the production frontend. |
yarn --cwd frontend test |
Run Vitest once. |
yarn --cwd frontend test:watch |
Run Vitest in watch mode. |
yarn --cwd frontend test:e2e |
Run Playwright end-to-end tests. |
yarn --cwd frontend lint |
Run ESLint. |
yarn --cwd frontend lint:css |
Fix Stylelint issues. |
yarn --cwd frontend format |
Format frontend files with Prettier. |
make frontend |
Shortcut for the Vite development server. |
make frontend-test |
Shortcut for frontend tests. |
Before running Playwright locally for the first time, install its browser with
yarn --cwd frontend playwright install chromium.
The backend loads .env through pydantic-settings and python-dotenv.
Compose also passes this file to the backend with env_file and refuses to
start PostgreSQL when the required database values are missing.
| Variable | Purpose | Example |
|---|---|---|
DB_NAME |
PostgreSQL database name. | scenario_manager |
DB_USER |
PostgreSQL user. | scenario_manager |
DB_PASSWORD |
PostgreSQL password. | change-me |
DB_HOST |
Compose PostgreSQL service host. | db |
DB_PORT |
PostgreSQL port inside the Compose network. | 5432 |
DB_ENCODING |
Database client encoding. | UTF8 |
CARLA_HOST |
CARLA host reachable from the backend container. | host.docker.internal |
CARLA_PORT |
CARLA RPC port. | 2000 |
CARLA_TRAFFIC_MANAGER_PORT |
Traffic Manager RPC port inside the backend container. | 8001 |
CARLA_TIMEOUT_SECONDS |
CARLA connection timeout. | 60 |
RUN_MIGRATIONS |
Run alembic upgrade head on backend startup when set to 1. |
1 |
Compose defines RUN_MIGRATIONS=1. Other values belong in .env; do not put
secrets in docker-compose.yml.
In production, set AUTH_JWT_SECRET (at least 32 characters),
AUTH_BOOTSTRAP_ADMIN_EMAIL, and AUTH_BOOTSTRAP_ADMIN_PASSWORD (at least
12 characters). The first startup creates that admin if it does not yet exist.
Authenticate via POST /api/auth/login; the access token is also stored in a
secure httpOnly cookie. The web interface shows a sign-in screen whenever JWT
authentication is enabled; it never stores the token in browser storage. Admins
can create operator and viewer users through POST /api/auth/users.
Viewers have read-only API access; operators and admins can edit scenarios and
control simulation runs.
When AUTH_JWT_SECRET is empty in development, the app deliberately uses a
local development admin and disables login/logout. Set all three AUTH_*
variables and restart Compose to enable the login screen and real user
sessions.
- The frontend creates or imports a road network and scenario objects.
- The backend validates and stores scenarios in PostgreSQL.
- Export uses the same canonical OpenCDA YAML builder as simulation startup.
- The backend launches OpenCDA against CARLA and streams status over WebSocket.
- Evaluation reports, plots, and diagnostic logs are exposed as run results.
The simulation request keeps its JSON structure and includes the generated
OpenCDA YAML as the opencda_config_yaml string. The current request and
response schemas are documented at /docs.
| Method | Endpoint | Purpose |
|---|---|---|
GET |
/api/v1/scenarios?offset=0&limit=50 |
List scenarios. |
GET |
/api/v1/scenarios/{scenario_id} |
Load a scenario. |
POST |
/api/v1/scenarios |
Create a scenario. |
PATCH |
/api/v1/scenarios/{scenario_id} |
Update a scenario. |
DELETE |
/api/v1/scenarios/{scenario_id} |
Delete a scenario. |
| Method | Endpoint | Purpose |
|---|---|---|
POST |
/api/start_opencda |
Start a simulation run. |
GET |
/api/status |
Read current simulation status. |
POST |
/api/stop |
Stop the current simulation. |
WS |
/api/ws/simulation |
Stream simulation status updates. |
GET |
/api/results |
List completed, partial and archived runs. |
GET |
/api/results/{run_id} |
Return chart samples, metadata and artifact links. |
DELETE |
/api/results/{run_id} |
Delete evaluation artifacts. |
GET |
/api/results/{run_id}/files/{filename} |
Download an artifact. |
GET |
/health |
Check backend and database health. |
docker compose logs -f backendcontains service startup, API, runner, and simulation process output.- Evaluation reports and detailed per-run diagnostics are written under
evaluation_outputs/, which is bind-mounted into the backend container. - On Linux hosts, keep
evaluation_outputs/owned by UID/GID10001and readable by all users (chmod -R a+rX) so the backend can write artifacts and the rootless frontend can serve them. - PostgreSQL data is stored in the
postgres_datanamed volume. - Uploaded custom OpenDRIVE maps are written under
assets/xodrs/. - Run artifacts are also available through
/api/results/{run_id}.
A ready-to-view example is included as DEMO · Town01 — GNSS drift. Open
Results, press Refresh and select it in the run history. It contains 33 interactive
charts for three vehicles: routes, speed, acceleration, GNSS/filter errors, safety
events and RSU coverage. The curves are synthetic and clearly labelled as demo
data; the original POC_Scenario_Town01 only supplied the scenario inspiration
and vehicle start/end positions. No CARLA simulation is needed to view it.
The history list and chart area scroll independently. To recreate the example after deleting it or after automatic result retention cleanup, run:
docker run --rm -v "$PWD:/app" -w /app python:3.11-slim python scripts/create_demo_results.pyNew runs save evaluation_outputs/<run_id>/charts.json instead of rendering
PNG charts on the server. Results use local files only; no new database tables
or migrations are needed. The existing scenario database is separate from this
results storage. The configured EVAL_DIR and the Docker bind mount determine
where files survive container restarts; the existing retention policy still applies.
GET /api/results/<run_id> returns files, run_id, data and data_error.
data is the versioned chart document:
{
"schema_version": 1,
"run_id": "Town01_example",
"fixed_delta_seconds": 0.05,
"elapsed_seconds": 10,
"actors": [{ "id": "7", "label": "Vehicle 7", "metrics": [] }],
"charts": [{
"id": "speed",
"actor_id": "7",
"title": "Vehicle speed",
"description": "Speed reported by the V2X manager.",
"category": "motion",
"kind": "line",
"x_axis": { "label": "Elapsed simulation time", "unit": "s" },
"y_axis": { "label": "Speed", "unit": "km/h" },
"series": [{ "id": "transmitted", "label": "V2X-transmitted speed", "points": [[0.05, 3.6], [0.1, 7.2]] }]
}],
"warnings": []
}Points are [x, y] pairs; null Y values mark unavailable measurements and
produce gaps. Time-stamped buffers use simulation seconds (tick × step duration).
IMU, localization and planner buffers without timestamps use explicitly labelled
sample indices. Routes use world coordinates in metres with equal axis scaling.
TTC values ≥ 1000 seconds and platoon gaps ≥ 100 are unavailable sentinels.
Full available samples are retained, including short and stopped runs. The planner
itself omits its first 100 updates. Safety counters count affected ticks, while the
collision indicator also includes late sensor callbacks.
Results provides vehicle and category selection, interactive legends, pointer and
keyboard inspection, a sample table, metrics and downloads. Select two runs for
comparison and choose the vehicle independently in each; CARLA actor IDs are not
stable across runs. Old PNG-only runs remain downloadable with data: null.
Unreadable chart files return data_error while keeping other downloads available.
metrics.json, configuration files and diagnostic logs remain available too.
Inspect generated files in the backend container when using Docker:
docker compose exec backend ls -la evaluation_outputsscenario-manager/
|-- app/
| |-- routers/ # REST and WebSocket endpoints
| |-- config.py # .env-backed application settings
| |-- database.py # SQLAlchemy engine and sessions
| |-- models.py # ORM database models
| |-- schemas.py # Pydantic API contracts
| |-- opencda_config.py # Canonical OpenCDA YAML processing
| |-- runner.py # CARLA/OpenCDA process orchestration
| |-- scenario_validation.py # Scenario validation rules
| `-- log_config.py # Backend logging setup
|-- migrations/
| |-- versions/ # Alembic migration revisions
| `-- env.py # Alembic runtime configuration
|-- opencda/ # Bundled OpenCDA simulation runtime
|-- frontend/
| |-- src/
| | |-- api/ # Backend API clients
| | |-- components/ # Shared React components
| | |-- pages/ # Editor and generator screens
| | `-- store/ # Zustand application state
| |-- tests/e2e/ # Playwright tests
| |-- public/ # Static frontend files
| |-- Dockerfile # Development and production stages
| `-- nginx/default.conf # Production reverse proxy
|-- assets/xodrs/ # Runtime custom OpenDRIVE maps
|-- evaluation_outputs/ # Local simulation artifacts and diagnostics
|-- tests/
| |-- backend/ # pytest backend suite
| `-- load/ # Load-test scenarios
|-- alembic.ini # Alembic CLI configuration
|-- docker-compose.yml # Backend, PostgreSQL, and production UI
|-- Dockerfile # Backend image
|-- entrypoint.sh # Optional migration startup hook
|-- main.py # FastAPI application entry point
|-- pyproject.toml # Python dependencies and tool settings
|-- uv.lock # Locked Python dependency graph
`-- Makefile # Common development shortcuts
Copyright (c) 2025 CAVISE. All rights reserved.






