mirror of
https://github.com/DJ2LS/FreeDATA
synced 2024-05-14 08:04:33 +00:00
Merge pull request #682 from DJ2LS/dev-remove-info-screen
removing info screen from gui
This commit is contained in:
commit
69672482b3
52 changed files with 512 additions and 2566 deletions
|
@ -14,6 +14,15 @@ jobs:
|
||||||
with:
|
with:
|
||||||
python-version: "3.11"
|
python-version: "3.11"
|
||||||
|
|
||||||
|
- name: Electron Builder
|
||||||
|
working-directory: gui
|
||||||
|
run: |
|
||||||
|
npm i
|
||||||
|
npm run build
|
||||||
|
|
||||||
|
- name: LIST ALL FILES
|
||||||
|
run: ls -R
|
||||||
|
|
||||||
- name: Install Python dependencies
|
- name: Install Python dependencies
|
||||||
run: |
|
run: |
|
||||||
python -m pip install --upgrade pip
|
python -m pip install --upgrade pip
|
||||||
|
@ -36,9 +45,9 @@ jobs:
|
||||||
run: ls -R
|
run: ls -R
|
||||||
|
|
||||||
- name: Create installer
|
- name: Create installer
|
||||||
uses: joncloud/makensis-action@v4
|
uses: joncloud/makensis-action@v4.1
|
||||||
with:
|
with:
|
||||||
script-file: "freedata-server-nsis-config.nsi"
|
script-file: "freedata-nsis-config.nsi"
|
||||||
arguments: '/V3'
|
arguments: '/V3'
|
||||||
|
|
||||||
- name: LIST ALL FILES
|
- name: LIST ALL FILES
|
||||||
|
@ -47,12 +56,14 @@ jobs:
|
||||||
- name: Upload artifact
|
- name: Upload artifact
|
||||||
uses: actions/upload-artifact@v4
|
uses: actions/upload-artifact@v4
|
||||||
with:
|
with:
|
||||||
name: 'FreeData-Server-Installer'
|
name: 'FreeDATA-Installer'
|
||||||
path: ./FreeData-Server-Installer.exe
|
path: ./FreeDATA-Installer.exe
|
||||||
|
|
||||||
- name: Upload Installer to Release
|
- name: Upload Installer to Release
|
||||||
uses: softprops/action-gh-release@v1
|
uses: softprops/action-gh-release@v1
|
||||||
if: startsWith(github.ref, 'refs/tags/v')
|
if: startsWith(github.ref, 'refs/tags/v')
|
||||||
with:
|
with:
|
||||||
draft: true
|
draft: true
|
||||||
files: ./FreeData-Server-Installer.exe
|
files: ./FreeDATA-Installer.exe
|
||||||
|
tag_name: ${{ github.ref_name }}
|
||||||
|
name: 'FreeDATA-Installer-${{ github.ref_name }}'
|
2
.github/workflows/build_server.yml
vendored
2
.github/workflows/build_server.yml
vendored
|
@ -48,7 +48,7 @@ jobs:
|
||||||
brew install portaudio
|
brew install portaudio
|
||||||
python -m pip install --upgrade pip
|
python -m pip install --upgrade pip
|
||||||
pip3 install pyaudio
|
pip3 install pyaudio
|
||||||
export PYTHONPATH=/opt/homebrew/opt/portaudio/lib/:$PYTHONPATH
|
export PYTHONPATH=/Library/Frameworks/Python.framework/Versions/3.11/lib/:$PYTHONPATH
|
||||||
|
|
||||||
- name: Install Python dependencies
|
- name: Install Python dependencies
|
||||||
run: |
|
run: |
|
||||||
|
|
132
freedata-nsis-config.nsi
Normal file
132
freedata-nsis-config.nsi
Normal file
|
@ -0,0 +1,132 @@
|
||||||
|
!include "MUI2.nsh"
|
||||||
|
|
||||||
|
; Request administrative rights
|
||||||
|
RequestExecutionLevel admin
|
||||||
|
|
||||||
|
; The name and file name of the installer
|
||||||
|
Name "FreeDATA Installer"
|
||||||
|
OutFile "FreeDATA-Installer.exe"
|
||||||
|
|
||||||
|
; Default installation directory for the server
|
||||||
|
InstallDir "$LOCALAPPDATA\FreeDATA"
|
||||||
|
|
||||||
|
; Registry key to store the installation directory
|
||||||
|
InstallDirRegKey HKCU "Software\FreeDATA" "Install_Dir"
|
||||||
|
|
||||||
|
; Modern UI settings
|
||||||
|
!define MUI_ABORTWARNING
|
||||||
|
|
||||||
|
; Installer interface settings
|
||||||
|
!define MUI_ICON "documentation\icon.ico"
|
||||||
|
!define MUI_UNICON "documentation\icon.ico" ; Icon for the uninstaller
|
||||||
|
|
||||||
|
; Define the welcome page text
|
||||||
|
!define MUI_WELCOMEPAGE_TEXT "Welcome to the FreeDATA Setup Wizard. This wizard will guide you through the installation process."
|
||||||
|
!define MUI_FINISHPAGE_TEXT "Folder: $INSTDIR"
|
||||||
|
!define MUI_DIRECTORYPAGE_TEXT_TOP "Please select the installation folder. It's recommended to use the suggested one to avoid permission problems."
|
||||||
|
|
||||||
|
; Pages
|
||||||
|
!insertmacro MUI_PAGE_WELCOME
|
||||||
|
!insertmacro MUI_PAGE_LICENSE "LICENSE"
|
||||||
|
!insertmacro MUI_PAGE_COMPONENTS
|
||||||
|
!insertmacro MUI_PAGE_DIRECTORY
|
||||||
|
!insertmacro MUI_PAGE_INSTFILES
|
||||||
|
!insertmacro MUI_PAGE_FINISH
|
||||||
|
|
||||||
|
; Uninstaller
|
||||||
|
!insertmacro MUI_UNPAGE_WELCOME
|
||||||
|
!insertmacro MUI_UNPAGE_CONFIRM
|
||||||
|
!insertmacro MUI_UNPAGE_INSTFILES
|
||||||
|
!insertmacro MUI_UNPAGE_FINISH
|
||||||
|
|
||||||
|
; Language (you can choose and configure the language(s) you want)
|
||||||
|
!insertmacro MUI_LANGUAGE "English"
|
||||||
|
|
||||||
|
|
||||||
|
; Installer Sections
|
||||||
|
Section "FreeData Server" SEC01
|
||||||
|
; Set output path to the installation directory
|
||||||
|
SetOutPath $INSTDIR\freedata-server
|
||||||
|
|
||||||
|
; Check if "config.ini" exists and back it up
|
||||||
|
IfFileExists $INSTDIR\freedata-server\config.ini backupConfig
|
||||||
|
|
||||||
|
doneBackup:
|
||||||
|
; Add your application files here
|
||||||
|
File /r "modem\server.dist\*"
|
||||||
|
|
||||||
|
; Restore the original "config.ini" if it was backed up
|
||||||
|
IfFileExists $INSTDIR\freedata-server\config.ini.bak restoreConfig
|
||||||
|
|
||||||
|
; Create a shortcut in the user's desktop
|
||||||
|
CreateShortCut "$DESKTOP\FreeDATA Server.lnk" "$INSTDIR\freedata-server\freedata-server.exe"
|
||||||
|
|
||||||
|
; Create Uninstaller
|
||||||
|
WriteUninstaller "$INSTDIR\Uninstall.exe"
|
||||||
|
|
||||||
|
; Create a Start Menu directory
|
||||||
|
CreateDirectory "$SMPROGRAMS\FreeDATA"
|
||||||
|
|
||||||
|
; Create shortcut in the Start Menu directory
|
||||||
|
CreateShortCut "$SMPROGRAMS\FreeDATA\FreeDATA Server.lnk" "$INSTDIR\freedata-server\freedata-server.exe"
|
||||||
|
|
||||||
|
; Create an Uninstall shortcut
|
||||||
|
CreateShortCut "$SMPROGRAMS\FreeDATA\Uninstall FreeDATA.lnk" "$INSTDIR\Uninstall.exe"
|
||||||
|
|
||||||
|
|
||||||
|
; Backup "config.ini" before overwriting files
|
||||||
|
backupConfig:
|
||||||
|
Rename $INSTDIR\freedata-server\config.ini $INSTDIR\freedata-server\config.ini.bak
|
||||||
|
Goto doneBackup
|
||||||
|
|
||||||
|
; Restore the original "config.ini"
|
||||||
|
restoreConfig:
|
||||||
|
Delete $INSTDIR\freedata-server\config.ini
|
||||||
|
Rename $INSTDIR\freedata-server\config.ini.bak $INSTDIR\freedata-server\config.ini
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
SectionEnd
|
||||||
|
|
||||||
|
Section "FreeData x64 GUI" SEC02
|
||||||
|
; Set output path to the GUI installation directory
|
||||||
|
SetOutPath $INSTDIR\freedata-gui
|
||||||
|
|
||||||
|
; Add GUI files here
|
||||||
|
File /r "gui\release\win-unpacked\*"
|
||||||
|
|
||||||
|
; Create a shortcut on the desktop for the GUI
|
||||||
|
CreateShortCut "$DESKTOP\FreeDATA GUI.lnk" "$INSTDIR\freedata-gui\freedata.exe"
|
||||||
|
|
||||||
|
; Create a start menu shortcut
|
||||||
|
CreateShortCut "$SMPROGRAMS\FreeDATA\FreeDATA GUI.lnk" "$INSTDIR\freedata-gui\freedata.exe"
|
||||||
|
|
||||||
|
; Create an Uninstall shortcut
|
||||||
|
CreateShortCut "$SMPROGRAMS\FreeDATA\Uninstall FreeDATA.lnk" "$INSTDIR\Uninstall.exe"
|
||||||
|
|
||||||
|
SectionEnd
|
||||||
|
|
||||||
|
; Uninstaller Section
|
||||||
|
Section "Uninstall"
|
||||||
|
; Delete files and directories for the server
|
||||||
|
Delete $INSTDIR\freedata-server\*.*
|
||||||
|
RMDir /r $INSTDIR\freedata-server
|
||||||
|
|
||||||
|
; Delete files and directories for the GUI
|
||||||
|
Delete $INSTDIR\freedata-gui\*.*
|
||||||
|
RMDir /r $INSTDIR\freedata-gui
|
||||||
|
|
||||||
|
; Remove the desktop shortcuts
|
||||||
|
Delete "$DESKTOP\FreeDATA Server.lnk"
|
||||||
|
Delete "$DESKTOP\FreeDATA GUI.lnk"
|
||||||
|
|
||||||
|
; Remove Start Menu shortcuts
|
||||||
|
Delete "$SMPROGRAMS\FreeDATA\*.*"
|
||||||
|
RMDir "$SMPROGRAMS\FreeDATA"
|
||||||
|
|
||||||
|
; Attempt to delete the uninstaller itself
|
||||||
|
Delete $EXEPATH
|
||||||
|
|
||||||
|
; Now remove the installation directory if it's empty
|
||||||
|
RMDir /r $INSTDIR
|
||||||
|
SectionEnd
|
|
@ -1,102 +0,0 @@
|
||||||
!include "MUI2.nsh"
|
|
||||||
|
|
||||||
; Request administrative rights
|
|
||||||
RequestExecutionLevel admin
|
|
||||||
|
|
||||||
; The name and file name of the installer
|
|
||||||
Name "FreeData Server"
|
|
||||||
OutFile "FreeData-Server-Installer.exe"
|
|
||||||
|
|
||||||
; Default installation directory
|
|
||||||
; InstallDir "$PROGRAMFILES\FreeData\freedata-server"
|
|
||||||
|
|
||||||
InstallDir "$LOCALAPPDATA\FreeData\freedata-server"
|
|
||||||
|
|
||||||
; Registry key to store the installation directory
|
|
||||||
InstallDirRegKey HKCU "Software\FreeData\freedata-server" "Install_Dir"
|
|
||||||
|
|
||||||
; Modern UI settings
|
|
||||||
!define MUI_ABORTWARNING
|
|
||||||
|
|
||||||
; Installer interface settings
|
|
||||||
!define MUI_ICON "documentation\icon.ico"
|
|
||||||
!define MUI_UNICON "documentation\icon.ico" ; Icon for the uninstaller
|
|
||||||
|
|
||||||
|
|
||||||
; Define the welcome page text
|
|
||||||
!define MUI_WELCOMEPAGE_TEXT "Welcome to the FreeData Server Setup Wizard. This wizard will guide you through the installation process."
|
|
||||||
!define MUI_FINISHPAGE_TEXT "Folder: $INSTDIR"
|
|
||||||
|
|
||||||
|
|
||||||
!define MUI_DIRECTORYPAGE_TEXT_TOP "Please select the installation folder. Its recommended using the suggested one for avoiding permission problems."
|
|
||||||
|
|
||||||
|
|
||||||
; Pages
|
|
||||||
!insertmacro MUI_PAGE_WELCOME
|
|
||||||
!insertmacro MUI_PAGE_LICENSE "LICENSE"
|
|
||||||
;!insertmacro MUI_PAGE_COMPONENTS
|
|
||||||
!insertmacro MUI_PAGE_DIRECTORY
|
|
||||||
!insertmacro MUI_PAGE_INSTFILES
|
|
||||||
!insertmacro MUI_PAGE_FINISH
|
|
||||||
|
|
||||||
; Uninstaller
|
|
||||||
!insertmacro MUI_UNPAGE_WELCOME
|
|
||||||
!insertmacro MUI_UNPAGE_CONFIRM
|
|
||||||
!insertmacro MUI_UNPAGE_INSTFILES
|
|
||||||
!insertmacro MUI_UNPAGE_FINISH
|
|
||||||
|
|
||||||
; Language (you can choose and configure the language(s) you want)
|
|
||||||
!insertmacro MUI_LANGUAGE "English"
|
|
||||||
|
|
||||||
; Installer Sections
|
|
||||||
Section "FreeData Server" SEC01
|
|
||||||
|
|
||||||
; Set output path to the installation directory
|
|
||||||
SetOutPath $INSTDIR
|
|
||||||
|
|
||||||
; Check if "config.ini" exists and back it up
|
|
||||||
IfFileExists $INSTDIR\config.ini backupConfig
|
|
||||||
|
|
||||||
doneBackup:
|
|
||||||
; Add your application files here
|
|
||||||
File /r "modem\server.dist\*.*"
|
|
||||||
|
|
||||||
; Restore the original "config.ini" if it was backed up
|
|
||||||
IfFileExists $INSTDIR\config.ini.bak restoreConfig
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
; Create a shortcut in the user's desktop
|
|
||||||
CreateShortCut "$DESKTOP\FreeData Server.lnk" "$INSTDIR\freedata-server.exe"
|
|
||||||
|
|
||||||
; Create Uninstaller
|
|
||||||
WriteUninstaller "$INSTDIR\Uninstall.exe"
|
|
||||||
|
|
||||||
; Backup "config.ini" before overwriting files
|
|
||||||
backupConfig:
|
|
||||||
Rename $INSTDIR\config.ini $INSTDIR\config.ini.bak
|
|
||||||
Goto doneBackup
|
|
||||||
|
|
||||||
; Restore the original "config.ini"
|
|
||||||
restoreConfig:
|
|
||||||
Delete $INSTDIR\config.ini
|
|
||||||
Rename $INSTDIR\config.ini.bak $INSTDIR\config.ini
|
|
||||||
|
|
||||||
|
|
||||||
SectionEnd
|
|
||||||
|
|
||||||
; Uninstaller Section
|
|
||||||
Section "Uninstall"
|
|
||||||
|
|
||||||
; Delete files and directories
|
|
||||||
Delete $INSTDIR\freedata-server.exe
|
|
||||||
RMDir /r $INSTDIR
|
|
||||||
|
|
||||||
; Remove the shortcut
|
|
||||||
Delete "$DESKTOP\FreeData Server.lnk"
|
|
||||||
|
|
||||||
; Additional uninstallation commands here
|
|
||||||
|
|
||||||
SectionEnd
|
|
||||||
|
|
||||||
|
|
|
@ -45,18 +45,12 @@
|
||||||
"icon": "build/icon.png",
|
"icon": "build/icon.png",
|
||||||
"target": [
|
"target": [
|
||||||
{
|
{
|
||||||
"target": "nsis",
|
"target": "portable",
|
||||||
"arch": ["arm64", "x64"]
|
"arch": ["arm64", "x64"]
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"artifactName": "${productName}-GUI-Windows-${version}.${ext}"
|
"artifactName": "${productName}-GUI-Windows-${version}.${ext}"
|
||||||
},
|
},
|
||||||
"nsis": {
|
|
||||||
"oneClick": false,
|
|
||||||
"perMachine": false,
|
|
||||||
"allowToChangeInstallationDirectory": true,
|
|
||||||
"deleteAppDataOnUninstall": true
|
|
||||||
},
|
|
||||||
"linux": {
|
"linux": {
|
||||||
"category": "Development",
|
"category": "Development",
|
||||||
"target": [
|
"target": [
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
import { app, BrowserWindow, shell, ipcMain } from "electron";
|
import { app, BrowserWindow, shell, ipcMain } from "electron";
|
||||||
import { release, platform } from "node:os";
|
import { release, platform } from "os";
|
||||||
import { join } from "node:path";
|
import { join, dirname } from "path";
|
||||||
import { autoUpdater } from "electron-updater";
|
|
||||||
import { existsSync } from "fs";
|
import { existsSync } from "fs";
|
||||||
import { spawn } from "child_process";
|
import { spawn } from "child_process";
|
||||||
|
|
||||||
|
@ -20,7 +19,6 @@ process.env.DIST = join(process.env.DIST_ELECTRON, "../dist");
|
||||||
process.env.VITE_PUBLIC = process.env.VITE_DEV_SERVER_URL
|
process.env.VITE_PUBLIC = process.env.VITE_DEV_SERVER_URL
|
||||||
? join(process.env.DIST_ELECTRON, "../public")
|
? join(process.env.DIST_ELECTRON, "../public")
|
||||||
: process.env.DIST;
|
: process.env.DIST;
|
||||||
process.env.FDUpdateAvail = "0";
|
|
||||||
|
|
||||||
// Disable GPU Acceleration for Windows 7
|
// Disable GPU Acceleration for Windows 7
|
||||||
if (release().startsWith("6.1")) app.disableHardwareAcceleration();
|
if (release().startsWith("6.1")) app.disableHardwareAcceleration();
|
||||||
|
@ -40,7 +38,7 @@ if (!app.requestSingleInstanceLock()) {
|
||||||
// process.env['ELECTRON_DISABLE_SECURITY_WARNINGS'] = 'true'
|
// process.env['ELECTRON_DISABLE_SECURITY_WARNINGS'] = 'true'
|
||||||
|
|
||||||
// set daemon process var
|
// set daemon process var
|
||||||
var daemonProcess = null;
|
var serverProcess = null;
|
||||||
let win: BrowserWindow | null = null;
|
let win: BrowserWindow | null = null;
|
||||||
// Here, you can also use other preload
|
// Here, you can also use other preload
|
||||||
const preload = join(__dirname, "../preload/index.js");
|
const preload = join(__dirname, "../preload/index.js");
|
||||||
|
@ -75,9 +73,9 @@ async function createWindow() {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Test actively push message to the Electron-Renderer
|
// Test actively push message to the Electron-Renderer
|
||||||
win.webContents.on("did-finish-load", () => {
|
//win.webContents.on("did-finish-load", () => {
|
||||||
win?.webContents.send("main-process-message", new Date().toLocaleString());
|
// win?.webContents.send("main-process-message", new Date().toLocaleString());
|
||||||
});
|
//});
|
||||||
|
|
||||||
// Make all links open with the browser, not with the application
|
// Make all links open with the browser, not with the application
|
||||||
win.webContents.setWindowOpenHandler(({ url }) => {
|
win.webContents.setWindowOpenHandler(({ url }) => {
|
||||||
|
@ -87,12 +85,7 @@ async function createWindow() {
|
||||||
// win.webContents.on('will-navigate', (event, url) => { }) #344
|
// win.webContents.on('will-navigate', (event, url) => { }) #344
|
||||||
|
|
||||||
win.once("ready-to-show", () => {
|
win.once("ready-to-show", () => {
|
||||||
//autoUpdater.logger = log.scope("updater");
|
//
|
||||||
//autoUpdater.channel = config.update_channel;
|
|
||||||
autoUpdater.autoInstallOnAppQuit = false;
|
|
||||||
autoUpdater.autoDownload = true;
|
|
||||||
autoUpdater.checkForUpdatesAndNotify();
|
|
||||||
//autoUpdater.quitAndInstall();
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -102,68 +95,61 @@ app.whenReady().then(() => {
|
||||||
|
|
||||||
console.log(platform());
|
console.log(platform());
|
||||||
//Generate daemon binary path
|
//Generate daemon binary path
|
||||||
var daemonPath = "";
|
var serverPath = "";
|
||||||
|
console.log(process.env);
|
||||||
|
|
||||||
|
// Attempt to find Installation Folder
|
||||||
|
console.log(app.getAppPath());
|
||||||
|
console.log(join(app.getAppPath(), "..", ".."));
|
||||||
|
console.log(join(app.getAppPath(), "..", "..", ".."));
|
||||||
|
|
||||||
|
//var basePath = join(app.getAppPath(), '..', '..', '..') || join(process.env.PWD, '..') || join(process.env.INIT_CWD, '..') || join(process.env.DIST, '..', '..', '..');
|
||||||
|
var basePath = join(app.getAppPath(), "..", "..", "..");
|
||||||
switch (platform().toLowerCase()) {
|
switch (platform().toLowerCase()) {
|
||||||
case "darwin":
|
//case "darwin":
|
||||||
daemonPath = join(process.resourcesPath, "modem", "freedata-server");
|
//serverPath = join(basePath, "freedata-server", "freedata-server.exe");
|
||||||
case "linux":
|
//serverProcess = spawn(serverPath, [], { detached: true });
|
||||||
daemonPath = join(process.resourcesPath, "modem", "freedata-server");
|
//serverProcess.unref(); // Allow the server process to continue running independently of the parent process
|
||||||
break;
|
// break;
|
||||||
|
//case "linux":
|
||||||
|
//serverPath = join(basePath, "freedata-server", "freedata-server.exe");
|
||||||
|
//serverProcess = spawn(serverPath, [], { detached: true });
|
||||||
|
//serverProcess.unref(); // Allow the server process to continue running independently of the parent process
|
||||||
|
// break;
|
||||||
case "win32":
|
case "win32":
|
||||||
daemonPath = join(process.resourcesPath, "modem", "freedata-server.exe");
|
serverPath = join(basePath, "freedata-server", "freedata-server.exe");
|
||||||
break;
|
console.log(`Starting server with path: ${serverPath}`);
|
||||||
case "win64":
|
serverProcess = spawn(
|
||||||
daemonPath = join(process.resourcesPath, "modem", "freedata-server.exe");
|
"cmd.exe",
|
||||||
|
["/c", "start", "cmd.exe", "/c", serverPath],
|
||||||
|
{ shell: true },
|
||||||
|
);
|
||||||
|
console.log(`Started server | PID: ${serverProcess.pid}`);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
console.log("Unhandled OS Platform: ", platform());
|
console.log("Unhandled OS Platform: ", platform());
|
||||||
|
serverProcess = null;
|
||||||
|
serverPath = null;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
//Start daemon binary if it exists
|
serverProcess.on("error", (err) => {
|
||||||
if (existsSync(daemonPath)) {
|
console.error("Failed to start server process:", err);
|
||||||
console.log("Starting freedata-server binary");
|
serverProcess = null;
|
||||||
console.log("daemonPath:", daemonPath);
|
serverPath = null;
|
||||||
console.log("CWD:", join(daemonPath, ".."));
|
|
||||||
/*
|
|
||||||
var daemonProcess = spawn("freedata-server", [], {
|
|
||||||
cwd: join(process.env.DIST, "modem"),
|
|
||||||
shell: true
|
|
||||||
});
|
});
|
||||||
*/
|
serverProcess.stdout.on("data", (data) => {
|
||||||
/*
|
//console.log(`stdout: ${data}`);
|
||||||
daemonProcess = spawn(daemonPath, [], {
|
|
||||||
shell: true
|
|
||||||
});
|
});
|
||||||
console.log(daemonProcess)
|
|
||||||
*/
|
|
||||||
daemonProcess = spawn(daemonPath, [], {});
|
|
||||||
|
|
||||||
// return process messages
|
serverProcess.stderr.on("data", (data) => {
|
||||||
daemonProcess.on("error", (err) => {
|
console.error(`stderr: ${data}`);
|
||||||
//daemonProcessLog.error(`error when starting daemon: ${err}`);
|
|
||||||
console.log(err);
|
|
||||||
});
|
});
|
||||||
daemonProcess.on("message", () => {
|
|
||||||
// daemonProcessLog.info(`${data}`);
|
|
||||||
});
|
});
|
||||||
daemonProcess.stdout.on("data", () => {
|
|
||||||
// daemonProcessLog.info(`${data}`);
|
|
||||||
});
|
|
||||||
daemonProcess.stderr.on("data", (data) => {
|
|
||||||
// daemonProcessLog.info(`${data}`);
|
|
||||||
console.log(data);
|
|
||||||
});
|
|
||||||
daemonProcess.on("close", (code) => {
|
|
||||||
// daemonProcessLog.warn(`daemonProcess exited with code ${code}`);
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
daemonProcess = null;
|
|
||||||
daemonPath = null;
|
|
||||||
console.log("Daemon binary doesn't exist--normal for dev environments.");
|
|
||||||
}
|
|
||||||
|
|
||||||
//)
|
app.on("before-quit", () => {
|
||||||
|
close_sub_processes();
|
||||||
});
|
});
|
||||||
|
|
||||||
app.on("window-all-closed", () => {
|
app.on("window-all-closed", () => {
|
||||||
|
@ -205,104 +191,33 @@ ipcMain.handle("open-win", (_, arg) => {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
//restart and install udpate
|
|
||||||
ipcMain.on("request-restart-and-install-update", (event, data) => {
|
|
||||||
close_sub_processes();
|
|
||||||
autoUpdater.quitAndInstall();
|
|
||||||
});
|
|
||||||
|
|
||||||
// LISTENER FOR UPDATER EVENTS
|
|
||||||
autoUpdater.on("update-available", (info) => {
|
|
||||||
process.env.FDUpdateAvail = "1";
|
|
||||||
console.log("update available");
|
|
||||||
|
|
||||||
let arg = {
|
|
||||||
status: "update-available",
|
|
||||||
info: info,
|
|
||||||
};
|
|
||||||
win.webContents.send("action-updater", arg);
|
|
||||||
});
|
|
||||||
|
|
||||||
autoUpdater.on("update-not-available", (info) => {
|
|
||||||
console.log("update not available");
|
|
||||||
let arg = {
|
|
||||||
status: "update-not-available",
|
|
||||||
info: info,
|
|
||||||
};
|
|
||||||
win.webContents.send("action-updater", arg);
|
|
||||||
});
|
|
||||||
|
|
||||||
autoUpdater.on("update-downloaded", (info) => {
|
|
||||||
process.env.FDUpdateAvail = "1";
|
|
||||||
console.log("update downloaded");
|
|
||||||
let arg = {
|
|
||||||
status: "update-downloaded",
|
|
||||||
info: info,
|
|
||||||
};
|
|
||||||
win.webContents.send("action-updater", arg);
|
|
||||||
// we need to call this at this point.
|
|
||||||
// if an update is available and we are force closing the app
|
|
||||||
// the entire screen crashes...
|
|
||||||
//console.log('quit application and install update');
|
|
||||||
//autoUpdater.quitAndInstall();
|
|
||||||
});
|
|
||||||
|
|
||||||
autoUpdater.on("checking-for-update", () => {
|
|
||||||
console.log("checking for update");
|
|
||||||
let arg = {
|
|
||||||
status: "checking-for-update",
|
|
||||||
version: app.getVersion(),
|
|
||||||
};
|
|
||||||
win.webContents.send("action-updater", arg);
|
|
||||||
});
|
|
||||||
|
|
||||||
autoUpdater.on("download-progress", (progress) => {
|
|
||||||
let arg = {
|
|
||||||
status: "download-progress",
|
|
||||||
progress: progress,
|
|
||||||
};
|
|
||||||
win.webContents.send("action-updater", arg);
|
|
||||||
});
|
|
||||||
|
|
||||||
autoUpdater.on("error", (error) => {
|
|
||||||
console.log("update error");
|
|
||||||
let arg = {
|
|
||||||
status: "error",
|
|
||||||
progress: error,
|
|
||||||
};
|
|
||||||
win.webContents.send("action-updater", arg);
|
|
||||||
console.log("AUTO UPDATER : " + error);
|
|
||||||
});
|
|
||||||
|
|
||||||
function close_sub_processes() {
|
function close_sub_processes() {
|
||||||
console.log("closing sub processes");
|
console.log("Closing sub processes...");
|
||||||
|
|
||||||
// closing the modem binary if not closed when closing application and also our daemon which has been started by the gui
|
if (serverProcess != null) {
|
||||||
try {
|
try {
|
||||||
if (daemonProcess != null) {
|
console.log(`Killing server process with PID: ${serverProcess.pid}`);
|
||||||
daemonProcess.kill();
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
console.log(e);
|
|
||||||
}
|
|
||||||
|
|
||||||
console.log("closing modem and daemon");
|
switch (platform().toLowerCase()) {
|
||||||
try {
|
//case "darwin":
|
||||||
if (platform() == "win32") {
|
// process.kill(serverProcess.pid);
|
||||||
spawn("Taskkill", ["/IM", "freedata-modem.exe", "/F"]);
|
// break;
|
||||||
spawn("Taskkill", ["/IM", "freedata-server.exe", "/F"]);
|
//case "linux":
|
||||||
}
|
// process.kill(serverProcess.pid);
|
||||||
|
// break;
|
||||||
|
case "win32":
|
||||||
|
// For Windows, use taskkill to ensure all child processes are also terminated
|
||||||
|
spawn("taskkill", ["/pid", serverProcess.pid.toString(), "/f", "/t"]);
|
||||||
|
break;
|
||||||
|
|
||||||
if (platform() == "linux") {
|
default:
|
||||||
spawn("pkill", ["-9", "freedata-modem"]);
|
console.log("Unhandled OS Platform: ", platform());
|
||||||
spawn("pkill", ["-9", "freedata-server"]);
|
serverProcess = null;
|
||||||
|
serverPath = null;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error(`Error killing server process: ${error}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (platform() == "darwin") {
|
|
||||||
spawn("pkill", ["-9", "freedata-modem"]);
|
|
||||||
spawn("pkill", ["-9", "freedata-server"]);
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
console.log(e);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -111,83 +111,4 @@ window.onmessage = (ev) => {
|
||||||
ev.data.payload === "removeLoading" && removeLoading();
|
ev.data.payload === "removeLoading" && removeLoading();
|
||||||
};
|
};
|
||||||
|
|
||||||
setTimeout(removeLoading, 4999);
|
setTimeout(removeLoading, 3999);
|
||||||
|
|
||||||
// IPC ACTION FOR AUTO UPDATER
|
|
||||||
ipcRenderer.on("action-updater", (event, arg) => {
|
|
||||||
if (arg.status == "download-progress") {
|
|
||||||
var progressinfo =
|
|
||||||
"(" +
|
|
||||||
Math.round(arg.progress.transferred / 1024) +
|
|
||||||
"kB /" +
|
|
||||||
Math.round(arg.progress.total / 1024) +
|
|
||||||
"kB)" +
|
|
||||||
" @ " +
|
|
||||||
Math.round(arg.progress.bytesPerSecond / 1024) +
|
|
||||||
"kByte/s";
|
|
||||||
document.getElementById("UpdateProgressInfo").innerHTML = progressinfo;
|
|
||||||
|
|
||||||
document
|
|
||||||
.getElementById("UpdateProgressBar")
|
|
||||||
.setAttribute("aria-valuenow", arg.progress.percent);
|
|
||||||
document
|
|
||||||
.getElementById("UpdateProgressBar")
|
|
||||||
.setAttribute("style", "width:" + arg.progress.percent + "%;");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (arg.status == "checking-for-update") {
|
|
||||||
//document.title = document.title + ' - v' + arg.version;
|
|
||||||
//updateTitle(
|
|
||||||
// config.myCall,
|
|
||||||
// config.tnc_host,
|
|
||||||
// config.tnc_port,
|
|
||||||
// " -v " + arg.version,
|
|
||||||
//);
|
|
||||||
document.getElementById("updater_status").innerHTML =
|
|
||||||
'<span class="spinner-border spinner-border-sm" role="status" aria-hidden="true"></span>';
|
|
||||||
|
|
||||||
document.getElementById("updater_status").className =
|
|
||||||
"btn btn-secondary btn-sm";
|
|
||||||
document.getElementById("update_and_install").style.display = "none";
|
|
||||||
}
|
|
||||||
if (arg.status == "update-downloaded") {
|
|
||||||
document.getElementById("update_and_install").removeAttribute("style");
|
|
||||||
document.getElementById("updater_status").innerHTML =
|
|
||||||
'<i class="bi bi-cloud-download ms-1 me-1" style="color: white;"></i>';
|
|
||||||
document.getElementById("updater_status").className =
|
|
||||||
"btn btn-success btn-sm";
|
|
||||||
|
|
||||||
// HERE WE NEED TO RUN THIS SOMEHOW...
|
|
||||||
//mainLog.info('quit application and install update');
|
|
||||||
//autoUpdater.quitAndInstall();
|
|
||||||
}
|
|
||||||
if (arg.status == "update-not-available") {
|
|
||||||
document.getElementById("updater_last_version").innerHTML =
|
|
||||||
arg.info.releaseName;
|
|
||||||
document.getElementById("updater_last_update").innerHTML =
|
|
||||||
arg.info.releaseDate;
|
|
||||||
document.getElementById("updater_release_notes").innerHTML =
|
|
||||||
arg.info.releaseNotes;
|
|
||||||
|
|
||||||
document.getElementById("updater_status").innerHTML =
|
|
||||||
'<i class="bi bi-check2-square ms-1 me-1" style="color: white;"></i>';
|
|
||||||
document.getElementById("updater_status").className =
|
|
||||||
"btn btn-success btn-sm";
|
|
||||||
document.getElementById("update_and_install").style.display = "none";
|
|
||||||
}
|
|
||||||
if (arg.status == "update-available") {
|
|
||||||
document.getElementById("updater_status").innerHTML =
|
|
||||||
'<i class="bi bi-hourglass-split ms-1 me-1" style="color: white;"></i>';
|
|
||||||
document.getElementById("updater_status").className =
|
|
||||||
"btn btn-warning btn-sm";
|
|
||||||
document.getElementById("update_and_install").style.display = "none";
|
|
||||||
}
|
|
||||||
|
|
||||||
if (arg.status == "error") {
|
|
||||||
document.getElementById("updater_status").innerHTML =
|
|
||||||
'<i class="bi bi-exclamation-square ms-1 me-1" style="color: white;"></i>';
|
|
||||||
document.getElementById("updater_status").className =
|
|
||||||
"btn btn-danger btn-sm";
|
|
||||||
document.getElementById("update_and_install").style.display = "none";
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
"name": "FreeDATA",
|
"name": "FreeDATA",
|
||||||
"description": "FreeDATA Client application for connecting to FreeDATA server",
|
"description": "FreeDATA Client application for connecting to FreeDATA server",
|
||||||
"private": true,
|
"private": true,
|
||||||
"version": "0.14.0-alpha",
|
"version": "0.14.2-alpha",
|
||||||
"main": "dist-electron/main/index.js",
|
"main": "dist-electron/main/index.js",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"start": "vite",
|
"start": "vite",
|
||||||
|
@ -13,7 +13,8 @@
|
||||||
"release": "vue-tsc --noEmit && vite build && electron-builder -p onTag",
|
"release": "vue-tsc --noEmit && vite build && electron-builder -p onTag",
|
||||||
"preview": "vite preview",
|
"preview": "vite preview",
|
||||||
"lint": "eslint --ext .js,.vue src",
|
"lint": "eslint --ext .js,.vue src",
|
||||||
"lint-fix": "eslint --ext .js,.vue --fix src"
|
"lint-fix": "eslint --ext .js,.vue --fix src",
|
||||||
|
"install-deps": "npm install && npm update"
|
||||||
},
|
},
|
||||||
"repository": {
|
"repository": {
|
||||||
"type": "git",
|
"type": "git",
|
||||||
|
|
Binary file not shown.
Before Width: | Height: | Size: 125 KiB |
Binary file not shown.
Before Width: | Height: | Size: 58 KiB |
Binary file not shown.
Before Width: | Height: | Size: 342 KiB |
|
@ -1,3 +0,0 @@
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-person-fill" viewBox="0 0 16 16">
|
|
||||||
<path d="M3 14s-1 0-1-1 1-4 6-4 6 3 6 4-1 1-1 1H3Zm5-6a3 3 0 1 0 0-6 3 3 0 0 0 0 6Z"/>
|
|
||||||
</svg>
|
|
Before Width: | Height: | Size: 225 B |
Binary file not shown.
Before Width: | Height: | Size: 136 KiB |
|
@ -25,7 +25,7 @@ import {
|
||||||
} from "chart.js";
|
} from "chart.js";
|
||||||
|
|
||||||
import { Bar } from "vue-chartjs";
|
import { Bar } from "vue-chartjs";
|
||||||
import { ref, computed } from "vue";
|
import { watch, nextTick, ref, computed } from "vue";
|
||||||
import annotationPlugin from "chartjs-plugin-annotation";
|
import annotationPlugin from "chartjs-plugin-annotation";
|
||||||
|
|
||||||
ChartJS.register(
|
ChartJS.register(
|
||||||
|
@ -101,6 +101,20 @@ const beaconHistogramData = computed(() => ({
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
const messagesContainer = ref(null);
|
||||||
|
watch(
|
||||||
|
() => chat.scrollTrigger,
|
||||||
|
(newVal, oldVal) => {
|
||||||
|
//console.log("Trigger changed from", oldVal, "to", newVal); // Debugging line
|
||||||
|
nextTick(() => {
|
||||||
|
if (messagesContainer.value) {
|
||||||
|
messagesContainer.value.scrollTop =
|
||||||
|
messagesContainer.value.scrollHeight;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
);
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
|
@ -143,7 +157,7 @@ const beaconHistogramData = computed(() => ({
|
||||||
</nav>
|
</nav>
|
||||||
|
|
||||||
<!-- Chat Messages Area -->
|
<!-- Chat Messages Area -->
|
||||||
<div class="flex-grow-1 overflow-auto">
|
<div class="flex-grow-1 overflow-auto" ref="messagesContainer">
|
||||||
<chat_messages />
|
<chat_messages />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
|
@ -13,11 +13,7 @@ const chat = useChatStore(pinia);
|
||||||
function chatSelected(callsign) {
|
function chatSelected(callsign) {
|
||||||
chat.selectedCallsign = callsign.toUpperCase();
|
chat.selectedCallsign = callsign.toUpperCase();
|
||||||
// scroll message container to bottom
|
// scroll message container to bottom
|
||||||
var messageBody = document.getElementById("message-container");
|
chat.triggerScrollToBottom();
|
||||||
if (messageBody != null) {
|
|
||||||
// needs sensible defaults
|
|
||||||
messageBody.scrollTop = messageBody.scrollHeight - messageBody.clientHeight;
|
|
||||||
}
|
|
||||||
|
|
||||||
processBeaconData(callsign);
|
processBeaconData(callsign);
|
||||||
}
|
}
|
||||||
|
|
|
@ -30,7 +30,7 @@ import {
|
||||||
Legend
|
Legend
|
||||||
} from 'chart.js'
|
} from 'chart.js'
|
||||||
import { Line } from 'vue-chartjs'
|
import { Line } from 'vue-chartjs'
|
||||||
import { ref, computed } from 'vue';
|
import { ref, computed, nextTick } from 'vue';
|
||||||
|
|
||||||
|
|
||||||
import { VuemojiPicker, EmojiClickEventDetail } from 'vuemoji-picker'
|
import { VuemojiPicker, EmojiClickEventDetail } from 'vuemoji-picker'
|
||||||
|
@ -90,6 +90,8 @@ function transmitNewMessage() {
|
||||||
chat.selectedCallsign = Object.keys(chat.callsign_list)[0];
|
chat.selectedCallsign = Object.keys(chat.callsign_list)[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
chat.inputText = chat.inputText.trim();
|
chat.inputText = chat.inputText.trim();
|
||||||
|
|
||||||
// Proceed only if there is text or files selected
|
// Proceed only if there is text or files selected
|
||||||
|
@ -101,6 +103,7 @@ function transmitNewMessage() {
|
||||||
type: file.type,
|
type: file.type,
|
||||||
data: file.content
|
data: file.content
|
||||||
};
|
};
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
if (chat.selectedCallsign.startsWith("BC-")) {
|
if (chat.selectedCallsign.startsWith("BC-")) {
|
||||||
|
@ -120,6 +123,7 @@ function transmitNewMessage() {
|
||||||
chat.inputText = '';
|
chat.inputText = '';
|
||||||
chatModuleMessage.value = "";
|
chatModuleMessage.value = "";
|
||||||
resetFile()
|
resetFile()
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function resetFile(event){
|
function resetFile(event){
|
||||||
|
|
|
@ -1,223 +0,0 @@
|
||||||
<script setup lang="ts">
|
|
||||||
import { ref, onMounted } from "vue";
|
|
||||||
|
|
||||||
import { setActivePinia } from "pinia";
|
|
||||||
import pinia from "../store/index";
|
|
||||||
setActivePinia(pinia);
|
|
||||||
|
|
||||||
import infoScreen_updater from "./infoScreen_updater.vue";
|
|
||||||
|
|
||||||
function openWebExternal(url) {
|
|
||||||
open(url);
|
|
||||||
}
|
|
||||||
const cards = ref([
|
|
||||||
{
|
|
||||||
titleName: "Simon",
|
|
||||||
titleCall: "DJ2LS",
|
|
||||||
role: "Founder & Core Developer",
|
|
||||||
imgSrc: "dj2ls.png",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
titleName: "Alan",
|
|
||||||
titleCall: "N1QM",
|
|
||||||
role: "Developer",
|
|
||||||
imgSrc: "person-fill.svg",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
titleName: "Stefan",
|
|
||||||
titleCall: "DK5SM",
|
|
||||||
role: "Tester",
|
|
||||||
imgSrc: "person-fill.svg",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
titleName: "Wolfgang",
|
|
||||||
titleCall: "DL4IAZ",
|
|
||||||
role: "Supporter",
|
|
||||||
imgSrc: "person-fill.svg",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
titleName: "David",
|
|
||||||
titleCall: "VK5DGR",
|
|
||||||
role: "Codec 2 Founder",
|
|
||||||
imgSrc: "vk5dgr.jpeg",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
titleName: "John",
|
|
||||||
titleCall: "EI7IG",
|
|
||||||
role: "Tester",
|
|
||||||
imgSrc: "ei7ig.jpeg",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
titleName: "Paul",
|
|
||||||
titleCall: "N2KIQ",
|
|
||||||
role: "Developer",
|
|
||||||
imgSrc: "person-fill.svg",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
titleName: "Trip",
|
|
||||||
titleCall: "KT4WO",
|
|
||||||
role: "Tester",
|
|
||||||
imgSrc: "kt4wo.png",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
titleName: "Manuel",
|
|
||||||
titleCall: "DF7MH",
|
|
||||||
role: "Tester",
|
|
||||||
imgSrc: "person-fill.svg",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
titleName: "Darren",
|
|
||||||
titleCall: "G0HWW",
|
|
||||||
role: "Tester",
|
|
||||||
imgSrc: "person-fill.svg",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
titleName: "Kai",
|
|
||||||
titleCall: "LA3QMA",
|
|
||||||
role: "Developer",
|
|
||||||
imgSrc: "person-fill.svg",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
titleName: "Pedro",
|
|
||||||
titleCall: "F4JAW",
|
|
||||||
role: "Core Developer",
|
|
||||||
imgSrc: "person-fill.svg",
|
|
||||||
},
|
|
||||||
]);
|
|
||||||
|
|
||||||
// Shuffle cards
|
|
||||||
function shuffleCards() {
|
|
||||||
cards.value = cards.value.sort(() => Math.random() - 0.5);
|
|
||||||
}
|
|
||||||
|
|
||||||
onMounted(shuffleCards);
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<template>
|
|
||||||
<!--<infoScreen_updater />-->
|
|
||||||
<div class="container-fluid">
|
|
||||||
<div class="row">
|
|
||||||
<h6>Important URLs</h6>
|
|
||||||
|
|
||||||
<div
|
|
||||||
class="btn-toolbar mx-auto"
|
|
||||||
role="toolbar"
|
|
||||||
aria-label="Toolbar with button groups"
|
|
||||||
>
|
|
||||||
<div class="btn-group">
|
|
||||||
<button
|
|
||||||
class="btn btn-sm bi bi-geo-alt btn-secondary me-2"
|
|
||||||
id="openExplorer"
|
|
||||||
type="button"
|
|
||||||
data-bs-placement="bottom"
|
|
||||||
@click="openWebExternal('https://explorer.freedata.app')"
|
|
||||||
>
|
|
||||||
Explorer map
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
<div class="btn-group">
|
|
||||||
<button
|
|
||||||
class="btn btn-sm btn-secondary me-2 bi bi-graph-up"
|
|
||||||
id="btnStats"
|
|
||||||
type="button"
|
|
||||||
data-bs-placement="bottom"
|
|
||||||
@click="openWebExternal('https://statistics.freedata.app/')"
|
|
||||||
>
|
|
||||||
Statistics
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
<div class="btn-group">
|
|
||||||
<button
|
|
||||||
class="btn btn-secondary bi bi-bookmarks me-2"
|
|
||||||
id="fdWww"
|
|
||||||
data-bs-toggle="tooltip"
|
|
||||||
data-bs-trigger="hover"
|
|
||||||
title="FreeDATA website"
|
|
||||||
role="button"
|
|
||||||
@click="openWebExternal('https://freedata.app')"
|
|
||||||
>
|
|
||||||
Website
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
<div class="btn-group">
|
|
||||||
<button
|
|
||||||
class="btn btn-secondary bi bi-github me-2"
|
|
||||||
id="ghUrl"
|
|
||||||
data-bs-toggle="tooltip"
|
|
||||||
data-bs-trigger="hover"
|
|
||||||
title="Github"
|
|
||||||
role="button"
|
|
||||||
@click="openWebExternal('https://github.com/dj2ls/freedata')"
|
|
||||||
>
|
|
||||||
Github
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
<div class="btn-group">
|
|
||||||
<button
|
|
||||||
class="btn btn-secondary bi bi-wikipedia me-2"
|
|
||||||
id="wikiUrl"
|
|
||||||
data-bs-toggle="tooltip"
|
|
||||||
data-bs-trigger="hover"
|
|
||||||
title="Wiki"
|
|
||||||
role="button"
|
|
||||||
@click="openWebExternal('https://wiki.freedata.app')"
|
|
||||||
>
|
|
||||||
Wiki
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
<div class="btn-group">
|
|
||||||
<button
|
|
||||||
class="btn btn-secondary bi bi-discord"
|
|
||||||
id="discordUrl"
|
|
||||||
data-bs-toggle="tooltip"
|
|
||||||
data-bs-trigger="hover"
|
|
||||||
title="Discord"
|
|
||||||
role="button"
|
|
||||||
@click="openWebExternal('https://discord.freedata.app')"
|
|
||||||
>
|
|
||||||
Discord
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<hr />
|
|
||||||
<div class="row">
|
|
||||||
<h6>We would like to especially thank the following</h6>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div
|
|
||||||
class="d-flex flex-nowrap overflow-y-auto w-100"
|
|
||||||
style="height: calc(100vh - 170px); overflow-x: hidden"
|
|
||||||
>
|
|
||||||
<div class="row row-cols-1 row-cols-sm-2 row-cols-md-4 row-cols-lg-6">
|
|
||||||
<div class="d-inline-block" v-for="card in cards" :key="card.titleName">
|
|
||||||
<div class="col">
|
|
||||||
<div class="card border-dark m-1" style="max-width: 10rem">
|
|
||||||
<img :src="card.imgSrc" class="card-img-top grayscale" />
|
|
||||||
<div class="card-body">
|
|
||||||
<p class="card-text text-center">{{ card.role }}</p>
|
|
||||||
</div>
|
|
||||||
<div class="card-footer text-body-secondary text-center">
|
|
||||||
<strong>{{ card.titleCall }}</strong>
|
|
||||||
</div>
|
|
||||||
<div class="card-footer text-body-secondary text-center">
|
|
||||||
<strong>{{ card.titleName }}</strong>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<style>
|
|
||||||
.grayscale {
|
|
||||||
filter: grayscale(100%);
|
|
||||||
transition: filter 0.3s ease-in-out;
|
|
||||||
}
|
|
||||||
|
|
||||||
.grayscale:hover {
|
|
||||||
filter: grayscale(0);
|
|
||||||
}
|
|
||||||
</style>
|
|
|
@ -5,31 +5,21 @@ setActivePinia(pinia);
|
||||||
|
|
||||||
import main_modals from "./main_modals.vue";
|
import main_modals from "./main_modals.vue";
|
||||||
import main_top_navbar from "./main_top_navbar.vue";
|
import main_top_navbar from "./main_top_navbar.vue";
|
||||||
import main_rig_control from "./main_rig_control.vue";
|
|
||||||
import settings_view from "./settings.vue";
|
import settings_view from "./settings.vue";
|
||||||
import main_active_rig_control from "./main_active_rig_control.vue";
|
|
||||||
import main_footer_navbar from "./main_footer_navbar.vue";
|
import main_footer_navbar from "./main_footer_navbar.vue";
|
||||||
|
|
||||||
import main_active_stats from "./main_active_stats.vue";
|
|
||||||
import main_active_broadcasts from "./main_active_broadcasts.vue";
|
|
||||||
import main_active_heard_stations from "./main_active_heard_stations.vue";
|
|
||||||
import main_active_audio_level from "./main_active_audio_level.vue";
|
|
||||||
|
|
||||||
import chat from "./chat.vue";
|
import chat from "./chat.vue";
|
||||||
import infoScreen from "./infoScreen.vue";
|
|
||||||
import main_modem_healthcheck from "./main_modem_healthcheck.vue";
|
import main_modem_healthcheck from "./main_modem_healthcheck.vue";
|
||||||
import Dynamic_components from "./dynamic_components.vue";
|
import Dynamic_components from "./dynamic_components.vue";
|
||||||
|
|
||||||
import { getFreedataMessages } from "../js/api";
|
import { getFreedataMessages } from "../js/api";
|
||||||
|
import { getRemote } from "../store/settingsStore.js";
|
||||||
|
import { loadAllData } from "../js/eventHandler";
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<!-------------------------------- INFO TOASTS ---------------->
|
<!-------------------------------- INFO TOASTS ---------------->
|
||||||
<div
|
<div aria-live="polite" aria-atomic="true" class="position-relative z-3">
|
||||||
aria-live="polite"
|
|
||||||
aria-atomic="true"
|
|
||||||
class="position-relative z-3"
|
|
||||||
>
|
|
||||||
<div
|
<div
|
||||||
class="toast-container position-absolute top-0 end-0 p-3"
|
class="toast-container position-absolute top-0 end-0 p-3"
|
||||||
id="mainToastContainer"
|
id="mainToastContainer"
|
||||||
|
@ -47,6 +37,7 @@ import { getFreedataMessages } from "../js/api";
|
||||||
id="main-list-tab"
|
id="main-list-tab"
|
||||||
role="tablist"
|
role="tablist"
|
||||||
style="margin-top: 100px"
|
style="margin-top: 100px"
|
||||||
|
@click="loadAllData"
|
||||||
>
|
>
|
||||||
<main_modem_healthcheck />
|
<main_modem_healthcheck />
|
||||||
|
|
||||||
|
@ -83,17 +74,6 @@ import { getFreedataMessages } from "../js/api";
|
||||||
><i class="bi bi-rocket h3"></i
|
><i class="bi bi-rocket h3"></i
|
||||||
></a>
|
></a>
|
||||||
|
|
||||||
<a
|
|
||||||
class="list-group-item list-group-item-dark list-group-item-action border border-0 rounded-3 mb-2"
|
|
||||||
id="list-info-list"
|
|
||||||
data-bs-toggle="list"
|
|
||||||
href="#list-info"
|
|
||||||
role="tab"
|
|
||||||
aria-controls="list-info"
|
|
||||||
title="About"
|
|
||||||
><i class="bi bi-info h3"></i
|
|
||||||
></a>
|
|
||||||
|
|
||||||
<a
|
<a
|
||||||
class="list-group-item list-group-item-dark list-group-item-action d-none border-0 rounded-3 mb-2"
|
class="list-group-item list-group-item-dark list-group-item-action d-none border-0 rounded-3 mb-2"
|
||||||
id="list-logger-list"
|
id="list-logger-list"
|
||||||
|
@ -111,6 +91,7 @@ import { getFreedataMessages } from "../js/api";
|
||||||
role="tab"
|
role="tab"
|
||||||
aria-controls="list-settings"
|
aria-controls="list-settings"
|
||||||
title="Settings"
|
title="Settings"
|
||||||
|
@click="loadAllData"
|
||||||
><i class="bi bi-gear-wide-connected h3"></i
|
><i class="bi bi-gear-wide-connected h3"></i
|
||||||
></a>
|
></a>
|
||||||
</div>
|
</div>
|
||||||
|
@ -302,14 +283,7 @@ import { getFreedataMessages } from "../js/api";
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div
|
|
||||||
class="tab-pane fade"
|
|
||||||
id="list-info"
|
|
||||||
role="tabpanel"
|
|
||||||
aria-labelledby="list-info-list"
|
|
||||||
>
|
|
||||||
<infoScreen />
|
|
||||||
</div>
|
|
||||||
<div
|
<div
|
||||||
class="tab-pane fade show active"
|
class="tab-pane fade show active"
|
||||||
id="list-grid"
|
id="list-grid"
|
||||||
|
|
|
@ -1,170 +0,0 @@
|
||||||
<script setup lang="ts">
|
|
||||||
import { setActivePinia } from "pinia";
|
|
||||||
import pinia from "../store/index";
|
|
||||||
setActivePinia(pinia);
|
|
||||||
|
|
||||||
import { useStateStore } from "../store/stateStore.js";
|
|
||||||
const state = useStateStore(pinia);
|
|
||||||
</script>
|
|
||||||
<template>
|
|
||||||
<div class="card mb-1">
|
|
||||||
<div class="card-header p-1">
|
|
||||||
<div class="container">
|
|
||||||
<div class="row">
|
|
||||||
<div class="col-1">
|
|
||||||
<i class="bi bi-volume-up" style="font-size: 1.2rem"></i>
|
|
||||||
</div>
|
|
||||||
<div class="col-3">
|
|
||||||
<strong>Audio</strong>
|
|
||||||
</div>
|
|
||||||
<div class="col-7">
|
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
id="audioModalButton"
|
|
||||||
data-bs-toggle="modal"
|
|
||||||
data-bs-target="#audioModal"
|
|
||||||
class="btn btn-sm btn-outline-secondary me-1"
|
|
||||||
>
|
|
||||||
Tune
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
<div class="col-1 text-end">
|
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
id="openHelpModalAudioLevel"
|
|
||||||
data-bs-toggle="modal"
|
|
||||||
data-bs-target="#audioLevelHelpModal"
|
|
||||||
class="btn m-0 p-0 border-0"
|
|
||||||
>
|
|
||||||
<i class="bi bi-question-circle" style="font-size: 1rem"></i>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="card-body p-2">
|
|
||||||
<div class="container">
|
|
||||||
<div class="row">
|
|
||||||
<div class="col-sm">
|
|
||||||
<div
|
|
||||||
class="progress mb-0 rounded-0 rounded-top"
|
|
||||||
style="height: 22px"
|
|
||||||
>
|
|
||||||
<div
|
|
||||||
class="progress-bar progress-bar-striped bg-primary force-gpu"
|
|
||||||
id="noise_level"
|
|
||||||
role="progressbar"
|
|
||||||
:style="{ width: state.s_meter_strength_percent + '%' }"
|
|
||||||
aria-valuenow="{{state.s_meter_strength_percent}}"
|
|
||||||
aria-valuemin="0"
|
|
||||||
aria-valuemax="100"
|
|
||||||
></div>
|
|
||||||
<p
|
|
||||||
class="justify-content-center d-flex position-absolute w-100"
|
|
||||||
id="noise_level_value"
|
|
||||||
>
|
|
||||||
S-Meter(dB): {{ state.s_meter_strength_raw }}
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
class="progress mb-0 rounded-0 rounded-bottom"
|
|
||||||
style="height: 8px"
|
|
||||||
>
|
|
||||||
<div
|
|
||||||
class="progress-bar progress-bar-striped bg-warning"
|
|
||||||
role="progressbar"
|
|
||||||
style="width: 1%"
|
|
||||||
aria-valuenow="1"
|
|
||||||
aria-valuemin="0"
|
|
||||||
aria-valuemax="100"
|
|
||||||
></div>
|
|
||||||
<div
|
|
||||||
class="progress-bar bg-success"
|
|
||||||
role="progressbar"
|
|
||||||
style="width: 89%"
|
|
||||||
aria-valuenow="50"
|
|
||||||
aria-valuemin="0"
|
|
||||||
aria-valuemax="100"
|
|
||||||
></div>
|
|
||||||
<div
|
|
||||||
class="progress-bar progress-bar-striped bg-warning"
|
|
||||||
role="progressbar"
|
|
||||||
style="width: 20%"
|
|
||||||
aria-valuenow="20"
|
|
||||||
aria-valuemin="0"
|
|
||||||
aria-valuemax="100"
|
|
||||||
></div>
|
|
||||||
<div
|
|
||||||
class="progress-bar progress-bar-striped bg-danger"
|
|
||||||
role="progressbar"
|
|
||||||
style="width: 29%"
|
|
||||||
aria-valuenow="29"
|
|
||||||
aria-valuemin="0"
|
|
||||||
aria-valuemax="100"
|
|
||||||
></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="col-sm">
|
|
||||||
<div
|
|
||||||
class="progress mb-0 rounded-0 rounded-top"
|
|
||||||
style="height: 22px"
|
|
||||||
>
|
|
||||||
<div
|
|
||||||
class="progress-bar progress-bar-striped bg-primary force-gpu"
|
|
||||||
id="dbfs_level"
|
|
||||||
role="progressbar"
|
|
||||||
:style="{ width: state.dbfs_level_percent + '%' }"
|
|
||||||
aria-valuenow="0"
|
|
||||||
aria-valuemin="0"
|
|
||||||
aria-valuemax="100"
|
|
||||||
></div>
|
|
||||||
<p
|
|
||||||
class="justify-content-center d-flex position-absolute w-100"
|
|
||||||
id="dbfs_level_value"
|
|
||||||
>
|
|
||||||
{{ state.dbfs_level }} dBFS
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
class="progress mb-0 rounded-0 rounded-bottom"
|
|
||||||
style="height: 8px"
|
|
||||||
>
|
|
||||||
<div
|
|
||||||
class="progress-bar progress-bar-striped bg-warning"
|
|
||||||
role="progressbar"
|
|
||||||
style="width: 1%"
|
|
||||||
aria-valuenow="1"
|
|
||||||
aria-valuemin="0"
|
|
||||||
aria-valuemax="100"
|
|
||||||
></div>
|
|
||||||
<div
|
|
||||||
class="progress-bar bg-success"
|
|
||||||
role="progressbar"
|
|
||||||
style="width: 89%"
|
|
||||||
aria-valuenow="50"
|
|
||||||
aria-valuemin="0"
|
|
||||||
aria-valuemax="100"
|
|
||||||
></div>
|
|
||||||
<div
|
|
||||||
class="progress-bar progress-bar-striped bg-warning"
|
|
||||||
role="progressbar"
|
|
||||||
style="width: 20%"
|
|
||||||
aria-valuenow="20"
|
|
||||||
aria-valuemin="0"
|
|
||||||
aria-valuemax="100"
|
|
||||||
></div>
|
|
||||||
<div
|
|
||||||
class="progress-bar progress-bar-striped bg-danger"
|
|
||||||
role="progressbar"
|
|
||||||
style="width: 29%"
|
|
||||||
aria-valuenow="29"
|
|
||||||
aria-valuemin="0"
|
|
||||||
aria-valuemax="100"
|
|
||||||
></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
|
@ -1,110 +0,0 @@
|
||||||
<script setup lang="ts">
|
|
||||||
|
|
||||||
import { setActivePinia } from "pinia";
|
|
||||||
import pinia from "../store/index";
|
|
||||||
setActivePinia(pinia);
|
|
||||||
|
|
||||||
import { settingsStore as settings} from "../store/settingsStore.js";
|
|
||||||
|
|
||||||
import { useStateStore } from "../store/stateStore.js";
|
|
||||||
const state = useStateStore(pinia);
|
|
||||||
|
|
||||||
import { sendModemCQ, sendModemPing, setModemBeacon } from "../js/api.js";
|
|
||||||
|
|
||||||
function transmitPing() {
|
|
||||||
sendModemPing((<HTMLInputElement>document.getElementById("dxCall")).value);
|
|
||||||
}
|
|
||||||
|
|
||||||
function startStopBeacon() {
|
|
||||||
if (state.beacon_state === true) {
|
|
||||||
setModemBeacon(false);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
setModemBeacon(true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
<template>
|
|
||||||
<div class="card mb-1">
|
|
||||||
<div class="card-header p-1">
|
|
||||||
<div class="container">
|
|
||||||
<div class="row">
|
|
||||||
<div class="col-1">
|
|
||||||
<i class="bi bi-broadcast" style="font-size: 1.2rem"></i>
|
|
||||||
</div>
|
|
||||||
<div class="col-10">
|
|
||||||
<strong class="fs-5">Broadcasts</strong>
|
|
||||||
</div>
|
|
||||||
<div class="col-1 text-end">
|
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
id="openHelpModalBroadcasts"
|
|
||||||
data-bs-toggle="modal"
|
|
||||||
data-bs-target="#broadcastsHelpModal"
|
|
||||||
class="btn m-0 p-0 border-0"
|
|
||||||
>
|
|
||||||
<i class="bi bi-question-circle" style="font-size: 1rem"></i>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="card-body p-2">
|
|
||||||
<div class="row">
|
|
||||||
<div class="col-md-auto">
|
|
||||||
<div class="input-group input-group-sm mb-0">
|
|
||||||
<input
|
|
||||||
type="text"
|
|
||||||
class="form-control"
|
|
||||||
style="max-width: 6rem; text-transform: uppercase"
|
|
||||||
placeholder="DXcall"
|
|
||||||
pattern="[A-Z]*"
|
|
||||||
id="dxCall"
|
|
||||||
maxlength="11"
|
|
||||||
aria-label="Input group"
|
|
||||||
aria-describedby="btnGroupAddon"
|
|
||||||
/>
|
|
||||||
<button
|
|
||||||
class="btn btn-sm btn-outline-secondary ms-1"
|
|
||||||
id="sendPing"
|
|
||||||
type="button"
|
|
||||||
data-bs-placement="bottom"
|
|
||||||
data-bs-toggle="tooltip"
|
|
||||||
data-bs-trigger="hover"
|
|
||||||
data-bs-html="false"
|
|
||||||
title="Send a ping request to a remote station"
|
|
||||||
@click="transmitPing()"
|
|
||||||
>
|
|
||||||
Ping
|
|
||||||
</button>
|
|
||||||
|
|
||||||
<button
|
|
||||||
class="btn btn-sm btn-outline-secondary ms-1"
|
|
||||||
id="sendCQ"
|
|
||||||
type="button"
|
|
||||||
title="Send a CQ to the world"
|
|
||||||
@click="sendModemCQ()"
|
|
||||||
>
|
|
||||||
Call CQ
|
|
||||||
</button>
|
|
||||||
|
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
id="startBeacon"
|
|
||||||
class="btn btn-sm ms-1"
|
|
||||||
@click="startStopBeacon()"
|
|
||||||
v-bind:class="{
|
|
||||||
'btn-success': state.beacon_state === true,
|
|
||||||
'btn-outline-secondary': state.beacon_state === false,
|
|
||||||
}"
|
|
||||||
title="Toggle beacon mode. The interval can be set in settings. While sending a beacon, you can receive ping requests and open a datachannel. If a datachannel is opened, the beacon pauses."
|
|
||||||
>
|
|
||||||
<i class="bi bi-soundwave"></i> Toggle beacon
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<!-- end of row-->
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
|
@ -1,122 +0,0 @@
|
||||||
<script setup lang="ts">
|
|
||||||
// @ts-nocheck
|
|
||||||
const { distance } = require("qth-locator");
|
|
||||||
|
|
||||||
import { setActivePinia } from "pinia";
|
|
||||||
import pinia from "../store/index";
|
|
||||||
setActivePinia(pinia);
|
|
||||||
|
|
||||||
import { settingsStore as settings } from "../store/settingsStore.js";
|
|
||||||
|
|
||||||
import { useStateStore } from "../store/stateStore.js";
|
|
||||||
const state = useStateStore(pinia);
|
|
||||||
|
|
||||||
function getDateTime(timestampRaw) {
|
|
||||||
var datetime = new Date(timestampRaw * 1000).toLocaleString(
|
|
||||||
navigator.language,
|
|
||||||
{
|
|
||||||
hourCycle: "h23",
|
|
||||||
year: "numeric",
|
|
||||||
month: "2-digit",
|
|
||||||
day: "2-digit",
|
|
||||||
hour: "2-digit",
|
|
||||||
minute: "2-digit",
|
|
||||||
second: "2-digit",
|
|
||||||
},
|
|
||||||
);
|
|
||||||
return datetime;
|
|
||||||
}
|
|
||||||
|
|
||||||
function getMaidenheadDistance(dxGrid) {
|
|
||||||
if (typeof dxGrid != "undefined") {
|
|
||||||
try {
|
|
||||||
return parseInt(distance(settings.remote.STATION.mygrid, dxGrid));
|
|
||||||
} catch (e) {
|
|
||||||
console.warn(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
<template>
|
|
||||||
<div class="card mb-1 h-100">
|
|
||||||
<!--325px-->
|
|
||||||
<div class="card-header p-1">
|
|
||||||
<div class="container">
|
|
||||||
<div class="row">
|
|
||||||
<div class="col-auto">
|
|
||||||
<i class="bi bi-list-columns-reverse" style="font-size: 1.2rem"></i>
|
|
||||||
</div>
|
|
||||||
<div class="col-10">
|
|
||||||
<strong class="fs-5">Heard stations</strong>
|
|
||||||
</div>
|
|
||||||
<div class="col-1 text-end">
|
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
id="openHelpModalHeardStations"
|
|
||||||
data-bs-toggle="modal"
|
|
||||||
data-bs-target="#heardStationsHelpModal"
|
|
||||||
class="btn m-0 p-0 border-0"
|
|
||||||
>
|
|
||||||
<i class="bi bi-question-circle" style="font-size: 1rem"></i>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="card-body p-0" style="overflow-y: overlay">
|
|
||||||
<div class="table-responsive">
|
|
||||||
<!-- START OF TABLE FOR HEARD STATIONS -->
|
|
||||||
<table class="table table-sm" id="tblHeardStationList">
|
|
||||||
<thead>
|
|
||||||
<tr>
|
|
||||||
<th scope="col" id="thTime">
|
|
||||||
<i id="hslSort" class="bi bi-sort-up"></i>Time
|
|
||||||
</th>
|
|
||||||
<th scope="col" id="thFreq">Freq</th>
|
|
||||||
<th scope="col" id="thDxcall">DXCall</th>
|
|
||||||
<th scope="col" id="thDxgrid">Grid</th>
|
|
||||||
<th scope="col" id="thDist">Dist</th>
|
|
||||||
<th scope="col" id="thType">Type</th>
|
|
||||||
<th scope="col" id="thSnr">SNR</th>
|
|
||||||
<!--<th scope="col">Off</th>-->
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody id="heardstations">
|
|
||||||
<!--https://vuejs.org/guide/essentials/list.html-->
|
|
||||||
<tr v-for="item in state.heard_stations" :key="item.origin">
|
|
||||||
<td>
|
|
||||||
<span class="badge bg-secondary">{{
|
|
||||||
getDateTime(item.timestamp)
|
|
||||||
}}</span>
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
<span class="badge bg-secondary"
|
|
||||||
>{{ item.frequency / 1000 }} kHz</span
|
|
||||||
>
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
<span class="badge bg-secondary">{{ item.origin }}</span>
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
<span class="badge bg-secondary">{{ item.gridsquare }}</span>
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
<span class="badge bg-secondary"
|
|
||||||
>{{ getMaidenheadDistance(item.gridsquare) }} km</span
|
|
||||||
>
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
<span class="badge bg-secondary">{{ item.activity_type }}</span>
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
<span class="badge bg-secondary">{{ item.snr }}</span>
|
|
||||||
</td>
|
|
||||||
<!--<td>{{ item.offset }}</td>-->
|
|
||||||
</tr>
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
<!-- END OF HEARD STATIONS TABLE -->
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
|
@ -1,261 +0,0 @@
|
||||||
<script setup lang="ts">
|
|
||||||
import { setActivePinia } from "pinia";
|
|
||||||
import pinia from "../store/index";
|
|
||||||
setActivePinia(pinia);
|
|
||||||
|
|
||||||
import { useStateStore } from "../store/stateStore.js";
|
|
||||||
const state = useStateStore(pinia);
|
|
||||||
|
|
||||||
import { setRadioParametersFrequency, setRadioParametersMode, setRadioParametersRFLevel } from "../js/api";
|
|
||||||
|
|
||||||
function updateFrequencyAndApply(frequency) {
|
|
||||||
state.new_frequency = frequency;
|
|
||||||
set_radio_parameter_frequency();
|
|
||||||
}
|
|
||||||
|
|
||||||
function set_radio_parameter_frequency(){
|
|
||||||
setRadioParametersFrequency(state.new_frequency)
|
|
||||||
}
|
|
||||||
|
|
||||||
function set_radio_parameter_mode(){
|
|
||||||
setRadioParametersMode(state.mode)
|
|
||||||
}
|
|
||||||
|
|
||||||
function set_radio_parameter_rflevel(){
|
|
||||||
setRadioParametersRFLevel(state.rf_level)
|
|
||||||
}
|
|
||||||
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<template>
|
|
||||||
<div class="mb-3">
|
|
||||||
<div class="card mb-1">
|
|
||||||
<div class="card-header p-1">
|
|
||||||
<div class="container">
|
|
||||||
<div class="row">
|
|
||||||
<div class="col-1">
|
|
||||||
<i class="bi bi-house-door" style="font-size: 1.2rem"></i>
|
|
||||||
</div>
|
|
||||||
<div class="col-10">
|
|
||||||
<strong class="fs-5 me-2">Radio control</strong>
|
|
||||||
<span
|
|
||||||
class="badge"
|
|
||||||
v-bind:class="{
|
|
||||||
'text-bg-success': state.hamlib_status === 'connected',
|
|
||||||
'text-bg-danger disabled':
|
|
||||||
state.hamlib_status === 'disconnected',
|
|
||||||
}"
|
|
||||||
>{{ state.hamlib_status }}</span
|
|
||||||
>
|
|
||||||
</div>
|
|
||||||
<div class="col-1 text-end">
|
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
id="openHelpModalStation"
|
|
||||||
data-bs-toggle="modal"
|
|
||||||
data-bs-target="#stationHelpModal"
|
|
||||||
class="btn m-0 p-0 border-0"
|
|
||||||
disabled
|
|
||||||
>
|
|
||||||
<i class="bi bi-question-circle" style="font-size: 1rem"></i>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="card-body p-2">
|
|
||||||
<div class="input-group input-group-sm bottom-0 m-0">
|
|
||||||
<div class="me-2">
|
|
||||||
<div class="input-group input-group-sm">
|
|
||||||
<span class="input-group-text">QRG</span>
|
|
||||||
<span class="input-group-text">{{ state.frequency }} Hz</span>
|
|
||||||
|
|
||||||
<!-- Dropdown Button -->
|
|
||||||
<button
|
|
||||||
v-bind:class="{
|
|
||||||
disabled: state.hamlib_status === 'disconnected',
|
|
||||||
}"
|
|
||||||
class="btn btn-secondary dropdown-toggle"
|
|
||||||
type="button"
|
|
||||||
id="dropdownMenuButton"
|
|
||||||
data-bs-toggle="dropdown"
|
|
||||||
aria-expanded="false"
|
|
||||||
></button>
|
|
||||||
|
|
||||||
<!-- Dropdown Menu -->
|
|
||||||
<ul class="dropdown-menu" aria-labelledby="dropdownMenuButton">
|
|
||||||
<li>
|
|
||||||
<div class="input-group p-1">
|
|
||||||
<span class="input-group-text">frequency</span>
|
|
||||||
|
|
||||||
<input
|
|
||||||
v-model="state.new_frequency"
|
|
||||||
style="max-width: 8rem"
|
|
||||||
pattern="[0-9]*"
|
|
||||||
type="text"
|
|
||||||
class="form-control form-control-sm"
|
|
||||||
v-bind:class="{
|
|
||||||
disabled: state.hamlib_status === 'disconnected',
|
|
||||||
}"
|
|
||||||
placeholder="Type frequency..."
|
|
||||||
aria-label="Frequency"
|
|
||||||
/>
|
|
||||||
<button
|
|
||||||
class="btn btn-sm btn-outline-success"
|
|
||||||
type="button"
|
|
||||||
@click="updateFrequencyAndApply(state.new_frequency)"
|
|
||||||
v-bind:class="{
|
|
||||||
disabled: state.hamlib_status === 'disconnected',
|
|
||||||
}"
|
|
||||||
>
|
|
||||||
<i class="bi bi-check-square"></i>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</li>
|
|
||||||
|
|
||||||
<!-- Dropdown Divider -->
|
|
||||||
<li><hr class="dropdown-divider" /></li>
|
|
||||||
<!-- Dropdown Items -->
|
|
||||||
<li>
|
|
||||||
<a
|
|
||||||
class="dropdown-item"
|
|
||||||
href="#"
|
|
||||||
@click="updateFrequencyAndApply(50616000)"
|
|
||||||
><strong>50616 kHz</strong>
|
|
||||||
<span class="badge bg-secondary">6m | USB</span>
|
|
||||||
<span class="badge bg-info">EU | US</span>
|
|
||||||
</a>
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<a
|
|
||||||
class="dropdown-item"
|
|
||||||
href="#"
|
|
||||||
@click="updateFrequencyAndApply(50308000)"
|
|
||||||
><strong>50308 kHz</strong>
|
|
||||||
<span class="badge bg-secondary">6m | USB</span>
|
|
||||||
<span class="badge bg-info">US</span></a
|
|
||||||
>
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<a
|
|
||||||
class="dropdown-item"
|
|
||||||
href="#"
|
|
||||||
@click="updateFrequencyAndApply(28093000)"
|
|
||||||
><strong>28093 kHz</strong>
|
|
||||||
<span class="badge bg-secondary">10m | USB</span>
|
|
||||||
<span class="badge bg-info">EU | US</span>
|
|
||||||
</a>
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<a
|
|
||||||
class="dropdown-item"
|
|
||||||
href="#"
|
|
||||||
@click="updateFrequencyAndApply(27265000)"
|
|
||||||
><strong>27265 kHz</strong>
|
|
||||||
<span class="badge bg-secondary">11m | USB</span>
|
|
||||||
<span class="badge bg-dark">Ch 26</span></a
|
|
||||||
>
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<a
|
|
||||||
class="dropdown-item"
|
|
||||||
href="#"
|
|
||||||
@click="updateFrequencyAndApply(27245000)"
|
|
||||||
><strong>27245 kHz</strong>
|
|
||||||
<span class="badge bg-secondary">11m | USB</span>
|
|
||||||
<span class="badge bg-dark">Ch 25</span></a
|
|
||||||
>
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<a
|
|
||||||
class="dropdown-item"
|
|
||||||
href="#"
|
|
||||||
@click="updateFrequencyAndApply(24908000)"
|
|
||||||
><strong>24908 kHz</strong>
|
|
||||||
<span class="badge bg-secondary">12m | USB</span>
|
|
||||||
<span class="badge bg-info">EU | US</span>
|
|
||||||
</a>
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<a
|
|
||||||
class="dropdown-item"
|
|
||||||
href="#"
|
|
||||||
@click="updateFrequencyAndApply(21093000)"
|
|
||||||
><strong>21093 kHz</strong>
|
|
||||||
<span class="badge bg-secondary">15m | USB</span>
|
|
||||||
<span class="badge bg-info">EU | US</span>
|
|
||||||
</a>
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<a
|
|
||||||
class="dropdown-item"
|
|
||||||
href="#"
|
|
||||||
@click="updateFrequencyAndApply(14093000)"
|
|
||||||
><strong>14093 kHz</strong>
|
|
||||||
<span class="badge bg-secondary">20m | USB</span>
|
|
||||||
<span class="badge bg-info">EU | US</span>
|
|
||||||
</a>
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<a
|
|
||||||
class="dropdown-item"
|
|
||||||
href="#"
|
|
||||||
@click="updateFrequencyAndApply(7053000)"
|
|
||||||
><strong>7053 kHz</strong>
|
|
||||||
<span class="badge bg-secondary">40m | USB</span>
|
|
||||||
<span class="badge bg-info">EU | US</span>
|
|
||||||
</a>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="me-2">
|
|
||||||
<div class="input-group input-group-sm">
|
|
||||||
<span class="input-group-text">Mode</span>
|
|
||||||
<select
|
|
||||||
class="form-control"
|
|
||||||
v-model="state.mode"
|
|
||||||
@click="set_radio_parameter_mode()"
|
|
||||||
v-bind:class="{
|
|
||||||
disabled: state.hamlib_status === 'disconnected',
|
|
||||||
}"
|
|
||||||
>
|
|
||||||
<option value="USB">USB</option>
|
|
||||||
<option value="USB-D">USB-D</option>
|
|
||||||
<option value="PKTUSB">PKT-U</option>
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="me-2">
|
|
||||||
<div class="input-group input-group-sm">
|
|
||||||
<span class="input-group-text">Power</span>
|
|
||||||
<select
|
|
||||||
class="form-control"
|
|
||||||
v-model="state.rf_level"
|
|
||||||
@click="set_radio_parameter_rflevel()"
|
|
||||||
v-bind:class="{
|
|
||||||
disabled: state.hamlib_status === 'disconnected',
|
|
||||||
}"
|
|
||||||
>
|
|
||||||
<option value="0">-</option>
|
|
||||||
<option value="10">10</option>
|
|
||||||
<option value="20">20</option>
|
|
||||||
<option value="30">30</option>
|
|
||||||
<option value="40">40</option>
|
|
||||||
<option value="50">50</option>
|
|
||||||
<option value="60">60</option>
|
|
||||||
<option value="70">70</option>
|
|
||||||
<option value="80">80</option>
|
|
||||||
<option value="90">90</option>
|
|
||||||
<option value="100">100</option>
|
|
||||||
</select>
|
|
||||||
<span class="input-group-text">%</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
|
@ -1,402 +0,0 @@
|
||||||
<script setup lang="ts">
|
|
||||||
// @ts-nocheck
|
|
||||||
// reason for no check is, that we have some mixing of typescript and chart js which seems to be not to be fixed that easy
|
|
||||||
|
|
||||||
import { setActivePinia } from "pinia";
|
|
||||||
import pinia from "../store/index";
|
|
||||||
setActivePinia(pinia);
|
|
||||||
|
|
||||||
import { settingsStore as settings } from "../store/settingsStore.js";
|
|
||||||
|
|
||||||
import { useStateStore } from "../store/stateStore.js";
|
|
||||||
const state = useStateStore(pinia);
|
|
||||||
|
|
||||||
import {
|
|
||||||
Chart as ChartJS,
|
|
||||||
CategoryScale,
|
|
||||||
LinearScale,
|
|
||||||
PointElement,
|
|
||||||
LineElement,
|
|
||||||
Title,
|
|
||||||
Tooltip,
|
|
||||||
Legend,
|
|
||||||
} from "chart.js";
|
|
||||||
import { Line, Scatter } from "vue-chartjs";
|
|
||||||
import { computed } from "vue";
|
|
||||||
|
|
||||||
function selectStatsControl(obj) {
|
|
||||||
switch (obj.delegateTarget.id) {
|
|
||||||
case "list-waterfall-list":
|
|
||||||
settings.local.spectrum = "waterfall";
|
|
||||||
break;
|
|
||||||
case "list-scatter-list":
|
|
||||||
settings.local.spectrum = "scatter";
|
|
||||||
break;
|
|
||||||
case "list-chart-list":
|
|
||||||
settings.local.spectrum = "chart";
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
settings.local.spectrum = "waterfall";
|
|
||||||
}
|
|
||||||
//saveSettingsToFile();
|
|
||||||
}
|
|
||||||
|
|
||||||
ChartJS.register(
|
|
||||||
CategoryScale,
|
|
||||||
LinearScale,
|
|
||||||
PointElement,
|
|
||||||
LineElement,
|
|
||||||
Title,
|
|
||||||
Tooltip,
|
|
||||||
Legend,
|
|
||||||
);
|
|
||||||
|
|
||||||
// https://www.chartjs.org/docs/latest/samples/line/segments.html
|
|
||||||
const skipped = (speedCtx, value) =>
|
|
||||||
speedCtx.p0.skip || speedCtx.p1.skip ? value : undefined;
|
|
||||||
const down = (speedCtx, value) =>
|
|
||||||
speedCtx.p0.parsed.y > speedCtx.p1.parsed.y ? value : undefined;
|
|
||||||
|
|
||||||
var transmissionSpeedChartOptions = {
|
|
||||||
//type: "line",
|
|
||||||
responsive: true,
|
|
||||||
animations: true,
|
|
||||||
maintainAspectRatio: false,
|
|
||||||
cubicInterpolationMode: "monotone",
|
|
||||||
tension: 0.4,
|
|
||||||
scales: {
|
|
||||||
SNR: {
|
|
||||||
type: "linear",
|
|
||||||
ticks: { beginAtZero: false, color: "rgb(255, 99, 132)" },
|
|
||||||
position: "right",
|
|
||||||
},
|
|
||||||
SPEED: {
|
|
||||||
type: "linear",
|
|
||||||
ticks: { beginAtZero: false, color: "rgb(120, 100, 120)" },
|
|
||||||
position: "left",
|
|
||||||
grid: {
|
|
||||||
drawOnChartArea: false, // only want the grid lines for one axis to show up
|
|
||||||
},
|
|
||||||
},
|
|
||||||
x: { ticks: { beginAtZero: true } },
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
const transmissionSpeedChartData = computed(() => ({
|
|
||||||
labels: state.arq_speed_list_timestamp,
|
|
||||||
datasets: [
|
|
||||||
{
|
|
||||||
type: "line",
|
|
||||||
label: "SNR[dB]",
|
|
||||||
data: state.arq_speed_list_snr,
|
|
||||||
borderColor: "rgb(75, 192, 192, 1.0)",
|
|
||||||
pointRadius: 1,
|
|
||||||
segment: {
|
|
||||||
borderColor: (speedCtx) =>
|
|
||||||
skipped(speedCtx, "rgb(0,0,0,0.4)") ||
|
|
||||||
down(speedCtx, "rgb(192,75,75)"),
|
|
||||||
borderDash: (speedCtx) => skipped(speedCtx, [3, 3]),
|
|
||||||
},
|
|
||||||
spanGaps: true,
|
|
||||||
backgroundColor: "rgba(75, 192, 192, 0.2)",
|
|
||||||
order: 1,
|
|
||||||
yAxisID: "SNR",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
type: "bar",
|
|
||||||
label: "Speed[bpm]",
|
|
||||||
data: state.arq_speed_list_bpm,
|
|
||||||
borderColor: "rgb(120, 100, 120, 1.0)",
|
|
||||||
backgroundColor: "rgba(120, 100, 120, 0.2)",
|
|
||||||
order: 0,
|
|
||||||
yAxisID: "SPEED",
|
|
||||||
},
|
|
||||||
],
|
|
||||||
}));
|
|
||||||
|
|
||||||
const scatterChartOptions = {
|
|
||||||
responsive: true,
|
|
||||||
maintainAspectRatio: false,
|
|
||||||
scales: {
|
|
||||||
x: {
|
|
||||||
type: "linear",
|
|
||||||
position: "bottom",
|
|
||||||
grid: {
|
|
||||||
display: true,
|
|
||||||
lineWidth: 1, // Set the line width for x-axis grid lines
|
|
||||||
},
|
|
||||||
ticks: {
|
|
||||||
display: false,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
y: {
|
|
||||||
type: "linear",
|
|
||||||
position: "left",
|
|
||||||
grid: {
|
|
||||||
display: true,
|
|
||||||
lineWidth: 1, // Set the line width for y-axis grid lines
|
|
||||||
},
|
|
||||||
ticks: {
|
|
||||||
display: false,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
plugins: {
|
|
||||||
legend: {
|
|
||||||
display: false,
|
|
||||||
},
|
|
||||||
tooltip: {
|
|
||||||
enabled: false,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
// dummy data
|
|
||||||
//state.scatter = [{"x":"166","y":"46"},{"x":"-193","y":"-139"},{"x":"-165","y":"-291"},{"x":"311","y":"-367"},{"x":"389","y":"199"},{"x":"78","y":"372"},{"x":"242","y":"-431"},{"x":"-271","y":"-248"},{"x":"28","y":"-130"},{"x":"-20","y":"187"},{"x":"74","y":"362"},{"x":"-316","y":"-229"},{"x":"-180","y":"261"},{"x":"321","y":"360"},{"x":"438","y":"-288"},{"x":"378","y":"-94"},{"x":"462","y":"-163"},{"x":"-265","y":"248"},{"x":"210","y":"314"},{"x":"230","y":"-320"},{"x":"261","y":"-244"},{"x":"-283","y":"-373"}]
|
|
||||||
|
|
||||||
const scatterChartData = computed(() => ({
|
|
||||||
datasets: [
|
|
||||||
{
|
|
||||||
type: "scatter",
|
|
||||||
fill: true,
|
|
||||||
data: state.scatter,
|
|
||||||
label: "Scatter",
|
|
||||||
tension: 0.1,
|
|
||||||
borderColor: "rgb(0, 255, 0)",
|
|
||||||
},
|
|
||||||
],
|
|
||||||
}));
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<script lang="ts">
|
|
||||||
import { initWaterfall, setColormap } from "../js/waterfallHandler.js";
|
|
||||||
var localSpectrum;
|
|
||||||
export default {
|
|
||||||
mounted() {
|
|
||||||
// This code will be executed after the component is mounted to the DOM
|
|
||||||
// You can access DOM elements or perform other initialization here
|
|
||||||
//const myElement = this.$refs.waterfall; // Access the DOM element with ref
|
|
||||||
|
|
||||||
// init waterfall
|
|
||||||
localSpectrum = initWaterfall("waterfall-main");
|
|
||||||
},
|
|
||||||
};
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<template>
|
|
||||||
<div class="card mb-1" style="height: calc(var(--variable-height) - 20px)">
|
|
||||||
<div class="card-header p-1">
|
|
||||||
<div class="container">
|
|
||||||
<div class="row">
|
|
||||||
<div class="col-11 p-0">
|
|
||||||
<div class="btn-group h-100" role="group">
|
|
||||||
<div
|
|
||||||
class="list-group bg-body-tertiary list-group-horizontal"
|
|
||||||
id="list-tab"
|
|
||||||
role="tablist"
|
|
||||||
>
|
|
||||||
<a
|
|
||||||
class="py-0 list-group-item list-group-item-dark list-group-item-action"
|
|
||||||
id="list-waterfall-list"
|
|
||||||
data-bs-toggle="list"
|
|
||||||
href="#list-waterfall"
|
|
||||||
role="tab"
|
|
||||||
aria-controls="list-waterfall"
|
|
||||||
v-bind:class="{
|
|
||||||
active: settings.local.spectrum === 'waterfall',
|
|
||||||
}"
|
|
||||||
@click="selectStatsControl($event)"
|
|
||||||
><strong><i class="bi bi-water"></i></strong
|
|
||||||
></a>
|
|
||||||
<a
|
|
||||||
class="py-0 list-group-item list-group-item-dark list-group-item-action"
|
|
||||||
id="list-scatter-list"
|
|
||||||
data-bs-toggle="list"
|
|
||||||
href="#list-scatter"
|
|
||||||
role="tab"
|
|
||||||
aria-controls="list-scatter"
|
|
||||||
v-bind:class="{
|
|
||||||
active: settings.local.spectrum === 'scatter',
|
|
||||||
}"
|
|
||||||
@click="selectStatsControl($event)"
|
|
||||||
><strong><i class="bi bi-border-outer"></i></strong
|
|
||||||
></a>
|
|
||||||
<a
|
|
||||||
class="py-0 list-group-item list-group-item-dark list-group-item-action"
|
|
||||||
id="list-chart-list"
|
|
||||||
data-bs-toggle="list"
|
|
||||||
href="#list-chart"
|
|
||||||
role="tab"
|
|
||||||
aria-controls="list-chart"
|
|
||||||
v-bind:class="{ active: settings.local.spectrum === 'chart' }"
|
|
||||||
@click="selectStatsControl($event)"
|
|
||||||
><strong><i class="bi bi-graph-up-arrow"></i></strong
|
|
||||||
></a>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="btn-group" role="group" aria-label="Busy indicators">
|
|
||||||
<button
|
|
||||||
class="btn btn-sm ms-1 p-1 disabled"
|
|
||||||
type="button"
|
|
||||||
data-bs-placement="top"
|
|
||||||
data-bs-toggle="tooltip"
|
|
||||||
data-bs-trigger="hover"
|
|
||||||
data-bs-html="true"
|
|
||||||
v-bind:class="{
|
|
||||||
'btn-warning': state.channel_busy_slot[0] === true,
|
|
||||||
'btn-outline-secondary': state.channel_busy_slot[0] === false,
|
|
||||||
}"
|
|
||||||
title="Channel busy state: <strong class='text-success'>not busy</strong> / <strong class='text-danger'>busy </strong>"
|
|
||||||
>
|
|
||||||
S1
|
|
||||||
</button>
|
|
||||||
|
|
||||||
<button
|
|
||||||
class="btn btn-sm p-1 disabled"
|
|
||||||
type="button"
|
|
||||||
data-bs-placement="top"
|
|
||||||
data-bs-toggle="tooltip"
|
|
||||||
data-bs-trigger="hover"
|
|
||||||
data-bs-html="true"
|
|
||||||
v-bind:class="{
|
|
||||||
'btn-warning': state.channel_busy_slot[1] === true,
|
|
||||||
'btn-outline-secondary': state.channel_busy_slot[1] === false,
|
|
||||||
}"
|
|
||||||
title="Channel busy state: <strong class='text-success'>not busy</strong> / <strong class='text-danger'>busy </strong>"
|
|
||||||
>
|
|
||||||
S2
|
|
||||||
</button>
|
|
||||||
|
|
||||||
<button
|
|
||||||
class="btn btn-sm p-1 disabled"
|
|
||||||
type="button"
|
|
||||||
data-bs-placement="top"
|
|
||||||
data-bs-toggle="tooltip"
|
|
||||||
data-bs-trigger="hover"
|
|
||||||
data-bs-html="true"
|
|
||||||
v-bind:class="{
|
|
||||||
'btn-warning': state.channel_busy_slot[2] === true,
|
|
||||||
'btn-outline-secondary': state.channel_busy_slot[2] === false,
|
|
||||||
}"
|
|
||||||
title="Channel busy state: <strong class='text-success'>not busy</strong> / <strong class='text-danger'>busy </strong>"
|
|
||||||
>
|
|
||||||
S3
|
|
||||||
</button>
|
|
||||||
|
|
||||||
<button
|
|
||||||
class="btn btn-sm p-1 disabled"
|
|
||||||
type="button"
|
|
||||||
data-bs-placement="top"
|
|
||||||
data-bs-toggle="tooltip"
|
|
||||||
data-bs-trigger="hover"
|
|
||||||
data-bs-html="true"
|
|
||||||
v-bind:class="{
|
|
||||||
'btn-warning': state.channel_busy_slot[3] === true,
|
|
||||||
'btn-outline-secondary': state.channel_busy_slot[3] === false,
|
|
||||||
}"
|
|
||||||
title="Channel busy state: <strong class='text-success'>not busy</strong> / <strong class='text-danger'>busy </strong>"
|
|
||||||
>
|
|
||||||
S4
|
|
||||||
</button>
|
|
||||||
|
|
||||||
<button
|
|
||||||
class="btn btn-sm p-1 disabled"
|
|
||||||
type="button"
|
|
||||||
data-bs-placement="top"
|
|
||||||
data-bs-toggle="tooltip"
|
|
||||||
data-bs-trigger="hover"
|
|
||||||
data-bs-html="true"
|
|
||||||
v-bind:class="{
|
|
||||||
'btn-warning': state.channel_busy_slot[4] === true,
|
|
||||||
'btn-outline-secondary': state.channel_busy_slot[4] === false,
|
|
||||||
}"
|
|
||||||
title="Channel busy state: <strong class='text-success'>not busy</strong> / <strong class='text-danger'>busy </strong>"
|
|
||||||
>
|
|
||||||
S5
|
|
||||||
</button>
|
|
||||||
|
|
||||||
<button
|
|
||||||
class="btn btn-sm p-1 disabled"
|
|
||||||
type="button"
|
|
||||||
data-bs-placement="top"
|
|
||||||
data-bs-toggle="tooltip"
|
|
||||||
data-bs-trigger="hover"
|
|
||||||
data-bs-html="true"
|
|
||||||
title="Recieving data: illuminates <strong class='text-success'>green</strong> if receiving codec2 data"
|
|
||||||
v-bind:class="{
|
|
||||||
'btn-success': state.is_codec2_traffic === true,
|
|
||||||
'btn-outline-secondary': state.is_codec2_traffic === false,
|
|
||||||
}"
|
|
||||||
>
|
|
||||||
data
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="col-1 text-end">
|
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
id="openHelpModalWaterfall"
|
|
||||||
data-bs-toggle="modal"
|
|
||||||
data-bs-target="#waterfallHelpModal"
|
|
||||||
class="btn m-0 p-0 border-0"
|
|
||||||
>
|
|
||||||
<i class="bi bi-question-circle" style="font-size: 1rem"></i>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="card-body p-1">
|
|
||||||
<div class="tab-content" id="nav-stats-tabContent">
|
|
||||||
<div
|
|
||||||
class="tab-pane fade"
|
|
||||||
v-bind:class="{
|
|
||||||
'show active': settings.local.spectrum === 'waterfall',
|
|
||||||
}"
|
|
||||||
id="list-waterfall"
|
|
||||||
role="stats_tabpanel"
|
|
||||||
aria-labelledby="list-waterfall-list"
|
|
||||||
>
|
|
||||||
<canvas
|
|
||||||
ref="waterfall-main"
|
|
||||||
id="waterfall-main"
|
|
||||||
style="
|
|
||||||
position: relative;
|
|
||||||
z-index: 2;
|
|
||||||
aspect-ratio: unset;
|
|
||||||
width: 100%;
|
|
||||||
height: 200px;
|
|
||||||
"
|
|
||||||
class="force-gpu'"
|
|
||||||
></canvas>
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
class="tab-pane fade"
|
|
||||||
v-bind:class="{
|
|
||||||
'show active': settings.local.spectrum === 'scatter',
|
|
||||||
}"
|
|
||||||
id="list-scatter"
|
|
||||||
role="tabpanel"
|
|
||||||
aria-labelledby="list-scatter-list"
|
|
||||||
>
|
|
||||||
<Scatter :data="scatterChartData" :options="scatterChartOptions" />
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
class="tab-pane fade"
|
|
||||||
v-bind:class="{ 'show active': settings.local.spectrum === 'chart' }"
|
|
||||||
id="list-chart"
|
|
||||||
role="tabpanel"
|
|
||||||
aria-labelledby="list-chart-list"
|
|
||||||
>
|
|
||||||
<Line
|
|
||||||
:data="transmissionSpeedChartData"
|
|
||||||
:options="transmissionSpeedChartOptions"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!--278px-->
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
|
@ -1,62 +0,0 @@
|
||||||
<script setup>
|
|
||||||
import { setActivePinia } from "pinia";
|
|
||||||
import pinia from "../store/index";
|
|
||||||
setActivePinia(pinia);
|
|
||||||
|
|
||||||
import { useAudioStore } from "../store/audioStore.js";
|
|
||||||
const audio = useAudioStore(pinia);
|
|
||||||
|
|
||||||
import { setConfig } from "../js/api";
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<template>
|
|
||||||
<div class="card mb-0">
|
|
||||||
<div class="card-header p-1">
|
|
||||||
<div class="container">
|
|
||||||
<div class="row">
|
|
||||||
<div class="col-1">
|
|
||||||
<i class="bi bi-volume-up" style="font-size: 1.2rem"></i>
|
|
||||||
</div>
|
|
||||||
<div class="col-10">
|
|
||||||
<strong class="fs-5">Audio devices</strong>
|
|
||||||
</div>
|
|
||||||
<div class="col-1 text-end">
|
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
data-bs-toggle="modal"
|
|
||||||
data-bs-target="#audioHelpModal"
|
|
||||||
class="btn m-0 p-0 border-0"
|
|
||||||
>
|
|
||||||
<i class="bi bi-question-circle" style="font-size: 1rem"></i>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="card-body p-2" style="height: 100px">
|
|
||||||
<div class="input-group input-group-sm mb-1">
|
|
||||||
<span class="input-group-text">
|
|
||||||
<i class="bi bi-mic-fill" style="font-size: 1rem"></i>
|
|
||||||
</span>
|
|
||||||
<select
|
|
||||||
class="form-select form-select-sm"
|
|
||||||
aria-label=".form-select-sm"
|
|
||||||
v-html="audio.getInputDevices()"
|
|
||||||
@change="setConfig"
|
|
||||||
></select>
|
|
||||||
</div>
|
|
||||||
<div class="input-group input-group-sm">
|
|
||||||
<span class="input-group-text">
|
|
||||||
<i class="bi bi-volume-up" style="font-size: 1rem"></i>
|
|
||||||
</span>
|
|
||||||
<select
|
|
||||||
class="form-select form-select-sm"
|
|
||||||
aria-label=".form-select-sm"
|
|
||||||
v-html="audio.getOutputDevices()"
|
|
||||||
@change="setConfig"
|
|
||||||
></select>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
|
@ -1,114 +0,0 @@
|
||||||
<script setup lang="ts">
|
|
||||||
import { setActivePinia } from "pinia";
|
|
||||||
import pinia from "../store/index";
|
|
||||||
setActivePinia(pinia);
|
|
||||||
|
|
||||||
import { settingsStore as settings } from "../store/settingsStore.js";
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<template>
|
|
||||||
<div class="card mb-1">
|
|
||||||
<div class="card-header p-1">
|
|
||||||
<div class="container">
|
|
||||||
<div class="row">
|
|
||||||
<div class="col-1">
|
|
||||||
<i class="bi bi-house-door" style="font-size: 1.2rem"></i>
|
|
||||||
</div>
|
|
||||||
<div class="col-10">
|
|
||||||
<strong class="fs-5">My station</strong>
|
|
||||||
</div>
|
|
||||||
<div class="col-1 text-end">
|
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
id="openHelpModalStation"
|
|
||||||
data-bs-toggle="modal"
|
|
||||||
data-bs-target="#stationHelpModal"
|
|
||||||
class="btn m-0 p-0 border-0"
|
|
||||||
>
|
|
||||||
<i class="bi bi-question-circle" style="font-size: 1rem"></i>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="card-body p-2">
|
|
||||||
<div class="row">
|
|
||||||
<div class="col-md-auto">
|
|
||||||
<div
|
|
||||||
class="input-group input-group-sm mb-0"
|
|
||||||
data-bs-placement="bottom"
|
|
||||||
data-bs-toggle="tooltip"
|
|
||||||
data-bs-trigger="hover"
|
|
||||||
data-bs-html="false"
|
|
||||||
title="Enter your callsign and save it"
|
|
||||||
>
|
|
||||||
<span class="input-group-text">
|
|
||||||
<i class="bi bi-person-bounding-box" style="font-size: 1rem"></i>
|
|
||||||
</span>
|
|
||||||
<input
|
|
||||||
type="text"
|
|
||||||
class="form-control"
|
|
||||||
style="width: 5rem; text-transform: uppercase"
|
|
||||||
placeholder="callsign"
|
|
||||||
pattern="[A-Z]*"
|
|
||||||
id="myCall"
|
|
||||||
maxlength="8"
|
|
||||||
aria-label="Input group"
|
|
||||||
aria-describedby="btnGroupAddon"
|
|
||||||
v-model="settings.remote.STATION.mycall"
|
|
||||||
/>
|
|
||||||
<select
|
|
||||||
class="form-select form-select-sm"
|
|
||||||
aria-label=".form-select-sm"
|
|
||||||
id="myCallSSID"
|
|
||||||
v-model="settings.remote.STATION.myssid"
|
|
||||||
>
|
|
||||||
<option selected value="0">0</option>
|
|
||||||
<option value="1">1</option>
|
|
||||||
<option value="2">2</option>
|
|
||||||
<option value="3">3</option>
|
|
||||||
<option value="4">4</option>
|
|
||||||
<option value="5">5</option>
|
|
||||||
<option value="6">6</option>
|
|
||||||
<option value="7">7</option>
|
|
||||||
<option value="8">8</option>
|
|
||||||
<option value="9">9</option>
|
|
||||||
<option value="10">10</option>
|
|
||||||
<option value="11">11</option>
|
|
||||||
<option value="12">12</option>
|
|
||||||
<option value="13">13</option>
|
|
||||||
<option value="14">14</option>
|
|
||||||
<option value="15">15</option>
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="col-md-auto">
|
|
||||||
<div
|
|
||||||
class="input-group input-group-sm mb-0"
|
|
||||||
data-bs-placement="bottom"
|
|
||||||
data-bs-toggle="tooltip"
|
|
||||||
data-bs-trigger="hover"
|
|
||||||
data-bs-html="false"
|
|
||||||
title="Enter your gridsquare and save it"
|
|
||||||
>
|
|
||||||
<span class="input-group-text">
|
|
||||||
<i class="bi bi-house-fill" style="font-size: 1rem"></i>
|
|
||||||
</span>
|
|
||||||
<input
|
|
||||||
type="text"
|
|
||||||
class="form-control mr-1"
|
|
||||||
style="max-width: 6rem"
|
|
||||||
placeholder="locator"
|
|
||||||
id="myGrid"
|
|
||||||
maxlength="6"
|
|
||||||
aria-label="Input group"
|
|
||||||
aria-describedby="btnGroupAddon"
|
|
||||||
v-model="settings.remote.STATION.mygrid"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<!-- end of row-->
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
|
@ -1,235 +0,0 @@
|
||||||
<script setup lang="ts">
|
|
||||||
import { setActivePinia } from "pinia";
|
|
||||||
import pinia from "../store/index";
|
|
||||||
setActivePinia(pinia);
|
|
||||||
|
|
||||||
import { settingsStore as settings } from "../store/settingsStore.js";
|
|
||||||
|
|
||||||
import { useStateStore } from "../store/stateStore.js";
|
|
||||||
const state = useStateStore(pinia);
|
|
||||||
|
|
||||||
function selectRadioControl() {
|
|
||||||
// @ts-expect-error
|
|
||||||
switch (event.target.id) {
|
|
||||||
case "list-rig-control-none-list":
|
|
||||||
settings.remote.RADIO.control = "disabled";
|
|
||||||
break;
|
|
||||||
case "list-rig-control-rigctld-list":
|
|
||||||
settings.remote.RADIO.control = "rigctld";
|
|
||||||
break;
|
|
||||||
case "list-rig-control-rigctld-list":
|
|
||||||
settings.remote.RADIO.control = "rigctld_bundle";
|
|
||||||
break;
|
|
||||||
|
|
||||||
case "list-rig-control-tci-list":
|
|
||||||
settings.remote.RADIO.control = "tci";
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
console.log("default=!==");
|
|
||||||
settings.remote.RADIO.control = "disabled";
|
|
||||||
}
|
|
||||||
//saveSettingsToFile();
|
|
||||||
}
|
|
||||||
|
|
||||||
function testHamlib() {
|
|
||||||
console.log("not yet implemented");
|
|
||||||
alert("not yet implemented");
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<template>
|
|
||||||
<div class="mb-3">
|
|
||||||
<div class="card mb-1">
|
|
||||||
<div class="card-header p-1">
|
|
||||||
<div class="container">
|
|
||||||
<div class="row">
|
|
||||||
<div class="col-1">
|
|
||||||
<i class="bi bi-projector" style="font-size: 1.2rem"></i>
|
|
||||||
</div>
|
|
||||||
<div class="col-4">
|
|
||||||
<strong class="fs-5">Rig control</strong>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="col-6">
|
|
||||||
<div
|
|
||||||
class="list-group bg-body-tertiary list-group-horizontal w-75"
|
|
||||||
id="rig-control-list-tab"
|
|
||||||
role="rig-control-tablist"
|
|
||||||
>
|
|
||||||
<a
|
|
||||||
class="p-1 list-group-item list-group-item-dark list-group-item-action"
|
|
||||||
id="list-rig-control-none-list"
|
|
||||||
data-bs-toggle="list"
|
|
||||||
href="#list-rig-control-none"
|
|
||||||
role="tab"
|
|
||||||
aria-controls="list-rig-control-none"
|
|
||||||
v-bind:class="{
|
|
||||||
active: settings.remote.RADIO.control === 'disabled',
|
|
||||||
}"
|
|
||||||
@click="selectRadioControl()"
|
|
||||||
>None</a
|
|
||||||
>
|
|
||||||
<a
|
|
||||||
class="p-1 list-group-item list-group-item-dark list-group-item-action"
|
|
||||||
id="list-rig-control-rigctld-list"
|
|
||||||
data-bs-toggle="list"
|
|
||||||
href="#list-rig-control-rigctld"
|
|
||||||
role="tab"
|
|
||||||
aria-controls="list-rig-control-rigctld"
|
|
||||||
v-bind:class="{
|
|
||||||
active: settings.remote.RADIO.control === 'rigctld',
|
|
||||||
}"
|
|
||||||
@click="selectRadioControl()"
|
|
||||||
>Rigctld</a
|
|
||||||
>
|
|
||||||
<a
|
|
||||||
class="p-1 list-group-item list-group-item-dark list-group-item-action"
|
|
||||||
id="list-rig-control-tci-list"
|
|
||||||
data-bs-toggle="list"
|
|
||||||
href="#list-rig-control-tci"
|
|
||||||
role="tab"
|
|
||||||
aria-controls="list-rig-control-tci"
|
|
||||||
v-bind:class="{
|
|
||||||
active: settings.remote.RADIO.control === 'tci',
|
|
||||||
}"
|
|
||||||
@click="selectRadioControl()"
|
|
||||||
>TCI</a
|
|
||||||
>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="col-1 text-end">
|
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
id="openHelpModalRigControl"
|
|
||||||
data-bs-toggle="modal"
|
|
||||||
data-bs-target="#rigcontrolHelpModal"
|
|
||||||
class="btn m-0 p-0 border-0"
|
|
||||||
>
|
|
||||||
<i class="bi bi-question-circle" style="font-size: 1rem"></i>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="card-body p-2" style="height: 100px">
|
|
||||||
<div class="tab-content" id="rig-control-nav-tabContent">
|
|
||||||
<div
|
|
||||||
class="tab-pane fade"
|
|
||||||
v-bind:class="{
|
|
||||||
'show active': settings.remote.RADIO.control === 'disabled',
|
|
||||||
}"
|
|
||||||
id="list-rig-control-none"
|
|
||||||
role="tabpanel"
|
|
||||||
aria-labelledby="list-rig-control-none-list"
|
|
||||||
>
|
|
||||||
<p class="small">
|
|
||||||
Modem will not utilize rig control and features will be limited.
|
|
||||||
While functional; it is recommended to configure hamlib. <br />
|
|
||||||
Use this setting also for <strong> VOX </strong>
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
class="tab-pane fade"
|
|
||||||
id="list-rig-control-rigctld"
|
|
||||||
v-bind:class="{
|
|
||||||
'show active': settings.remote.RADIO.control === 'rigctld',
|
|
||||||
}"
|
|
||||||
role="tabpanel"
|
|
||||||
aria-labelledby="list-rig-control-rigctld-list"
|
|
||||||
>
|
|
||||||
<div class="input-group input-group-sm mb-1">
|
|
||||||
<div class="input-group input-group-sm mb-1">
|
|
||||||
<span class="input-group-text">Rigctld service</span>
|
|
||||||
|
|
||||||
<input
|
|
||||||
type="text"
|
|
||||||
class="form-control"
|
|
||||||
placeholder="Status"
|
|
||||||
id="hamlib_rigctld_status"
|
|
||||||
aria-label="State"
|
|
||||||
aria-describedby="basic-addon1"
|
|
||||||
v-model="state.rigctld_started"
|
|
||||||
/>
|
|
||||||
|
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
id="testHamlib"
|
|
||||||
class="btn btn-sm btn-outline-secondary ms-1"
|
|
||||||
data-bs-placement="bottom"
|
|
||||||
data-bs-toggle="tooltip"
|
|
||||||
data-bs-trigger="hover"
|
|
||||||
data-bs-html="true"
|
|
||||||
@click="testHamlib"
|
|
||||||
title="Test your hamlib settings and toggle PTT once. Button will become <strong class='text-success'>green</strong> on success and <strong class='text-danger'>red</strong> if fails."
|
|
||||||
>
|
|
||||||
PTT Test
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
class="tab-pane fade"
|
|
||||||
id="list-rig-control-tci"
|
|
||||||
v-bind:class="{
|
|
||||||
'show active': settings.remote.RADIO.control === 'tci',
|
|
||||||
}"
|
|
||||||
role="tabpanel"
|
|
||||||
aria-labelledby="list-rig-control-tci-list"
|
|
||||||
>
|
|
||||||
<div class="input-group input-group-sm mb-1">
|
|
||||||
<div class="input-group input-group-sm mb-1">
|
|
||||||
<span class="input-group-text">TCI</span>
|
|
||||||
<span class="input-group-text">Address</span>
|
|
||||||
<input
|
|
||||||
type="text"
|
|
||||||
class="form-control"
|
|
||||||
placeholder="tci IP"
|
|
||||||
id="tci_ip"
|
|
||||||
aria-label="Device IP"
|
|
||||||
v-model="settings.remote.TCI.tci_ip"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="input-group input-group-sm mb-1">
|
|
||||||
<span class="input-group-text">Port</span>
|
|
||||||
<input
|
|
||||||
type="text"
|
|
||||||
class="form-control"
|
|
||||||
placeholder="tci port"
|
|
||||||
id="tci_port"
|
|
||||||
aria-label="Device Port"
|
|
||||||
v-model="settings.remote.TCI.tci_port"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- RADIO CONTROL DISABLED -->
|
|
||||||
<div id="radio-control-disabled"></div>
|
|
||||||
|
|
||||||
<!-- RADIO CONTROL RIGCTLD -->
|
|
||||||
<div id="radio-control-rigctld"></div>
|
|
||||||
<!-- RADIO CONTROL TCI-->
|
|
||||||
<div id="radio-control-tci"></div>
|
|
||||||
<!-- RADIO CONTROL HELP -->
|
|
||||||
<div id="radio-control-help">
|
|
||||||
<!--
|
|
||||||
<strong>VOX:</strong> Use rig control mode 'none'
|
|
||||||
<br />
|
|
||||||
<strong>HAMLIB locally:</strong> configure in settings, then
|
|
||||||
start/stop service.
|
|
||||||
<br />
|
|
||||||
<strong>HAMLIB remotely:</strong> Enter IP/Port, connection
|
|
||||||
happens automatically.
|
|
||||||
-->
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<!--<div class="card-footer text-muted small" id="hamlib_info_field">
|
|
||||||
Define Modem rig control mode (none/hamlib)
|
|
||||||
</div>
|
|
||||||
-->
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
|
@ -2,8 +2,6 @@
|
||||||
import { Modal } from "bootstrap";
|
import { Modal } from "bootstrap";
|
||||||
import { onMounted } from "vue";
|
import { onMounted } from "vue";
|
||||||
|
|
||||||
import settings_updater_core from "./settings_updater_core.vue";
|
|
||||||
|
|
||||||
import { setActivePinia } from "pinia";
|
import { setActivePinia } from "pinia";
|
||||||
import pinia from "../store/index";
|
import pinia from "../store/index";
|
||||||
setActivePinia(pinia);
|
setActivePinia(pinia);
|
||||||
|
@ -14,6 +12,11 @@ import { sendModemCQ } from "../js/api.js";
|
||||||
import { useStateStore } from "../store/stateStore.js";
|
import { useStateStore } from "../store/stateStore.js";
|
||||||
const state = useStateStore(pinia);
|
const state = useStateStore(pinia);
|
||||||
|
|
||||||
|
import { useAudioStore } from "../store/audioStore";
|
||||||
|
const audioStore = useAudioStore();
|
||||||
|
import { useSerialStore } from "../store/serialStore";
|
||||||
|
const serialStore = useSerialStore();
|
||||||
|
|
||||||
import {
|
import {
|
||||||
getVersion,
|
getVersion,
|
||||||
setConfig,
|
setConfig,
|
||||||
|
@ -21,11 +24,8 @@ import {
|
||||||
stopModem,
|
stopModem,
|
||||||
getModemState,
|
getModemState,
|
||||||
} from "../js/api";
|
} from "../js/api";
|
||||||
import { audioInputOptions, audioOutputOptions } from "../js/deviceFormHelper";
|
|
||||||
import { serialDeviceOptions } from "../js/deviceFormHelper";
|
|
||||||
|
|
||||||
const version = import.meta.env.PACKAGE_VERSION;
|
const version = import.meta.env.PACKAGE_VERSION;
|
||||||
var updateAvailable = process.env.FDUpdateAvail;
|
|
||||||
|
|
||||||
// start modemCheck modal once on startup
|
// start modemCheck modal once on startup
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
|
@ -126,6 +126,7 @@ function testHamlib() {
|
||||||
max="65534"
|
max="65534"
|
||||||
min="1025"
|
min="1025"
|
||||||
v-model="settings.local.port"
|
v-model="settings.local.port"
|
||||||
|
@change="onChange"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@ -137,6 +138,7 @@ function testHamlib() {
|
||||||
placeholder="modem host (default 127.0.0.1)"
|
placeholder="modem host (default 127.0.0.1)"
|
||||||
id="modem_port"
|
id="modem_port"
|
||||||
v-model="settings.local.host"
|
v-model="settings.local.host"
|
||||||
|
@change="onChange"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -203,6 +205,7 @@ function testHamlib() {
|
||||||
</button>
|
</button>
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Audio Input Device -->
|
<!-- Audio Input Device -->
|
||||||
<div class="input-group input-group-sm mb-1">
|
<div class="input-group input-group-sm mb-1">
|
||||||
<label class="input-group-text w-50"
|
<label class="input-group-text w-50"
|
||||||
|
@ -215,10 +218,10 @@ function testHamlib() {
|
||||||
v-model="settings.remote.AUDIO.input_device"
|
v-model="settings.remote.AUDIO.input_device"
|
||||||
>
|
>
|
||||||
<option
|
<option
|
||||||
v-for="option in audioInputOptions()"
|
v-for="device in audioStore.audioInputs"
|
||||||
v-bind:value="option.id"
|
:value="device.id"
|
||||||
>
|
>
|
||||||
{{ option.name }} [{{ option.api }}]
|
{{ device.name }} [{{ device.api }}]
|
||||||
</option>
|
</option>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
|
@ -235,10 +238,10 @@ function testHamlib() {
|
||||||
v-model="settings.remote.AUDIO.output_device"
|
v-model="settings.remote.AUDIO.output_device"
|
||||||
>
|
>
|
||||||
<option
|
<option
|
||||||
v-for="option in audioOutputOptions()"
|
v-for="device in audioStore.audioOutputs"
|
||||||
v-bind:value="option.id"
|
:value="device.id"
|
||||||
>
|
>
|
||||||
{{ option.name }} [{{ option.api }}]
|
{{ device.name }} [{{ device.api }}]
|
||||||
</option>
|
</option>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
|
@ -310,18 +313,16 @@ function testHamlib() {
|
||||||
>
|
>
|
||||||
|
|
||||||
<select
|
<select
|
||||||
class="form-select form-select-sm"
|
|
||||||
aria-label=".form-select-sm"
|
|
||||||
id="hamlib_deviceport"
|
|
||||||
style="width: 7rem"
|
|
||||||
@change="onChange"
|
@change="onChange"
|
||||||
v-model="settings.remote.RADIO.serial_port"
|
v-model="settings.remote.RADIO.serial_port"
|
||||||
|
class="form-select form-select-sm"
|
||||||
>
|
>
|
||||||
<option
|
<option
|
||||||
v-for="option in serialDeviceOptions()"
|
v-for="device in serialStore.serialDevices"
|
||||||
v-bind:value="option.port"
|
:value="device.port"
|
||||||
|
:key="device.port"
|
||||||
>
|
>
|
||||||
{{ option.description }}
|
{{ device.description }}
|
||||||
</option>
|
</option>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
|
@ -392,18 +393,6 @@ function testHamlib() {
|
||||||
data-bs-toggle="collapse"
|
data-bs-toggle="collapse"
|
||||||
>
|
>
|
||||||
Version
|
Version
|
||||||
<span
|
|
||||||
class="badge ms-2"
|
|
||||||
:class="
|
|
||||||
updateAvailable === '1' ? 'bg-warning' : 'bg-success'
|
|
||||||
"
|
|
||||||
>
|
|
||||||
{{
|
|
||||||
updateAvailable === "1"
|
|
||||||
? "Update available ! ! ! !"
|
|
||||||
: "Current"
|
|
||||||
}}</span
|
|
||||||
>
|
|
||||||
</button>
|
</button>
|
||||||
</h2>
|
</h2>
|
||||||
<div
|
<div
|
||||||
|
@ -426,9 +415,6 @@ function testHamlib() {
|
||||||
>
|
>
|
||||||
Modem version | {{ state.modem_version }}
|
Modem version | {{ state.modem_version }}
|
||||||
</button>
|
</button>
|
||||||
<div :class="updateAvailable === '1' ? '' : 'd-none'">
|
|
||||||
<settings_updater_core />
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import settings_updater from "./settings_updater.vue";
|
|
||||||
import settings_station from "./settings_station.vue";
|
import settings_station from "./settings_station.vue";
|
||||||
import settings_gui from "./settings_gui.vue";
|
import settings_gui from "./settings_gui.vue";
|
||||||
import settings_chat from "./settings_chat.vue";
|
import settings_chat from "./settings_chat.vue";
|
||||||
|
@ -23,20 +22,6 @@ import settings_exp from "./settings_exp.vue";
|
||||||
<li class="nav-item" role="presentation">
|
<li class="nav-item" role="presentation">
|
||||||
<button
|
<button
|
||||||
class="nav-link active"
|
class="nav-link active"
|
||||||
id="updater-tab"
|
|
||||||
data-bs-toggle="tab"
|
|
||||||
data-bs-target="#updater"
|
|
||||||
type="button"
|
|
||||||
role="tab"
|
|
||||||
aria-controls="home"
|
|
||||||
aria-selected="true"
|
|
||||||
>
|
|
||||||
Updater
|
|
||||||
</button>
|
|
||||||
</li>
|
|
||||||
<li class="nav-item" role="presentation">
|
|
||||||
<button
|
|
||||||
class="nav-link"
|
|
||||||
id="station-tab"
|
id="station-tab"
|
||||||
data-bs-toggle="tab"
|
data-bs-toggle="tab"
|
||||||
data-bs-target="#station"
|
data-bs-target="#station"
|
||||||
|
@ -141,23 +126,10 @@ import settings_exp from "./settings_exp.vue";
|
||||||
>
|
>
|
||||||
<!-- SETTINGS Nav Tab panes -->
|
<!-- SETTINGS Nav Tab panes -->
|
||||||
|
|
||||||
<!-- Updater tab contents-->
|
|
||||||
<div class="tab-content">
|
|
||||||
<div
|
|
||||||
class="tab-pane active"
|
|
||||||
id="updater"
|
|
||||||
role="tabpanel"
|
|
||||||
aria-labelledby="updater-tab"
|
|
||||||
tabindex="0"
|
|
||||||
>
|
|
||||||
<settings_updater />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Station tab contents-->
|
<!-- Station tab contents-->
|
||||||
<div class="tab-content">
|
<div class="tab-content">
|
||||||
<div
|
<div
|
||||||
class="tab-pane"
|
class="tab-pane active"
|
||||||
id="station"
|
id="station"
|
||||||
role="tabpanel"
|
role="tabpanel"
|
||||||
aria-labelledby="station-tab"
|
aria-labelledby="station-tab"
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { settingsStore as settings, onChange } from "../store/settingsStore.js";
|
import { settingsStore as settings, onChange } from "../store/settingsStore.js";
|
||||||
import { serialDeviceOptions } from "../js/deviceFormHelper";
|
import { useSerialStore } from "../store/serialStore";
|
||||||
|
const serialStore = useSerialStore();
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
|
@ -21,13 +22,13 @@ import { serialDeviceOptions } from "../js/deviceFormHelper";
|
||||||
<div class="input-group input-group-sm mb-1">
|
<div class="input-group input-group-sm mb-1">
|
||||||
<span class="input-group-text" style="width: 180px">Rigctld port</span>
|
<span class="input-group-text" style="width: 180px">Rigctld port</span>
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="number"
|
||||||
class="form-control"
|
class="form-control"
|
||||||
placeholder="rigctld port"
|
placeholder="rigctld port"
|
||||||
id="hamlib_rigctld_port"
|
id="hamlib_rigctld_port"
|
||||||
aria-label="Device Port"
|
aria-label="Device Port"
|
||||||
@change="onChange"
|
@change="onChange"
|
||||||
v-model="settings.remote.RIGCTLD.port"
|
v-model.number="settings.remote.RIGCTLD.port"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@ -334,18 +335,16 @@ import { serialDeviceOptions } from "../js/deviceFormHelper";
|
||||||
<span class="input-group-text" style="width: 180px">Radio port</span>
|
<span class="input-group-text" style="width: 180px">Radio port</span>
|
||||||
|
|
||||||
<select
|
<select
|
||||||
class="form-select form-select-sm"
|
|
||||||
aria-label=".form-select-sm"
|
|
||||||
id="hamlib_deviceport"
|
|
||||||
style="width: 7rem"
|
|
||||||
@change="onChange"
|
@change="onChange"
|
||||||
v-model="settings.remote.RADIO.serial_port"
|
v-model="settings.remote.RADIO.serial_port"
|
||||||
|
class="form-select form-select-sm"
|
||||||
>
|
>
|
||||||
<option
|
<option
|
||||||
v-for="option in serialDeviceOptions()"
|
v-for="device in serialStore.serialDevices"
|
||||||
v-bind:value="option.port"
|
:value="device.port"
|
||||||
|
:key="device.port"
|
||||||
>
|
>
|
||||||
{{ option.description }}
|
{{ device.description }}
|
||||||
</option>
|
</option>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
|
@ -422,18 +421,18 @@ import { serialDeviceOptions } from "../js/deviceFormHelper";
|
||||||
|
|
||||||
<div class="input-group input-group-sm mb-1">
|
<div class="input-group input-group-sm mb-1">
|
||||||
<span class="input-group-text" style="width: 180px">PTT device port</span>
|
<span class="input-group-text" style="width: 180px">PTT device port</span>
|
||||||
|
|
||||||
<select
|
<select
|
||||||
class="form-select form-select-sm"
|
|
||||||
aria-label=".form-select-sm"
|
|
||||||
id="hamlib_ptt_port"
|
|
||||||
@change="onChange"
|
@change="onChange"
|
||||||
v-model="settings.remote.RADIO.ptt_port"
|
v-model="settings.remote.RADIO.ptt_port"
|
||||||
|
class="form-select form-select-sm"
|
||||||
>
|
>
|
||||||
<option
|
<option
|
||||||
v-for="option in serialDeviceOptions()"
|
v-for="device in serialStore.serialDevices"
|
||||||
v-bind:value="option.port"
|
:value="device.port"
|
||||||
|
:key="device.port"
|
||||||
>
|
>
|
||||||
{{ option.description }}
|
{{ device.description }}
|
||||||
</option>
|
</option>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -6,7 +6,9 @@ import { useStateStore } from "../store/stateStore.js";
|
||||||
const state = useStateStore(pinia);
|
const state = useStateStore(pinia);
|
||||||
|
|
||||||
import { startModem, stopModem } from "../js/api.js";
|
import { startModem, stopModem } from "../js/api.js";
|
||||||
import { audioInputOptions, audioOutputOptions } from "../js/deviceFormHelper";
|
|
||||||
|
import { useAudioStore } from "../store/audioStore";
|
||||||
|
const audioStore = useAudioStore();
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
|
@ -74,8 +76,8 @@ import { audioInputOptions, audioOutputOptions } from "../js/deviceFormHelper";
|
||||||
@change="onChange"
|
@change="onChange"
|
||||||
v-model="settings.remote.AUDIO.input_device"
|
v-model="settings.remote.AUDIO.input_device"
|
||||||
>
|
>
|
||||||
<option v-for="option in audioInputOptions()" v-bind:value="option.id">
|
<option v-for="device in audioStore.audioInputs" :value="device.id">
|
||||||
{{ option.name }} [{{ option.api }}]
|
{{ device.name }} [{{ device.api }}]
|
||||||
</option>
|
</option>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
|
@ -89,11 +91,12 @@ import { audioInputOptions, audioOutputOptions } from "../js/deviceFormHelper";
|
||||||
@change="onChange"
|
@change="onChange"
|
||||||
v-model="settings.remote.AUDIO.output_device"
|
v-model="settings.remote.AUDIO.output_device"
|
||||||
>
|
>
|
||||||
<option v-for="option in audioOutputOptions()" v-bind:value="option.id">
|
<option v-for="device in audioStore.audioOutputs" :value="device.id">
|
||||||
{{ option.name }} [{{ option.api }}]
|
{{ device.name }} [{{ device.api }}]
|
||||||
</option>
|
</option>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Audio rx level-->
|
<!-- Audio rx level-->
|
||||||
<div class="input-group input-group-sm mb-1">
|
<div class="input-group input-group-sm mb-1">
|
||||||
<span class="input-group-text w-25">RX Audio Level</span>
|
<span class="input-group-text w-25">RX Audio Level</span>
|
||||||
|
@ -161,56 +164,6 @@ import { audioInputOptions, audioOutputOptions } from "../js/deviceFormHelper";
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="input-group input-group-sm mb-1">
|
|
||||||
<label class="input-group-text w-25">Tuning range</label>
|
|
||||||
<label class="input-group-text">fmin</label>
|
|
||||||
<select
|
|
||||||
class="form-select form-select-sm"
|
|
||||||
id="tuning_range_fmin"
|
|
||||||
@change="onChange"
|
|
||||||
v-model.number="settings.remote.MODEM.tuning_range_fmin"
|
|
||||||
>
|
|
||||||
<option value="-50">-50</option>
|
|
||||||
<option value="-100">-100</option>
|
|
||||||
<option value="-150">-150</option>
|
|
||||||
<option value="-200">-200</option>
|
|
||||||
<option value="-250">-250</option>
|
|
||||||
</select>
|
|
||||||
<label class="input-group-text">fmax</label>
|
|
||||||
<select
|
|
||||||
class="form-select form-select-sm"
|
|
||||||
id="tuning_range_fmax"
|
|
||||||
@change="onChange"
|
|
||||||
v-model.number="settings.remote.MODEM.tuning_range_fmax"
|
|
||||||
>
|
|
||||||
<option value="50">50</option>
|
|
||||||
<option value="100">100</option>
|
|
||||||
<option value="150">150</option>
|
|
||||||
<option value="200">200</option>
|
|
||||||
<option value="250">250</option>
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
<div class="input-group input-group-sm mb-1">
|
|
||||||
<span class="input-group-text w-50">Beacon interval</span>
|
|
||||||
<select
|
|
||||||
class="form-select form-select-sm"
|
|
||||||
aria-label=".form-select-sm"
|
|
||||||
id="beaconInterval"
|
|
||||||
style="width: 6rem"
|
|
||||||
@change="onChange"
|
|
||||||
v-model.number="settings.remote.MODEM.beacon_interval"
|
|
||||||
>
|
|
||||||
<option value="60">60 secs</option>
|
|
||||||
<option value="90">90 secs</option>
|
|
||||||
<option value="120">2 mins</option>
|
|
||||||
<option selected value="300">5 mins</option>
|
|
||||||
<option value="600">10 mins</option>
|
|
||||||
<option value="900">15 mins</option>
|
|
||||||
<option value="1800">30 mins</option>
|
|
||||||
<option value="3600">60 mins</option>
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="input-group input-group-sm mb-1">
|
<div class="input-group input-group-sm mb-1">
|
||||||
<label class="input-group-text w-50">Enable 250Hz bandwidth mode</label>
|
<label class="input-group-text w-50">Enable 250Hz bandwidth mode</label>
|
||||||
<label class="input-group-text w-50">
|
<label class="input-group-text w-50">
|
||||||
|
@ -241,27 +194,4 @@ import { audioInputOptions, audioOutputOptions } from "../js/deviceFormHelper";
|
||||||
</div>
|
</div>
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
<div class="input-group input-group-sm mb-1">
|
|
||||||
<label class="input-group-text w-50">RX buffer size</label>
|
|
||||||
<label class="input-group-text w-50">
|
|
||||||
<select
|
|
||||||
class="form-select form-select-sm"
|
|
||||||
id="rx_buffer_size"
|
|
||||||
@change="onChange"
|
|
||||||
v-model.number="settings.remote.MODEM.rx_buffer_size"
|
|
||||||
>
|
|
||||||
<option value="1">1</option>
|
|
||||||
<option value="2">2</option>
|
|
||||||
<option value="4">4</option>
|
|
||||||
<option value="8">8</option>
|
|
||||||
<option value="16">16</option>
|
|
||||||
<option value="32">32</option>
|
|
||||||
<option value="64">64</option>
|
|
||||||
<option value="128">128</option>
|
|
||||||
<option value="256">256</option>
|
|
||||||
<option value="512">512</option>
|
|
||||||
<option value="1024">1024</option>
|
|
||||||
</select>
|
|
||||||
</label>
|
|
||||||
</div>
|
|
||||||
</template>
|
</template>
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { settingsStore as settings, onChange } from "../store/settingsStore.js";
|
import { settingsStore as settings, onChange } from "../store/settingsStore.js";
|
||||||
import { serialDeviceOptions } from "../js/deviceFormHelper";
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
|
|
|
@ -1,16 +0,0 @@
|
||||||
<script setup lang="ts">
|
|
||||||
import settings_updater_core from "./settings_updater_core.vue";
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<template>
|
|
||||||
<div>
|
|
||||||
<div class="alert alert-warning" role="alert">
|
|
||||||
The updater might not working, yet! Please update manually if you are running into problems!
|
|
||||||
</div>
|
|
||||||
<div class="alert alert-warning" role="alert">
|
|
||||||
The updater doesnt contain the server related parts, yet! We are discussing this topic actually, feel free contributing with your opinion on Discord!
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<settings_updater_core />
|
|
||||||
</div>
|
|
||||||
</template>
|
|
|
@ -1,102 +0,0 @@
|
||||||
<script setup lang="ts">
|
|
||||||
import { setActivePinia } from "pinia";
|
|
||||||
import pinia from "../store/index";
|
|
||||||
setActivePinia(pinia);
|
|
||||||
|
|
||||||
import { useStateStore } from "../store/stateStore.js";
|
|
||||||
import { settingsStore } from "../store/settingsStore";
|
|
||||||
import { onMounted } from "vue";
|
|
||||||
import { ipcRenderer } from "electron";
|
|
||||||
const state = useStateStore(pinia);
|
|
||||||
onMounted(() => {
|
|
||||||
window.addEventListener("DOMContentLoaded", () => {
|
|
||||||
// we are using this area for implementing the electron runUpdater
|
|
||||||
// we need access to DOM for displaying updater results in GUI
|
|
||||||
// close app, update and restart
|
|
||||||
document
|
|
||||||
.getElementById("update_and_install")
|
|
||||||
.addEventListener("click", () => {
|
|
||||||
ipcRenderer.send("request-restart-and-install-update");
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<template>
|
|
||||||
<div class="card m-2">
|
|
||||||
<div class="card-header p-1 d-flex">
|
|
||||||
<div class="container">
|
|
||||||
<div class="row">
|
|
||||||
<div class="col-1">
|
|
||||||
<i class="bi bi-cloud-download" style="font-size: 1.2rem"></i>
|
|
||||||
</div>
|
|
||||||
<div class="col-3">
|
|
||||||
<strong class="fs-5">Updater</strong>
|
|
||||||
</div>
|
|
||||||
<div class="col-7">
|
|
||||||
<div class="progress w-100 ms-1 m-1">
|
|
||||||
<div
|
|
||||||
class="progress-bar"
|
|
||||||
style="width: 0%"
|
|
||||||
role="progressbar"
|
|
||||||
id="UpdateProgressBar"
|
|
||||||
aria-valuenow="0"
|
|
||||||
aria-valuemin="0"
|
|
||||||
aria-valuemax="100"
|
|
||||||
>
|
|
||||||
<span id="UpdateProgressInfo"></span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="col-1 text-end">
|
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
id="openHelpModalUpdater"
|
|
||||||
data-bs-toggle="modal"
|
|
||||||
data-bs-target="#updaterHelpModal"
|
|
||||||
class="btn m-0 p-0 border-0"
|
|
||||||
>
|
|
||||||
<i class="bi bi-question-circle" style="font-size: 1rem"></i>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="card-body p-2 mb-1">
|
|
||||||
<button
|
|
||||||
class="btn btn-secondary btn-sm ms-1 me-1"
|
|
||||||
id="updater_channel"
|
|
||||||
type="button"
|
|
||||||
disabled
|
|
||||||
>
|
|
||||||
Update channel: {{ settingsStore.local.update_channel }}
|
|
||||||
</button>
|
|
||||||
<button
|
|
||||||
class="btn btn-secondary btn-sm ms-1"
|
|
||||||
id="updater_status"
|
|
||||||
type="button"
|
|
||||||
disabled
|
|
||||||
>
|
|
||||||
...
|
|
||||||
</button>
|
|
||||||
<button
|
|
||||||
class="btn btn-secondary btn-sm ms-1"
|
|
||||||
id="updater_changelog"
|
|
||||||
type="button"
|
|
||||||
data-bs-toggle="modal"
|
|
||||||
data-bs-target="#updaterReleaseNotes"
|
|
||||||
>
|
|
||||||
Changelog
|
|
||||||
</button>
|
|
||||||
<button
|
|
||||||
class="btn btn-primary btn-sm ms-1"
|
|
||||||
id="update_and_install"
|
|
||||||
type="button"
|
|
||||||
style="display: none"
|
|
||||||
>
|
|
||||||
Install & Restart
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
|
@ -1,53 +0,0 @@
|
||||||
import { getAudioDevices, getSerialDevices } from "./api";
|
|
||||||
|
|
||||||
let audioDevices = await getAudioDevices();
|
|
||||||
let serialDevices = await getSerialDevices();
|
|
||||||
|
|
||||||
//Dummy device data sent if unable to get devices from modem to prevent GUI crash
|
|
||||||
const skel = JSON.parse(`
|
|
||||||
[{
|
|
||||||
"api": "MME",
|
|
||||||
"id": "0000",
|
|
||||||
"name": "No devices received from modem",
|
|
||||||
"native_index": 0
|
|
||||||
}]`);
|
|
||||||
|
|
||||||
export function loadAudioDevices() {
|
|
||||||
getAudioDevices().then((devices) => {
|
|
||||||
audioDevices = devices;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
export function loadSerialDevices() {
|
|
||||||
getSerialDevices().then((devices) => {
|
|
||||||
serialDevices = devices;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
export function audioInputOptions() {
|
|
||||||
if (audioDevices === undefined) {
|
|
||||||
return skel;
|
|
||||||
}
|
|
||||||
return audioDevices.in;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function audioOutputOptions() {
|
|
||||||
if (audioDevices === undefined) {
|
|
||||||
return skel;
|
|
||||||
}
|
|
||||||
|
|
||||||
return audioDevices.out;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function serialDeviceOptions() {
|
|
||||||
//Return ignore option if no serialDevices
|
|
||||||
if (serialDevices === undefined)
|
|
||||||
return [{ description: "-- ignore --", port: "ignore" }];
|
|
||||||
|
|
||||||
if (serialDevices.findIndex((device) => device.port == "ignore") == -1) {
|
|
||||||
//Add an ignore option for rig and ptt for transceivers that don't require them
|
|
||||||
serialDevices.push({ description: "-- ignore --", port: "ignore" });
|
|
||||||
}
|
|
||||||
|
|
||||||
return serialDevices;
|
|
||||||
}
|
|
|
@ -8,16 +8,15 @@ import {
|
||||||
} from "./chatHandler";
|
} from "./chatHandler";
|
||||||
*/
|
*/
|
||||||
import { displayToast } from "./popupHandler";
|
import { displayToast } from "./popupHandler";
|
||||||
import {
|
import { getFreedataMessages, getModemState, getAudioDevices } from "./api";
|
||||||
getFreedataMessages,
|
|
||||||
getConfig,
|
|
||||||
getAudioDevices,
|
|
||||||
getSerialDevices,
|
|
||||||
getModemState,
|
|
||||||
} from "./api";
|
|
||||||
import { processFreedataMessages } from "./messagesHandler.ts";
|
import { processFreedataMessages } from "./messagesHandler.ts";
|
||||||
import { processRadioStatus } from "./radioHandler.ts";
|
import { processRadioStatus } from "./radioHandler.ts";
|
||||||
|
|
||||||
|
import { useAudioStore } from "../store/audioStore";
|
||||||
|
const audioStore = useAudioStore();
|
||||||
|
import { useSerialStore } from "../store/serialStore";
|
||||||
|
const serialStore = useSerialStore();
|
||||||
|
|
||||||
// ----------------- init pinia stores -------------
|
// ----------------- init pinia stores -------------
|
||||||
import { setActivePinia } from "pinia";
|
import { setActivePinia } from "pinia";
|
||||||
import pinia from "../store/index";
|
import pinia from "../store/index";
|
||||||
|
@ -30,6 +29,17 @@ import {
|
||||||
getRemote,
|
getRemote,
|
||||||
} from "../store/settingsStore.js";
|
} from "../store/settingsStore.js";
|
||||||
|
|
||||||
|
export function loadAllData() {
|
||||||
|
getModemState();
|
||||||
|
getRemote();
|
||||||
|
getOverallHealth();
|
||||||
|
audioStore.loadAudioDevices();
|
||||||
|
serialStore.loadSerialDevices();
|
||||||
|
getFreedataMessages();
|
||||||
|
processFreedataMessages();
|
||||||
|
processRadioStatus();
|
||||||
|
}
|
||||||
|
|
||||||
export function connectionFailed(endpoint, event) {
|
export function connectionFailed(endpoint, event) {
|
||||||
stateStore.modem_connection = "disconnected";
|
stateStore.modem_connection = "disconnected";
|
||||||
}
|
}
|
||||||
|
@ -95,12 +105,7 @@ export function eventDispatcher(data) {
|
||||||
switch (data["modem"]) {
|
switch (data["modem"]) {
|
||||||
case "started":
|
case "started":
|
||||||
displayToast("success", "bi-arrow-left-right", "Modem started", 5000);
|
displayToast("success", "bi-arrow-left-right", "Modem started", 5000);
|
||||||
getModemState();
|
loadAllData();
|
||||||
getConfig();
|
|
||||||
getAudioDevices();
|
|
||||||
getSerialDevices();
|
|
||||||
getFreedataMessages();
|
|
||||||
processRadioStatus();
|
|
||||||
return;
|
return;
|
||||||
|
|
||||||
case "stopped":
|
case "stopped":
|
||||||
|
@ -109,12 +114,7 @@ export function eventDispatcher(data) {
|
||||||
|
|
||||||
case "restarted":
|
case "restarted":
|
||||||
displayToast("secondary", "bi-bootstrap-reboot", "Modem restarted", 5000);
|
displayToast("secondary", "bi-bootstrap-reboot", "Modem restarted", 5000);
|
||||||
getModemState();
|
loadAllData();
|
||||||
getConfig();
|
|
||||||
getAudioDevices();
|
|
||||||
getSerialDevices();
|
|
||||||
getFreedataMessages();
|
|
||||||
processRadioStatus();
|
|
||||||
return;
|
return;
|
||||||
|
|
||||||
case "failed":
|
case "failed":
|
||||||
|
@ -135,19 +135,7 @@ export function eventDispatcher(data) {
|
||||||
displayToast("success", "bi-ethernet", message, 5000);
|
displayToast("success", "bi-ethernet", message, 5000);
|
||||||
stateStore.modem_connection = "connected";
|
stateStore.modem_connection = "connected";
|
||||||
|
|
||||||
getRemote().then(() => {
|
loadAllData();
|
||||||
//initConnections();
|
|
||||||
getModemState();
|
|
||||||
});
|
|
||||||
|
|
||||||
//getConfig();
|
|
||||||
getModemState();
|
|
||||||
getOverallHealth();
|
|
||||||
getAudioDevices();
|
|
||||||
getSerialDevices();
|
|
||||||
getFreedataMessages();
|
|
||||||
processFreedataMessages();
|
|
||||||
processRadioStatus();
|
|
||||||
|
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
|
|
@ -83,6 +83,7 @@ function createSortedMessagesList(data: {
|
||||||
|
|
||||||
export function newMessage(dxcall, body, attachments) {
|
export function newMessage(dxcall, body, attachments) {
|
||||||
sendFreedataMessage(dxcall, body, attachments);
|
sendFreedataMessage(dxcall, body, attachments);
|
||||||
|
chatStore.triggerScrollToBottom();
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ------ TEMPORARY DUMMY FUNCTIONS --- */
|
/* ------ TEMPORARY DUMMY FUNCTIONS --- */
|
||||||
|
|
39
gui/src/store/audioStore.js
Normal file
39
gui/src/store/audioStore.js
Normal file
|
@ -0,0 +1,39 @@
|
||||||
|
import { defineStore } from "pinia";
|
||||||
|
import { getAudioDevices } from "../js/api";
|
||||||
|
import { ref } from "vue";
|
||||||
|
|
||||||
|
// Define skel fallback data
|
||||||
|
const skel = [
|
||||||
|
{
|
||||||
|
api: "ERR",
|
||||||
|
id: "0000",
|
||||||
|
name: "No devices received from modem",
|
||||||
|
native_index: 0,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
export const useAudioStore = defineStore("audioStore", () => {
|
||||||
|
const audioInputs = ref([]);
|
||||||
|
const audioOutputs = ref([]);
|
||||||
|
|
||||||
|
const loadAudioDevices = async () => {
|
||||||
|
try {
|
||||||
|
const devices = await getAudioDevices();
|
||||||
|
// Check if devices are valid and have entries, otherwise use skel
|
||||||
|
audioInputs.value = devices && devices.in.length > 0 ? devices.in : skel;
|
||||||
|
audioOutputs.value =
|
||||||
|
devices && devices.out.length > 0 ? devices.out : skel;
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Failed to load audio devices:", error);
|
||||||
|
// Use skel as fallback in case of error
|
||||||
|
audioInputs.value = skel;
|
||||||
|
audioOutputs.value = skel;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return {
|
||||||
|
audioInputs,
|
||||||
|
audioOutputs,
|
||||||
|
loadAudioDevices,
|
||||||
|
};
|
||||||
|
});
|
|
@ -7,6 +7,14 @@ export const useChatStore = defineStore("chatStore", () => {
|
||||||
var newChatCallsign = ref();
|
var newChatCallsign = ref();
|
||||||
var newChatMessage = ref();
|
var newChatMessage = ref();
|
||||||
|
|
||||||
|
/* ------------------------------------------------ */
|
||||||
|
// Scroll to bottom functions
|
||||||
|
const scrollTrigger = ref(0);
|
||||||
|
|
||||||
|
function triggerScrollToBottom() {
|
||||||
|
scrollTrigger.value++;
|
||||||
|
}
|
||||||
|
|
||||||
/* ------------------------------------------------ */
|
/* ------------------------------------------------ */
|
||||||
|
|
||||||
var chat_filter = ref([
|
var chat_filter = ref([
|
||||||
|
@ -92,5 +100,7 @@ export const useChatStore = defineStore("chatStore", () => {
|
||||||
arq_speed_list_bpm,
|
arq_speed_list_bpm,
|
||||||
arq_speed_list_snr,
|
arq_speed_list_snr,
|
||||||
arq_speed_list_timestamp,
|
arq_speed_list_timestamp,
|
||||||
|
scrollTrigger,
|
||||||
|
triggerScrollToBottom,
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
38
gui/src/store/serialStore.js
Normal file
38
gui/src/store/serialStore.js
Normal file
|
@ -0,0 +1,38 @@
|
||||||
|
import { defineStore } from "pinia";
|
||||||
|
import { getSerialDevices } from "../js/api"; // Make sure this points to the correct file
|
||||||
|
import { ref } from "vue";
|
||||||
|
|
||||||
|
// Define "skel" fallback data for serial devices
|
||||||
|
const skelSerial = [
|
||||||
|
{
|
||||||
|
description: "No devices received from modem",
|
||||||
|
port: "ignore", // Using "ignore" as a placeholder value
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
export const useSerialStore = defineStore("serialStore", () => {
|
||||||
|
const serialDevices = ref([]);
|
||||||
|
|
||||||
|
const loadSerialDevices = async () => {
|
||||||
|
try {
|
||||||
|
const devices = await getSerialDevices();
|
||||||
|
// Check if devices are valid and have entries, otherwise use skelSerial
|
||||||
|
serialDevices.value =
|
||||||
|
devices && devices.length > 0 ? devices : skelSerial;
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Failed to load serial devices:", error);
|
||||||
|
// Use skelSerial as fallback in case of error
|
||||||
|
serialDevices.value = skelSerial;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ensure the "-- ignore --" option is always available
|
||||||
|
if (!serialDevices.value.some((device) => device.port === "ignore")) {
|
||||||
|
serialDevices.value.push({ description: "-- ignore --", port: "ignore" });
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return {
|
||||||
|
serialDevices,
|
||||||
|
loadSerialDevices,
|
||||||
|
};
|
||||||
|
});
|
|
@ -54,14 +54,9 @@ const defaultConfig = {
|
||||||
enable_protocol: false,
|
enable_protocol: false,
|
||||||
},
|
},
|
||||||
MODEM: {
|
MODEM: {
|
||||||
enable_fsk: false,
|
|
||||||
enable_low_bandwidth_mode: false,
|
enable_low_bandwidth_mode: false,
|
||||||
respond_to_cq: false,
|
respond_to_cq: false,
|
||||||
rx_buffer_size: 0,
|
|
||||||
tuning_range_fmax: 0,
|
|
||||||
tuning_range_fmin: 0,
|
|
||||||
tx_delay: 0,
|
tx_delay: 0,
|
||||||
beacon_interval: 0,
|
|
||||||
enable_hamc: false,
|
enable_hamc: false,
|
||||||
enable_morse_identifier: false,
|
enable_morse_identifier: false,
|
||||||
},
|
},
|
||||||
|
|
|
@ -74,23 +74,20 @@ def freedv_get_mode_name_by_value(mode: int) -> str:
|
||||||
return FREEDV_MODE(mode).name
|
return FREEDV_MODE(mode).name
|
||||||
|
|
||||||
|
|
||||||
# Check if we are running in a pyinstaller environment
|
# Get the directory of the current script file
|
||||||
#if hasattr(sys, "_MEIPASS"):
|
script_dir = os.path.dirname(os.path.abspath(__file__))
|
||||||
# sys.path.append(getattr(sys, "_MEIPASS"))
|
sys.path.append(script_dir)
|
||||||
#else:
|
# Use script_dir to construct the paths for file search
|
||||||
sys.path.append(os.path.abspath("."))
|
|
||||||
|
|
||||||
#log.info("[C2 ] Searching for libcodec2...")
|
|
||||||
if sys.platform == "linux":
|
if sys.platform == "linux":
|
||||||
files = glob.glob(r"**/*libcodec2*", recursive=True)
|
files = glob.glob(os.path.join(script_dir, "**/*libcodec2*"), recursive=True)
|
||||||
files.append("libcodec2.so")
|
files.append(os.path.join(script_dir, "libcodec2.so"))
|
||||||
elif sys.platform == "darwin":
|
elif sys.platform == "darwin":
|
||||||
if hasattr(sys, "_MEIPASS"):
|
if hasattr(sys, "_MEIPASS"):
|
||||||
files = glob.glob(getattr(sys, "_MEIPASS") + '/**/*libcodec2*', recursive=True)
|
files = glob.glob(os.path.join(getattr(sys, "_MEIPASS"), '**/*libcodec2*'), recursive=True)
|
||||||
else:
|
else:
|
||||||
files = glob.glob(r"**/*libcodec2*.dylib", recursive=True)
|
files = glob.glob(os.path.join(script_dir, "**/*libcodec2*.dylib"), recursive=True)
|
||||||
elif sys.platform in ["win32", "win64"]:
|
elif sys.platform in ["win32", "win64"]:
|
||||||
files = glob.glob(r"**\*libcodec2*.dll", recursive=True)
|
files = glob.glob(os.path.join(script_dir, "**\\*libcodec2*.dll"), recursive=True)
|
||||||
else:
|
else:
|
||||||
files = []
|
files = []
|
||||||
|
|
||||||
|
|
|
@ -23,6 +23,11 @@ class SendMessageCommand(TxCommand):
|
||||||
self.log("Modem busy, waiting until ready...")
|
self.log("Modem busy, waiting until ready...")
|
||||||
return
|
return
|
||||||
|
|
||||||
|
if not modem:
|
||||||
|
self.log("Modem not running...", isWarning=True)
|
||||||
|
return
|
||||||
|
|
||||||
|
|
||||||
first_queued_message = DatabaseManagerMessages(self.event_manager).get_first_queued_message()
|
first_queued_message = DatabaseManagerMessages(self.event_manager).get_first_queued_message()
|
||||||
if not first_queued_message:
|
if not first_queued_message:
|
||||||
self.log("No queued message in database.")
|
self.log("No queued message in database.")
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
[NETWORK]
|
[NETWORK]
|
||||||
|
modemaddress = 127.0.0.1
|
||||||
modemport = 5000
|
modemport = 5000
|
||||||
|
|
||||||
[STATION]
|
[STATION]
|
||||||
|
@ -45,15 +46,10 @@ enable_protocol = False
|
||||||
|
|
||||||
[MODEM]
|
[MODEM]
|
||||||
enable_hmac = False
|
enable_hmac = False
|
||||||
tuning_range_fmax = 50
|
|
||||||
tuning_range_fmin = -50
|
|
||||||
enable_fsk = False
|
|
||||||
enable_low_bandwidth_mode = False
|
enable_low_bandwidth_mode = False
|
||||||
enable_morse_identifier = False
|
enable_morse_identifier = False
|
||||||
respond_to_cq = True
|
respond_to_cq = True
|
||||||
rx_buffer_size = 64
|
|
||||||
tx_delay = 200
|
tx_delay = 200
|
||||||
beacon_interval = 300
|
|
||||||
|
|
||||||
[MESSAGES]
|
[MESSAGES]
|
||||||
enable_auto_repeat = False
|
enable_auto_repeat = False
|
||||||
|
|
|
@ -10,6 +10,7 @@ class CONFIG:
|
||||||
|
|
||||||
config_types = {
|
config_types = {
|
||||||
'NETWORK': {
|
'NETWORK': {
|
||||||
|
'modemaddress': str,
|
||||||
'modemport': int,
|
'modemport': int,
|
||||||
},
|
},
|
||||||
'STATION': {
|
'STATION': {
|
||||||
|
@ -55,16 +56,11 @@ class CONFIG:
|
||||||
'enable_protocol': bool,
|
'enable_protocol': bool,
|
||||||
},
|
},
|
||||||
'MODEM': {
|
'MODEM': {
|
||||||
'tuning_range_fmax': int,
|
|
||||||
'tuning_range_fmin': int,
|
|
||||||
'enable_fsk': bool,
|
|
||||||
'enable_hmac': bool,
|
'enable_hmac': bool,
|
||||||
'enable_morse_identifier': bool,
|
'enable_morse_identifier': bool,
|
||||||
'enable_low_bandwidth_mode': bool,
|
'enable_low_bandwidth_mode': bool,
|
||||||
'respond_to_cq': bool,
|
'respond_to_cq': bool,
|
||||||
'rx_buffer_size': int,
|
'tx_delay': int
|
||||||
'tx_delay': int,
|
|
||||||
'beacon_interval': int,
|
|
||||||
},
|
},
|
||||||
'MESSAGES': {
|
'MESSAGES': {
|
||||||
'enable_auto_repeat': bool,
|
'enable_auto_repeat': bool,
|
||||||
|
|
|
@ -7,6 +7,7 @@ import audio
|
||||||
import os
|
import os
|
||||||
from modem_frametypes import FRAME_TYPE
|
from modem_frametypes import FRAME_TYPE
|
||||||
import itertools
|
import itertools
|
||||||
|
from time import sleep
|
||||||
|
|
||||||
TESTMODE = False
|
TESTMODE = False
|
||||||
|
|
||||||
|
@ -27,11 +28,9 @@ class Demodulator():
|
||||||
'decoding_thread': None
|
'decoding_thread': None
|
||||||
}
|
}
|
||||||
|
|
||||||
def __init__(self, config, audio_rx_q, modem_rx_q, data_q_rx, states, event_manager, fft_queue):
|
def __init__(self, config, audio_rx_q, data_q_rx, states, event_manager, fft_queue):
|
||||||
self.log = structlog.get_logger("Demodulator")
|
self.log = structlog.get_logger("Demodulator")
|
||||||
|
|
||||||
self.tuning_range_fmin = config['MODEM']['tuning_range_fmin']
|
|
||||||
self.tuning_range_fmax = config['MODEM']['tuning_range_fmax']
|
|
||||||
self.rx_audio_level = config['AUDIO']['rx_audio_level']
|
self.rx_audio_level = config['AUDIO']['rx_audio_level']
|
||||||
|
|
||||||
self.AUDIO_FRAMES_PER_BUFFER_RX = 4800
|
self.AUDIO_FRAMES_PER_BUFFER_RX = 4800
|
||||||
|
@ -40,7 +39,6 @@ class Demodulator():
|
||||||
self.is_codec2_traffic_cooldown = 5
|
self.is_codec2_traffic_cooldown = 5
|
||||||
|
|
||||||
self.audio_received_queue = audio_rx_q
|
self.audio_received_queue = audio_rx_q
|
||||||
self.modem_received_queue = modem_rx_q
|
|
||||||
self.data_queue_received = data_q_rx
|
self.data_queue_received = data_q_rx
|
||||||
|
|
||||||
self.states = states
|
self.states = states
|
||||||
|
@ -79,13 +77,6 @@ class Demodulator():
|
||||||
codec2.api.freedv_open(mode), ctypes.c_void_p
|
codec2.api.freedv_open(mode), ctypes.c_void_p
|
||||||
)
|
)
|
||||||
|
|
||||||
# set tuning range
|
|
||||||
codec2.api.freedv_set_tuning_range(
|
|
||||||
c2instance,
|
|
||||||
ctypes.c_float(float(self.tuning_range_fmin)),
|
|
||||||
ctypes.c_float(float(self.tuning_range_fmax)),
|
|
||||||
)
|
|
||||||
|
|
||||||
# get bytes per frame
|
# get bytes per frame
|
||||||
bytes_per_frame = int(
|
bytes_per_frame = int(
|
||||||
codec2.api.freedv_get_bits_per_modem_frame(c2instance) / 8
|
codec2.api.freedv_get_bits_per_modem_frame(c2instance) / 8
|
||||||
|
@ -136,6 +127,10 @@ class Demodulator():
|
||||||
self.MODE_DICT[mode]['decoding_thread'].start()
|
self.MODE_DICT[mode]['decoding_thread'].start()
|
||||||
|
|
||||||
def sd_input_audio_callback(self, indata: np.ndarray, frames: int, time, status) -> None:
|
def sd_input_audio_callback(self, indata: np.ndarray, frames: int, time, status) -> None:
|
||||||
|
if status:
|
||||||
|
self.log.warning("[AUDIO STATUS]", status=status, time=time, frames=frames)
|
||||||
|
return
|
||||||
|
try:
|
||||||
audio_48k = np.frombuffer(indata, dtype=np.int16)
|
audio_48k = np.frombuffer(indata, dtype=np.int16)
|
||||||
audio_8k = self.resampler.resample48_to_8(audio_48k)
|
audio_8k = self.resampler.resample48_to_8(audio_48k)
|
||||||
|
|
||||||
|
@ -157,27 +152,9 @@ class Demodulator():
|
||||||
self.event_manager.send_buffer_overflow(self.buffer_overflow_counter)
|
self.event_manager.send_buffer_overflow(self.buffer_overflow_counter)
|
||||||
elif decode:
|
elif decode:
|
||||||
audiobuffer.push(audio_8k_level_adjusted)
|
audiobuffer.push(audio_8k_level_adjusted)
|
||||||
|
except Exception as e:
|
||||||
|
self.log.warning("[AUDIO EXCEPTION]", status=status, time=time, frames=frames, e=e)
|
||||||
|
|
||||||
def worker_received(self) -> None:
|
|
||||||
"""Worker for FIFO queue for processing received frames"""
|
|
||||||
while True:
|
|
||||||
data = self.modem_received_queue.get()
|
|
||||||
self.log.debug("[MDM] worker_received: received data!")
|
|
||||||
# data[0] = bytes_out
|
|
||||||
# data[1] = freedv session
|
|
||||||
# data[2] = bytes_per_frame
|
|
||||||
# data[3] = snr
|
|
||||||
|
|
||||||
item = {
|
|
||||||
'payload': data[0],
|
|
||||||
'freedv': data[1],
|
|
||||||
'bytes_per_frame': data[2],
|
|
||||||
'snr': data[3],
|
|
||||||
'frequency_offset': self.get_frequency_offset(data[1]),
|
|
||||||
}
|
|
||||||
|
|
||||||
self.data_queue_received.put(item)
|
|
||||||
self.modem_received_queue.task_done()
|
|
||||||
|
|
||||||
def get_frequency_offset(self, freedv: ctypes.c_void_p) -> float:
|
def get_frequency_offset(self, freedv: ctypes.c_void_p) -> float:
|
||||||
"""
|
"""
|
||||||
|
@ -247,7 +224,16 @@ class Demodulator():
|
||||||
snr = self.calculate_snr(freedv)
|
snr = self.calculate_snr(freedv)
|
||||||
self.get_scatter(freedv)
|
self.get_scatter(freedv)
|
||||||
|
|
||||||
self.modem_received_queue.put([bytes_out, freedv, bytes_per_frame, snr])
|
item = {
|
||||||
|
'payload': bytes_out,
|
||||||
|
'freedv': freedv,
|
||||||
|
'bytes_per_frame': bytes_per_frame,
|
||||||
|
'snr': snr,
|
||||||
|
'frequency_offset': self.get_frequency_offset(freedv),
|
||||||
|
}
|
||||||
|
self.data_queue_received.put(item)
|
||||||
|
|
||||||
|
|
||||||
state_buffer = []
|
state_buffer = []
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
error_message = str(e)
|
error_message = str(e)
|
||||||
|
@ -257,6 +243,7 @@ class Demodulator():
|
||||||
self.log.debug(
|
self.log.debug(
|
||||||
"[MDM] [demod_audio] demod loop ended", mode=mode_name, e=e
|
"[MDM] [demod_audio] demod loop ended", mode=mode_name, e=e
|
||||||
)
|
)
|
||||||
|
|
||||||
def tci_rx_callback(self) -> None:
|
def tci_rx_callback(self) -> None:
|
||||||
"""
|
"""
|
||||||
Callback for TCI RX
|
Callback for TCI RX
|
||||||
|
@ -297,6 +284,7 @@ class Demodulator():
|
||||||
frames_per_burst = min(frames_per_burst, 1)
|
frames_per_burst = min(frames_per_burst, 1)
|
||||||
frames_per_burst = max(frames_per_burst, 5)
|
frames_per_burst = max(frames_per_burst, 5)
|
||||||
|
|
||||||
|
# FIXME
|
||||||
frames_per_burst = 1
|
frames_per_burst = 1
|
||||||
|
|
||||||
codec2.api.freedv_set_frames_per_burst(self.dat0_datac1_freedv, frames_per_burst)
|
codec2.api.freedv_set_frames_per_burst(self.dat0_datac1_freedv, frames_per_burst)
|
||||||
|
|
|
@ -2,12 +2,16 @@ from message_system_db_manager import DatabaseManager
|
||||||
from message_system_db_model import MessageAttachment, Attachment, P2PMessage
|
from message_system_db_model import MessageAttachment, Attachment, P2PMessage
|
||||||
import json
|
import json
|
||||||
import hashlib
|
import hashlib
|
||||||
|
import os
|
||||||
|
|
||||||
|
|
||||||
class DatabaseManagerAttachments(DatabaseManager):
|
class DatabaseManagerAttachments(DatabaseManager):
|
||||||
def __init__(self, uri='sqlite:///freedata-messages.db'):
|
def __init__(self, db_file=None):
|
||||||
super().__init__(uri)
|
if not db_file:
|
||||||
|
script_dir = os.path.dirname(os.path.abspath(__file__))
|
||||||
|
db_path = os.path.join(script_dir, 'freedata-messages.db')
|
||||||
|
db_file = 'sqlite:///' + db_path
|
||||||
|
super().__init__(db_file)
|
||||||
|
|
||||||
|
|
||||||
def add_attachment(self, session, message, attachment_data):
|
def add_attachment(self, session, message, attachment_data):
|
||||||
|
|
|
@ -4,14 +4,16 @@ from sqlalchemy.orm import scoped_session, sessionmaker
|
||||||
from threading import local
|
from threading import local
|
||||||
from message_system_db_model import Base, Beacon, Station, Status, Attachment, P2PMessage
|
from message_system_db_model import Base, Beacon, Station, Status, Attachment, P2PMessage
|
||||||
from datetime import timezone, timedelta, datetime
|
from datetime import timezone, timedelta, datetime
|
||||||
import json
|
import os
|
||||||
import structlog
|
|
||||||
import helpers
|
|
||||||
|
|
||||||
|
|
||||||
class DatabaseManagerBeacon(DatabaseManager):
|
class DatabaseManagerBeacon(DatabaseManager):
|
||||||
def __init__(self, uri):
|
def __init__(self, db_file=None):
|
||||||
super().__init__(uri)
|
if not db_file:
|
||||||
|
script_dir = os.path.dirname(os.path.abspath(__file__))
|
||||||
|
db_path = os.path.join(script_dir, 'freedata-messages.db')
|
||||||
|
db_file = 'sqlite:///' + db_path
|
||||||
|
|
||||||
|
super().__init__(db_file)
|
||||||
|
|
||||||
def add_beacon(self, timestamp, callsign, snr, gridsquare):
|
def add_beacon(self, timestamp, callsign, snr, gridsquare):
|
||||||
session = None
|
session = None
|
||||||
|
|
|
@ -7,12 +7,17 @@ from threading import local
|
||||||
from message_system_db_model import Base, Station, Status
|
from message_system_db_model import Base, Station, Status
|
||||||
import structlog
|
import structlog
|
||||||
import helpers
|
import helpers
|
||||||
|
import os
|
||||||
|
|
||||||
class DatabaseManager:
|
class DatabaseManager:
|
||||||
def __init__(self, event_manger, uri='sqlite:///freedata-messages.db'):
|
def __init__(self, event_manger, db_file=None):
|
||||||
self.event_manager = event_manger
|
self.event_manager = event_manger
|
||||||
|
if not db_file:
|
||||||
|
script_dir = os.path.dirname(os.path.abspath(__file__))
|
||||||
|
db_path = os.path.join(script_dir, 'freedata-messages.db')
|
||||||
|
db_file = 'sqlite:///' + db_path
|
||||||
|
|
||||||
self.engine = create_engine(uri, echo=False)
|
self.engine = create_engine(db_file, echo=False)
|
||||||
self.thread_local = local()
|
self.thread_local = local()
|
||||||
self.session_factory = sessionmaker(bind=self.engine)
|
self.session_factory = sessionmaker(bind=self.engine)
|
||||||
Base.metadata.create_all(self.engine)
|
Base.metadata.create_all(self.engine)
|
||||||
|
|
|
@ -4,12 +4,18 @@ from message_system_db_model import Status, P2PMessage
|
||||||
from sqlalchemy.exc import IntegrityError
|
from sqlalchemy.exc import IntegrityError
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
import json
|
import json
|
||||||
|
import os
|
||||||
|
|
||||||
|
|
||||||
class DatabaseManagerMessages(DatabaseManager):
|
class DatabaseManagerMessages(DatabaseManager):
|
||||||
def __init__(self, uri='sqlite:///freedata-messages.db'):
|
def __init__(self, db_file=None):
|
||||||
super().__init__(uri)
|
if not db_file:
|
||||||
self.attachments_manager = DatabaseManagerAttachments(uri)
|
script_dir = os.path.dirname(os.path.abspath(__file__))
|
||||||
|
db_path = os.path.join(script_dir, 'freedata-messages.db')
|
||||||
|
db_file = 'sqlite:///' + db_path
|
||||||
|
|
||||||
|
super().__init__(db_file)
|
||||||
|
self.attachments_manager = DatabaseManagerAttachments(db_file)
|
||||||
|
|
||||||
def add_message(self, message_data, statistics, direction='receive', status=None, is_read=True):
|
def add_message(self, message_data, statistics, direction='receive', status=None, is_read=True):
|
||||||
session = self.get_thread_scoped_session()
|
session = self.get_thread_scoped_session()
|
||||||
|
|
|
@ -74,14 +74,12 @@ class RF:
|
||||||
# Make sure our resampler will work
|
# Make sure our resampler will work
|
||||||
assert (self.AUDIO_SAMPLE_RATE / self.MODEM_SAMPLE_RATE) == codec2.api.FDMDV_OS_48 # type: ignore
|
assert (self.AUDIO_SAMPLE_RATE / self.MODEM_SAMPLE_RATE) == codec2.api.FDMDV_OS_48 # type: ignore
|
||||||
|
|
||||||
self.modem_received_queue = queue.Queue()
|
|
||||||
self.audio_received_queue = queue.Queue()
|
self.audio_received_queue = queue.Queue()
|
||||||
self.data_queue_received = queue.Queue()
|
self.data_queue_received = queue.Queue()
|
||||||
self.fft_queue = fft_queue
|
self.fft_queue = fft_queue
|
||||||
|
|
||||||
self.demodulator = demodulator.Demodulator(self.config,
|
self.demodulator = demodulator.Demodulator(self.config,
|
||||||
self.audio_received_queue,
|
self.audio_received_queue,
|
||||||
self.modem_received_queue,
|
|
||||||
self.data_queue_received,
|
self.data_queue_received,
|
||||||
self.states,
|
self.states,
|
||||||
self.event_manager,
|
self.event_manager,
|
||||||
|
@ -109,7 +107,6 @@ class RF:
|
||||||
|
|
||||||
# Initialize codec2, rig control, and data threads
|
# Initialize codec2, rig control, and data threads
|
||||||
self.init_codec2()
|
self.init_codec2()
|
||||||
self.init_data_threads()
|
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
@ -405,11 +402,6 @@ class RF:
|
||||||
self.freedv_datac4_tx = codec2.open_instance(codec2.FREEDV_MODE.datac4.value)
|
self.freedv_datac4_tx = codec2.open_instance(codec2.FREEDV_MODE.datac4.value)
|
||||||
self.freedv_datac13_tx = codec2.open_instance(codec2.FREEDV_MODE.datac13.value)
|
self.freedv_datac13_tx = codec2.open_instance(codec2.FREEDV_MODE.datac13.value)
|
||||||
|
|
||||||
def init_data_threads(self):
|
|
||||||
worker_received = threading.Thread(
|
|
||||||
target=self.demodulator.worker_received, name="WORKER_THREAD", daemon=True
|
|
||||||
)
|
|
||||||
worker_received.start()
|
|
||||||
|
|
||||||
# Low level modem audio transmit
|
# Low level modem audio transmit
|
||||||
def transmit_audio(self, audio_48k) -> None:
|
def transmit_audio(self, audio_48k) -> None:
|
||||||
|
|
|
@ -16,13 +16,12 @@ class ScheduleManager:
|
||||||
self.state_manager = state_manger
|
self.state_manager = state_manger
|
||||||
self.event_manager = event_manager
|
self.event_manager = event_manager
|
||||||
self.config = self.config_manager.read()
|
self.config = self.config_manager.read()
|
||||||
self.beacon_interval = self.config['MODEM']['beacon_interval']
|
|
||||||
|
|
||||||
self.scheduler = sched.scheduler(time.time, time.sleep)
|
self.scheduler = sched.scheduler(time.time, time.sleep)
|
||||||
self.events = {
|
self.events = {
|
||||||
'check_for_queued_messages': {'function': self.check_for_queued_messages, 'interval': 10},
|
'check_for_queued_messages': {'function': self.check_for_queued_messages, 'interval': 10},
|
||||||
'explorer_publishing': {'function': self.push_to_explorer, 'interval': 60},
|
'explorer_publishing': {'function': self.push_to_explorer, 'interval': 60},
|
||||||
'transmitting_beacon': {'function': self.transmit_beacon, 'interval': self.beacon_interval},
|
'transmitting_beacon': {'function': self.transmit_beacon, 'interval': 600},
|
||||||
'beacon_cleanup': {'function': self.delete_beacons, 'interval': 600},
|
'beacon_cleanup': {'function': self.delete_beacons, 'interval': 600},
|
||||||
}
|
}
|
||||||
self.running = False # Flag to control the running state
|
self.running = False # Flag to control the running state
|
||||||
|
|
|
@ -13,6 +13,7 @@ import json
|
||||||
import websocket_manager as wsm
|
import websocket_manager as wsm
|
||||||
import api_validations as validations
|
import api_validations as validations
|
||||||
import command_cq
|
import command_cq
|
||||||
|
import command_beacon
|
||||||
import command_ping
|
import command_ping
|
||||||
import command_feq
|
import command_feq
|
||||||
import command_test
|
import command_test
|
||||||
|
@ -26,17 +27,17 @@ from message_system_db_beacon import DatabaseManagerBeacon
|
||||||
from schedule_manager import ScheduleManager
|
from schedule_manager import ScheduleManager
|
||||||
|
|
||||||
app = Flask(__name__)
|
app = Flask(__name__)
|
||||||
CORS(app)
|
|
||||||
CORS(app, resources={r"/*": {"origins": "*"}})
|
CORS(app, resources={r"/*": {"origins": "*"}})
|
||||||
sock = Sock(app)
|
sock = Sock(app)
|
||||||
MODEM_VERSION = "0.14.0-alpha"
|
MODEM_VERSION = "0.14.2-alpha"
|
||||||
|
|
||||||
# set config file to use
|
# set config file to use
|
||||||
def set_config():
|
def set_config():
|
||||||
if 'FREEDATA_CONFIG' in os.environ:
|
if 'FREEDATA_CONFIG' in os.environ:
|
||||||
config_file = os.environ['FREEDATA_CONFIG']
|
config_file = os.environ['FREEDATA_CONFIG']
|
||||||
else:
|
else:
|
||||||
config_file = 'config.ini'
|
script_dir = os.path.dirname(os.path.abspath(__file__))
|
||||||
|
config_file = os.path.join(script_dir, 'config.ini')
|
||||||
|
|
||||||
if os.path.exists(config_file):
|
if os.path.exists(config_file):
|
||||||
print(f"Using config from {config_file}")
|
print(f"Using config from {config_file}")
|
||||||
|
@ -149,6 +150,8 @@ def post_beacon():
|
||||||
|
|
||||||
if not app.state_manager.is_beacon_running:
|
if not app.state_manager.is_beacon_running:
|
||||||
app.state_manager.set('is_beacon_running', request.json['enabled'])
|
app.state_manager.set('is_beacon_running', request.json['enabled'])
|
||||||
|
if not app.state_manager.getARQ():
|
||||||
|
enqueue_tx_command(command_beacon.BeaconCommand, request.json)
|
||||||
else:
|
else:
|
||||||
app.state_manager.set('is_beacon_running', request.json['enabled'])
|
app.state_manager.set('is_beacon_running', request.json['enabled'])
|
||||||
|
|
||||||
|
@ -344,4 +347,13 @@ if __name__ == "__main__":
|
||||||
# initialize database default values
|
# initialize database default values
|
||||||
DatabaseManager(app.event_manager).initialize_default_values()
|
DatabaseManager(app.event_manager).initialize_default_values()
|
||||||
wsm.startThreads(app)
|
wsm.startThreads(app)
|
||||||
app.run()
|
|
||||||
|
conf = app.config_manager.read()
|
||||||
|
modemaddress = conf['NETWORK']['modemaddress']
|
||||||
|
modemport = conf['NETWORK']['modemport']
|
||||||
|
|
||||||
|
if not modemaddress:
|
||||||
|
modemaddress = '0.0.0.0'
|
||||||
|
if not modemport:
|
||||||
|
modemport = 5000
|
||||||
|
app.run(modemaddress, modemport)
|
||||||
|
|
Loading…
Reference in a new issue