decidron-simulator / backend /app /api /decidron.py
NeonClary
Make simulator runnable on first click + editable commands
7f237a3
Raw
History Blame Contribute Delete
2.58 kB
"""REST API for the Decidron network simulator."""
from __future__ import annotations
from fastapi import APIRouter, HTTPException
from pydantic import BaseModel
from app.decidron.engine import engine
from app.decidron.diagrams import to_vis_network
from app.decidron.models import ProcessingCommand, RunResult, SensorInput
router = APIRouter(prefix="/decidron", tags=["decidron"])
def _network_payload() -> dict:
nodes = engine.network.nodes_list()
return {
"version": engine.network.version,
"nodes": [n.model_dump() for n in nodes],
"edges": [e.model_dump(by_alias=True) for e in engine.network.edges],
"diagram": to_vis_network(nodes, engine.network.edges),
}
@router.get("/network")
async def get_network() -> dict:
return _network_payload()
@router.post("/sensors/input")
async def submit_sensor_input(sensor_input: SensorInput) -> dict:
engine.add_sensor_input(sensor_input)
return {
"queued": sensor_input.model_dump(),
"pending_count": len(engine.pending_inputs),
}
@router.get("/commands")
async def list_commands() -> list[ProcessingCommand]:
return engine.list_commands()
@router.post("/commands")
async def create_command(command: ProcessingCommand) -> ProcessingCommand:
return engine.add_command(command)
@router.delete("/commands/{name}")
async def delete_command(name: str) -> dict:
if not engine.remove_command(name):
raise HTTPException(status_code=404, detail=f"No command named '{name}'")
return {"deleted": name}
class RunRequest(BaseModel):
inputs: list[SensorInput] | None = None
@router.post("/run")
async def run_simulation(req: RunRequest | None = None) -> dict:
inputs = req.inputs if req else None
results: list[RunResult] = engine.run(inputs)
return {
"results": [r.model_dump() for r in results],
"matches": sum(1 for r in results if r.matched),
"network": _network_payload(),
}
@router.get("/stats")
async def get_stats() -> dict:
return engine.stats.as_dict()
@router.get("/history")
async def get_history() -> dict:
return {
"version": engine.network.version,
"snapshots": [
{
"version": s.version,
"reason": s.reason,
"timestamp": s.timestamp,
"diagram": to_vis_network(s.nodes, s.edges),
}
for s in engine.network.history
],
}
@router.post("/reset")
async def reset_simulation() -> dict:
engine.reset()
return _network_payload()