Secondary SDR priority: every spare SDR runs the next decoder in an ordered list
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>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
1c74edffea
commit
9b6c64fbbf
@@ -1,3 +1,5 @@
|
||||
from typing import List, Optional
|
||||
|
||||
from fastapi import APIRouter, HTTPException
|
||||
from pydantic import BaseModel
|
||||
|
||||
@@ -11,9 +13,25 @@ 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:
|
||||
@@ -21,12 +39,12 @@ def create_secondary_router():
|
||||
except ValueError as e:
|
||||
raise HTTPException(status_code=400, detail=str(e))
|
||||
if not ok:
|
||||
raise HTTPException(status_code=500, detail="Failed to start secondary SDR decoder")
|
||||
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():
|
||||
decoder_control.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")
|
||||
|
||||
Reference in New Issue
Block a user