From 8ba3817aedd288e7afad9e2db5bb1b430963bab3 Mon Sep 17 00:00:00 2001 From: Szymon Porwolik Date: Sat, 25 Oct 2025 16:19:35 +0200 Subject: [PATCH] Refactoring --- application/controllers/Radio.php | 18 +- application/views/interface_assets/footer.php | 198 ++++++++---------- 2 files changed, 96 insertions(+), 120 deletions(-) diff --git a/application/controllers/Radio.php b/application/controllers/Radio.php index 2a5b12c1b..bca293f98 100644 --- a/application/controllers/Radio.php +++ b/application/controllers/Radio.php @@ -353,10 +353,10 @@ class Radio extends CI_Controller { return; } - // Validate frequency parameter - if (empty($frequency)) { + // Validate frequency parameter (must be numeric and positive) + if (empty($frequency) || !is_numeric($frequency) || $frequency <= 0) { header('Content-Type: application/json'); - echo json_encode(array('error' => 'missing_frequency'), JSON_PRETTY_PRINT); + echo json_encode(array('error' => 'invalid_frequency'), JSON_PRETTY_PRINT); return; } @@ -396,9 +396,14 @@ class Radio extends CI_Controller { } } - // Format: {cat_url}/{frequency}/{mode} - // If mode is not provided, default to 'usb' + // Validate and normalize mode parameter + $valid_modes = array('lsb', 'usb', 'cw', 'fm', 'am', 'rtty', 'pkt', 'dig', 'pktlsb', 'pktusb', 'pktfm'); $cat_mode = !empty($mode) ? strtolower($mode) : 'usb'; + if (!in_array($cat_mode, $valid_modes)) { + $cat_mode = 'usb'; // Default to USB for invalid modes + } + + // Format: {cat_url}/{frequency}/{mode} $url = $cat_url . '/' . $frequency . '/' . $cat_mode; // Initialize cURL for GET request @@ -406,8 +411,9 @@ class Radio extends CI_Controller { curl_setopt($ch, CURLOPT_URL, $url); curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); curl_setopt($ch, CURLOPT_TIMEOUT, 5); + curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 2); // Separate connection timeout - // Execute request (gateway requires two requests due to known issue of changing mod see https://github.com/wavelog/WaveLogGate/issues/82) + // Execute request (gateway requires two requests due to known issue of changing mode see https://github.com/wavelog/WaveLogGate/issues/82) // First request $response = curl_exec($ch); $http_code = curl_getinfo($ch, CURLINFO_HTTP_CODE); diff --git a/application/views/interface_assets/footer.php b/application/views/interface_assets/footer.php index c26c8bb20..615c289a4 100644 --- a/application/views/interface_assets/footer.php +++ b/application/views/interface_assets/footer.php @@ -59,7 +59,6 @@ var lang_notes_error_loading = ""; var lang_notes_sort = ""; var lang_notes_duplication_disabled = ""; - var lang_notes_duplicate = ""; var lang_general_word_delete = ""; var lang_general_word_duplicate = ""; var lang_notes_delete = ""; @@ -1433,9 +1432,6 @@ mymap.on('mousemove', onQsoMapMove); // Global variable for currently selected radio var selectedRadioId = null; - // CAT poll interval (used even without DX Waterfall) - var catPollInterval = config->item('cat_poll_interval') ?? 3000; ?>; - $( document ).ready(function() { // Javascript for controlling rig frequency. let websocket = null; @@ -1443,7 +1439,17 @@ mymap.on('mousemove', onQsoMapMove); let websocketEnabled = false; let websocketIntentionallyClosed = false; // Flag to prevent auto-reconnect when user switches away let CATInterval=null; - var updateFromCAT_lock =0; // This mechanism prevents multiple simultaneous calls to query the CAT interface information + var updateFromCAT_lock = 0; // This mechanism prevents multiple simultaneous calls to query the CAT interface information + var updateFromCAT_lockTimeout = null; // Timeout to release lock if AJAX fails + + // CAT Configuration Constants + const CAT_CONFIG = { + POLL_INTERVAL: config->item('cat_poll_interval') ?? 3000; ?>, + WEBSOCKET_RECONNECT_MAX: 5, + WEBSOCKET_RECONNECT_DELAY_MS: 2000, + AJAX_TIMEOUT_MS: 5000, + LOCK_TIMEOUT_MS: 10000 + }; function initializeWebSocketConnection() { try { @@ -1458,12 +1464,13 @@ mymap.on('mousemove', onQsoMapMove); try { const data = JSON.parse(event.data); handleWebSocketData(data); - console.log("websocket data received:", data); } catch (error) { + console.error("WebSocket message parsing error:", error, event.data); } }; websocket.onerror = function(error) { + console.error("WebSocket error:", error); websocketEnabled=false; }; @@ -1471,11 +1478,11 @@ mymap.on('mousemove', onQsoMapMove); websocketEnabled = false; // Only attempt to reconnect if the closure was not intentional - if (!websocketIntentionallyClosed && reconnectAttempts < 5) { + if (!websocketIntentionallyClosed && reconnectAttempts < CAT_CONFIG.WEBSOCKET_RECONNECT_MAX) { setTimeout(() => { reconnectAttempts++; initializeWebSocketConnection(); - }, 2000 * reconnectAttempts); + }, CAT_CONFIG.WEBSOCKET_RECONNECT_DELAY_MS * reconnectAttempts); } else if (!websocketIntentionallyClosed) { // Only show error if it wasn't an intentional close $(".radio_cat_state" ).remove(); @@ -1589,7 +1596,7 @@ mymap.on('mousemove', onQsoMapMove); mode: mode }, dataType: 'text', - timeout: 5000, + timeout: CAT_CONFIG.AJAX_TIMEOUT_MS, success: function(data, textStatus, jqXHR) { if (typeof onSuccess === 'function') { onSuccess(data, textStatus, jqXHR); @@ -1605,6 +1612,13 @@ mymap.on('mousemove', onQsoMapMove); } function updateCATui(data) { + // Cache frequently used DOM selectors + var $frequency = $('#frequency'); + var $band = $('#band'); + var $frequencyRx = $('#frequency_rx'); + var $bandRx = $('#band_rx'); + var $mode = $('.mode'); + // If radio name is not in data, get it from the selected radio dropdown if (!data.radio || data.radio == null || data.radio == '') { data.radio = $('select.radios option:selected').text(); @@ -1612,46 +1626,46 @@ mymap.on('mousemove', onQsoMapMove); session->userdata('user_dxwaterfall_enable') == 'Y') { ?> // DX Waterfall: Force update by clearing catValue (prevents cat2UI from blocking updates) - $('#frequency').removeData('catValue'); + $frequency.removeData('catValue'); cat_updating_frequency = true; // Set flag before CAT update // DX Waterfall: Check debounce lock before updating frequency if (typeof handleCATFrequencyUpdate === 'function') { handleCATFrequencyUpdate(data.frequency, function() { - cat2UI($('#frequency'),data.frequency,false,true,function(d){ - $('#frequency').trigger('change'); // Trigger for other event handlers + cat2UI($frequency,data.frequency,false,true,function(d){ + $frequency.trigger('change'); // Trigger for other event handlers const newBand = frequencyToBand(d); - if ($("#band").val() != newBand) { - $("#band").val(newBand).trigger('change'); // Trigger band change + if ($band.val() != newBand) { + $band.val(newBand).trigger('change'); // Trigger band change } cat_updating_frequency = false; // Clear flag after updates }); }); } else { // Fallback if dxwaterfall.js not loaded - cat2UI($('#frequency'),data.frequency,false,true,function(d){ - $('#frequency').trigger('change'); - if ($("#band").val() != frequencyToBand(d)) { - $("#band").val(frequencyToBand(d)).trigger('change'); + cat2UI($frequency,data.frequency,false,true,function(d){ + $frequency.trigger('change'); + if ($band.val() != frequencyToBand(d)) { + $band.val(frequencyToBand(d)).trigger('change'); } cat_updating_frequency = false; }); } // Standard frequency update - $('#frequency').removeData('catValue'); + $frequency.removeData('catValue'); cat_updating_frequency = true; // Set flag before CAT update - cat2UI($('#frequency'),data.frequency,false,true,function(d){ - $('#frequency').trigger('change'); - if ($("#band").val() != frequencyToBand(d)) { - $("#band").val(frequencyToBand(d)).trigger('change'); + cat2UI($frequency,data.frequency,false,true,function(d){ + $frequency.trigger('change'); + if ($band.val() != frequencyToBand(d)) { + $band.val(frequencyToBand(d)).trigger('change'); } cat_updating_frequency = false; // Clear flag after updates }); - cat2UI($('#frequency_rx'),data.frequency_rx,false,true,function(d){$("#band_rx").val(frequencyToBand(d))}); - cat2UI($('.mode'),catmode(data.mode),false,false,function(d){setRst($(".mode").val())}); + cat2UI($frequencyRx,data.frequency_rx,false,true,function(d){$bandRx.val(frequencyToBand(d))}); + cat2UI($mode,catmode(data.mode),false,false,function(d){setRst($mode.val())}); cat2UI($('#sat_name'),data.satname,false,false); cat2UI($('#sat_mode'),data.satmode,false,false); cat2UI($('#transmit_power'),data.power,false,false); @@ -1727,97 +1741,53 @@ mymap.on('mousemove', onQsoMapMove); } var updateFromCAT = function() { - if($('select.radios option:selected').val() != '0') { + if ($('select.radios option:selected').val() != '0') { + var radioID = $('select.radios option:selected').val(); - radioID = $('select.radios option:selected').val(); - if ((typeof radioID !== 'undefined') && (radioID !== null) && (radioID !== "") && (updateFromCAT_lock == 0)) { - updateFromCAT_lock = 1; - $.getJSON( "radio/json/" + radioID, function( data ) { + if ((typeof radioID !== 'undefined') && (radioID !== null) && (radioID !== '') && (updateFromCAT_lock == 0)) { + updateFromCAT_lock = 1; + // Set timeout to release lock if AJAX fails + if (updateFromCAT_lockTimeout) { + clearTimeout(updateFromCAT_lockTimeout); + } + updateFromCAT_lockTimeout = setTimeout(function() { + console.warn('CAT lock timeout - forcing release'); + updateFromCAT_lock = 0; + }, CAT_CONFIG.LOCK_TIMEOUT_MS); + + $.getJSON('radio/json/' + radioID, function(data) { if (data.error) { if (data.error == 'not_logged_in') { - $(".radio_cat_state" ).remove(); - if($('.radio_login_error').length == 0) { + $('.radio_cat_state').remove(); + if ($('.radio_login_error').length == 0) { $('.qso_panel').prepend(''); } } // Put future Errorhandling here } else { - if($('.radio_login_error').length != 0) { - $(".radio_login_error" ).remove(); + if ($('.radio_login_error').length != 0) { + $('.radio_login_error').remove(); } - // Display CAT Timeout warning based on the figure given in the config file - var minutes = Math.floor(optionslib->get_option('cat_timeout_interval'); ?> / 60); - - if(data.updated_minutes_ago > minutes) { - - session->userdata('user_dxwaterfall_enable') == 'Y') { ?> - dxwaterfall_cat_state = "none"; - - $(".radio_cat_state" ).remove(); - if($('.radio_timeout_error').length == 0) { - $('#radio_status').prepend(''); - } else { - $('.radio_timeout_error').html('Radio connection timed-out: ' + $('select.radios option:selected').text() + ' data is ' + data.updated_minutes_ago + ' minutes old.'); - } - } else { - - // Handle frequency updates - session->userdata('user_dxwaterfall_enable') == 'Y') { ?> - // DX Waterfall: Check debounce lock before updating frequency - if (typeof handleCATFrequencyUpdate === 'function') { - handleCATFrequencyUpdate(data.frequency, function() { - cat2UI($('#frequency'),data.frequency,false,true,function(d){ - $('#frequency').trigger('change'); - if ($("#band").val() != frequencyToBand(d)) { - $("#band").val(frequencyToBand(d)).trigger('change'); - } - }); - }); - } else { - // Fallback if dxwaterfall.js not loaded - cat_frequency_updating = true; - $('#frequency').removeData('catValue'); // Force update - cat2UI($('#frequency'),data.frequency,false,true,function(d){ - // $('#frequency').trigger('change'); - if ($("#band").val() != frequencyToBand(d)) { - $("#band").val(frequencyToBand(d)); // Update silently - } - cat_frequency_updating = false; - }); - } - - // Standard frequency update - cat_frequency_updating = true; - $('#frequency').removeData('catValue'); // Force update - cat2UI($('#frequency'),data.frequency,false,true,function(d){ - // $('#frequency').trigger('change'); - if ($("#band").val() != frequencyToBand(d)) { - $("#band").val(frequencyToBand(d)); // Update silently - } - cat_frequency_updating = false; - }); - - cat2UI($('#frequency_rx'),data.frequency_rx,false,true,function(d){$("#band_rx").val(frequencyToBand(d))}); - cat2UI($('.mode'),catmode(data.mode),false,false,function(d){setRst($(".mode").val())}); - cat2UI($('#sat_name'),data.satname,false,false); - cat2UI($('#sat_mode'),data.satmode,false,false); - cat2UI($('#transmit_power'),data.power,false,false); - cat2UI($('#selectPropagation'),data.prop_mode,false,false); - - session->userdata('user_dxwaterfall_enable') == 'Y') { ?> - // Set CAT state based on active radio connection type - if ($(".radios option:selected").val() == 'ws') { - dxwaterfall_cat_state = "websocket"; - } else { - dxwaterfall_cat_state = "polling"; - } - - } + // Update CAT UI with received data updateCATui(data); } + + // Clear lock timeout and release lock + if (updateFromCAT_lockTimeout) { + clearTimeout(updateFromCAT_lockTimeout); + updateFromCAT_lockTimeout = null; + } updateFromCAT_lock = 0; + }).fail(function() { + // Release lock on AJAX failure + if (updateFromCAT_lockTimeout) { + clearTimeout(updateFromCAT_lockTimeout); + updateFromCAT_lockTimeout = null; + } + updateFromCAT_lock = 0; + console.error('CAT AJAX request failed'); }); } } @@ -1826,14 +1796,14 @@ mymap.on('mousemove', onQsoMapMove); // Initialize DX_WATERFALL_CONSTANTS CAT timings based on poll interval session->userdata('user_dxwaterfall_enable') == 'Y') { ?> if (typeof initCATTimings === 'function') { - initCATTimings(catPollInterval); + initCATTimings(CAT_CONFIG.POLL_INTERVAL); } // If no radio is selected clear data - $( ".radios" ).change(function() { + $('.radios').change(function() { // Update global selected radio variable - selectedRadioId = $(".radios option:selected").val(); + selectedRadioId = $('.radios option:selected').val(); if (CATInterval) { // We've a change - stop polling if active clearInterval(CATInterval); @@ -1845,14 +1815,14 @@ mymap.on('mousemove', onQsoMapMove); websocketEnabled = false; } if (selectedRadioId == '0') { - $("#sat_name").val(""); - $("#sat_mode").val(""); - $("#frequency").val(""); - $("#frequency_rx").val(""); - $("#band_rx").val(""); - $("#selectPropagation").val($("#selectPropagation option:first").val()); - $(".radio_timeout_error" ).remove(); - $(".radio_cat_state" ).remove(); + $('#sat_name').val(''); + $('#sat_mode').val(''); + $('#frequency').val(''); + $('#frequency_rx').val(''); + $('#band_rx').val(''); + $('#selectPropagation').val($('#selectPropagation option:first').val()); + $('.radio_timeout_error').remove(); + $('.radio_cat_state').remove(); session->userdata('user_dxwaterfall_enable') == 'Y') { ?> dxwaterfall_cat_state = "none"; @@ -1868,7 +1838,7 @@ mymap.on('mousemove', onQsoMapMove); dxwaterfall_cat_state = "polling"; // Update frequency at configured interval - CATInterval=setInterval(updateFromCAT, catPollInterval); + CATInterval=setInterval(updateFromCAT, CAT_CONFIG.POLL_INTERVAL); } }); $('.radios').change(); // Initial trigger for pre-chosen radio