Replaces the single secondary_sdr_mode with secondary_sdr_priority, e.g. ["adsb", "ais"]. OP25 always keeps its own dongle; the secondary-sdr container starts decoders top-down until it runs out of free SDRs, so a 3-SDR node runs ADS-B and AIS at once and a 2-SDR node runs the top pick. - secondary-sdr: one decoder per mode, POST /secondary/apply(priority) (no-op when the right prefix is already running), orphaned decoders from a uvicorn reload are reaped on start, /status reports sdr_count via lsusb (op25's :stable image has none). - edge-node: one apply path (set_secondary_priority) for the local dashboard, a new C2 'set_secondary_priority' MQTT command, and config pushes; it never restarts op25. Legacy mode migrates on load. Checkin reports priority, what's running, and sdr_count. Uplink forwards aircraft and vessels whenever either is present. - Local dashboard: 'Secondary SDRs' card to enable/reorder/save. Verified: edge-node pytest 190 passed, flake8 clean. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
59 lines
1.5 KiB
Python
59 lines
1.5 KiB
Python
from typing import List, Optional
|
|
|
|
from fastapi import APIRouter, HTTPException
|
|
from pydantic import BaseModel
|
|
|
|
from internal import decoder_control
|
|
from internal.logger import create_logger
|
|
|
|
LOGGER = create_logger(__name__)
|
|
|
|
|
|
class StartBody(BaseModel):
|
|
mode: str # adsb | ais
|
|
|
|
|
|
class StopBody(BaseModel):
|
|
mode: Optional[str] = None # omit to stop every decoder
|
|
|
|
|
|
class ApplyBody(BaseModel):
|
|
priority: List[str] # ordered, e.g. ["adsb", "ais"]
|
|
|
|
|
|
def create_secondary_router():
|
|
router = APIRouter()
|
|
|
|
@router.post("/apply")
|
|
async def apply(body: ApplyBody):
|
|
try:
|
|
live = decoder_control.apply(body.priority)
|
|
except ValueError as e:
|
|
raise HTTPException(status_code=400, detail=str(e))
|
|
return {"running": live}
|
|
|
|
@router.post("/start")
|
|
async def start(body: StartBody):
|
|
try:
|
|
ok = decoder_control.start(body.mode)
|
|
except ValueError as e:
|
|
raise HTTPException(status_code=400, detail=str(e))
|
|
if not ok:
|
|
raise HTTPException(status_code=409, detail="No free SDR for this decoder")
|
|
return {"status": f"secondary SDR started ({body.mode})"}
|
|
|
|
@router.post("/stop")
|
|
async def stop(body: Optional[StopBody] = None):
|
|
decoder_control.stop(body.mode if body else None)
|
|
return {"status": "secondary SDR stopped"}
|
|
|
|
@router.get("/status")
|
|
async def get_status():
|
|
return decoder_control.status()
|
|
|
|
@router.get("/data")
|
|
async def get_data():
|
|
return decoder_control.data()
|
|
|
|
return router
|