From 4b405841a646939ed303e0833cc70e3ed6d6fdb2 Mon Sep 17 00:00:00 2001 From: DJ2LS <75909252+DJ2LS@users.noreply.github.com> Date: Sat, 17 Jul 2021 09:03:40 +0200 Subject: [PATCH] Add files via upload --- gui/daemon.js | 201 +++++++++++++++++++++++++++++++++ gui/hamlib-list.html | 261 +++++++++++++++++++++++++++++++++++++++++++ gui/package.json | 30 +++++ gui/preload-data.js | 37 ++++++ gui/preload-main.js | 218 ++++++++++++++++++++++++++++++++++++ gui/sock.js | 193 ++++++++++++++++++++++++++++++++ 6 files changed, 940 insertions(+) create mode 100644 gui/daemon.js create mode 100644 gui/hamlib-list.html create mode 100644 gui/package.json create mode 100644 gui/preload-data.js create mode 100644 gui/preload-main.js create mode 100644 gui/sock.js diff --git a/gui/daemon.js b/gui/daemon.js new file mode 100644 index 00000000..2205326d --- /dev/null +++ b/gui/daemon.js @@ -0,0 +1,201 @@ +var net = require('net'); + +var daemon = new net.Socket(); +var msg = ''; // Current message, per connection. + + + +const { ipcRenderer } = require('electron'); + + + + + +setTimeout(connectDAEMON, 500) + +function connectDAEMON(){ + + console.log('connecting to DAEMON...') + + //clear message buffer after reconnecting or inital connection + msg = ''; + + //daemon_host = document.getElementById("daemon_adress").value + //daemon_port = document.getElementById("daemon_port").value + daemon_host = '192.168.178.163' + daemon_port = 3001 + + daemon.connect(daemon_port, daemon_host) + //client.setTimeout(5000); +} + +daemon.on('connect', function(data) { + console.log('DAEMON connection established') +}) + +daemon.on('error', function(data) { + console.log('DAEMON connection error'); + setTimeout(connectDAEMON, 2000) +}); + +/* +client.on('close', function(data) { + console.log(' TNC connection closed'); + setTimeout(connectTNC, 2000) +}); +*/ + +daemon.on('end', function(data) { + console.log('DAEMON connection ended'); + setTimeout(connectDAEMON, 2000) +}); + + +//exports.writeCommand = function(command){ +writeDaemonCommand = function(command){ + + + // we use the writingCommand function to update our TCPIP state because we are calling this function a lot + // if socket openend, we are able to run commands + if(daemon.readyState == 'open'){ + //uiMain.setDAEMONconnection('open') + + daemon.write(command + '\n'); + } + + if(daemon.readyState == 'closed'){ + //uiMain.setDAEMONconnection('closed') + } + + if(daemon.readyState == 'opening'){ + //uiMain.setDAEMONconnection('opening') + } + + + let Data = { + daemon_connection: daemon.readyState, + }; + ipcRenderer.send('request-update-daemon-connection', Data); +} + + + + // "https://stackoverflow.com/questions/9070700/nodejs-net-createserver-large-amount-of-data-coming-in" + + +daemon.on('data', function(data) { + + data = data.toString('utf8'); /* convert data to string */ + msg += data.toString('utf8'); /*append data to buffer so we can stick long data together */ + + + + /* check if we reached an EOF, if true, clear buffer and parse JSON data */ + if (data.endsWith('}')) { + /*console.log(msg)*/ + try { + /*console.log(msg)*/ + data = JSON.parse(msg) + } catch (e) { + console.log(e); /* "SyntaxError */ + } + msg = ''; + /*console.log("EOF detected!")*/ + + if(data['COMMAND'] == 'DAEMON_STATE'){ + + + + let Data = { + input_devices: data['INPUT_DEVICES'], + output_devices: data['OUTPUT_DEVICES'], + tnc_running_state: data['DAEMON_STATE'][0]['STATUS'] + + }; + ipcRenderer.send('request-update-daemon-state', Data); + + + //input_devices = data['INPUT_DEVICES'] + //uiMain.updateAudioInput(input_devices) + + //output_devices = data['OUTPUT_DEVICES'] + //uiMain.updateAudioOutput(output_devices) + + //daemon_state = data['DAEMON_STATE'][0]['STATUS'] + //uiMain.updateTncRunningState(daemon_state) + } + + + + + +////// check if EOF ... + } + + + +}); + +function hexToBytes(hex) { + for (var bytes = [], c = 0; c < hex.length; c += 2) + bytes.push(parseInt(hex.substr(c, 2), 16)); + return bytes; +} + + + + + + + exports.getDaemonState = function(){ + command = '{"type" : "GET", "command": "DAEMON_STATE"}' + writeDaemonCommand(command) +} + + + + + + + + + + + + +// START TNC +// ` `== multi line string + + +exports.startTNC = function(rx_audio, tx_audio, deviceid, deviceport, ptt){ + var json_command = JSON.stringify({ + type: 'SET', + command: 'STARTTNC', + parameter: [{ + rx_audio: rx_audio, + tx_audio: tx_audio, + deviceid: deviceid, + deviceport: deviceport, + ptt: ptt + }] + }) + + + + //console.log(json_command) + writeDaemonCommand(json_command) + + +} + +// STOP TNC +exports.stopTNC = function(){ + command = '{"type" : "SET", "command": "STOPTNC" , "parameter": "---" }' + writeDaemonCommand(command) +} + + + + + + diff --git a/gui/hamlib-list.html b/gui/hamlib-list.html new file mode 100644 index 00000000..ff293ccc --- /dev/null +++ b/gui/hamlib-list.html @@ -0,0 +1,261 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/gui/package.json b/gui/package.json new file mode 100644 index 00000000..aed1bbb9 --- /dev/null +++ b/gui/package.json @@ -0,0 +1,30 @@ +{ + "name": "freedv-jate-gui", + "version": "0.0.1", + "description": "GUI for FreeDV JATE ", + "main": "main.js", + "scripts": { + "start": "electron .", + "test": "echo \"Error: no test specified\" && exit 1" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/DJ2LS/FreeDV-JATE-GUI.git" + }, + "keywords": [ + "TNC", + "GUI", + "FreeDV", + "codec2" + ], + "author": "DJ2LS", + "license": "LGPL-2.1", + "bugs": { + "url": "https://github.com/DJ2LS/FreeDV-JATE-GUI/issues" + }, + "homepage": "https://github.com/DJ2LS/FreeDV-JATE-GUI#readme", + "dependencies": { + "@electron/remote": "^1.2.0", + "bootstrap": "^5.0.1" + } +} diff --git a/gui/preload-data.js b/gui/preload-data.js new file mode 100644 index 00000000..1e0df1c3 --- /dev/null +++ b/gui/preload-data.js @@ -0,0 +1,37 @@ + +const { ipcRenderer } = require('electron'); + + + +window.addEventListener('DOMContentLoaded', () => { + const replaceText = (selector, text) => { + const element = document.getElementById(selector) + if (element) element.innerText = text + } + + + + document.getElementById("startTransmission").addEventListener("click", () => { + alert("HALLO ") + }) + + + + +}) + + + + + ipcRenderer.on('action-update-tnc-state', (event, arg) => { + + + + if(arg.ptt_state == 'True'){ + document.getElementById("ptt_state").className = "btn btn-danger"; + } else if(arg.ptt_state == 'False'){ + document.getElementById("ptt_state").className = "btn btn-success"; + } else { + document.getElementById("ptt_state").className = "btn btn-secondary" + } + }); \ No newline at end of file diff --git a/gui/preload-main.js b/gui/preload-main.js new file mode 100644 index 00000000..e0bf55e5 --- /dev/null +++ b/gui/preload-main.js @@ -0,0 +1,218 @@ +const sock = require('./sock.js') +const daemon = require('./daemon.js') +const { ipcRenderer } = require('electron'); + + + + + +window.addEventListener('DOMContentLoaded', () => { + +setInterval(sock.getTncState, 1000) +setInterval(daemon.getDaemonState, 250) +//setInterval(uiMain.updateFFT, 250) + + + +// Create spectrum object on canvas with ID "waterfall" + global.spectrum = new Spectrum( + "waterfall", { + spectrumPercent: 20 + }); + + + + + // saveMyCall button clicked + document.getElementById("saveMyCall").addEventListener("click", () => { + callsign = document.getElementById("myCall").value + sock.saveMyCall(callsign) + //uiMain.getTncState() + }) + + // saveMyGrid button clicked + document.getElementById("saveMyGrid").addEventListener("click", () => { + grid = document.getElementById("myGrid").value + sock.saveMyGrid(grid) + //uiMain.getTncState() + }) + + + // startTNC button clicked + document.getElementById("startTNC").addEventListener("click", () => { + var rx_audio = document.getElementById("audio_input_selectbox").value + var tx_audio = document.getElementById("audio_output_selectbox").value + var deviceid = document.getElementById("hamlib_deviceid").value + var deviceport = document.getElementById("hamlib_deviceport").value + var ptt = document.getElementById("hamlib_ptt").value + + daemon.startTNC(rx_audio, tx_audio, deviceid, deviceport, ptt) + }) + + // stopTNC button clicked + document.getElementById("stopTNC").addEventListener("click", () => { + daemon.stopTNC() + }) + + // openDataModule button clicked + document.getElementById("openDataModule").addEventListener("click", () => { + //data.show() + let Data = { + message: "Hello World !" + }; + ipcRenderer.send('show-data-window', Data); + }) + + + + + + + +}) + + + + + + +ipcRenderer.on('action-update-tnc-state', (event, arg) => { + +// PTT STATE + if(arg.ptt_state == 'True'){ + document.getElementById("ptt_state").className = "btn btn-danger"; + } else if(arg.ptt_state == 'False'){ + document.getElementById("ptt_state").className = "btn btn-success"; + } else { + document.getElementById("ptt_state").className = "btn btn-secondary" + } + +// BUSY STATE + if(arg.busy_state == 'BUSY'){ + document.getElementById("busy_state").className = "btn btn-danger"; + } else if(arg.busy_state == 'IDLE'){ + document.getElementById("busy_state").className = "btn btn-success"; + } else { + document.getElementById("busy_state").className = "btn btn-secondary" + } + +// ARQ STATE + if(arg.arq_state == 'DATA'){ + document.getElementById("arq_state").className = "btn btn-warning"; + } else if(arg.arq_state == 'IDLE'){ + document.getElementById("arq_state").className = "btn btn-secondary"; + } else { + document.getElementById("arq_state").className = "btn btn-secondary" + } + +// RMS + document.getElementById("rms_level").setAttribute("aria-valuenow", arg.rms_level) + document.getElementById("rms_level").setAttribute("style", "width:" + arg.rms_level + "%;") + +// CHANNEL STATE + if(arg.channel_state == 'RECEIVING_SIGNALLING'){ + document.getElementById("signalling_state").className = "btn btn-success"; + document.getElementById("data_state").className = "btn btn-secondary"; + + } else if(arg.channel_state == 'SENDING_SIGNALLING'){ + document.getElementById("signalling_state").className = "btn btn-danger"; + document.getElementById("data_state").className = "btn btn-secondary"; + + } else if(arg.channel_state == 'RECEIVING_DATA'){ + document.getElementById("signalling_state").className = "btn btn-secondary"; + document.getElementById("data_state").className = "btn btn-success"; + + } else if(arg.channel_state == 'SENDING_DATA'){ + document.getElementById("signalling_state").className = "btn btn-secondary"; + document.getElementById("data_state").className = "btn btn-danger"; + } else { + document.getElementById("signalling_state").className = "btn btn-secondary" + document.getElementById("busy_state").className = "btn btn-secondary" + + } + +// SET FREQUENCY + document.getElementById("frequency").value = arg.frequency + +// SET MODE + document.getElementById("mode").value = arg.mode + +// SET BANDWITH + document.getElementById("bandwith").value = arg.bandwith + }); + + + + +ipcRenderer.on('action-update-daemon-state', (event, arg) => { +// UPDATE AUDIO INPUT + + if(document.getElementById("audio_input_selectbox").length != arg.input_devices.length){ + document.getElementById("audio_input_selectbox").innerHTML = "" + for (i = 0; i < arg.input_devices.length; i++) { + var option = document.createElement("option"); + option.text = arg.input_devices[i]['NAME']; + option.value = arg.input_devices[i]['ID']; + + document.getElementById("audio_input_selectbox").add(option); + } + } + // UPDATE AUDIO OUTPUT + + if(document.getElementById("audio_output_selectbox").length != arg.output_devices.length){ + document.getElementById("audio_output_selectbox").innerHTML = "" + for (i = 0; i < arg.output_devices.length; i++) { + var option = document.createElement("option"); + option.text = arg.output_devices[i]['NAME']; + option.value = arg.output_devices[i]['ID']; + document.getElementById("audio_output_selectbox").add(option); + } + } + +// TNC RUNNING STATE +document.getElementById("tnc_running_state").innerHTML = arg.tnc_running_state; + if(arg.tnc_running_state == "running"){ + document.getElementById('hamlib_deviceid').disabled = true + document.getElementById('hamlib_deviceport').disabled = true + document.getElementById('hamlib_ptt').disabled = true + document.getElementById('audio_input_selectbox').disabled = true + document.getElementById('audio_output_selectbox').disabled = true + document.getElementById('stopTNC').disabled = false + document.getElementById('startTNC').disabled = true + document.getElementById('myCall').disabled = false + document.getElementById('saveMyCall').disabled = false + document.getElementById('myGrid').disabled = false + document.getElementById('saveMyGrid').disabled = false + + } else { + document.getElementById('hamlib_deviceid').disabled = false + document.getElementById('hamlib_deviceport').disabled = false + document.getElementById('hamlib_ptt').disabled = false + document.getElementById('audio_input_selectbox').disabled = false + document.getElementById('audio_output_selectbox').disabled = false + document.getElementById('stopTNC').disabled = true + document.getElementById('startTNC').disabled = false + document.getElementById('myCall').disabled = true + document.getElementById('saveMyCall').disabled = true + document.getElementById('myGrid').disabled = true + document.getElementById('saveMyGrid').disabled = true + } + + + }); + + +ipcRenderer.on('action-update-daemon-connection', (event, arg) => { + + + if(arg.daemon_connection == 'open'){ + document.getElementById("daemon_connection_state").className = "btn btn-success"; + } + if(arg.daemon_connection == 'opening'){ + document.getElementById("daemon_connection_state").className = "btn btn-warning"; + } + if(arg.daemon_connection == 'closed'){ + document.getElementById("daemon_connection_state").className = "btn btn-danger"; + } + + }); \ No newline at end of file diff --git a/gui/sock.js b/gui/sock.js new file mode 100644 index 00000000..13d38afa --- /dev/null +++ b/gui/sock.js @@ -0,0 +1,193 @@ +var net = require('net'); + +const { ipcRenderer } = require('electron'); + +var client = new net.Socket(); +var msg = ''; // Current message, per connection. + + + +setTimeout(connectTNC, 500) + +function connectTNC(){ + + console.log('connecting to TNC...') + + //clear message buffer after reconnecting or inital connection + msg = ''; + + tnc_host = document.getElementById("tnc_adress").value + tnc_port = document.getElementById("tnc_port").value + client.connect(tnc_port, tnc_host) + //client.setTimeout(5000); +} + +client.on('connect', function(data) { + console.log('TNC connection established') +}) + +client.on('error', function(data) { + console.log('TNC connection error'); + + + let Data = { + busy_state: "-", + arq_state: "-", + channel_state: "-", + frequency : "-", + mode: "-", + bandwith: "-", + rms_level : 0 + + }; + ipcRenderer.send('request-update-tnc-state', Data); + + setTimeout(connectTNC, 2000) +}); + +/* +client.on('close', function(data) { + console.log(' TNC connection closed'); + setTimeout(connectTNC, 2000) +}); +*/ + +client.on('end', function(data) { + console.log('TNC connection ended'); + setTimeout(connectTNC, 2000) +}); + + +//exports.writeTncCommand = function(command){ + writeTncCommand = function(command){ + + console.log(command) + // we use the writingCommand function to update our TCPIP state because we are calling this function a lot + // if socket openend, we are able to run commands + if(client.readyState == 'open'){ + //uiMain.setTNCconnection('open') + client.write(command + '\n'); + } + + if(client.readyState == 'closed'){ + //uiMain.setTNCconnection('closed') + } + + if(client.readyState == 'opening'){ + //uiMain.setTNCconnection('opening') + } +} + + + +client.on('data', function(data) { + +/* +stackoverflow.com questions 9070700 nodejs-net-createserver-large-amount-of-data-coming-in +*/ + + data = data.toString('utf8'); // convert data to string + msg += data.toString('utf8'); // append data to buffer so we can stick long data together + +/* + if (msg.charCodeAt(msg.length - 1) == 0) { + client.emit('message', msg.substring(0, msg.length - 1)); + msg = ''; + console.log("END OF FILE") + } + */ + + /* + if (msg.startsWith('{"COMMAND":')) { + msg = ''; + msg += data.toString('utf8'); + console.log("BOF detected!") + } + */ + + // check if we reached an EOF, if true, clear buffer and parse JSON data + if (data.endsWith('}')) { + //console.log(msg) + try { + //console.log(msg) + data = JSON.parse(msg) + } catch (e) { + console.log(e); /* "SyntaxError*/ + } + msg = ''; + /* console.log("EOF detected!") */ + + + + + + if(data['COMMAND'] == 'TNC_STATE'){ + + + + + // FFT + //fft_raw = data['FFT'] + //var fft = Buffer.from(fft_raw, "hex") + //var fft = hexToBytes(fft_raw) + //var fft = Array.from(fft_raw) + //console.log(typeof(fft)) + + + + let Data = { + ptt_state: data['PTT_STATE'], + busy_state: data['TNC_STATE'], + arq_state: data['ARQ_STATE'], + channel_state: data['CHANNEL_STATE'], + frequency : data['FREQUENCY'], + mode: data['MODE'], + bandwith: data['BANDWITH'], + rms_level : (data['AUDIO_RMS']/1000)*100 + + }; + ipcRenderer.send('request-update-tnc-state', Data); + + + var fft = Array.from({length: 2048}, () => Math.floor(Math.random() * 10)); + //console.log(fft) + //uiMain.updateFFT(fft) + + + } + + // check if EOF ... + } + + + +}); + +function hexToBytes(hex) { + for (var bytes = [], c = 0; c < hex.length; c += 2) + bytes.push(parseInt(hex.substr(c, 2), 16)); + return bytes; +} + + + + + + + + +//Save callsign + exports.saveMyCall = function(callsign){ + command = '{"type" : "SET", "command": "MYCALLSIGN" , "parameter": "'+ callsign +'" }' + writeTncCommand(command) +} + + exports.saveMyGrid = function(grid){ + command = '{"type" : "SET", "command": "MYGRID" , "parameter": "'+ grid +'" }' + writeTncCommand(command) +} + + exports.getTncState = function(){ + command = '{"type" : "GET", "command": "TNC_STATE"}'; + writeTncCommand(command) +}