feat(slm): wire unit live view to the /monitor fan-out feed
The SLM live view now consumes SLMM's shared DOD /monitor feed instead of
the per-client DRD /stream. This fixes the single-connection contention
(many viewers share one device feed) and finally puts L1/L10 in the live
chart (DRD couldn't carry percentiles).
- New WS proxy handler /api/slmm/{unit}/monitor -> SLMM /api/nl43/{unit}/monitor.
Uses asyncio.wait(FIRST_COMPLETED) + cancel-sibling instead of gather(), so
it doesn't leave a task sending into a closed socket ("Unexpected ASGI
message after close").
- Live view JS points at /monitor; onmessage reflects feed_status and ignores
heartbeat / unreachable frames so they don't blank the cards or zero-spike
the chart. Adds a small Live/Device-offline badge.
Still on the old /live (DRD): the dashboard live tile (sound_level_meters.html)
— next slice.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -143,6 +143,8 @@
|
||||
</svg>
|
||||
Stop Live Stream
|
||||
</button>
|
||||
|
||||
<span id="live-feed-status" class="ml-3 self-center" style="display: none;"></span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -512,9 +514,11 @@ function initLiveDataStream(unitId) {
|
||||
window.liveChart.update();
|
||||
}
|
||||
|
||||
// WebSocket URL for SLMM backend via proxy
|
||||
// WebSocket URL for SLMM backend via proxy.
|
||||
// /monitor = the shared fan-out DOD feed (many viewers, one device connection,
|
||||
// and it carries L1/L10 which the DRD /stream cannot).
|
||||
const wsProtocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:';
|
||||
const wsUrl = `${wsProtocol}//${window.location.host}/api/slmm/${unitId}/live`;
|
||||
const wsUrl = `${wsProtocol}//${window.location.host}/api/slmm/${unitId}/monitor`;
|
||||
|
||||
window.currentWebSocket = new WebSocket(wsUrl);
|
||||
|
||||
@@ -530,7 +534,11 @@ function initLiveDataStream(unitId) {
|
||||
window.currentWebSocket.onmessage = function(event) {
|
||||
try {
|
||||
const data = JSON.parse(event.data);
|
||||
console.log('WebSocket data received:', data);
|
||||
// The DOD monitor sends keepalive 'heartbeat' frames (no metrics) and a
|
||||
// 'feed_status' on each frame. Reflect status, but don't let a heartbeat
|
||||
// or an 'unreachable' frame blank the cards / spike the chart with zeros.
|
||||
updateFeedStatus(data.feed_status);
|
||||
if (data.heartbeat || data.feed_status === 'unreachable') return;
|
||||
updateLiveMetrics(data);
|
||||
updateLiveChart(data);
|
||||
} catch (error) {
|
||||
@@ -559,6 +567,21 @@ function stopLiveDataStream() {
|
||||
}
|
||||
}
|
||||
|
||||
// Reflect device reachability from the monitor feed's feed_status. Safe no-op
|
||||
// if the badge element isn't on the page.
|
||||
function updateFeedStatus(status) {
|
||||
const el = document.getElementById('live-feed-status');
|
||||
if (!el || status == null) return;
|
||||
if (status === 'unreachable') {
|
||||
el.textContent = 'Device offline';
|
||||
el.className = 'text-xs font-medium px-2 py-0.5 rounded bg-red-100 text-red-700 dark:bg-red-900/40 dark:text-red-300';
|
||||
} else {
|
||||
el.textContent = 'Live';
|
||||
el.className = 'text-xs font-medium px-2 py-0.5 rounded bg-green-100 text-green-700 dark:bg-green-900/40 dark:text-green-300';
|
||||
}
|
||||
el.style.display = '';
|
||||
}
|
||||
|
||||
// Update metrics display
|
||||
function updateLiveMetrics(data) {
|
||||
if (document.getElementById('live-lp')) {
|
||||
|
||||
Reference in New Issue
Block a user