| @@ -1180,6 +1180,7 @@ input.input-error { | |||||
| <div class="health-line"><div class="name">Buffer Duration</div><div class="val" id="health-buffer-duration">--</div></div> | <div class="health-line"><div class="name">Buffer Duration</div><div class="val" id="health-buffer-duration">--</div></div> | ||||
| <div class="health-line"><div class="name">High Watermark</div><div class="val" id="health-buffer-highwater">--</div></div> | <div class="health-line"><div class="name">High Watermark</div><div class="val" id="health-buffer-highwater">--</div></div> | ||||
| <div class="health-line"><div class="name">Queue Fill</div><div class="val" id="health-queue-fill">--</div></div> | <div class="health-line"><div class="name">Queue Fill</div><div class="val" id="health-queue-fill">--</div></div> | ||||
| <div class="health-line"><div class="name">Underrun Streak</div><div class="val" id="health-underrun-streak">--</div></div> | |||||
| <div class="health-line"><div class="name">Last Update</div><div class="val" id="health-last">--</div></div> | <div class="health-line"><div class="name">Last Update</div><div class="val" id="health-last">--</div></div> | ||||
| <div class="health-trend"> | <div class="health-trend"> | ||||
| <div class="health-trend-label">High Watermark Trend</div> | <div class="health-trend-label">High Watermark Trend</div> | ||||
| @@ -1972,7 +1973,7 @@ function render() { | |||||
| updateText('info-fmmod', fmtBool(cfg.fm?.fmModulationEnabled)); | updateText('info-fmmod', fmtBool(cfg.fm?.fmModulationEnabled)); | ||||
| updateText('info-live', engine.state ? `${String(engine.state).toUpperCase()} / ${state.server.runtimeOk ? 'runtime ok' : 'runtime pending'}` : (state.server.configOk ? 'config only' : '--')); | updateText('info-live', engine.state ? `${String(engine.state).toUpperCase()} / ${state.server.runtimeOk ? 'runtime ok' : 'runtime pending'}` : (state.server.configOk ? 'config only' : '--')); | ||||
| updateHealth(engine, audioStream); | |||||
| updateHealth(engine, driver, audioStream); | |||||
| updateFaultHistory(engine); | updateFaultHistory(engine); | ||||
| updateTransitionHistory(); | updateTransitionHistory(); | ||||
| updateResetHint(engine); | updateResetHint(engine); | ||||
| @@ -2077,8 +2078,9 @@ function notifyRuntimeTransition(engine, pushHistory = true) { | |||||
| } | } | ||||
| function updateHealth(engine, audioStream) { | |||||
| function updateHealth(engine, driver, audioStream) { | |||||
| engine = engine || {}; | engine = engine || {}; | ||||
| driver = driver || {}; | |||||
| updateText('health-http', state.server.configOk ? 'OK' : 'OFFLINE'); | updateText('health-http', state.server.configOk ? 'OK' : 'OFFLINE'); | ||||
| $('health-http').className = 'val ' + (state.server.configOk ? 'good' : 'err'); | $('health-http').className = 'val ' + (state.server.configOk ? 'good' : 'err'); | ||||
| @@ -2175,6 +2177,35 @@ function updateHealth(engine, audioStream) { | |||||
| queueFillEl.className = 'val ' + queueFillClass; | queueFillEl.className = 'val ' + queueFillClass; | ||||
| } | } | ||||
| const streakEl = $('health-underrun-streak'); | |||||
| if (streakEl) { | |||||
| const streakRaw = driver?.underrunStreak; | |||||
| const streakMaxRaw = driver?.maxUnderrunStreak; | |||||
| const streakCurrent = Number.isFinite(Number(streakRaw)) ? Number(streakRaw) : null; | |||||
| const streakMax = Number.isFinite(Number(streakMaxRaw)) ? Number(streakMaxRaw) : null; | |||||
| let streakLabel = '--'; | |||||
| if (streakCurrent != null) { | |||||
| streakLabel = String(streakCurrent); | |||||
| if (streakMax != null) { | |||||
| streakLabel += ` (max ${streakMax})`; | |||||
| } | |||||
| } else if (streakMax != null) { | |||||
| streakLabel = `Max ${streakMax}`; | |||||
| } | |||||
| let streakSeverity = ''; | |||||
| if (streakCurrent != null || streakMax != null) { | |||||
| const highestStreak = Math.max( | |||||
| streakCurrent != null ? streakCurrent : 0, | |||||
| streakMax != null ? streakMax : 0 | |||||
| ); | |||||
| if (highestStreak >= 6) streakSeverity = ' err'; | |||||
| else if (highestStreak > 0) streakSeverity = ' warn'; | |||||
| else streakSeverity = ' good'; | |||||
| } | |||||
| streakEl.textContent = streakLabel; | |||||
| streakEl.className = 'val' + streakSeverity; | |||||
| } | |||||
| const last = Math.max(state.server.lastConfigAt || 0, state.server.lastRuntimeAt || 0); | const last = Math.max(state.server.lastConfigAt || 0, state.server.lastRuntimeAt || 0); | ||||
| updateText('health-last', ageString(last)); | updateText('health-last', ageString(last)); | ||||