Procházet zdrojové kódy

feat: show queue fill telemetry

tags/v0.9.0
Jan Svabenik před 1 měsícem
rodič
revize
4d9895918a
2 změnil soubory, kde provedl 34 přidání a 0 odebrání
  1. +2
    -0
      docs/pro-runtime-hardening-workboard.md
  2. +32
    -0
      internal/control/ui.html

+ 2
- 0
docs/pro-runtime-hardening-workboard.md Zobrazit soubor

@@ -385,11 +385,13 @@ Vollständige Sichtbarkeit auf Runtime, Queue, Writer, Generator, RF-Selbsttests
| Datum | Entscheidung | Notiz |
| --- | --- | --- |
| 2026-04-06 | High-watermark trend sparkline | Captured audio high-watermark duration history and surface it as a new Health-panel sparkline for queue pressure visibility. |
| 2026-04-06 | Queue fill visibility | Added queue fill ratio health line and sparklines to highlight real-time queue pressure alongside high-watermark trends. |

## WS-04 Verifikation
| Datum | Fokus | Ergebnis |
| --- | --- | --- |
| 2026-04-06 | High-watermark trend sparkline | `go test ./...` plus manual UI check confirm the new sparkline updates with runtime audio stats. |
| 2026-04-06 | Queue fill visibility | `go test ./...` plus UI smoke check confirm queue fill stats stay available and the new sparkline/health line react to queue health changes. |

---



+ 32
- 0
internal/control/ui.html Zobrazit soubor

@@ -1179,11 +1179,16 @@ input.input-error {
<div class="health-line"><div class="name">Audio Buffer</div><div class="val" id="health-audio">--</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">Queue Fill</div><div class="val" id="health-queue-fill">--</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-label">High Watermark Trend</div>
<svg class="spark warn" id="spark-high-watermark" viewBox="0 0 160 34" preserveAspectRatio="none"></svg>
</div>
<div class="health-trend">
<div class="health-trend-label">Queue Fill Trend</div>
<svg class="spark good" id="spark-queue-fill" viewBox="0 0 160 34" preserveAspectRatio="none"></svg>
</div>
</div>
</div>

@@ -1319,6 +1324,7 @@ const state = {
underruns: [],
tx: [],
highWatermark: [],
queueFill: [],
},
runtimeTransitions: [],
freqPresetIndex: 0,
@@ -1497,6 +1503,8 @@ function pushHistory(runtime) {
const highWatermarkDurationSeconds = Number(audio.highWatermarkDurationSeconds);
const normalizedHighWatermark = Number.isFinite(highWatermarkDurationSeconds) ? highWatermarkDurationSeconds : 0;
pushChart(state.charts.highWatermark, normalizedHighWatermark);
const queueFill = Number(engine.queue?.fillLevel ?? 0);
pushChart(state.charts.queueFill, Number.isFinite(queueFill) ? queueFill : 0);
pushChart(state.charts.underruns, Number(engine.underruns ?? driver.underruns ?? 0));
const txState = String(engine.state || 'idle').toLowerCase();
pushChart(state.charts.tx, txState === 'running' ? 1 : state.txBusy ? 0.55 : 0.05);
@@ -1988,8 +1996,13 @@ function render() {
hasHighWatermarkDuration ? highWatermarkDurationSeconds : 0,
hasBufferedDuration ? bufferedDurationSeconds : 0
);
const queueHealthRaw = String(engine.queue?.health || '').toLowerCase();
let queueSparkMode = 'good';
if (queueHealthRaw === 'critical') queueSparkMode = 'err';
else if (queueHealthRaw === 'low') queueSparkMode = 'warn';
drawSparkline('spark-audio', state.charts.audio, 'good', 1);
drawSparkline('spark-high-watermark', state.charts.highWatermark, highWatermarkMode, sparkHighWatermarkMax);
drawSparkline('spark-queue-fill', state.charts.queueFill, queueSparkMode, 1);
drawSparkline('spark-underruns', state.charts.underruns, underruns > 0 ? 'err' : 'warn');
drawSparkline('spark-tx', state.charts.tx, txStateValue === 'running' ? 'good' : 'warn', 1);
applyMobilePanelDefaults();
@@ -2143,6 +2156,25 @@ function updateHealth(engine, audioStream) {
}
updateText('health-buffer-highwater', highWatermarkLabel);

const queueFill = Number(engine.queue?.fillLevel);
const queueHealthRaw = String(engine.queue?.health || '').toLowerCase();
const queueHealthLabel = queueHealthRaw ? queueHealthRaw[0].toUpperCase() + queueHealthRaw.slice(1) : '';
let queueFillLabel = '--';
if (Number.isFinite(queueFill)) {
queueFillLabel = fmtPercent(queueFill);
if (queueHealthLabel) queueFillLabel += ` · ${queueHealthLabel}`;
} else if (queueHealthLabel) {
queueFillLabel = queueHealthLabel;
}
updateText('health-queue-fill', queueFillLabel);
const queueFillEl = $('health-queue-fill');
if (queueFillEl) {
let queueFillClass = 'good';
if (queueHealthRaw === 'critical') queueFillClass = 'err';
else if (queueHealthRaw === 'low') queueFillClass = 'warn';
queueFillEl.className = 'val ' + queueFillClass;
}

const last = Math.max(state.server.lastConfigAt || 0, state.server.lastRuntimeAt || 0);
updateText('health-last', ageString(last));



Načítá se…
Zrušit
Uložit