From a246a11d5323a971ec253753ea6aa750b2a6e833 Mon Sep 17 00:00:00 2001 From: Mashintime Date: Mon, 8 Jan 2024 17:01:26 -0500 Subject: [PATCH 01/71] GUI Build Fix? --- gui/vite.config.ts | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/gui/vite.config.ts b/gui/vite.config.ts index 9933d2cd..cb5eb5c8 100644 --- a/gui/vite.config.ts +++ b/gui/vite.config.ts @@ -15,6 +15,14 @@ export default defineConfig(({ command }) => { const sourcemap = isServe || !!process.env.VSCODE_DEBUG; return { + optimizeDeps: { + esbuildOptions: { + target: 'esnext' + } + }, + build: { + target: 'esnext' + }, plugins: [ vue(), electron([ From 15d6ba4fcaad555b0482418b3ad662bfa3c48147 Mon Sep 17 00:00:00 2001 From: codefactor-io Date: Mon, 8 Jan 2024 22:01:50 +0000 Subject: [PATCH 02/71] [CodeFactor] Apply fixes to commit a246a11 --- gui/vite.config.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/gui/vite.config.ts b/gui/vite.config.ts index cb5eb5c8..5b79ebb8 100644 --- a/gui/vite.config.ts +++ b/gui/vite.config.ts @@ -17,11 +17,11 @@ export default defineConfig(({ command }) => { return { optimizeDeps: { esbuildOptions: { - target: 'esnext' - } + target: "esnext", + }, }, build: { - target: 'esnext' + target: "esnext", }, plugins: [ vue(), From 1637b7bd59d71e4d470759e02608d3c2be478fa1 Mon Sep 17 00:00:00 2001 From: DJ2LS Date: Tue, 9 Jan 2024 10:19:55 +0100 Subject: [PATCH 03/71] slightly adjusted connection state --- gui/src/js/eventHandler.js | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/gui/src/js/eventHandler.js b/gui/src/js/eventHandler.js index 51fcc01c..36f44b36 100644 --- a/gui/src/js/eventHandler.js +++ b/gui/src/js/eventHandler.js @@ -1,4 +1,3 @@ -import { addDataToWaterfall } from "../js/waterfallHandler.js"; import { newMessageReceived, @@ -23,8 +22,10 @@ export function connectionFailed(endpoint, event) { } export function stateDispatcher(data) { data = JSON.parse(data); - //console.log(data); + console.log(data); if (data["type"] == "state-change" || data["type"] == "state") { + stateStore.modem_connection = "connected"; + stateStore.channel_busy = data["channel_busy"]; stateStore.is_codec2_traffic = data["is_codec2_traffic"]; stateStore.is_modem_running = data["is_modem_running"]; From 20b9988bb9131104fc9801deefe62d24dd97bfca Mon Sep 17 00:00:00 2001 From: codefactor-io Date: Tue, 9 Jan 2024 09:20:14 +0000 Subject: [PATCH 04/71] [CodeFactor] Apply fixes to commit 1637b7b --- gui/src/js/eventHandler.js | 1 - 1 file changed, 1 deletion(-) diff --git a/gui/src/js/eventHandler.js b/gui/src/js/eventHandler.js index 36f44b36..bba549f4 100644 --- a/gui/src/js/eventHandler.js +++ b/gui/src/js/eventHandler.js @@ -1,4 +1,3 @@ - import { newMessageReceived, newBeaconReceived, From 8fa3fbea566121d405bc5f85336c330406d06bb7 Mon Sep 17 00:00:00 2001 From: Mashintime Date: Wed, 10 Jan 2024 19:34:47 -0500 Subject: [PATCH 05/71] Update windows batch files --- gui/GUI-Install-Requirements.bat | 8 ++++++++ tools/Windows/TNC-Launch.bat => gui/GUI-Launch.bat | 4 ++-- gui/GUI-Update-Requirements.bat | 8 ++++++++ modem/Modem-Install-Requrements.bat | 5 +++++ modem/Modem-Launch.bat | 10 ++++++++++ modem/Modem-list-audio-devs.bat | 6 ++++++ tools/Windows/GUI-Install-Requirements.bat | 7 +++++-- tools/Windows/GUI-Launch.bat | 4 ++-- tools/Windows/GUI-Update-Requirements.bat | 7 +++++-- tools/Windows/Modem-Install-Requrements.bat | 5 +++++ tools/Windows/Modem-Launch.bat | 10 ++++++++++ tools/Windows/Modem-list-audio-devs.bat | 6 ++++++ tools/Windows/TNC-Install-Requrements.bat | 5 ----- tools/Windows/copy-files.bat | 8 ++++++-- 14 files changed, 78 insertions(+), 15 deletions(-) create mode 100644 gui/GUI-Install-Requirements.bat rename tools/Windows/TNC-Launch.bat => gui/GUI-Launch.bat (62%) create mode 100644 gui/GUI-Update-Requirements.bat create mode 100644 modem/Modem-Install-Requrements.bat create mode 100644 modem/Modem-Launch.bat create mode 100644 modem/Modem-list-audio-devs.bat create mode 100644 tools/Windows/Modem-Install-Requrements.bat create mode 100644 tools/Windows/Modem-Launch.bat create mode 100644 tools/Windows/Modem-list-audio-devs.bat delete mode 100644 tools/Windows/TNC-Install-Requrements.bat diff --git a/gui/GUI-Install-Requirements.bat b/gui/GUI-Install-Requirements.bat new file mode 100644 index 00000000..c61dab25 --- /dev/null +++ b/gui/GUI-Install-Requirements.bat @@ -0,0 +1,8 @@ +@echo off +REM Place this batch file in FreeData/gui and then run it +REM ie. c:\FD-Src\gui +echo Install requirements for GUI... + +call npm install + +pause \ No newline at end of file diff --git a/tools/Windows/TNC-Launch.bat b/gui/GUI-Launch.bat similarity index 62% rename from tools/Windows/TNC-Launch.bat rename to gui/GUI-Launch.bat index 8ac7af61..937c86bc 100644 --- a/tools/Windows/TNC-Launch.bat +++ b/gui/GUI-Launch.bat @@ -1,5 +1,5 @@ REM Place this batch file in FreeData/tnc and then run it -REM ie. c:\FD-Src\tnc +REM ie. c:\FD-Src\gui -python daemon.py +call npm start pause \ No newline at end of file diff --git a/gui/GUI-Update-Requirements.bat b/gui/GUI-Update-Requirements.bat new file mode 100644 index 00000000..4746422d --- /dev/null +++ b/gui/GUI-Update-Requirements.bat @@ -0,0 +1,8 @@ +@echo off +REM Place this batch file in FreeData/tnc and then run it +REM ie. c:\FD-Src\gui +echo Checking and install for updated requirements + +call npm update + +pause \ No newline at end of file diff --git a/modem/Modem-Install-Requrements.bat b/modem/Modem-Install-Requrements.bat new file mode 100644 index 00000000..b7acb064 --- /dev/null +++ b/modem/Modem-Install-Requrements.bat @@ -0,0 +1,5 @@ +REM Place this batch file in FreeData/modem and then run it +REM ie. c:\FD-Src\modem + +python -m pip install -r ..\requirements.txt +pause \ No newline at end of file diff --git a/modem/Modem-Launch.bat b/modem/Modem-Launch.bat new file mode 100644 index 00000000..80d92f3f --- /dev/null +++ b/modem/Modem-Launch.bat @@ -0,0 +1,10 @@ +REM Place this batch file in FreeData/tnc and then run it +REM ie. c:\FD-Src\tnc + +REM Set environment variable to let modem know where to find config, change if you need to specify a different config +set FREEDATA_CONFIG=.\config.ini + +REM launch modem +flask --app server run + +pause \ No newline at end of file diff --git a/modem/Modem-list-audio-devs.bat b/modem/Modem-list-audio-devs.bat new file mode 100644 index 00000000..bf15556d --- /dev/null +++ b/modem/Modem-list-audio-devs.bat @@ -0,0 +1,6 @@ +@echo off +REM PLace in modem directory and run to retrieve list of audio devices; you'll need the CRC for the config.ini + +python ..\tools\list_audio_devices.py + +pause \ No newline at end of file diff --git a/tools/Windows/GUI-Install-Requirements.bat b/tools/Windows/GUI-Install-Requirements.bat index ecd0c113..c61dab25 100644 --- a/tools/Windows/GUI-Install-Requirements.bat +++ b/tools/Windows/GUI-Install-Requirements.bat @@ -1,5 +1,8 @@ -REM Place this batch file in FreeData/tnc and then run it -REM ie. c:\FD-Src\gui_vue +@echo off +REM Place this batch file in FreeData/gui and then run it +REM ie. c:\FD-Src\gui +echo Install requirements for GUI... call npm install + pause \ No newline at end of file diff --git a/tools/Windows/GUI-Launch.bat b/tools/Windows/GUI-Launch.bat index 8b82932d..3d54f81c 100644 --- a/tools/Windows/GUI-Launch.bat +++ b/tools/Windows/GUI-Launch.bat @@ -1,5 +1,5 @@ -REM Place this batch file in FreeData/tnc and then run it -REM ie. c:\FD-Src\gui_vue +REM Place this batch file in FreeData/gui and then run it +REM ie. c:\FD-Src\gui call npm start pause \ No newline at end of file diff --git a/tools/Windows/GUI-Update-Requirements.bat b/tools/Windows/GUI-Update-Requirements.bat index 510b180e..f6ae7248 100644 --- a/tools/Windows/GUI-Update-Requirements.bat +++ b/tools/Windows/GUI-Update-Requirements.bat @@ -1,5 +1,8 @@ -REM Place this batch file in FreeData/tnc and then run it -REM ie. c:\FD-Src\gui_vue +@echo off +REM Place this batch file in FreeData/gui and then run it +REM ie. c:\FD-Src\gui +echo Check for and install updated requirements call npm update + pause \ No newline at end of file diff --git a/tools/Windows/Modem-Install-Requrements.bat b/tools/Windows/Modem-Install-Requrements.bat new file mode 100644 index 00000000..b7acb064 --- /dev/null +++ b/tools/Windows/Modem-Install-Requrements.bat @@ -0,0 +1,5 @@ +REM Place this batch file in FreeData/modem and then run it +REM ie. c:\FD-Src\modem + +python -m pip install -r ..\requirements.txt +pause \ No newline at end of file diff --git a/tools/Windows/Modem-Launch.bat b/tools/Windows/Modem-Launch.bat new file mode 100644 index 00000000..6a02ae79 --- /dev/null +++ b/tools/Windows/Modem-Launch.bat @@ -0,0 +1,10 @@ +REM Place this batch file in FreeData/modem and then run it +REM ie. c:\FD-Src\modem + +REM Set environment variable to let modem know where to find config, change if you need to specify a different config +set FREEDATA_CONFIG=.\config.ini + +REM launch modem +flask --app server run + +pause \ No newline at end of file diff --git a/tools/Windows/Modem-list-audio-devs.bat b/tools/Windows/Modem-list-audio-devs.bat new file mode 100644 index 00000000..bf15556d --- /dev/null +++ b/tools/Windows/Modem-list-audio-devs.bat @@ -0,0 +1,6 @@ +@echo off +REM PLace in modem directory and run to retrieve list of audio devices; you'll need the CRC for the config.ini + +python ..\tools\list_audio_devices.py + +pause \ No newline at end of file diff --git a/tools/Windows/TNC-Install-Requrements.bat b/tools/Windows/TNC-Install-Requrements.bat deleted file mode 100644 index bdbe4e6b..00000000 --- a/tools/Windows/TNC-Install-Requrements.bat +++ /dev/null @@ -1,5 +0,0 @@ -REM Place this batch file in FreeData/tnc and then run it -REM ie. c:\FD-Src\tnc - -python -m pip install -r ..\requirements.txt -pause \ No newline at end of file diff --git a/tools/Windows/copy-files.bat b/tools/Windows/copy-files.bat index 02ecffb9..f51b3fa8 100644 --- a/tools/Windows/copy-files.bat +++ b/tools/Windows/copy-files.bat @@ -1,6 +1,10 @@ +@echo off REM This will copy the helper batch files to the approriate places for you -copy GUI* ..\..\gui_vue\ -copy TNC* ..\..\tnc\ +echo Copying GUI scripts to GUI directory +copy GUI* ..\..\gui\ + +echo Copying Modem scripts to Modem directory +copy MODEM* ..\..\modem\ pause \ No newline at end of file From 1a927f66c7708452fdd032625bf93765940c8782 Mon Sep 17 00:00:00 2001 From: Mashintime Date: Wed, 10 Jan 2024 19:47:10 -0500 Subject: [PATCH 06/71] Fix stop grid widget. --- gui/src/components/grid/grid_stop.vue | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/gui/src/components/grid/grid_stop.vue b/gui/src/components/grid/grid_stop.vue index 73348a53..6f6d5317 100644 --- a/gui/src/components/grid/grid_stop.vue +++ b/gui/src/components/grid/grid_stop.vue @@ -1,7 +1,5 @@ + + \ No newline at end of file From 368afc0501f2caf157a1cbf6a44dfda5b7bb2dd0 Mon Sep 17 00:00:00 2001 From: Mashintime Date: Wed, 10 Jan 2024 21:20:20 -0500 Subject: [PATCH 07/71] Grid tweaks --- gui/src/components/dynamic_components2.vue | 40 ++++++++++++++++------ gui/src/components/grid/grid_mycall.vue | 8 ++--- 2 files changed, 32 insertions(+), 16 deletions(-) diff --git a/gui/src/components/dynamic_components2.vue b/gui/src/components/dynamic_components2.vue index 757e1f52..4633e119 100644 --- a/gui/src/components/dynamic_components2.vue +++ b/gui/src/components/dynamic_components2.vue @@ -28,6 +28,8 @@ import grid_stop from "./grid/grid_stop.vue"; import grid_CQ_btn from "./grid/grid_CQ.vue"; import grid_ping from "./grid/grid_ping.vue"; import grid_freq from "./grid/grid_frequency.vue"; +import grid_beacon from "./grid/grid_beacon.vue"; +import grid_mycall_small from "./grid/grid_mycall small.vue"; import { stateDispatcher } from "../js/eventHandler"; let count = ref(0); @@ -59,7 +61,7 @@ class gridWidget { const gridWidgets = [ new gridWidget( active_heard_stations, - { x: 0, y: 0, w: 16, h: 40 }, + { x: 0, y: 13, w: 16, h: 40 }, "Detailed heard stations list", true, true, @@ -67,7 +69,7 @@ const gridWidgets = [ ), new gridWidget( active_stats, - { x: 16, y: 16, w: 8, h: 80 }, + { x: 16, y: 16, w: 8, h: 72 }, "Stats (waterfall, etc)", true, true, @@ -116,14 +118,14 @@ const gridWidgets = [ new gridWidget( dbfs_meter, { x: 20, y: 0, w: 4, h: 8 }, - "Dbfs Meter", + "Dbfs meter", true, true, "Audio", ), new gridWidget( grid_activities, - { x: 0, y: 40, w: 6, h: 55 }, + { x: 0, y: 53, w: 6, h: 55 }, "Activities list", true, true, @@ -131,7 +133,7 @@ const gridWidgets = [ ), new gridWidget( active_broadcasts_vert, - { x: 9, y: 55, w: 10, h: 40 }, + { x: 6, y: 53, w: 10, h: 35 }, "Broadcasts main (vertical)", true, true, @@ -139,7 +141,7 @@ const gridWidgets = [ ), new gridWidget( grid_ptt, - { x: 17, y: 8, w: 5, h: 12 }, + { x: 2, y: 0, w: 5, h: 13 }, "Tx/PTT indicator", true, true, @@ -147,7 +149,7 @@ const gridWidgets = [ ), new gridWidget( grid_mycall, - { x: 8, y: 40, w: 5, h: 15 }, + { x: 7, y: 0, w: 9, h: 13 }, "My callsign widget", true, true, @@ -156,7 +158,7 @@ const gridWidgets = [ new gridWidget( grid_CQ_btn, { x: 3, y: 27, w: 2, h: 8 }, - "CQ Button", + "CQ button", false, true, "Broadcasts", @@ -164,7 +166,7 @@ const gridWidgets = [ new gridWidget( grid_ping, { x: 3, y: 27, w: 4, h: 9 }, - "Ping Widget", + "Ping widget", false, true, "Broadcasts", @@ -179,12 +181,28 @@ const gridWidgets = [ ), new gridWidget( grid_stop, - { x: 8, y: 40, w: 5, h: 15 }, - "Stop Widget", + { x: 0, y: 0, w: 2, h: 13 }, + "Stop widget", true, true, "Other", ), + new gridWidget( + grid_beacon, + { x: 3, y: 27, w: 3, h: 8 }, + "Beacon button", + false, + true, + "Broadcasts", + ), + new gridWidget( + grid_mycall_small, + { x: 8, y: 40, w: 4, h: 8 }, + "My callsign widget (small)", + false, + true, + "Other", + ), ]; function updateFrequencyAndApply(frequency) { diff --git a/gui/src/components/grid/grid_mycall.vue b/gui/src/components/grid/grid_mycall.vue index 487ac7f9..eb604e23 100644 --- a/gui/src/components/grid/grid_mycall.vue +++ b/gui/src/components/grid/grid_mycall.vue @@ -7,18 +7,16 @@ setActivePinia(pinia); import { settingsStore } from "../../store/settingsStore.js"; -function updateMyCall() { - setConfig(); -} + From 3e54a57e244af2d21932e4db066cc3227e079a9b Mon Sep 17 00:00:00 2001 From: Mashintime Date: Wed, 10 Jan 2024 21:20:35 -0500 Subject: [PATCH 08/71] New beacon grid widget --- gui/src/components/grid/grid_beacon.vue | 38 +++++++++++++++++++++++++ 1 file changed, 38 insertions(+) create mode 100644 gui/src/components/grid/grid_beacon.vue diff --git a/gui/src/components/grid/grid_beacon.vue b/gui/src/components/grid/grid_beacon.vue new file mode 100644 index 00000000..66dfec11 --- /dev/null +++ b/gui/src/components/grid/grid_beacon.vue @@ -0,0 +1,38 @@ + + From 7cc5841d8317170f14095ba6d8c3f3e811a7a340 Mon Sep 17 00:00:00 2001 From: Mashintime Date: Wed, 10 Jan 2024 21:20:52 -0500 Subject: [PATCH 09/71] New smaller call sign grid widget --- gui/src/components/grid/grid_mycall small.vue | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 gui/src/components/grid/grid_mycall small.vue diff --git a/gui/src/components/grid/grid_mycall small.vue b/gui/src/components/grid/grid_mycall small.vue new file mode 100644 index 00000000..9412d53c --- /dev/null +++ b/gui/src/components/grid/grid_mycall small.vue @@ -0,0 +1,21 @@ + + From 8e78111b67d92b55e3049f7a59ecd2c7959d0f56 Mon Sep 17 00:00:00 2001 From: Mashintime Date: Wed, 10 Jan 2024 21:21:19 -0500 Subject: [PATCH 10/71] Move api logging to debug level for easier filtering --- gui/src/js/eventHandler.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gui/src/js/eventHandler.js b/gui/src/js/eventHandler.js index bba549f4..1aa24db7 100644 --- a/gui/src/js/eventHandler.js +++ b/gui/src/js/eventHandler.js @@ -21,7 +21,7 @@ export function connectionFailed(endpoint, event) { } export function stateDispatcher(data) { data = JSON.parse(data); - console.log(data); + console.debug(data); if (data["type"] == "state-change" || data["type"] == "state") { stateStore.modem_connection = "connected"; From da881d8bd2afe50f32c0c15d52e1b74914f15b4f Mon Sep 17 00:00:00 2001 From: codefactor-io Date: Thu, 11 Jan 2024 02:21:54 +0000 Subject: [PATCH 11/71] [CodeFactor] Apply fixes --- gui/src/components/grid/grid_beacon.vue | 18 +++++------- gui/src/components/grid/grid_mycall small.vue | 29 ++++++++++++------- gui/src/components/grid/grid_mycall.vue | 2 -- gui/src/components/grid/grid_stop.vue | 24 +++++++-------- 4 files changed, 39 insertions(+), 34 deletions(-) diff --git a/gui/src/components/grid/grid_beacon.vue b/gui/src/components/grid/grid_beacon.vue index 66dfec11..c24dfd24 100644 --- a/gui/src/components/grid/grid_beacon.vue +++ b/gui/src/components/grid/grid_beacon.vue @@ -14,7 +14,6 @@ function startStopBeacon() { setModemBeacon(true); } } - diff --git a/gui/src/components/grid/grid_mycall small.vue b/gui/src/components/grid/grid_mycall small.vue index 9412d53c..1fbd29c5 100644 --- a/gui/src/components/grid/grid_mycall small.vue +++ b/gui/src/components/grid/grid_mycall small.vue @@ -6,16 +6,25 @@ import pinia from "../../store/index"; setActivePinia(pinia); import { settingsStore } from "../../store/settingsStore.js"; - - diff --git a/gui/src/components/grid/grid_mycall.vue b/gui/src/components/grid/grid_mycall.vue index eb604e23..17bc1043 100644 --- a/gui/src/components/grid/grid_mycall.vue +++ b/gui/src/components/grid/grid_mycall.vue @@ -6,8 +6,6 @@ import pinia from "../../store/index"; setActivePinia(pinia); import { settingsStore } from "../../store/settingsStore.js"; - - From b5b1db99a19783228662c7e848c9e271c6097ae4 Mon Sep 17 00:00:00 2001 From: Mashintime Date: Wed, 10 Jan 2024 22:18:15 -0500 Subject: [PATCH 12/71] Grid preset --- gui/src/store/settingsStore.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/gui/src/store/settingsStore.js b/gui/src/store/settingsStore.js index 40df8d85..0462db56 100644 --- a/gui/src/store/settingsStore.js +++ b/gui/src/store/settingsStore.js @@ -22,6 +22,7 @@ nconf.defaults({ update_channel: "alpha", enable_sys_notification: false, grid_layout: "[]", + grid_preset: "[]", }, }); @@ -38,6 +39,7 @@ export const settingsStore = reactive({ update_channel: "alpha", enable_sys_notification: false, grid_layout: "[]", + grid_preset: "[]", }, remote: { AUDIO: { From 0be22c33dd482aac4b1901f48cd857431a84a0ad Mon Sep 17 00:00:00 2001 From: Mashintime Date: Wed, 10 Jan 2024 22:18:34 -0500 Subject: [PATCH 13/71] Grid tweaks --- gui/src/components/dynamic_components2.vue | 268 +++++++++++++-------- gui/src/components/grid/button.vue | 2 +- 2 files changed, 166 insertions(+), 104 deletions(-) diff --git a/gui/src/components/dynamic_components2.vue b/gui/src/components/dynamic_components2.vue index 4633e119..34197c91 100644 --- a/gui/src/components/dynamic_components2.vue +++ b/gui/src/components/dynamic_components2.vue @@ -48,24 +48,37 @@ class gridWidget { autoPlace; //Category to place widget in widget picker category; - constructor(component, size, text, quickfill, autoPlace, category) { + //Unique ID for widget + id; + constructor(component, size, text, quickfill, autoPlace, category, id) { this.component2 = component; this.size = size; this.text = text; this.quickFill = quickfill; this.autoPlace = autoPlace; this.category = category; + this.id = id; } } //Array of grid widgets, do not change array order as it'll affect saved configs const gridWidgets = [ - new gridWidget( - active_heard_stations, - { x: 0, y: 13, w: 16, h: 40 }, - "Detailed heard stations list", +new gridWidget( + grid_activities, + { x: 0, y: 53, w: 6, h: 55 }, + "Activities list", true, true, "Activity", + 8, + ), + new gridWidget( + active_heard_stations, + { x: 0, y: 13, w: 16, h: 40 }, + "Heard stations list (detailed)", + true, + true, + "Activity", + 0, ), new gridWidget( active_stats, @@ -74,6 +87,7 @@ const gridWidgets = [ true, true, "Stats", + 1, ), new gridWidget( active_audio_level, @@ -82,94 +96,7 @@ const gridWidgets = [ false, true, "Audio", - ), - new gridWidget( - active_rig_control, - { x: 6, y: 40, w: 9, h: 15 }, - "Rig control main", - false, - true, - "Rig", - ), - new gridWidget( - active_broadcasts, - { x: 6, y: 70, w: 6, h: 15 }, - "Broadcasts main (horizontal)", - false, - true, - "Broadcasts", - ), - new gridWidget( - mini_heard_stations, - { x: 1, y: 1, w: 6, h: 54 }, - "Mini heard stations list", - false, - true, - "Activity", - ), - new gridWidget( - s_meter, - { x: 16, y: 0, w: 4, h: 8 }, - "S-Meter", - true, - true, - "Rig", - ), - new gridWidget( - dbfs_meter, - { x: 20, y: 0, w: 4, h: 8 }, - "Dbfs meter", - true, - true, - "Audio", - ), - new gridWidget( - grid_activities, - { x: 0, y: 53, w: 6, h: 55 }, - "Activities list", - true, - true, - "Activity", - ), - new gridWidget( - active_broadcasts_vert, - { x: 6, y: 53, w: 10, h: 35 }, - "Broadcasts main (vertical)", - true, - true, - "Broadcasts", - ), - new gridWidget( - grid_ptt, - { x: 2, y: 0, w: 5, h: 13 }, - "Tx/PTT indicator", - true, - true, - "Rig", - ), - new gridWidget( - grid_mycall, - { x: 7, y: 0, w: 9, h: 13 }, - "My callsign widget", - true, - true, - "Other", - ), - new gridWidget( - grid_CQ_btn, - { x: 3, y: 27, w: 2, h: 8 }, - "CQ button", - false, - true, - "Broadcasts", - ), - new gridWidget( - grid_ping, - { x: 3, y: 27, w: 4, h: 9 }, - "Ping widget", - false, - true, - "Broadcasts", + 2, ), new gridWidget( grid_freq, @@ -178,14 +105,16 @@ const gridWidgets = [ true, true, "Rig", + 14, ), - new gridWidget( - grid_stop, - { x: 0, y: 0, w: 2, h: 13 }, - "Stop widget", + new gridWidget( + active_rig_control, + { x: 6, y: 40, w: 9, h: 15 }, + "Rig control main", + false, true, - true, - "Other", + "Rig", + 3, ), new gridWidget( grid_beacon, @@ -194,6 +123,71 @@ const gridWidgets = [ false, true, "Broadcasts", + 16, + ), + new gridWidget( + active_broadcasts, + { x: 6, y: 70, w: 6, h: 15 }, + "Broadcasts main (horizontal)", + false, + true, + "Broadcasts", + 4, + ), + new gridWidget( + mini_heard_stations, + { x: 1, y: 1, w: 6, h: 54 }, + "Heard stations list (small)", + false, + true, + "Activity", + 5, + ), + new gridWidget( + s_meter, + { x: 16, y: 0, w: 4, h: 8 }, + "S-Meter", + true, + true, + "Rig", + 6, + ), + new gridWidget( + dbfs_meter, + { x: 20, y: 0, w: 4, h: 8 }, + "Dbfs meter", + true, + true, + "Audio", + 7, + ), + + new gridWidget( + active_broadcasts_vert, + { x: 6, y: 53, w: 10, h: 35 }, + "Broadcasts main (vertical)", + true, + true, + "Broadcasts", + 9, + ), + new gridWidget( + grid_ptt, + { x: 2, y: 0, w: 5, h: 13 }, + "Tx/PTT indicator", + true, + true, + "Rig", + 10, + ), + new gridWidget( + grid_mycall, + { x: 7, y: 0, w: 9, h: 13 }, + "My callsign widget", + true, + true, + "Other", + 11, ), new gridWidget( grid_mycall_small, @@ -202,7 +196,39 @@ const gridWidgets = [ false, true, "Other", + 17, ), + new gridWidget( + grid_CQ_btn, + { x: 3, y: 27, w: 2, h: 8 }, + "CQ button", + false, + true, + "Broadcasts", + 12, + ), + new gridWidget( + grid_ping, + { x: 3, y: 27, w: 4, h: 9 }, + "Ping widget", + false, + true, + "Broadcasts", + 13, + ), + + new gridWidget( + grid_stop, + { x: 0, y: 0, w: 2, h: 13 }, + "Stop widget", + true, + true, + "Other", + 15, + ), + + + //New new widget ID should be 18 ]; function updateFrequencyAndApply(frequency) { @@ -213,7 +239,19 @@ function updateFrequencyAndApply(frequency) { function set_hamlib_frequency_manually() { setModemFrequency(state.new_frequency); } - +function savePreset() +{ + settingsStore.local.grid_preset=settingsStore.local.grid_layout; + console.log("Saved grid preset") +} +function loadPreset() +{ + + clearAllItems(); + settingsStore.local.grid_layout=settingsStore.local.grid_preset; + restoreGridLayoutFromConfig(); + console.log("Restored grid preset") +} onMounted(() => { grid = GridStack.init({ // DO NOT use grid.value = GridStack.init(), see above @@ -308,13 +346,17 @@ function onChange(event, changeItems) { function restoreGridLayoutFromConfig(){ //Try to load grid from saved config //On mounted seems to be called multiple times; so check to make sure items is empty first - //array format: 0 = x, 1 = y, 2 = w, 3 = h, 4 = gridwidget index + //array format: 0 = x, 1 = y, 2 = w, 3 = h, 4 = gridwidget ID if (items.value.length == 0){ let savedGrid = JSON.parse(settingsStore.local.grid_layout); if (savedGrid.length > 0 ) console.info("Restoring " + savedGrid.length + " widget(s) from config"); for (let i=0; i < savedGrid.length;i++ ){ + //Find widget by ID + var widgetIndex = gridWidgets.findIndex((gw) => gw.id == savedGrid[i][4]) + //Refs are passed, so grab original settings for restoration - let tempGW = gridWidgets[parseInt(savedGrid[i][4])]; + //let tempGW = gridWidgets[parseInt(savedGrid[i][4])]; + let tempGW = gridWidgets[parseInt(widgetIndex)]; let backupGWsize = tempGW.size; tempGW.autoPlace=false; tempGW.size={x:savedGrid[i][0], y:savedGrid[i][1], w:savedGrid[i][2], h:savedGrid[i][3]} @@ -330,7 +372,10 @@ function saveGridLayout() let cfg = []; for (let i=0; items.value.length > i; i++) { var widget = gridWidgets.findIndex((gw) => gw.component2.__name == items.value[i].component2.__name) - cfg[i] = [items.value[i].x, items.value[i].y, items.value[i].w,items.value[i].h, widget ]; + //Get the widget's id to store in config + var widgetid = gridWidgets[widget].id; + console.log(widgetid + "-" + widget); + cfg[i] = [items.value[i].x, items.value[i].y, items.value[i].w,items.value[i].h, widgetid ]; } settingsStore.local.grid_layout=JSON.stringify(cfg); saveLocalSettingsToConfig(); @@ -601,6 +646,23 @@ function quickfill() { > Clear grid +
+   + diff --git a/gui/src/components/grid/button.vue b/gui/src/components/grid/button.vue index fe5547b5..f6947df5 100644 --- a/gui/src/components/grid/button.vue +++ b/gui/src/components/grid/button.vue @@ -6,7 +6,7 @@ function emitClick() { } From ad7bfb2e413fedb7ba82df2c5fc5c771614042fe Mon Sep 17 00:00:00 2001 From: codefactor-io Date: Thu, 11 Jan 2024 03:22:21 +0000 Subject: [PATCH 14/71] [CodeFactor] Apply fixes --- gui/src/components/grid/button.vue | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/gui/src/components/grid/button.vue b/gui/src/components/grid/button.vue index f6947df5..ab23b81c 100644 --- a/gui/src/components/grid/button.vue +++ b/gui/src/components/grid/button.vue @@ -6,7 +6,10 @@ function emitClick() { } From 22382a182b7bfcb1387663d8f0ccb0d56efae6ff Mon Sep 17 00:00:00 2001 From: Mashintime Date: Wed, 10 Jan 2024 22:27:07 -0500 Subject: [PATCH 15/71] Fix TS --- gui/src/components/dynamic_components2.vue | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gui/src/components/dynamic_components2.vue b/gui/src/components/dynamic_components2.vue index 34197c91..4de1ebed 100644 --- a/gui/src/components/dynamic_components2.vue +++ b/gui/src/components/dynamic_components2.vue @@ -356,7 +356,7 @@ function restoreGridLayoutFromConfig(){ //Refs are passed, so grab original settings for restoration //let tempGW = gridWidgets[parseInt(savedGrid[i][4])]; - let tempGW = gridWidgets[parseInt(widgetIndex)]; + let tempGW = gridWidgets[widgetIndex]; let backupGWsize = tempGW.size; tempGW.autoPlace=false; tempGW.size={x:savedGrid[i][0], y:savedGrid[i][1], w:savedGrid[i][2], h:savedGrid[i][3]} From 80b21e6bef89985a26bd7939816c3fe5fd452a12 Mon Sep 17 00:00:00 2001 From: DJ2LS Date: Thu, 11 Jan 2024 14:03:00 +0100 Subject: [PATCH 16/71] fixed callsign check if ssid_list empty --- modem/helpers.py | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/modem/helpers.py b/modem/helpers.py index eb2d1bcd..27e346c5 100644 --- a/modem/helpers.py +++ b/modem/helpers.py @@ -302,25 +302,28 @@ def check_callsign(callsign: str, crc_to_check: bytes, ssid_list): [True, Callsign + SSID] False """ + print(callsign) if not isinstance(callsign, (bytes)): callsign = bytes(callsign,'utf-8') - - log.debug("[HLP] check_callsign: Checking:", callsign=callsign) + try: # We want the callsign without SSID - callsign = callsign.split(b"-")[0] + splitted_callsign = callsign.split(b"-") + callsign = splitted_callsign[0] + ssid = splitted_callsign[1].decode() except IndexError: # This is expected when `callsign` doesn't have a dash. - pass + ssid = 0 except Exception as err: log.debug("[HLP] check_callsign: Error converting to bytes:", e=err) + # ensure, we are always have the own ssid in ssid_list even if it is empty + if ssid not in ssid_list: + ssid_list.append(str(ssid)) + for ssid in ssid_list: call_with_ssid = callsign + b'-' + (str(ssid)).encode('utf-8') - #call_with_ssid.extend("-".encode("utf-8")) - #call_with_ssid.extend(str(ssid).encode("utf-8")) - callsign_crc = get_crc_24(call_with_ssid) callsign_crc = callsign_crc.hex() @@ -328,6 +331,7 @@ def check_callsign(callsign: str, crc_to_check: bytes, ssid_list): log.debug("[HLP] check_callsign matched:", call_with_ssid=call_with_ssid, checksum=crc_to_check) return [True, call_with_ssid.decode()] + log.debug("[HLP] check_callsign: Checking:", callsign=callsign, crc_to_check=crc_to_check, own_crc=callsign_crc) return [False, b''] From eff03832025e78965197a3a4b8fcd602dae44393 Mon Sep 17 00:00:00 2001 From: DJ2LS Date: Thu, 11 Jan 2024 14:16:10 +0100 Subject: [PATCH 17/71] moved bat files to tools... --- {modem => tools}/Modem-Install-Requrements.bat | 0 {modem => tools}/Modem-Launch.bat | 0 {modem => tools}/Modem-list-audio-devs.bat | 0 3 files changed, 0 insertions(+), 0 deletions(-) rename {modem => tools}/Modem-Install-Requrements.bat (100%) rename {modem => tools}/Modem-Launch.bat (100%) rename {modem => tools}/Modem-list-audio-devs.bat (100%) diff --git a/modem/Modem-Install-Requrements.bat b/tools/Modem-Install-Requrements.bat similarity index 100% rename from modem/Modem-Install-Requrements.bat rename to tools/Modem-Install-Requrements.bat diff --git a/modem/Modem-Launch.bat b/tools/Modem-Launch.bat similarity index 100% rename from modem/Modem-Launch.bat rename to tools/Modem-Launch.bat diff --git a/modem/Modem-list-audio-devs.bat b/tools/Modem-list-audio-devs.bat similarity index 100% rename from modem/Modem-list-audio-devs.bat rename to tools/Modem-list-audio-devs.bat From a1c92a0a03772db2f2b54081407b63d3678cd6c4 Mon Sep 17 00:00:00 2001 From: DJ2LS Date: Thu, 11 Jan 2024 14:22:00 +0100 Subject: [PATCH 18/71] start/stop explorer publising in correlation with modem --- modem/service_manager.py | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/modem/service_manager.py b/modem/service_manager.py index b352f4ac..af567867 100644 --- a/modem/service_manager.py +++ b/modem/service_manager.py @@ -14,6 +14,7 @@ class SM: self.modem = False self.beacon = False + self.explorer = False self.app = app self.config = self.app.config_manager.read() self.modem_fft = app.modem_fft @@ -27,8 +28,6 @@ class SM: ) runner_thread.start() - self.start_explorer_publishing() - def runner(self): while True: cmd = self.modem_service.get() @@ -36,14 +35,17 @@ class SM: self.log.info("------------------ FreeDATA ------------------") self.log.info("------------------ MODEM ------------------") self.start_modem() + self.start_explorer_publishing() elif cmd in ['stop'] and self.modem: self.stop_modem() + self.stop_explorer_publishing() # we need to wait a bit for avoiding a portaudio crash threading.Event().wait(0.5) elif cmd in ['restart']: self.stop_modem() + self.stop_explorer_publishing() # we need to wait a bit for avoiding a portaudio crash threading.Event().wait(0.5) if self.start_modem(): @@ -121,6 +123,9 @@ class SM: try: # optionally start explorer module if self.config['STATION']['enable_explorer']: - explorer.explorer(self.app, self.config, self.states) + self.explorer = explorer.explorer(self.app, self.config, self.states) except Exception as e: - self.log.warning("[EXPLORER] Publishin not started because of error", e=e) \ No newline at end of file + self.log.warning("[EXPLORER] Publishing not started because of error", e=e) + + def stop_explorer_publishing(self): + del self.explorer \ No newline at end of file From a4dd5a25cfe2c788a58f4e5922350f5c7459e4f8 Mon Sep 17 00:00:00 2001 From: DJ2LS Date: Thu, 11 Jan 2024 14:39:53 +0100 Subject: [PATCH 19/71] improved explorer publising using sched --- modem/explorer.py | 30 ++++++++++++++++++------------ 1 file changed, 18 insertions(+), 12 deletions(-) diff --git a/modem/explorer.py b/modem/explorer.py index 9af681f4..13e302c5 100644 --- a/modem/explorer.py +++ b/modem/explorer.py @@ -11,6 +11,8 @@ import requests import threading import ujson as json import structlog +import sched +import time log = structlog.get_logger("explorer") @@ -21,18 +23,16 @@ class explorer(): self.states = states self.explorer_url = "https://api.freedata.app/explorer.php" self.publish_interval = 120 - self.enable_explorer = config["STATION"]["enable_explorer"] - if self.enable_explorer: - self.interval_thread = threading.Thread(target=self.interval, name="interval", daemon=True) - self.interval_thread.start() + self.scheduler = sched.scheduler(time.time, time.sleep) + self.schedule_thread = threading.Thread(target=self.run_scheduler) + self.schedule_thread.start() - def interval(self): - # Wait for just a little bit incase modem is contionously restarting due to a bug or user configuration issue - threading.Event().wait(30) - while True: - self.push() - threading.Event().wait(self.publish_interval) + def run_scheduler(self): + # Schedule the first execution of push + self.scheduler.enter(self.publish_interval, 1, self.push) + # Run the scheduler in a loop + self.scheduler.run() def push(self): @@ -67,8 +67,14 @@ class explorer(): station_data = json.dumps(station_data) try: response = requests.post(self.explorer_url, json=station_data, headers=headers) - # print(response.status_code) - # print(response.content) except Exception as e: log.warning("[EXPLORER] connection lost") + + # Reschedule the push method + self.scheduler.enter(self.publish_interval, 1, self.push) + + def shutdown(self): + # If there are other cleanup tasks, include them here + if self.schedule_thread: + self.schedule_thread.join() From cbfc141c890f02b3c1f493d7cde5dc2ec341b2eb Mon Sep 17 00:00:00 2001 From: DJ2LS Date: Thu, 11 Jan 2024 14:56:45 +0100 Subject: [PATCH 20/71] adjusted beacon --- modem/beacon.py | 56 ++++++++++++++++++---------------------- modem/service_manager.py | 2 +- 2 files changed, 26 insertions(+), 32 deletions(-) diff --git a/modem/beacon.py b/modem/beacon.py index dbfc8acf..909110eb 100644 --- a/modem/beacon.py +++ b/modem/beacon.py @@ -1,47 +1,41 @@ -import threading -import data_frame_factory import command_beacon +import sched +import time +import threading class Beacon: - - BEACON_LOOP_INTERVAL = 1 - def __init__(self, config, states, event_manager, logger, modem): - - self.modem_config = config + self.config = config self.states = states self.event_manager = event_manager self.log = logger self.modem = modem - self.loop_running = True - self.paused = False - self.thread = None + self.scheduler = sched.scheduler(time.time, time.sleep) + self.beacon_interval = self.config['MODEM']['beacon_interval'] + self.beacon_enabled = False self.event = threading.Event() - self.frame_factory = data_frame_factory.DataFrameFactory(config) - def start(self): - beacon_thread = threading.Thread(target=self.run_beacon, name="beacon", daemon=True) - beacon_thread.start() + self.beacon_enabled = True + self.schedule_beacon() def stop(self): - self.loop_running = False + self.beacon_enabled = False + + def schedule_beacon(self): + if self.beacon_enabled: + self.scheduler.enter(self.beacon_interval, 1, self.run_beacon) + threading.Thread(target=self.scheduler.run, daemon=True).start() + + def run_beacon(self): + if self.beacon_enabled: + # Your beacon logic here + cmd = command_beacon.BeaconCommand(self.config, self.states, self.event_manager) + cmd.run(self.event_manager, self.modem) + self.schedule_beacon() # Reschedule the next beacon def refresh(self): - self.event.set() - self.event.clear() - - def run_beacon(self) -> None: - while self.loop_running: - while (self.states.is_beacon_running and - not self.paused and - True): - #not self.states.channel_busy): - - cmd = command_beacon.BeaconCommand(self.modem_config, self.states, self.event_manager) - cmd.run(self.event_manager, self.modem) - self.event.wait(self.modem_config['MODEM']['beacon_interval']) - - self.event.wait(self.BEACON_LOOP_INTERVAL) - \ No newline at end of file + # Interrupt and reschedule the beacon + self.scheduler = sched.scheduler(time.time, time.sleep) + self.schedule_beacon() \ No newline at end of file diff --git a/modem/service_manager.py b/modem/service_manager.py index af567867..6c20fa1d 100644 --- a/modem/service_manager.py +++ b/modem/service_manager.py @@ -117,7 +117,7 @@ class SM: self.beacon.start() def stop_beacon(self): - del self.beacon + self.beacon.stop() def start_explorer_publishing(self): try: From e23c2e5cdd6f377b0548290a87c435b766be8b3d Mon Sep 17 00:00:00 2001 From: Mashintime Date: Thu, 11 Jan 2024 17:35:41 -0500 Subject: [PATCH 21/71] Remove batch files from gui dir, opps --- gui/GUI-Install-Requirements.bat | 8 -------- gui/GUI-Launch.bat | 5 ----- gui/GUI-Update-Requirements.bat | 8 -------- 3 files changed, 21 deletions(-) delete mode 100644 gui/GUI-Install-Requirements.bat delete mode 100644 gui/GUI-Launch.bat delete mode 100644 gui/GUI-Update-Requirements.bat diff --git a/gui/GUI-Install-Requirements.bat b/gui/GUI-Install-Requirements.bat deleted file mode 100644 index c61dab25..00000000 --- a/gui/GUI-Install-Requirements.bat +++ /dev/null @@ -1,8 +0,0 @@ -@echo off -REM Place this batch file in FreeData/gui and then run it -REM ie. c:\FD-Src\gui -echo Install requirements for GUI... - -call npm install - -pause \ No newline at end of file diff --git a/gui/GUI-Launch.bat b/gui/GUI-Launch.bat deleted file mode 100644 index 937c86bc..00000000 --- a/gui/GUI-Launch.bat +++ /dev/null @@ -1,5 +0,0 @@ -REM Place this batch file in FreeData/tnc and then run it -REM ie. c:\FD-Src\gui - -call npm start -pause \ No newline at end of file diff --git a/gui/GUI-Update-Requirements.bat b/gui/GUI-Update-Requirements.bat deleted file mode 100644 index 4746422d..00000000 --- a/gui/GUI-Update-Requirements.bat +++ /dev/null @@ -1,8 +0,0 @@ -@echo off -REM Place this batch file in FreeData/tnc and then run it -REM ie. c:\FD-Src\gui -echo Checking and install for updated requirements - -call npm update - -pause \ No newline at end of file From 9ad9e9b8c84ae5f252cee996bf5ac96ab072ee28 Mon Sep 17 00:00:00 2001 From: DJ2LS Date: Fri, 12 Jan 2024 14:08:07 +0100 Subject: [PATCH 22/71] fixing stopping of explorer --- modem/service_manager.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/modem/service_manager.py b/modem/service_manager.py index 6c20fa1d..cdb9a5ec 100644 --- a/modem/service_manager.py +++ b/modem/service_manager.py @@ -128,4 +128,5 @@ class SM: self.log.warning("[EXPLORER] Publishing not started because of error", e=e) def stop_explorer_publishing(self): - del self.explorer \ No newline at end of file + if self.config['STATION']['enable_explorer']: + del self.explorer \ No newline at end of file From 543cbbdff81d84c8d0366f87d15e759838682dbc Mon Sep 17 00:00:00 2001 From: DJ2LS Date: Fri, 12 Jan 2024 15:51:23 +0100 Subject: [PATCH 23/71] RADIO MANAGER - WIP initial release --- modem/modem.py | 74 +----- modem/radio_manager.py | 56 +++++ modem/rigctld.py | 472 +++++++++++++-------------------------- modem/rigdummy.py | 29 ++- modem/server.py | 2 + modem/service_manager.py | 45 ++-- tests/test_protocols.py | 5 +- 7 files changed, 276 insertions(+), 407 deletions(-) create mode 100644 modem/radio_manager.py diff --git a/modem/modem.py b/modem/modem.py index ef9af3e1..55393c40 100644 --- a/modem/modem.py +++ b/modem/modem.py @@ -31,11 +31,12 @@ class RF: log = structlog.get_logger("RF") - def __init__(self, config, event_manager, fft_queue, service_queue, states) -> None: + def __init__(self, config, event_manager, fft_queue, service_queue, states, radio_manager) -> None: self.config = config self.service_queue = service_queue self.states = states self.event_manager = event_manager + self.radio = radio_manager self.sampler_avg = 0 self.buffer_avg = 0 @@ -108,7 +109,6 @@ class RF: # Initialize codec2, rig control, and data threads self.init_codec2() - self.init_rig_control() self.init_data_threads() return True @@ -423,73 +423,3 @@ class RF: else: sd.play(audio_48k, blocksize=4096, blocking=True) return - - def init_rig_control(self): - # Check how we want to control the radio - if self.radiocontrol == "rigctld": - import rigctld as rig - elif self.radiocontrol == "tci": - self.radio = self.tci_module - else: - import rigdummy as rig - - if not self.radiocontrol in ["tci"]: - self.radio = rig.radio() - self.radio.open_rig( - rigctld_ip=self.rigctld_ip, - rigctld_port=self.rigctld_port, - ) - hamlib_thread = threading.Thread( - target=self.update_rig_data, name="HAMLIB_THREAD", daemon=True - ) - hamlib_thread.start() - - hamlib_set_thread = threading.Thread( - target=self.set_rig_data, name="HAMLIB_SET_THREAD", daemon=True - ) - hamlib_set_thread.start() - - def set_rig_data(self) -> None: - """ - Set rigctld parameters like frequency, mode - THis needs to be processed in a queue - """ - while True: - cmd = RIGCTLD_COMMAND_QUEUE.get() - if cmd[0] == "set_frequency": - # [1] = Frequency - self.radio.set_frequency(cmd[1]) - if cmd[0] == "set_mode": - # [1] = Mode - self.radio.set_mode(cmd[1]) - - def update_rig_data(self) -> None: - """ - Request information about the current state of the radio via hamlib - """ - while True: - try: - # this looks weird, but is necessary for avoiding rigctld packet colission sock - #threading.Event().wait(0.1) - self.states.set("radio_status", self.radio.get_status()) - #threading.Event().wait(0.25) - self.states.set("radio_frequency", self.radio.get_frequency()) - threading.Event().wait(0.1) - self.states.set("radio_mode", self.radio.get_mode()) - threading.Event().wait(0.1) - self.states.set("radio_bandwidth", self.radio.get_bandwidth()) - threading.Event().wait(0.1) - if self.states.isTransmitting(): - self.radio_alc = self.radio.get_alc() - threading.Event().wait(0.1) - self.states.set("radio_rf_power", self.radio.get_level()) - threading.Event().wait(0.1) - self.states.set("radio_strength", self.radio.get_strength()) - - except Exception as e: - self.log.warning( - "[MDM] error getting radio data", - e=e, - ) - threading.Event().wait(1) - diff --git a/modem/radio_manager.py b/modem/radio_manager.py new file mode 100644 index 00000000..1ddbc68b --- /dev/null +++ b/modem/radio_manager.py @@ -0,0 +1,56 @@ +import rigctld +import tci +import rigdummy +import time +import threading +class RadioManager: + def __init__(self, config, state_manager, event_manager): + self.config = config + self.state_manager = state_manager + self.event_manager = event_manager + + self.radiocontrol = config['RADIO']['control'] + self.rigctld_ip = config['RIGCTLD']['ip'] + self.rigctld_port = config['RIGCTLD']['port'] + + self.refresh_rate = 1 + self.stop_event = threading.Event() + self.update_thread = threading.Thread(target=self.update_parameters, daemon=True) + self._init_rig_control() + + def _init_rig_control(self): + # Check how we want to control the radio + if self.radiocontrol == "rigctld": + self.radio = rigctld.radio(self.state_manager, hostname=self.rigctld_ip,port=self.rigctld_port) + elif self.radiocontrol == "tci": + raise NotImplementedError + # self.radio = self.tci_module + else: + self.radio = rigdummy.radio() + + self.update_thread.start() + + def set_ptt(self, state): + self.radio.set_ptt(state) + + def set_frequency(self, frequency): + self.radio.set_frequency(frequency) + + def set_mode(self, mode): + self.radio.set_mode(mode) + + def update_parameters(self): + while not self.stop_event.is_set(): + parameters = self.radio.get_parameters() + self.state_manager.set("radio_frequency", parameters['frequency']) + self.state_manager.set("radio_mode", parameters['mode']) + self.state_manager.set("radio_bandwidth", parameters['bandwidth']) + self.state_manager.set("radio_rf_power", parameters['rf']) + + if self.state_manager.isTransmitting(): + self.radio_alc = parameters['alc'] + self.state_manager.set("radio_strength", parameters['strength']) + time.sleep(self.refresh_rate) + def stop(self): + self.radio.disconnect() + self.stop_event.set() \ No newline at end of file diff --git a/modem/rigctld.py b/modem/rigctld.py index 6d761b00..ef47651d 100644 --- a/modem/rigctld.py +++ b/modem/rigctld.py @@ -1,345 +1,189 @@ -#!/usr/bin/env python3 -# class taken from darksidelemm -# rigctl - https://github.com/darksidelemm/rotctld-web-gui/blob/master/rotatorgui.py#L35 -# -# modified and adjusted to FreeDATA needs by DJ2LS - -import contextlib import socket import structlog -import threading class radio: """rigctld (hamlib) communication class""" log = structlog.get_logger("radio (rigctld)") - def __init__(self, hostname="localhost", port=4532, poll_rate=5, timeout=5): - """Open a connection to rigctld, and test it for validity""" - self.ptt_sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) - self.data_sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) - - self.ptt_connected = False - self.data_connected = False + def __init__(self, states, hostname="localhost", port=4532, timeout=5): self.hostname = hostname self.port = port - self.connection_attempts = 5 + self.timeout = timeout + self.states = states - # class wide variable for some parameters - self.bandwidth = '' - self.frequency = '' - self.mode = '' - self.alc = '' - self.strength = '' - self.rf = '' + self.connection = None + self.connected = False - def open_rig( - self, - rigctld_ip, - rigctld_port - ): - """ + self.parameters = { + 'frequency': '---', + 'mode': '---', + 'alc': '---', + 'strength': '---', + 'bandwidth': '---', + 'rf': '---', + 'ptt': False # Initial PTT state is set to False + } - Args: - rigctld_ip: - rigctld_port: + # connect to radio + self.connect() - Returns: + def connect(self): + try: + self.connection = socket.create_connection((self.hostname, self.port), timeout=self.timeout) + self.connected = True + self.states.set("radio_status", True) + self.log.info(f"[RIGCTLD] Connected to rigctld at {self.hostname}:{self.port}") + except Exception as err: + self.log.warning(f"[RIGCTLD] Failed to connect to rigctld: {err}") + self.connected = False + self.states.set("radio_status", False) - """ - self.hostname = rigctld_ip - self.port = int(rigctld_port) + def disconnect(self): + self.connected = False + self.connection.close() + del self.connection + self.connection = None + self.states.set("radio_status", False) + self.parameters = { + 'frequency': '---', + 'mode': '---', + 'alc': '---', + 'strength': '---', + 'bandwidth': '---', + 'rf': '---', + 'ptt': False # Initial PTT state is set to False + } - # _ptt_connect = self.ptt_connect() - # _data_connect = self.data_connect() - - ptt_thread = threading.Thread(target=self.ptt_connect, args=[], daemon=True) - ptt_thread.start() - - data_thread = threading.Thread(target=self.data_connect, args=[], daemon=True) - data_thread.start() - - # wait some time - threading.Event().wait(0.5) - - if self.ptt_connected and self.data_connected: - self.log.debug("Rigctl DATA/PTT initialized") - return True - - self.log.error( - "[RIGCTLD] Can't connect!", ip=self.hostname, port=self.port - ) - return False - - def ptt_connect(self): - """Connect to rigctld instance""" - while True: - - if not self.ptt_connected: - try: - self.ptt_connection = socket.create_connection((self.hostname, self.port)) - self.ptt_connected = True - self.log.info( - "[RIGCTLD] Connected PTT instance to rigctld!", ip=self.hostname, port=self.port - ) - except Exception as err: - # ConnectionRefusedError: [Errno 111] Connection refused - self.close_rig() - self.log.warning( - "[RIGCTLD] PTT Reconnect...", - ip=self.hostname, - port=self.port, - e=err, - ) - - threading.Event().wait(0.5) - - def data_connect(self): - """Connect to rigctld instance""" - while True: - if not self.data_connected: - try: - self.data_connection = socket.create_connection((self.hostname, self.port)) - self.data_connected = True - self.log.info( - "[RIGCTLD] Connected DATA instance to rigctld!", ip=self.hostname, port=self.port - ) - except Exception as err: - # ConnectionRefusedError: [Errno 111] Connection refused - self.close_rig() - self.log.warning( - "[RIGCTLD] DATA Reconnect...", - ip=self.hostname, - port=self.port, - e=err, - ) - threading.Event().wait(0.5) - - def close_rig(self): - """ """ - self.ptt_sock.close() - self.data_sock.close() - self.ptt_connected = False - self.data_connected = False - - def send_ptt_command(self, command, expect_answer) -> bytes: - """Send a command to the connected rotctld instance, - and return the return value. - - Args: - command: - - """ - if self.ptt_connected: + def send_command(self, command) -> str: + if self.connected: try: - self.ptt_connection.sendall(command + b"\n") - except Exception: - self.log.warning( - "[RIGCTLD] Command not executed!", - command=command, - ip=self.hostname, - port=self.port, - ) - self.ptt_connected = False - return b"" - - def send_data_command(self, command, expect_answer) -> bytes: - """Send a command to the connected rotctld instance, - and return the return value. - - Args: - command: - - """ - if self.data_connected: - self.data_connection.setblocking(False) - #Allow a little more time for a response from rigctld before generating a timeout, seems to have no ill effects on a well behaving setup and fixes Issue #373 - self.data_connection.settimeout(0.30) - try: - self.data_connection.sendall(command + b"\n") - - - except Exception: - self.log.warning( - "[RIGCTLD] Command not executed!", - command=command, - ip=self.hostname, - port=self.port, - ) - self.data_connected = False - - try: - # recv seems to be blocking so in case of ptt we don't need the response - # maybe this speeds things up and avoids blocking states - recv = True - data = b'' - - while recv: - try: - - data = self.data_connection.recv(4800) - - except socket.timeout: - recv = False - - return data - - # return self.data_connection.recv(64) if expect_answer else True - except Exception: - self.log.warning( - "[RIGCTLD] No command response!", - command=command, - ip=self.hostname, - port=self.port, - ) - self.data_connected = False - return b"" - - def get_status(self): - """ """ - return True if self.data_connected and self.ptt_connected else False - - def get_level(self): - try: - data = self.send_data_command(b"l RF", True) - data = data.split(b"\n") - rf = data[0].decode("utf-8") - if 'RPRT' not in rf: - try: - self.rf = str(rf) - except ValueError: - self.rf = str(rf) - - return self.rf - except Exception: - return self.rf - - def get_strength(self): - try: - data = self.send_data_command(b"l STRENGTH", True) - data = data.split(b"\n") - strength = data[0].decode("utf-8") - if 'RPRT' not in strength: - try: - self.strength = str(strength) - except ValueError: - self.strength = str(strength) - - return self.strength - except Exception: - return self.strength - - def get_alc(self): - try: - data = self.send_data_command(b"l ALC", True) - data = data.split(b"\n") - alc = data[0].decode("utf-8") - if 'RPRT' not in alc: - try: - alc = float(alc) - except ValueError: - self.alc = 0.0 - - return self.alc - except Exception: - return self.alc - - def get_mode(self): - """ """ - try: - data = self.send_data_command(b"m", True) - data = data.split(b"\n") - data = data[0].decode("utf-8") - if 'RPRT' not in data: - try: - data = int(data) - except ValueError: - self.mode = str(data) - - return self.mode - except Exception: - return self.mode - - def get_bandwidth(self): - """ """ - try: - data = self.send_data_command(b"m", True) - data = data.split(b"\n") - data = data[1].decode("utf-8") - - if 'RPRT' not in data and data not in ['']: - with contextlib.suppress(ValueError): - self.bandwidth = int(data) - return self.bandwidth - except Exception: - return self.bandwidth - - def get_frequency(self): - """ """ - try: - data = self.send_data_command(b"f", True) - data = data.decode("utf-8") - if 'RPRT' not in data and data not in [0, '0', '']: - with contextlib.suppress(ValueError): - data = int(data) - # make sure we have a frequency and not bandwidth - if data >= 10000: - self.frequency = data - return self.frequency - except Exception: - return self.frequency - - def get_ptt(self): - """ """ - try: - return self.send_data_command(b"t", True) - except Exception: - return False + self.connection.sendall(command.encode('utf-8') + b"\n") + response = self.connection.recv(1024) + return response.decode('utf-8').strip() + except Exception as err: + self.log.warning(f"[RIGCTLD] Error sending command to rigctld: {err}") + self.connected = False + return "" def set_ptt(self, state): - """ + """Set the PTT (Push-to-Talk) state. Args: - state: + state (bool): True to enable PTT, False to disable. Returns: - + bool: True if the PTT state was set successfully, False otherwise. """ - try: - if state: - self.send_ptt_command(b"T 1", False) - else: - self.send_ptt_command(b"T 0", False) - return state - except Exception: - return False - - def set_frequency(self, frequency): - """ - - Args: - frequency: - - Returns: - - """ - try: - command = bytes(f"F {frequency}", "utf-8") - self.send_data_command(command, False) - except Exception: - return False + if self.connected: + try: + if state: + self.send_command('T 1') # Enable PTT + else: + self.send_command('T 0') # Disable PTT + self.parameters['ptt'] = state # Update PTT state in parameters + return True + except Exception as err: + self.log.warning(f"[RIGCTLD] Error setting PTT state: {err}") + self.connected = False + return False def set_mode(self, mode): - """ + """Set the mode. Args: - mode: + mode (str): The mode to set. Returns: - + bool: True if the mode was set successfully, False otherwise. """ - try: - command = bytes(f"M {mode} {self.bandwidth}", "utf-8") - self.send_data_command(command, False) - except Exception: - return False \ No newline at end of file + if self.connected: + try: + command = f"M {mode}" + self.send_command(command) + self.parameters['mode'] = mode + return True + except Exception as err: + self.log.warning(f"[RIGCTLD] Error setting mode: {err}") + self.connected = False + return False + + def set_frequency(self, frequency): + """Set the frequency. + + Args: + frequency (str): The frequency to set. + + Returns: + bool: True if the frequency was set successfully, False otherwise. + """ + if self.connected: + try: + command = f"F {frequency}" + self.send_command(command) + self.parameters['frequency'] = frequency + return True + except Exception as err: + self.log.warning(f"[RIGCTLD] Error setting frequency: {err}") + self.connected = False + return False + + def set_bandwidth(self, bandwidth): + """Set the bandwidth. + + Args: + bandwidth (str): The bandwidth to set. + + Returns: + bool: True if the bandwidth was set successfully, False otherwise. + """ + if self.connected: + try: + command = f"M {self.parameters['mode']} {bandwidth}" + self.send_command(command) + self.parameters['bandwidth'] = bandwidth + return True + except Exception as err: + self.log.warning(f"[RIGCTLD] Error setting bandwidth: {err}") + self.connected = False + return False + + def set_rf(self, rf): + """Set the RF. + + Args: + rf (str): The RF to set. + + Returns: + bool: True if the RF was set successfully, False otherwise. + """ + if self.connected: + try: + command = f"l RF {rf}" + self.send_command(command) + self.parameters['rf'] = rf + return True + except Exception as err: + self.log.warning(f"[RIGCTLD] Error setting RF: {err}") + self.connected = False + return False + + def get_parameters(self): + if not self.connected: + self.connect() + + if self.connected: + self.parameters['frequency'] = self.send_command('f') + response = self.send_command( + 'm').strip() # Get the mode/bandwidth response and remove leading/trailing spaces + mode, bandwidth = response.split('\n', 1) # Split the response into mode and bandwidth + + self.parameters['mode'] = mode + self.parameters['bandwidth'] = bandwidth + + self.parameters['alc'] = self.send_command('l ALC') + self.parameters['strength'] = self.send_command('l STRENGTH') + self.parameters['rf'] = self.send_command('l RF') + + """Return the latest fetched parameters.""" + return self.parameters diff --git a/modem/rigdummy.py b/modem/rigdummy.py index 1b63834d..dacdbde6 100644 --- a/modem/rigdummy.py +++ b/modem/rigdummy.py @@ -1,13 +1,30 @@ -hamlib_version = 0 - class radio: """ """ def __init__(self): - pass + self.parameters = { + 'frequency': '---', + 'mode': '---', + 'alc': '---', + 'strength': '---', + 'bandwidth': '---', + 'rf': '---', + 'ptt': False # Initial PTT state is set to False + } - def open_rig(self, **kwargs): + def connect(self, **kwargs): + """ + + Args: + **kwargs: + + Returns: + + """ + return True + + def disconnect(self, **kwargs): """ Args: @@ -98,3 +115,7 @@ class radio: def close_rig(self): """ """ return + + + def get_parameters(self): + return self.parameters \ No newline at end of file diff --git a/modem/server.py b/modem/server.py index 4c532c86..35b18926 100644 --- a/modem/server.py +++ b/modem/server.py @@ -18,6 +18,7 @@ import command_feq import command_test import command_arq_raw import event_manager +import radio_manager app = Flask(__name__) CORS(app) @@ -58,6 +59,7 @@ app.state_manager = state_manager.StateManager(app.state_queue) # start service manager app.service_manager = service_manager.SM(app) + # start modem service app.modem_service.put("start") diff --git a/modem/service_manager.py b/modem/service_manager.py index cdb9a5ec..8bfd6f7e 100644 --- a/modem/service_manager.py +++ b/modem/service_manager.py @@ -6,6 +6,7 @@ import audio import ujson as json import explorer import beacon +import radio_manager class SM: @@ -15,11 +16,12 @@ class SM: self.modem = False self.beacon = False self.explorer = False + self.radio = False self.app = app self.config = self.app.config_manager.read() self.modem_fft = app.modem_fft self.modem_service = app.modem_service - self.states = app.state_manager + self.state_manager = app.state_manager self.event_manager = app.event_manager @@ -34,22 +36,31 @@ class SM: if cmd in ['start'] and not self.modem: self.log.info("------------------ FreeDATA ------------------") self.log.info("------------------ MODEM ------------------") + self.config = self.app.config_manager.read() + self.start_radio_manager() self.start_modem() self.start_explorer_publishing() elif cmd in ['stop'] and self.modem: self.stop_modem() self.stop_explorer_publishing() + self.stop_radio_manager() # we need to wait a bit for avoiding a portaudio crash threading.Event().wait(0.5) elif cmd in ['restart']: self.stop_modem() self.stop_explorer_publishing() + self.stop_radio_manager() # we need to wait a bit for avoiding a portaudio crash threading.Event().wait(0.5) + + self.config = self.app.config_manager.read() + self.start_radio_manager() + if self.start_modem(): self.event_manager.modem_restarted() + self.start_explorer_publishing() elif cmd in ['start_beacon']: self.start_beacon() @@ -59,37 +70,34 @@ class SM: else: - self.log.warning("[SVC] modem command processing failed", cmd=cmd, state=self.states.is_modem_running) + self.log.warning("[SVC] modem command processing failed", cmd=cmd, state=self.state_manager.is_modem_running) def start_modem(self): - # read config - self.config = self.app.config_manager.read() - - if self.states.is_modem_running: + if self.state_manager.is_modem_running: self.log.warning("modem already running") return False # test audio devices audio_test = self.test_audio() - if False in audio_test or None in audio_test or self.states.is_modem_running: + if False in audio_test or None in audio_test or self.state_manager.is_modem_running: self.log.warning("starting modem failed", input_test=audio_test[0], output_test=audio_test[1]) - self.states.set("is_modem_running", False) + self.state_manager.set("is_modem_running", False) self.event_manager.modem_failed() return False self.log.info("starting modem....") - self.modem = modem.RF(self.config, self.event_manager, self.modem_fft, self.modem_service, self.states) + self.modem = modem.RF(self.config, self.event_manager, self.modem_fft, self.modem_service, self.state_manager, self.radio) self.frame_dispatcher = frame_dispatcher.DISPATCHER(self.config, self.event_manager, - self.states, + self.state_manager, self.modem) self.frame_dispatcher.start() self.event_manager.modem_started() - self.states.set("is_modem_running", True) + self.state_manager.set("is_modem_running", True) self.modem.start_modem() return True @@ -98,7 +106,7 @@ class SM: self.log.info("stopping modem....") del self.modem self.modem = False - self.states.set("is_modem_running", False) + self.state_manager.set("is_modem_running", False) self.event_manager.modem_stopped() def test_audio(self): @@ -113,7 +121,7 @@ class SM: return [False, False] def start_beacon(self): - self.beacon = beacon.Beacon(self.config, self.states, self.event_manager, self.log, self.modem) + self.beacon = beacon.Beacon(self.config, self.state_manager, self.event_manager, self.log, self.modem) self.beacon.start() def stop_beacon(self): @@ -123,10 +131,17 @@ class SM: try: # optionally start explorer module if self.config['STATION']['enable_explorer']: - self.explorer = explorer.explorer(self.app, self.config, self.states) + self.explorer = explorer.explorer(self.app, self.config, self.state_manager) except Exception as e: self.log.warning("[EXPLORER] Publishing not started because of error", e=e) def stop_explorer_publishing(self): if self.config['STATION']['enable_explorer']: - del self.explorer \ No newline at end of file + del self.explorer + + def start_radio_manager(self): + self.radio = radio_manager.RadioManager(self.config, self.state_manager, self.event_manager) + + def stop_radio_manager(self): + self.radio.stop() + del self.radio \ No newline at end of file diff --git a/tests/test_protocols.py b/tests/test_protocols.py index 3aedb47e..6b820225 100755 --- a/tests/test_protocols.py +++ b/tests/test_protocols.py @@ -12,7 +12,7 @@ from command_ping import PingCommand from command_cq import CQCommand import modem import frame_handler - +from radio_manager import RadioManager class TestProtocols(unittest.TestCase): @@ -27,9 +27,10 @@ class TestProtocols(unittest.TestCase): cls.event_queue = queue.Queue() cls.event_manager = EventManager([cls.event_queue]) + cls.radio_manager = RadioManager(cls.config, cls.state_manager, cls.event_manager) cls.modem_transmit_queue = queue.Queue() - cls.modem = modem.RF(cls.config, cls.event_queue, queue.Queue(), queue.Queue(), cls.state_manager) + cls.modem = modem.RF(cls.config, cls.event_queue, queue.Queue(), queue.Queue(), cls.state_manager, cls.radio_manager) modem.TESTMODE = True frame_handler.TESTMODE = True From baae3a5bb49f4df7696fbb3dbcc98f73ea8c3fc8 Mon Sep 17 00:00:00 2001 From: DJ2LS Date: Fri, 12 Jan 2024 16:29:22 +0100 Subject: [PATCH 24/71] RADIO MANAGER - WIP added radio api endpoints --- gui/src/components/dynamic_components2.vue | 23 +++++++++++------ .../grid/grid_active_rig_control.vue | 9 +++---- .../components/main_active_rig_control.vue | 25 ++++++++----------- gui/src/js/api.js | 16 ++++++------ modem/server.py | 13 +++++++--- modem/service_manager.py | 13 +++++----- modem/state_manager.py | 7 ++++++ 7 files changed, 58 insertions(+), 48 deletions(-) diff --git a/gui/src/components/dynamic_components2.vue b/gui/src/components/dynamic_components2.vue index 4de1ebed..86613c01 100644 --- a/gui/src/components/dynamic_components2.vue +++ b/gui/src/components/dynamic_components2.vue @@ -8,7 +8,7 @@ import "../../node_modules/gridstack/dist/gridstack.min.css"; import { GridStack } from "gridstack"; import { useStateStore } from "../store/stateStore.js"; const state = useStateStore(pinia); -import { setModemFrequency } from "../js/api"; +import { setRadioParameters } from "../js/api"; import { saveLocalSettingsToConfig, settingsStore } from "../store/settingsStore"; import active_heard_stations from "./grid/grid_active_heard_stations.vue"; @@ -233,12 +233,20 @@ new gridWidget( function updateFrequencyAndApply(frequency) { state.new_frequency = frequency; - setModemFrequency(state.new_frequency); + set_radio_parameters(); } -function set_hamlib_frequency_manually() { - setModemFrequency(state.new_frequency); +function set_radio_parameters(){ + setRadioParameters(state.new_frequency, state.mode, state.rf_level); + } + + + + + + + function savePreset() { settingsStore.local.grid_preset=settingsStore.local.grid_layout; @@ -246,7 +254,7 @@ function savePreset() } function loadPreset() { - + clearAllItems(); settingsStore.local.grid_layout=settingsStore.local.grid_preset; restoreGridLayoutFromConfig(); @@ -392,7 +400,7 @@ function addNewWidget2(componentToAdd :gridWidget,saveToConfig :boolean) { if (saveToConfig) saveGridLayout(); }); - + } function remove(widget) { @@ -677,7 +685,7 @@ function quickfill() { aria-label="Close" > - +
@@ -707,7 +715,6 @@ function quickfill() {