From ee8483356ac523c4fb492e843ee5193c931827cb Mon Sep 17 00:00:00 2001 From: DJ2LS <75909252+DJ2LS@users.noreply.github.com> Date: Fri, 23 Jul 2021 15:39:57 +0200 Subject: [PATCH] updated command set --- tnc/main.js | 197 +++++++++++++++++++++++++ tnc/preload-data.js | 95 ++++++++++++ tnc/preload-main.js | 346 ++++++++++++++++++++++++++++++++++++++++++++ tnc/sock.js | 256 ++++++++++++++++++++++++++++++++ 4 files changed, 894 insertions(+) create mode 100644 tnc/main.js create mode 100644 tnc/preload-data.js create mode 100644 tnc/preload-main.js create mode 100644 tnc/sock.js diff --git a/tnc/main.js b/tnc/main.js new file mode 100644 index 00000000..c548c11b --- /dev/null +++ b/tnc/main.js @@ -0,0 +1,197 @@ +const { app, BrowserWindow, ipcMain } = require('electron') +const path = require('path') + +let win = null; +let data = null; + +function createWindow () { + + + win = new BrowserWindow({ + width: 1220, + height: 900, + webPreferences: { + //preload: path.join(__dirname, 'preload-main.js'), + preload: require.resolve('./preload-main.js'), + nodeIntegration: true, + contextIsolation: false, + enableRemoteModule: false, //https://stackoverflow.com/questions/53390798/opening-new-window-electron/53393655 https://github.com/electron/remote + } + }) + //open dev tools + win.webContents.openDevTools({ + mode : 'undocked', + activate: true, +}) + win.loadFile('src/index.html') + + + + data = new BrowserWindow({ + height: 900, + width: 600, + parent: win, + webPreferences: { + preload: require.resolve('./preload-data.js'), + nodeIntegration: true, + + } + }) + //open dev tools + data.webContents.openDevTools({ + mode : 'undocked', + activate: true, +}) + data.loadFile('src/data-module.html') + data.hide() + + + + + + + + // Emitted when the window is closed. + win.on('closed', function () { + // Dereference the window object, usually you would store windows + // in an array if your app supports multi windows, this is the time + // when you should delete the corresponding element. + win = null; + data = null; + }) + +/* + data.on('closed', function () { + // Dereference the window object, usually you would store windows + // in an array if your app supports multi windows, this is the time + // when you should delete the corresponding element. + }) +*/ + + +// https://stackoverflow.com/questions/44258831/only-hide-the-window-when-closing-it-electron +data.on('close', function (evt) { + evt.preventDefault(); + data.hide() +}); + + + + + + +} + + + + + + + + + + +app.whenReady().then(() => { + createWindow() + + app.on('activate', () => { + if (BrowserWindow.getAllWindows().length === 0) { + createWindow() + } + }) +}) + +app.on('window-all-closed', () => { + + if (process.platform !== 'darwin') { + app.quit() + } +}) + + + + + + + + + + + + +// IPC HANDLER +ipcMain.on('show-data-window', (event, arg) => { + data.show() +}); + + +ipcMain.on('request-update-tnc-state', (event, arg) => { + win.webContents.send('action-update-tnc-state', arg); + data.webContents.send('action-update-tnc-state', arg); +}); + +ipcMain.on('request-update-data-state', (event, arg) => { + //win.webContents.send('action-update-data-state', arg); + data.webContents.send('action-update-data-state', arg); +}); + +ipcMain.on('request-update-heard-stations', (event, arg) => { + //win.webContents.send('action-update-heard-stations', arg); +}); + +ipcMain.on('request-update-daemon-state', (event, arg) => { + win.webContents.send('action-update-daemon-state', arg); +}); + +ipcMain.on('request-update-daemon-connection', (event, arg) => { + win.webContents.send('action-update-daemon-connection', arg); +}); + +ipcMain.on('run-tnc-command', (event, arg) => { + win.webContents.send('run-tnc-command', arg); + +/* + if (arg.command == 'saveMyCall'){ + sock.saveMyCall(arg.callsign) + } + if (arg.command == 'saveMyGrid'){ + sock.saveMyGrid(arg.grid) + } + if (arg.command == 'ping'){ + sock.sendPing(arg.dxcallsign) + } +*/ +}); + + +/* +ipcMain.on('run-daemon-command', (event, arg) => { + win.webContents.send('run-daemon-command', arg); +*/ +/* + if (arg.command == 'startTNC'){ + daemon.startTNC(arg.rx_audio, arg.tx_audio, arg.deviceid, arg.deviceport, arg.ptt) + + } + if (arg.command == 'stopTNC'){ + daemon.stopTNC() + + } +}); + +*/ + + +//setInterval(sock.getTncState, 500) +//setInterval(daemon.getDaemonState, 500) +/* +setInterval(function(){ + sock.getTncState(); + }, 1000); + */ +/* +setInterval(function(){ + daemon.getDaemonState(); + }, 1000); + */ + \ No newline at end of file diff --git a/tnc/preload-data.js b/tnc/preload-data.js new file mode 100644 index 00000000..6ec8faf3 --- /dev/null +++ b/tnc/preload-data.js @@ -0,0 +1,95 @@ + +const { ipcRenderer } = require('electron'); + //const sock = require('./sock.js') +//const globals = require('./globals.js') + + + +window.addEventListener('DOMContentLoaded', () => { + const replaceText = (selector, text) => { + const element = document.getElementById(selector) + if (element) element.innerText = text + } + + // sendPing button clicked + document.getElementById("sendPing").addEventListener("click", () => { + dxcallsign = document.getElementById("dxCall").value + //sock.sendPing(dxcallsign) + + let Data = { + command: "ping", + dxcallsign: document.getElementById("dxCall").value + }; + ipcRenderer.send('run-tnc-command', Data); + }) + + + document.getElementById("startTransmission").addEventListener("click", () => { + alert("HALLO ") + }) + + + + +}) + + + + +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" + } + +// 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" + + } + + }); + + + ipcRenderer.on('action-update-data-state', (event, arg) => { + + }); \ No newline at end of file diff --git a/tnc/preload-main.js b/tnc/preload-main.js new file mode 100644 index 00000000..cb190447 --- /dev/null +++ b/tnc/preload-main.js @@ -0,0 +1,346 @@ +const sock = require('./sock.js') +const daemon = require('./daemon.js') + +setInterval(daemon.getDaemonState, 1000) +setInterval(sock.getTncState, 250) +setInterval(sock.getDataState, 500) +setInterval(sock.getHeardStations, 500) + + + +// UPDATE FFT DEMO + + updateFFT = function(fft){ + var fft = Array.from({length: 2048}, () => Math.floor(Math.random() * 10)); + spectrum.addData(fft); +} +setInterval(updateFFT, 250) + + + + + + +const { ipcRenderer } = require('electron'); + + + + + +window.addEventListener('DOMContentLoaded', () => { + +/* +globals.tnc_host = document.getElementById("tnc_adress").value +globals.tnc_port = document.getElementById("tnc_port").value +console.log(globals.tnc_host) +console.log(globals.tnc_port) +setInterval(sock.connectTNC, 500) +*/ +//setInterval( function() { sock.connectTNC(tnc_host, tnc_port); }, 500 ); + + +//setInterval(sock.getTncState, 500) +//setInterval(daemon.getDaemonState, 500) +//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) + /* + let Data = { + command: "saveMyCall", + callsign: document.getElementById("myCall").value + }; + ipcRenderer.send('run-tnc-command', Data); + + */ + + + }) + + // saveMyGrid button clicked + document.getElementById("saveMyGrid").addEventListener("click", () => { + grid = document.getElementById("myGrid").value + sock.saveMyGrid(grid) + /* + let Data = { + command: "saveMyGrid", + grid: document.getElementById("myGrid").value + }; + ipcRenderer.send('run-tnc-command', Data); +*/ + }) + + // startPing button clicked + document.getElementById("sendPing").addEventListener("click", () => { + dxcallsign = document.getElementById("dxCall").value + sock.sendPing(dxcallsign) + /* + let Data = { + command: "saveMyCall", + callsign: document.getElementById("myCall").value + }; + ipcRenderer.send('run-tnc-command', Data); + + */ + + + }) + + // sendCQ button clicked + document.getElementById("sendCQ").addEventListener("click", () => { + sock.sendCQ() + + + + }) + + + + + + + // 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) + /* + let Data = { + command: "startTNC", + rx_audio : document.getElementById("audio_input_selectbox").value, + tx_audio : document.getElementById("audio_output_selectbox").value, + deviceid : document.getElementById("hamlib_deviceid").value, + deviceport : document.getElementById("hamlib_deviceport").value, + ptt : document.getElementById("hamlib_ptt").value, + }; + ipcRenderer.send('run-daemon-command', Data); + */ + + + }) + + // stopTNC button clicked + document.getElementById("stopTNC").addEventListener("click", () => { + daemon.stopTNC() + /* let Data = { + command: "stopTNC", + }; + ipcRenderer.send('run-daemon-command', Data); + */ + }) + + // 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"; + console.log("PTT TRUE!!!") + + } 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"; + } + + }); + + + + + + ipcRenderer.on('run-tnc-command', (event, arg) => { + if (arg.command == 'saveMyCall'){ + sock.saveMyCall(arg.callsign) + } + if (arg.command == 'saveMyGrid'){ + sock.saveMyGrid(arg.grid) + } + if (arg.command == 'ping'){ + sock.sendPing(arg.dxcallsign) + } + }); + + +/* + ipcRenderer.on('run-daemon-command', (event, arg) => { + if (arg.command == 'startTNC'){ + daemon.startTNC(arg.rx_audio, arg.tx_audio, arg.deviceid, arg.deviceport, arg.ptt) + + } + if (arg.command == 'stopTNC'){ + daemon.stopTNC() + + } + }); + +*/ + + + \ No newline at end of file diff --git a/tnc/sock.js b/tnc/sock.js new file mode 100644 index 00000000..a63073e2 --- /dev/null +++ b/tnc/sock.js @@ -0,0 +1,256 @@ +var net = require('net'); +//const globals = require('./globals.js') + +const { ipcRenderer } = require('electron'); + +//var client = new net.Socket(); +var client = new net.Socket(); +var msg = ''; // Current message, per connection. + + setTimeout(connectTNC, 3000) + + + + +function connectTNC(){ +//exports.connectTNC = function(){ + + 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 + + tnc_host = '192.168.178.163' + tnc_port = 3000 + 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) + // setTimeout( function() { exports.connectTNC(tnc_host, tnc_port); }, 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) + setTimeout(connectTNC, 0) + + // setTimeout( function() { exports.connectTNC(tnc_host, tnc_port); }, 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') + console.log("CLOSED!!!!!") + } + + if(client.readyState == 'opening'){ + //uiMain.setTNCconnection('opening') + console.log("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'){ + 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 + }; + console.log(Data) + ipcRenderer.send('request-update-tnc-state', Data); + } + + if(data['COMMAND'] == 'DATA_STATE'){ + let Data = { + rx_buffer_length: data['RX_BUFFER_LENGTH'], + tx_n_max_retries: data['TX_N_MAX_RETRIES'], + arq_tx_n_frames_per_burst: data['ARQ_TX_N_FRAMES_PER_BURST'], + arq_tx_n_bursts: data['ARQ_TX_N_BURSTS'], + arq_tx_n_current_arq_frame: data['ARQ_TX_N_CURRENT_ARQ_FRAME'], + arq_tx_n_total_arq_frames : data['ARQ_TX_N_TOTAL_ARQ_FRAMES'], + arq_rx_frame_n_bursts: data['ARQ_RX_FRAME_N_BURSTS'], + arq_rx_n_current_arq_frame: data['ARQ_RX_N_CURRENT_ARQ_FRAME'], + arq_n_arq_frames_per_data_frame: data['ARQ_N_ARQ_FRAMES_PER_DATA_FRAME'], + }; + console.log(Data) + ipcRenderer.send('request-update-data-state', Data); + } + + if(data['COMMAND'] == 'HEARD_STATIONS'){ + let Data = { + dxcallsign: data['DXCALLSIGN'], + dxgrid: data['DXGRID'], + timestamp: data['TIMESTAMP'], + datatype: data['DATATYPE'], + }; + console.log(Data) + ipcRenderer.send('request-update-heard-stations', Data); + } + + + + // 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 myCall + exports.saveMyCall = function(callsign){ + command = '{"type" : "SET", "command": "MYCALLSIGN" , "parameter": "'+ callsign +'" }' + writeTncCommand(command) +} + +// Save myGrid + exports.saveMyGrid = function(grid){ + command = '{"type" : "SET", "command": "MYGRID" , "parameter": "'+ grid +'" }' + writeTncCommand(command) +} + +//Get TNC State + exports.getTncState = function(){ + command = '{"type" : "GET", "command": "TNC_STATE"}'; + writeTncCommand(command) +} + +//Get DATA State + exports.getDataState = function(){ + command = '{"type" : "GET", "command": "DATA_STATE"}'; + writeTncCommand(command) +} + +//Get Heard Stations + exports.getHeardStations = function(){ + command = '{"type" : "GET", "command": "HEARD_STATIONS"}'; + writeTncCommand(command) +} + + +// Send Ping + exports.sendPing = function(dxcallsign){ + command = '{"type" : "PING", "command" : "PING", "dxcallsign" : "' + dxcallsign + '"}' + writeTncCommand(command) +} + +// Send CQ + exports.sendCQ = function(){ + command = '{"type" : "CQ", "command" : "CQCQCQ"}' + + writeTncCommand(command) + console.log("COMMAND WURDE GESCHRIEBEN UND AUSGEFIEHT!!!!") + + tnc_host = '192.168.178.163' + tnc_port = 3000 + var testclient = new net.Socket(); + + //testclient.connect(tnc_port, tnc_host) + //testclient.write(command + '\n'); + + +} \ No newline at end of file