Secondary SDR priority: C2 field, node command, dashboard section
Pairs with node-26 feat/secondary-sdr-priority. NodeRecord gains
secondary_sdr_priority (ordered; SDRs beyond OP25's run it top-down) and
secondary_sdr_running, both mirrored from the node's checkin.
PATCH /nodes/{id} accepts the priority, validates it, and sends it as a
'set_secondary_priority' MQTT command. A priority-only change never
re-pushes system config, because that restarts OP25. The node detail page
gets a 'Secondary SDRs' section (admin-editable) with enable, reorder,
save, and live Running / Waiting-for-SDR state from the checkin.
Verified: c2-core pytest 482 passed; frontend tsc --noEmit clean.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
fa207e494d
commit
3cbb0828ae
@@ -1,5 +1,5 @@
|
||||
import secrets
|
||||
from typing import Optional
|
||||
from typing import List, Optional
|
||||
from fastapi import APIRouter, HTTPException, Depends, Query
|
||||
from pydantic import BaseModel
|
||||
from app.models import CommandPayload
|
||||
@@ -192,10 +192,15 @@ async def assign_system(
|
||||
return {"ok": True}
|
||||
|
||||
|
||||
SECONDARY_SDR_MODES = ("adsb", "ais")
|
||||
|
||||
|
||||
class NodeUpdateBody(BaseModel):
|
||||
node_type: Optional[str] = None
|
||||
enforce_override_timeout: Optional[bool] = None
|
||||
secondary_sdr_mode: Optional[str] = None # none | adsb | ais | op25_2
|
||||
secondary_sdr_mode: Optional[str] = None # legacy: none | adsb | ais
|
||||
# Ordered, e.g. ["adsb", "ais"]: SDRs beyond op25's run these top-down.
|
||||
secondary_sdr_priority: Optional[List[str]] = None
|
||||
|
||||
|
||||
@router.patch("/{node_id}")
|
||||
@@ -212,8 +217,23 @@ async def update_node(
|
||||
if not updates:
|
||||
return {"ok": True}
|
||||
|
||||
priority = updates.get("secondary_sdr_priority")
|
||||
if priority is not None:
|
||||
unknown = [m for m in priority if m not in SECONDARY_SDR_MODES]
|
||||
if unknown or len(set(priority)) != len(priority):
|
||||
raise HTTPException(400, f"secondary_sdr_priority must be distinct values from {SECONDARY_SDR_MODES}.")
|
||||
updates["secondary_sdr_mode"] = priority[0] if priority else "none"
|
||||
|
||||
await fstore.doc_update("nodes", node_id, updates)
|
||||
|
||||
# Priority goes as its own command: a config re-push restarts OP25, and
|
||||
# changing what the spare dongles do must never interrupt P25 recording.
|
||||
# The node applies it, then its checkin reports back what's really running.
|
||||
if priority is not None:
|
||||
mqtt_handler.send_command(node_id, {"action": "set_secondary_priority", "priority": priority})
|
||||
if set(updates) <= {"secondary_sdr_priority", "secondary_sdr_mode"}:
|
||||
return {"ok": True}
|
||||
|
||||
# Re-push config to apply new node settings locally
|
||||
updated_node = await fstore.doc_get("nodes", node_id)
|
||||
assigned_system_id = updated_node.get("assigned_system_id")
|
||||
@@ -228,7 +248,9 @@ async def update_node(
|
||||
}
|
||||
if updated_node.get("ppm_override") is not None:
|
||||
push_payload["ppm_override"] = updated_node["ppm_override"]
|
||||
if updated_node.get("secondary_sdr_mode") is not None:
|
||||
if updated_node.get("secondary_sdr_priority") is not None:
|
||||
push_payload["secondary_sdr_priority"] = updated_node["secondary_sdr_priority"]
|
||||
elif updated_node.get("secondary_sdr_mode") is not None:
|
||||
push_payload["secondary_sdr_mode"] = updated_node["secondary_sdr_mode"]
|
||||
mqtt_handler.push_config(node_id, push_payload)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user