From 7a804b8ba6d8775f29b57b2057d3811609beb5be Mon Sep 17 00:00:00 2001 From: Youssof Date: Fri, 25 Oct 2024 15:10:01 +0000 Subject: [PATCH] Cleanup formatting and code logic for spirilink --- ui/general/assets/icons/question_help.svg | 1 + ui/general/assets/icons/settings_temp.svg | 11 + ui/general/assets/icons/settings_video.svg | 11 + ui/general/assets/icons/settings_wifi.svg | 11 + ui/general/spirilink.html | 146 ++++-------- ui/general/spirilink.js | 244 +++++++++++++-------- 6 files changed, 224 insertions(+), 200 deletions(-) create mode 100644 ui/general/assets/icons/question_help.svg create mode 100644 ui/general/assets/icons/settings_temp.svg create mode 100644 ui/general/assets/icons/settings_video.svg create mode 100644 ui/general/assets/icons/settings_wifi.svg diff --git a/ui/general/assets/icons/question_help.svg b/ui/general/assets/icons/question_help.svg new file mode 100644 index 0000000..37f672e --- /dev/null +++ b/ui/general/assets/icons/question_help.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/ui/general/assets/icons/settings_temp.svg b/ui/general/assets/icons/settings_temp.svg new file mode 100644 index 0000000..1606d6b --- /dev/null +++ b/ui/general/assets/icons/settings_temp.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/ui/general/assets/icons/settings_video.svg b/ui/general/assets/icons/settings_video.svg new file mode 100644 index 0000000..f1bc842 --- /dev/null +++ b/ui/general/assets/icons/settings_video.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/ui/general/assets/icons/settings_wifi.svg b/ui/general/assets/icons/settings_wifi.svg new file mode 100644 index 0000000..c0daa10 --- /dev/null +++ b/ui/general/assets/icons/settings_wifi.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/ui/general/spirilink.html b/ui/general/spirilink.html index 0d702de..891072f 100644 --- a/ui/general/spirilink.html +++ b/ui/general/spirilink.html @@ -11,84 +11,43 @@ -
-
+
+

SpiriLink Configuration

-

Configuration File:

-

SpiriLink provides Mavlink, RTSP video and a UDP tunnel over a modified 8812EU wireless card to provide a long distance, high speed wireless link. Edit the configuration settings below.

+

Configuration File:

+

SpiriLink provides MAVLink, RTSP video, and a UDP tunnel over a modified 8812EU wireless card to provide a long distance, high speed wireless link. Edit the configuration settings below.

+ +
+
+ +
+
+ +
+
+ +

