from __future__ import annotations import asyncio from typing import Any from fastapi import APIRouter, HTTPException, Query, WebSocket, status from fastapi.encoders import jsonable_encoder from fastapi.websockets import WebSocketDisconnect from sqlmodel import Session from app.api.deps import CurrentUser, SessionDep, resolve_user_from_token from app.core.db import engine from app.mqtt.telemetry import build_telemetry_snapshot router = APIRouter(prefix="/telemetry", tags=["telemetry"]) STREAM_INTERVAL_SECONDS = 1.0 @router.get("/latest") def read_telemetry_latest( session: SessionDep, _current_user: CurrentUser, device_id: str | None = None, limit: int = Query(default=50, ge=1, le=200), ) -> dict[str, Any]: """ Return the latest parsed GNSS telemetry snapshot. """ return build_telemetry_snapshot(session=session, device_id=device_id, limit=limit) @router.websocket("/ws") async def telemetry_websocket( websocket: WebSocket, token: str | None = Query(default=None), device_id: str | None = Query(default=None), limit: int = Query(default=50, ge=1, le=200), ) -> None: if not token: await websocket.close(code=status.WS_1008_POLICY_VIOLATION) return with Session(engine) as session: try: resolve_user_from_token(session=session, token=token) except HTTPException: await websocket.close(code=status.WS_1008_POLICY_VIOLATION) return await websocket.accept() try: while True: with Session(engine) as session: payload = build_telemetry_snapshot( session=session, device_id=device_id, limit=limit, ) await websocket.send_json(jsonable_encoder(payload)) await asyncio.sleep(STREAM_INTERVAL_SECONDS) except (WebSocketDisconnect, RuntimeError): return