feat: implement silent hybrid websocket connection and simplify labels

- Implemented a 'Single-Shot Silent Attempt' in cat.js. This tries to establish a WebSocket connection (with 1 retry) even when a standard Radio Profile is selected, enabling hybrid integrations to receive metadata without spamming the console for other users.
- Updated radio dropdown labels in Bandmap, Contesting, and QSO views to 'Live - WebSocket', removing the specific 'Requires WLGate' text to be tool-agnostic.
This commit is contained in:
TnxQSO-Admin
2026-02-03 16:44:30 +01:00
parent a81a165324
commit c76b32a13a
4 changed files with 54 additions and 28 deletions

View File

@@ -237,7 +237,7 @@
<small class="text-muted me-1 flex-shrink-0 d-none d-md-inline"><?= __("TRX:"); ?></small>
<select class="form-select form-select-sm radios flex-shrink-0" id="radio" name="radio" style="width: auto;">
<option value="0" selected="selected"><?= __("None"); ?></option>
<option value="ws"<?php if ($this->session->userdata('radio') == 'ws') { echo ' selected="selected"'; } ?>><?= __("Live - ") . __("WebSocket (Requires WLGate>=1.1.10)"); ?></option>
<option value="ws"<?php if ($this->session->userdata('radio') == 'ws') { echo ' selected="selected"'; } ?>><?= __("Live - WebSocket"); ?></option>
<?php foreach ($radios->result() as $row) { ?>
<option value="<?php echo $row->id; ?>" <?php if($this->session->userdata('radio') == $row->id) { echo "selected=\"selected\""; } ?>><?= __("Polling - ") . $row->radio; ?><?php if ($radio_last_updated->id == $row->id) { echo " (".__("last updated").")"; } else { echo ''; } ?></option>
<?php } ?>

View File

@@ -157,7 +157,7 @@
<label for="inputRadio"><?= __("Radio"); ?></label>
<select class="form-select form-select-sm radios" id="radio" name="radio">
<option value="0" selected="selected"><?= __("None"); ?></option>
<option value="ws"<?php if ($this->session->userdata('radio') == 'ws') { echo ' selected="selected"'; } ?>><?= __("WebSocket (Requires WLGate>1.1.10)"); ?></option>
<option value="ws"<?php if ($this->session->userdata('radio') == 'ws') { echo ' selected="selected"'; } ?>><?= __("Live - WebSocket"); ?></option>
<?php foreach ($radios->result() as $row) { ?>
<option value="<?php echo $row->id; ?>" <?php if($this->session->userdata('radio') == $row->id) { echo "selected=\"selected\""; } ?>><?php echo $row->radio; ?> <?php if ($radio_last_updated->id == $row->id) { echo "(".__("last updated").")"; } else { echo ''; } ?></option>
<?php } ?>

View File

