Add resilient CAT polling with backoff and warnings

Replace the fixed 3s setInterval CAT updater with a scheduled poller that supports exponential backoff, failure counting, and UI warnings. Introduces consecutiveCatPollFailures, catPollTimer, lastSuccessfulCatUpdateAt, and constants for base/max intervals and warning threshold. Adds helper functions to compute delays, schedule polls, handle success/failure, show/clear warning banners, and only poll the currently selected radio. Clears warnings on login error/reset/selection changes and resets failure counters when selection changes to maintain responsive UI.
This commit is contained in:
Peter Goodhall 2026-05-01 10:23:08 +01:00
parent e6f6dd2265
commit 7e40a0288e

View file

@ -2096,6 +2096,13 @@ $(document).ready(function() {
let catRequestCounter = 0;
let lastProcessedCatRequest = 0;
let catSelectionContextVersion = 0;
let consecutiveCatPollFailures = 0;
let catPollTimer = null;
let lastSuccessfulCatUpdateAt = null;
const CAT_POLL_BASE_INTERVAL_MS = 3000;
const CAT_POLL_MAX_INTERVAL_MS = 15000;
const CAT_POLL_WARNING_THRESHOLD = 3;
// Helper function to update a UI element with CAT data
const cat2UI = (ui, cat, allowEmpty = true, allowZero = true, callbackOnUpdate) => {
@ -2141,15 +2148,98 @@ $(document).ready(function() {
if (data.error === 'not_logged_in') {
handleLoginError();
}
handleCATPollFailure(requestedRadioID);
return;
}
clearLoginError();
handleCATPollSuccess();
updateUIWithCATData(data);
},
error: () => {
handleCATPollFailure(requestedRadioID);
}
});
};
const getNextCATPollDelay = () => {
if (consecutiveCatPollFailures <= 0) {
return CAT_POLL_BASE_INTERVAL_MS;
}
return Math.min(
CAT_POLL_MAX_INTERVAL_MS,
CAT_POLL_BASE_INTERVAL_MS * Math.pow(2, consecutiveCatPollFailures - 1)
);
};
const clearCATPollWarning = () => {
$('.radio_poll_warning').remove();
};
const showCATPollWarning = () => {
const secondsSinceSuccess = lastSuccessfulCatUpdateAt === null
? null
: Math.max(0, Math.floor((Date.now() - lastSuccessfulCatUpdateAt) / 1000));
let warningText = 'Live rig sync is temporarily unstable. Retrying automatically.';
if (secondsSinceSuccess !== null) {
warningText += ` Last successful update was ${secondsSinceSuccess} seconds ago.`;
}
if ($('.radio_poll_warning').length === 0) {
$('#radio_status').prepend(
`<div class="alert alert-warning radio_poll_warning" role="alert"><i class="fas fa-exclamation-triangle"></i> ${warningText}</div>`
);
} else {
$('.radio_poll_warning').html(`<i class="fas fa-exclamation-triangle"></i> ${warningText}`);
}
};
const handleCATPollSuccess = () => {
consecutiveCatPollFailures = 0;
lastSuccessfulCatUpdateAt = Date.now();
clearCATPollWarning();
scheduleNextCATPoll(CAT_POLL_BASE_INTERVAL_MS);
};
const handleCATPollFailure = (radioID) => {
const currentSelectedRadioID = String($('select.radios option:selected').val() || '0');
if (!radioID || currentSelectedRadioID !== String(radioID)) {
return;
}
consecutiveCatPollFailures += 1;
if (consecutiveCatPollFailures >= CAT_POLL_WARNING_THRESHOLD) {
showCATPollWarning();
}
scheduleNextCATPoll(getNextCATPollDelay());
};
const scheduleNextCATPoll = (delayMs) => {
if (catPollTimer !== null) {
clearTimeout(catPollTimer);
}
catPollTimer = setTimeout(() => {
pollSelectedRadio();
}, delayMs);
};
const pollSelectedRadio = () => {
const selectedRadioID = String($('select.radios option:selected').val() || '0');
if (selectedRadioID !== '0') {
updateFromCAT(selectedRadioID);
return;
}
consecutiveCatPollFailures = 0;
clearCATPollWarning();
scheduleNextCATPoll(CAT_POLL_BASE_INTERVAL_MS);
};
// Handle login error display
const handleLoginError = () => {
$(".radio_cat_state").remove();
@ -2244,6 +2334,7 @@ $(document).ready(function() {
// Clear CAT value cache so re-selecting a radio with identical values still repopulates fields.
$('#frequency, #frequency_rx, #sat_name, #sat_mode, #transmit_power, #selectPropagation, #mode').removeData('catValue');
$(".radio_timeout_error").remove();
clearCATPollWarning();
};
// Event listeners
@ -2270,20 +2361,17 @@ $(document).ready(function() {
isSubmitting = true;
});
// Update frequency every three seconds for the selected radio
setInterval(() => {
const selectedRadioID = $('select.radios option:selected').val();
if (selectedRadioID !== '0') {
updateFromCAT(selectedRadioID);
}
}, 3000);
scheduleNextCATPoll(CAT_POLL_BASE_INTERVAL_MS);
// Trigger updateFromCAT when any <select> with class 'radios' changes
$('.radios').on('change', function() {
catSelectionContextVersion++;
consecutiveCatPollFailures = 0;
clearCATPollWarning();
const selectedRadioID = $(this).val();
if (selectedRadioID === '0') {
resetUI();
scheduleNextCATPoll(CAT_POLL_BASE_INTERVAL_MS);
} else {
updateFromCAT(selectedRadioID);
}