|
|
|
@@ -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; |
|
|
|
|