Bladeren bron

ui: add manual fault reset action

tags/v0.9.0
Jan Svabenik 1 maand geleden
bovenliggende
commit
c2a27e71ec
2 gewijzigde bestanden met toevoegingen van 31 en 0 verwijderingen
  1. +2
    -0
      docs/pro-runtime-hardening-workboard.md
  2. +29
    -0
      internal/control/ui.html

+ 2
- 0
docs/pro-runtime-hardening-workboard.md Bestand weergeven

@@ -279,6 +279,7 @@ Einführen eines klaren Betriebsmodells mit Fault-, Recovery- und Muted-Zuständ
- `EngineStats` and `txBridge` now expose transition/fault counters plus `lastFault`, surfacing the new telemetry through `/runtime`.
- Control-plane UI now renders those WS-02 transition counters, fault count, and last-fault summary so operators can watch runtime escalations without digging through logs.
- Control-plane now exposes `POST /runtime/fault/reset` so operators can acknowledge `faulted` state; `TestRuntimeFaultReset*` covers the new HTTP path.
- Control-plane UI now also offers a Danger Zone `Reset Fault` button that calls the same endpoint so operators can acknowledge faults from the dashboard.


## Zielzustände laut Konzept
@@ -329,6 +330,7 @@ Einführen eines klaren Betriebsmodells mit Fault-, Recovery- und Muted-Zuständ
|---|---|---|
| 2026-04-05 | Faulted escalation on persistent critical queue | `muted` now surfaces `RuntimeStateFaulted` when queue health stays critical and metrics capture every transition. |
| 2026-04-05 | Manual fault reset endpoint | Added `POST /runtime/fault/reset` so operators can acknowledge `faulted` before the supervisor re-enters recovery. |
| 2026-04-05 | Fault-reset UI shortcut | Danger Zone now hosts a Reset Fault button wired to `/runtime/fault/reset` so operators get an in-app acknowledgement path without manual HTTP calls. |

## WS-02 Verifikation
| Datum | Fokus | Ergebnis |


+ 29
- 0
internal/control/ui.html Bestand weergeven

@@ -1121,6 +1121,7 @@ input.input-error {
<div class="actions-row" style="margin-top:0">
<button class="danger-btn" id="danger-stop" type="button">Emergency Stop TX</button>
<button class="danger-btn" id="danger-refresh" type="button">Hard Refresh Runtime</button>
<button class="danger-btn secondary" id="danger-reset-fault" type="button">Reset Fault</button>
</div>
</div>
</div>
@@ -1175,6 +1176,7 @@ const state = {
dirty: new Set(),
pendingRequests: 0,
txBusy: false,
faultResetBusy: false,
toggleBusy: {},
pollersStarted: false,
mobilePanelsApplied: false,
@@ -1489,6 +1491,26 @@ async function txAction(action) {
}
}

async function resetFaultAction() {
if (state.faultResetBusy) return;
state.faultResetBusy = true;
render();
beginRequest();
try {
await api('/runtime/fault/reset', { method: 'POST' });
toast('Fault reset', 'ok');
log('Fault reset request accepted', 'ok');
await loadRuntime({ silent: true });
} catch (error) {
toast(error.message, 'err');
log('Fault reset failed: ' + error.message, 'err');
} finally {
state.faultResetBusy = false;
endRequest();
render();
}
}

function fmt(n) {
if (n == null) return '--';
if (n >= 1e9) return (n / 1e9).toFixed(2) + 'G';
@@ -1686,6 +1708,12 @@ function render() {
$('btn-refresh').disabled = state.pendingRequests > 0;
$('danger-stop').disabled = stopDisabled;
$('danger-refresh').disabled = state.pendingRequests > 0;
const resetFaultBtn = $('danger-reset-fault');
if (resetFaultBtn) {
const resetDisabled = state.faultResetBusy || !state.server.runtimeOk;
resetFaultBtn.disabled = resetDisabled;
resetFaultBtn.textContent = state.faultResetBusy ? 'Resetting…' : 'Reset Fault';
}

syncDirtyInput('freq-slider', 'frequencyMHz', (v) => typeof v === 'number' ? v.toFixed(1) : '100.0');
syncDirtyInput('freq-num', 'frequencyMHz', (v) => typeof v === 'number' ? v.toFixed(1) : '100.0');
@@ -1901,6 +1929,7 @@ function bindInputs() {
$('danger-stop').addEventListener('click', () => txAction('stop'));
$('btn-refresh').addEventListener('click', manualRefresh);
$('danger-refresh').addEventListener('click', manualRefresh);
$('danger-reset-fault').addEventListener('click', () => resetFaultAction());

document.querySelectorAll('.toggle[data-toggle]').forEach((toggle) => {
const key = toggle.dataset.toggle;


Laden…
Annuleren
Opslaan