Files
full-stack-fastapi/backend/app/api/routes/telemetry.py
魏风 e81aadcd99
All checks were successful
Deploy to Production / deploy (push) Successful in 1m10s
feat: add realtime GNSS monitor with telemetry websocket
2026-03-20 11:48:04 +08:00

64 lines
1.9 KiB
Python

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