fix(drone): resolve critical pipeline, frontend, and input validation issues

Data pipeline (critical): scanners/detectors now write to a separate _obs_queue;
a relay thread reads observations and calls correlator.process(), which emits
processed DroneContact dicts to drone_queue for SSE. Without this the SSE stream
received raw unserializable dataclass objects causing JSON errors.

Frontend (critical):
- Add droneContactList container to drone.html so contact cards render
- Add droneMap container and initialize Leaflet in drone.js init()
- Define dsc-distress-pulse keyframes in drone.css (was referenced but missing)
- Fix SSE reconnect: null _sse before setTimeout to prevent _connectSSE no-op loop

Other fixes:
- Validate rtl_sdr_index with validate_device_index(), return 400 on bad input
- Move _ensure_workers() inside _drone_lock to prevent double-initialization race
- Add double-call guard to RemoteIDScanner.start()

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
James Smith
2026-05-03 21:47:12 +01:00
parent 14e6305aa4
commit 8632e31c01
5 changed files with 69 additions and 7 deletions
+12
View File
@@ -69,6 +69,18 @@
color: var(--accent-red);
}
.drone-map {
height: 280px;
border-radius: 4px;
border: 1px solid var(--border-color);
margin: 0 12px 12px;
}
.drone-marker-high-risk {
animation: dsc-distress-pulse 1.5s infinite;
}
@keyframes dsc-distress-pulse {
0%, 100% { opacity: 1; transform: scale(1); }
50% { opacity: 0.4; transform: scale(1.4); }
}
+14
View File
@@ -10,10 +10,22 @@
function init() {
document.getElementById('droneStartBtn')?.addEventListener('click', _start);
document.getElementById('droneStopBtn')?.addEventListener('click', _stop);
_initMap();
_connectSSE();
_refreshStatus();
}
function _initMap() {
if (_map) return;
const mapEl = document.getElementById('droneMap');
if (!mapEl || typeof L === 'undefined') return;
_map = L.map('droneMap', { zoomControl: true }).setView([20, 0], 2);
L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
attribution: '© OpenStreetMap',
maxZoom: 18,
}).addTo(_map);
}
function destroy() {
_disconnectSSE();
if (_map) {
@@ -34,6 +46,8 @@
} catch (_) {}
});
_sse.onerror = function () {
_sse.close();
_sse = null;
setTimeout(_connectSSE, 3000);
};
}