+ SDRs
+
+ {reported && node.sdr_devices != null
+ ? `This node reports ${devices.length} SDR${devices.length === 1 ? "" : "s"}.`
+ : "This node hasn't reported its SDRs yet."}
+
+
+
+
+
+
OP25 SDR
+
+ {op25Moving
+ ? "Saving restarts OP25 on the selected SDR."
+ : node.op25_sdr_serial
+ ? `Using serial ${node.op25_sdr_serial}`
+ : "P25 / analog scanning"}
+
+
+
{ setOp25Pin(v); setDirty(true); setMessage(null); }}
+ />
+
+
+ {rows.map((row, i) => {
+ const meta = MODES.find((x) => x.mode === row.mode)!;
+ const pinMissing = !!row.pin && !devices.some((d) => d.serial === row.pin);
+ const state = !row.enabled
+ ? "Off"
+ : dirty
+ ? "Unsaved"
+ : !reported
+ ? "Not reported"
+ : running.includes(row.mode)
+ ? "Running"
+ : pinMissing
+ ? "Pinned SDR missing"
+ : "Waiting for SDR";
+ return (
+
+
{row.enabled ? ++rank : ""}
+
edit(rows.map((r, j) => (j === i ? { ...r, enabled: e.target.checked } : r)))}
+ className="rounded bg-gray-800 border-gray-700 text-indigo-600 focus:ring-indigo-500 focus:ring-offset-gray-900"
+ />
+
+
{meta.name}
+
{meta.hint}
+
+
edit(rows.map((r, j) => (j === i ? { ...r, pin: v } : r)))}
+ />
+ {canEdit && (
+
+
+
+
+ )}
+
+ {state}
+
+
+ );
+ })}
+
+
+
+ Every SDR besides OP25's runs the next enabled service, top first. Pin a service to the SDR with its
+ antenna, or leave it on "Any spare SDR".
+
+ {devices.some((d) => d.duplicate_serial) && (
+
+ Two SDRs share a serial number, so they can't be told apart. Give each a unique serial (rtl_eeprom -s)
+ before pinning.
+
+ )}
+ {clash && Two services are pinned to the same SDR.
}
+
+ {canEdit && (
+
+
+ {message && {message}}
+
+ )}
+
+ );
+}
diff --git a/drb-frontend/components/SecondarySdrPriority.tsx b/drb-frontend/components/SecondarySdrPriority.tsx
deleted file mode 100644
index 2fa6d4f..0000000
--- a/drb-frontend/components/SecondarySdrPriority.tsx
+++ /dev/null
@@ -1,154 +0,0 @@
-"use client";
-
-import { useEffect, useState } from "react";
-import { c2api } from "@/lib/c2api";
-import type { NodeRecord } from "@/lib/types";
-
-// node-26#9. OP25 always keeps its own SDR; every other SDR on the node runs
-// the next enabled item here, top first — so a 3-SDR node runs both.
-const MODES: { mode: string; name: string; hint: string }[] = [
- { mode: "adsb", name: "ADS-B", hint: "Aircraft · 1090 MHz" },
- { mode: "ais", name: "AIS", hint: "Vessels · 162 MHz" },
-];
-
-type Row = { mode: string; enabled: boolean };
-
-function rowsFrom(priority: string[]): Row[] {
- return [
- ...priority.filter((m) => MODES.some((x) => x.mode === m)).map((mode) => ({ mode, enabled: true })),
- ...MODES.filter((x) => !priority.includes(x.mode)).map((x) => ({ mode: x.mode, enabled: false })),
- ];
-}
-
-export function SecondarySdrPriority({ node, canEdit }: { node: NodeRecord; canEdit: boolean }) {
- const priority = node.secondary_sdr_priority ?? [];
- // null/absent = the node has never reported (older firmware, container down
- // at checkin): unknown, not "nothing running" (server-26#187).
- const reported = node.secondary_sdr_running != null;
- const running = node.secondary_sdr_running ?? [];
- const [rows, setRows] = useState