@@ -408,7 +408,7 @@ if (typeof window.DX_WATERFALL_FIELD_MAP === 'undefined') {
<label for="radio"><?= __("Radio"); ?></label>
<select class="form-select radios" id="radio" name="radio">
<option value="0" selected="selected"><?= __("None"); ?></option>
<option value="ws"<?php if ($this->session->userdata('radio') == 'ws' && $manual_mode == '0') { echo ' selected="selected"'; } ?>><?= __("Live - ") . __("WebSocket (Requires WLGate>=1.1.10)"); ?></option>
<option value="ws"<?php if ($this->session->userdata('radio') == 'ws' && $manual_mode == '0') { echo ' selected="selected"'; } ?>><?= __("Live - WebSocket"); ?></option>
<?php foreach ($radios->result() as $row) { ?>
<option value="<?php echo $row->id; ?>" <?php if($this->session->userdata('radio') == $row->id && $manual_mode == '0') { echo "selected=\"selected\""; } ?>><?= __("Polling - ") . $row->radio; ?> <?php if ($radio_last_updated->id == $row->id) { echo "(".__("last updated").")"; } else { echo ''; } ?></option>
<?php } ?>

View File

@@ -78,6 +78,7 @@ $(document).ready(function() {
POLL_INTERVAL: 3000, // Polling interval in milliseconds
WEBSOCKET_RECONNECT_MAX: 5,
WEBSOCKET_RECONNECT_DELAY_MS: 2000,
HYBRID_WS_MAX_RETRIES: 1,
AJAX_TIMEOUT_MS: 5000,
LOCK_TIMEOUT_MS: 10000
};
@@ -131,6 +132,11 @@ $(document).ready(function() {
reconnectAttempts = 0;
websocketEnabled = true;
activeWebSocketProtocol = protocol; // Remember which protocol worked
// Debug log if connected in Hybrid/Auto mode
if (isHybridMode) {
console.log("CAT: Hybrid WebSocket connected successfully.");
}
};
websocket.onmessage = function(event) {
@@ -157,8 +163,9 @@ $(document).ready(function() {
return;
}
// Original error handling for when both protocols have been tried
if ($('.radios option:selected').val() != '0') {
// Error handling: Only show GUI error if NOT in hybrid mode
// In hybrid mode, we fail silently to avoid annoying users without a WS server
if (!isHybridMode && $('.radios option:selected').val() != '0') {
var radioName = $('select.radios option:selected').text();
displayRadioStatus('error', radioName);
}
@@ -173,15 +180,19 @@ $(document).ready(function() {
hasTriedWsFallback = false;
}
// Determine max retries: standard limit or reduced limit for hybrid mode
const maxRetries = isHybridMode ? CAT_CONFIG.HYBRID_WS_MAX_RETRIES : CAT_CONFIG.WEBSOCKET_RECONNECT_MAX;
// Only attempt to reconnect if the closure was not intentional
if (!websocketIntentionallyClosed && reconnectAttempts < CAT_CONFIG.WEBSOCKET_RECONNECT_MAX) {
if (!websocketIntentionallyClosed && reconnectAttempts < maxRetries) {
setTimeout(() => {
reconnectAttempts++;
initializeWebSocketConnection();
}, CAT_CONFIG.WEBSOCKET_RECONNECT_DELAY_MS * reconnectAttempts);
}, CAT_CONFIG.WEBSOCKET_RECONNECT_DELAY_MS * (reconnectAttempts + 1)); // Progressive delay
} else if (!websocketIntentionallyClosed) {
// Only show error if it wasn't an intentional close AND radio is not "None"
if ($('.radios option:selected').val() != '0') {
// AND we are NOT in hybrid mode
if (!isHybridMode && $('.radios option:selected').val() != '0') {
var radioName = $('select.radios option:selected').text();
displayRadioStatus('error', radioName);
}
@@ -189,7 +200,7 @@ $(document).ready(function() {
}
};
} catch (error) {
websocketEnabled=false;
websocketEnabled = false;
}
}
@@ -1246,6 +1257,9 @@ $(document).ready(function() {
radioCatUrlCache = {};
radioNameCache = {};
// Reset Hybrid Mode flag
isHybridMode = false;
// If switching to None, disable CAT tracking FIRST before stopping connections
// This prevents any pending updates from interfering with the offline status
if (selectedRadioId == '0') {
@@ -1290,6 +1304,8 @@ $(document).ready(function() {
websocketIntentionallyClosed = false; // Reset flag when opening WebSocket
reconnectAttempts = 0; // Reset reconnect attempts
hasTriedWsFallback = false; // Reset WSS failover state - try WSS first again
isHybridMode = false; // Explicitly NOT hybrid
// Set DX Waterfall CAT state to websocket if variable exists
if (typeof dxwaterfall_cat_state !== 'undefined') {
dxwaterfall_cat_state = "websocket";
@@ -1312,6 +1328,16 @@ $(document).ready(function() {
// Start standard polling
CATInterval = setInterval(updateFromCAT, CAT_CONFIG.POLL_INTERVAL);
// Attempt Hybrid WebSocket Connection (Silent, limited retries)
// We try to connect to localhost to receive metadata/lookup broadcasts
// even though we are in Polling mode for frequency updates.
websocketIntentionallyClosed = false;
reconnectAttempts = 0;
hasTriedWsFallback = false;
isHybridMode = true; // Activate Hybrid Mode restrictions (limited retries, no UI errors)
initializeWebSocketConnection();
if ((window.CAT_COMPACT_MODE === 'ultra-compact' || window.CAT_COMPACT_MODE === 'icon-only') && typeof window.isCatTrackingEnabled !== 'undefined' && !window.isCatTrackingEnabled) {
displayOfflineStatus('cat_disabled');
}