+
         
         
- WiFi Configuration + WiFi Configuration
- +
@@ -107,7 +66,7 @@
- Temperature Configuration + Temperature Configuration
@@ -125,33 +84,30 @@
- +
- Drone MAVLink Configuration + Drone Video Configuration
- - + +
- - + +
- - + +
- - + +
@@ -159,33 +115,30 @@
- +
- Drone Video Configuration + Drone MAVLink Configuration
- - + +
- - + +
- - + +
- - + +
@@ -198,21 +151,21 @@
- MAVLink Settings + MAVLink Settings
- +
- +
- +
@@ -224,19 +177,6 @@
- - -
-
- - -
-
- -
-
- -

     
diff --git a/ui/general/spirilink.js b/ui/general/spirilink.js index 96ff835..c4b1ee0 100644 --- a/ui/general/spirilink.js +++ b/ui/general/spirilink.js @@ -23,114 +23,143 @@ const mavlinkSysId = document.getElementById("mavlinkSysId"); const mavlinkCompId = document.getElementById("mavlinkCompId"); const mavlinkTcpPort = document.getElementById("mavlinkTcpPort"); +const output = document.getElementById("output"); + +const wifiChannels = [ + // 2.4 GHz Channels + ["1", "1 (2.412 GHz)"], ["2", "2 (2.417 GHz)"], ["3", "3 (2.422 GHz)"], + ["4", "4 (2.427 GHz)"], ["5", "5 (2.432 GHz)"], ["6", "6 (2.437 GHz)"], + ["7", "7 (2.442 GHz)"], ["8", "8 (2.447 GHz)"], ["9", "9 (2.452 GHz)"], + ["10", "10 (2.457 GHz)"], ["11", "11 (2.462 GHz)"], ["12", "12 (2.467 GHz)"], + ["13", "13 (2.472 GHz)"], ["14", "14 (2.484 GHz, Japan only)"], + // 5 GHz Channels + ["36", "36 (5.180 GHz)"], ["40", "40 (5.200 GHz)"], ["44", "44 (5.220 GHz)"], + ["48", "48 (5.240 GHz)"], ["52", "52 (5.260 GHz)"], ["56", "56 (5.280 GHz)"], + ["60", "60 (5.300 GHz)"], ["64", "64 (5.320 GHz)"], ["100", "100 (5.500 GHz)"], + ["104", "104 (5.520 GHz)"], ["108", "108 (5.540 GHz)"], ["112", "112 (5.560 GHz)"], + ["116", "116 (5.580 GHz)"], ["120", "120 (5.600 GHz)"], ["124", "124 (5.620 GHz)"], + ["128", "128 (5.640 GHz)"], ["132", "132 (5.660 GHz)"], ["136", "136 (5.680 GHz)"], + ["140", "140 (5.700 GHz)"], ["149", "149 (5.745 GHz)"], ["153", "153 (5.765 GHz)"], + ["157", "157 (5.785 GHz)"], ["161", "161 (5.805 GHz)"], ["165", "165 (5.825 GHz)"] +] + +const wifiRegions = [ + ["BO", "BO (Max TX)"], + ["US", "US (United States)"], + ["EU", "EU (Europe)"], + ["JP", "JP (Japan)"], + ["AU", "AU (Australia, Max TX)"], + ["CA", "CA (Canada)"], + ["CN", "CN (China)"], + ["IN", "IN (India)"], + ["ZA", "ZA (South Africa)"], + ["KR", "KR (South Korea)"] +] + +const connectionTypes = [ + ["listen", "Listen"], + ["connect", "Connect"] +] + // Load initial settings -document.onload = InitPage(); +document.onload = initPage(); // Save file button -document.getElementById("save").addEventListener("click", SaveSettings); +document.getElementById("save").addEventListener("click", saveSettings); // Apply encryption file button -document.getElementById("applyEncryptionFile").addEventListener("click", ApplyEncryptionFile); +document.getElementById("applyEncryptionFile").addEventListener("click", applyEncryptionFile); // Function to initialize the page -function InitPage() { - cockpit.script(scriptLocation + "cockpitScript.sh -v") +function initPage() { file_location.innerHTML = confLocation; + const inputs = document.querySelectorAll("input, select, textarea"); + inputs.forEach(input => { + input.addEventListener("focus", clearResult); + }); + cockpit.file(confLocation) - .read() - .then((content) => { - console.log("Configuration file content:", content); - SuccessReadFile(content); - }) - .catch(error => { - console.error("Failed to read configuration file:", error); - FailureReadFile(error); - }); + .read().then((content) => successReadFile(content)) + .catch(error => failureReadFile(error)); } // Load configuration values from the configuration file -function SuccessReadFile(content) { +function successReadFile(content) { try { // WiFi Configuration - wifiChannel.value = getValueByKey(content, "common", "wifi_channel"); - console.log("wifi_channel:", wifiChannel.value); - // Remove surrounding quotes from wifi_region value if present - wifiRegion.value = getValueByKey(content, "common", "wifi_region").replace(/^['"]|['"]$/g, ""); - console.log("wifi_region:", wifiRegion.value); - - // Handle "None" value for wifi_txpower + const currentWifiChannel = getValueByKey(content, "common", "wifi_channel"); + const currentWifiRegion = getValueByKey(content, "common", "wifi_region").replace(/^['"]|['"]$/g, ""); const txPowerValue = getValueByKey(content, "common", "wifi_txpower"); - wifiTxPower.value = txPowerValue === "None" ? "" : txPowerValue; - console.log("wifi_txpower:", wifiTxPower.value); - - tempInterval.value = getValueByKey(content, "common", "temp_measurement_interval"); - console.log("temp_measurement_interval:", tempInterval.value); - - tempWarning.value = getValueByKey(content, "common", "temp_overheat_warning"); - console.log("temp_overheat_warning:", tempWarning.value); - // Drone MAVLink Configuration - droneMavlinkFwmark.value = getValueByKey(content, "drone_mavlink", "fwmark"); - console.log("drone_mavlink_fwmark:", droneMavlinkFwmark.value); + addDropDown(wifiChannel, wifiChannels, currentWifiChannel); + addDropDown(wifiRegion, wifiRegions, currentWifiRegion); + wifiTxPower.value = txPowerValue === "None" ? "" : txPowerValue; // None should result in an empty string - const droneMavlinkPeer = getValueByKey(content, "drone_mavlink", "peer"); - console.log("drone_mavlink_peer:", droneMavlinkPeer); - if (droneMavlinkPeer) { - parsePeer(droneMavlinkPeer, droneMavlinkType, droneMavlinkIP, droneMavlinkPort); - } + // Temperature Configuration + tempInterval.value = getValueByKey(content, "common", "temp_measurement_interval"); + tempWarning.value = getValueByKey(content, "common", "temp_overheat_warning"); // Drone Video Configuration droneVideoFwmark.value = getValueByKey(content, "drone_video", "fwmark"); - console.log("drone_video_fwmark:", droneVideoFwmark.value); - const droneVideoPeer = getValueByKey(content, "drone_video", "peer"); - console.log("drone_video_peer:", droneVideoPeer); if (droneVideoPeer) { - parsePeer(droneVideoPeer, droneVideoType, droneVideoIP, droneVideoPort); + const peer = parsePeer(droneVideoPeer); + addDropDown(droneVideoType, connectionTypes, droneVideoType.value); + droneVideoIP.value = peer.ip; + droneVideoPort.value = peer.port; } - // MAVLink Configuration - injectRssi.checked = getValueByKey(content, "mavlink", "inject_rssi") === "True"; - console.log("inject_rssi:", injectRssi.checked); - - mavlinkSysId.value = getValueByKey(content, "mavlink", "mavlink_sys_id"); - console.log("mavlink_sys_id:", mavlinkSysId.value); - - mavlinkCompId.value = getValueByKey(content, "mavlink", "mavlink_comp_id"); - console.log("mavlink_comp_id:", mavlinkCompId.value); + // Drone MAVLink Configuration + droneMavlinkFwmark.value = getValueByKey(content, "drone_mavlink", "fwmark"); + const droneMavlinkPeer = getValueByKey(content, "drone_mavlink", "peer"); + if (droneMavlinkPeer) { + const peer = parsePeer(droneMavlinkPeer); + addDropDown(droneMavlinkType, connectionTypes, peer.type); + droneMavlinkIP.value = peer.ip; + droneMavlinkPort.value = peer.port; + } + // MAVLink Settings + injectRssi.checked = getValueByKey(content, "mavlink", "inject_rssi") === "True"; + mavlinkSysId.value = getValueByKey(content, "mavlink", "mavlink_sys_id"); + mavlinkCompId.value = getValueByKey(content, "mavlink", "mavlink_comp_id"); const tcpPortValue = getValueByKey(content, "mavlink", "mavlink_tcp_port"); mavlinkTcpPort.value = tcpPortValue === "None" ? "" : tcpPortValue; - console.log("mavlink_tcp_port:", mavlinkTcpPort.value); + } catch (e) { - console.error("Error parsing configuration file:", e); - FailureReadFile(e); + failureReadFile(e); } } +function failureReadFile(error) { + // Display error message + console.log("Error : " + error.message); + displayFail(error.message) +} + // Save configuration values to the configuration file -function SaveSettings() { +function saveSettings() { try { cockpit.file(confLocation) .read() .then((content) => { - // WiFi Configuration + // WiFi & Temperature Configuration content = setValueByKey(content, "[common]", "wifi_channel", wifiChannel.value); content = setValueByKey(content, "[common]", "wifi_region", wifiRegion.value); content = setValueByKey(content, "[common]", "wifi_txpower", wifiTxPower.value === "" ? "None" : wifiTxPower.value); content = setValueByKey(content, "[common]", "temp_measurement_interval", tempInterval.value); content = setValueByKey(content, "[common]", "temp_overheat_warning", tempWarning.value); + + // Drone Video Configuration + const droneVideoPeer = `${droneVideoType.value}://${droneVideoIP.value}:${droneVideoPort.value}`; + content = setValueByKey(content, "[drone_video]", "fwmark", droneVideoFwmark.value); + content = setValueByKey(content, "[drone_video]", "peer", droneVideoPeer); // Drone MAVLink Configuration const droneMavlinkPeer = `${droneMavlinkType.value}://${droneMavlinkIP.value}:${droneMavlinkPort.value}`; content = setValueByKey(content, "[drone_mavlink]", "fwmark", droneMavlinkFwmark.value); content = setValueByKey(content, "[drone_mavlink]", "peer", droneMavlinkPeer); - // Drone Video Configuration - const droneVideoPeer = `${droneVideoType.value}://${droneVideoIP.value}:${droneVideoPort.value}`; - content = setValueByKey(content, "[drone_video]", "fwmark", droneVideoFwmark.value); - content = setValueByKey(content, "[drone_video]", "peer", droneVideoPeer); - // MAVLink Configuration content = setValueByKey(content, "[mavlink]", "inject_rssi", injectRssi.checked ? "True" : "False"); content = setValueByKey(content, "[mavlink]", "mavlink_sys_id", mavlinkSysId.value); @@ -139,27 +168,24 @@ function SaveSettings() { cockpit.file(confLocation, { superuser: "try" }).replace(content) .then(() => { - console.log("Configuration saved successfully."); - RestartWifibroadcastService(); - Success(); + restartWifibroadcastService(); + displaySuccess("Configuration saved successfully."); }) .catch((error) => { - console.error("Failed to save configuration:", error); - Fail(error); + displayFail("Failed to save configuration: " + error.message); }); }) .catch(error => { - console.error("Failed to read configuration file:", error); - FailureReadFile(error); + displayFail("Failed to save configuration: " + error.message); }); } catch (e) { console.error("Error during save operation:", e); - FailureReadFile(e); + failureReadFile(e); } } // Apply encryption file from base station -function ApplyEncryptionFile() { +function applyEncryptionFile() { const command = ` /usr/bin/sshpass -p 'spirifriend' scp -o StrictHostKeyChecking=no spiri@spiri-base.local:/home/spiri/drone.key /tmp/drone.key && /usr/bin/sudo /bin/mv /tmp/drone.key ${encryptionFileLocation} && @@ -168,56 +194,82 @@ function ApplyEncryptionFile() { cockpit.spawn(["bash", "-c", command], { superuser: "require" }) .then(() => { - console.log("Encryption key applied and wifibroadcast service restarted."); - Success(); + displaySuccess("Encryption key applied and wifibroadcast service restarted."); }) .catch((error) => { console.error("Failed to apply encryption key or restart service:", error); - Fail(error); + displayFail("Failed to apply encryption key or restart service: " + error); }); } - // Restart wifibroadcast service -function RestartWifibroadcastService() { +function restartWifibroadcastService() { cockpit.spawn(["systemctl", "restart", "wifibroadcast@drone"], { superuser: "require" }) .then(() => { - console.log("wifibroadcast@drone service restarted."); + displaySuccess("wifibroadcast@drone service restarted."); }) .catch((error) => { console.error("Failed to restart wifibroadcast@drone service:", error); + displayFail("Failed to restart wifibroadcast@drone service: " + error); }); } -// Success handler -function Success() { +// Display Success result +function displaySuccess(msg) { result.style.color = "green"; - result.innerHTML = "Success, settings saved."; - setTimeout(() => result.innerHTML = "", 5000); + if (result.innerHTML === "") + result.innerHTML = msg; + else + result.innerHTML += "
" + msg; + + // setTimeout(() => result.innerHTML = "", 5000); } -// Failure handler -function Fail(error) { +// Display failure message +function displayFail(error) { result.style.color = "red"; - result.innerHTML = error.message; + result.innerHTML = "Error : " + error; +} + +// Clear the result message +function clearResult() { + result.innerHTML = ""; +} + +function addDropDown(box, pairs, defaultValue) { + try { + for(let i = 0; i < pairs.length; i++){ + if (pairs[i].length == 0 || pairs[i][0] === "" || pairs[i][1] === "") continue; + const option = document.createElement("option"); + option.value = pairs[i][0]; + option.text = pairs[i][1]; + box.add(option); + if (defaultValue === option.value) { + console.log("found") + box.value = option.value; + } + } + } + catch(e) { + displayFail(e) + } } // Parse Peer configuration (type, IP, port) -function parsePeer(peer, typeElement, ipElement, portElement) { - if (!peer) { - console.warn("Peer value is empty or undefined."); - return; // Skip if peer is empty - } +function parsePeer(peerString) { + const peer = { type: "", ip: "", port: "" }; + + if (!peerString) return peer; const regex = /(listen|connect):\/\/([\d.]+):(\d+)/; - const match = peer.match(regex); + const match = peerString.match(regex); if (match) { - typeElement.value = match[1]; - ipElement.value = match[2]; - portElement.value = match[3]; - } else { - console.error("Failed to parse peer:", peer); + peer.type = match[1]; + peer.ip = match[2]; + peer.port = match[3]; } + + return peer; } // Get value by key from configuration content @@ -225,11 +277,9 @@ function getValueByKey(content, section, key) { const sectionRegex = new RegExp(`\\[${section}\\][\\s\\S]*?^${key}\\s*=\\s*(.*)`, "m"); const match = content.match(sectionRegex); if (match) { - let value = match[1].trim(); - console.log(`Found key [${key}] in section [${section}]:`, value); + const value = match[1].trim(); return value; } else { - console.warn(`Key [${key}] not found in section [${section}]`); return ""; } }