mirror of
https://github.com/KubaPro010/fm-dx-webserver.git
synced 2026-07-30 00:39:16 +02:00
better plugin configs (add backend path)
This commit is contained in:
+1
-1
@@ -10,7 +10,7 @@
|
|||||||
"webserver": "node index.js"
|
"webserver": "node index.js"
|
||||||
},
|
},
|
||||||
"author": "",
|
"author": "",
|
||||||
"license": "ISC",
|
"license": "GPL-3.0-only",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@mapbox/node-pre-gyp": "2.0.3",
|
"@mapbox/node-pre-gyp": "2.0.3",
|
||||||
"body-parser": "2.2.2",
|
"body-parser": "2.2.2",
|
||||||
|
|||||||
+4
-3
@@ -7,10 +7,10 @@ const LOG_FILE = process.argv.includes('--config') && process.argv[process.argv.
|
|||||||
? `serverlog_${process.argv[process.argv.indexOf('--config') + 1]}.txt`
|
? `serverlog_${process.argv[process.argv.indexOf('--config') + 1]}.txt`
|
||||||
: 'serverlog.txt';
|
: 'serverlog.txt';
|
||||||
const ANSI_ESCAPE_CODE_PATTERN = /\x1b\[[0-9;]*m/g;
|
const ANSI_ESCAPE_CODE_PATTERN = /\x1b\[[0-9;]*m/g;
|
||||||
const MAX_LOG_LINES = 3500;
|
const MAX_LOG_LINES = 2500;
|
||||||
const FLUSH_INTERVAL = 60000;
|
const FLUSH_INTERVAL = 60000;
|
||||||
const logs = [];
|
const logs = [];
|
||||||
const maxConsoleLogLines = 250;
|
const maxConsoleLogLines = 230;
|
||||||
let logBuffer = [];
|
let logBuffer = [];
|
||||||
|
|
||||||
// Message prefixes with ANSI codes
|
// Message prefixes with ANSI codes
|
||||||
@@ -27,7 +27,7 @@ const MESSAGE_PREFIX = {
|
|||||||
const getCurrentTime = () => {
|
const getCurrentTime = () => {
|
||||||
const currentTime = new Date();
|
const currentTime = new Date();
|
||||||
const date = currentTime.toLocaleDateString().replace(/\ /g, '');
|
const date = currentTime.toLocaleDateString().replace(/\ /g, '');
|
||||||
const time = currentTime.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' });
|
const time = currentTime.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit', hour12: false });
|
||||||
return `\x1b[90m[${date} ${time}]\x1b[0m`;
|
return `\x1b[90m[${date} ${time}]\x1b[0m`;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -57,6 +57,7 @@ function appendLogToBuffer(logMessage) {
|
|||||||
const cleanLogMessage = removeANSIEscapeCodes(logMessage);
|
const cleanLogMessage = removeANSIEscapeCodes(logMessage);
|
||||||
logBuffer.push(cleanLogMessage + '\n');
|
logBuffer.push(cleanLogMessage + '\n');
|
||||||
}
|
}
|
||||||
|
appendLogToBuffer("Server started.");
|
||||||
|
|
||||||
async function flushLogBuffer() {
|
async function flushLogBuffer() {
|
||||||
if (logBuffer.length === 0) return;
|
if (logBuffer.length === 0) return;
|
||||||
|
|||||||
@@ -0,0 +1,198 @@
|
|||||||
|
const { SerialPort } = require('serialport');
|
||||||
|
const { serverConfig, configExists } = require('./server_config');
|
||||||
|
const { logError, logInfo, logWarn } = require('./console');
|
||||||
|
const pluginsApi = require('./plugins_api');
|
||||||
|
const dataHandler = require('./datahandler');
|
||||||
|
const client = new (require('net')).Socket();
|
||||||
|
const helpers = require('./helpers');
|
||||||
|
|
||||||
|
let serialport;
|
||||||
|
|
||||||
|
connectToXdrd();
|
||||||
|
connectToSerial();
|
||||||
|
|
||||||
|
dataHandler.state.isSerialportRetrying = false;
|
||||||
|
|
||||||
|
setInterval(() => {
|
||||||
|
if (!dataHandler.state.isSerialportAlive && serverConfig.xdrd.wirelessConnection === false) {
|
||||||
|
dataHandler.state.isSerialportAlive = true;
|
||||||
|
dataHandler.state.isSerialportRetrying = true;
|
||||||
|
if (serialport && serialport.isOpen) {
|
||||||
|
logWarn('Communication lost from ' + serverConfig.xdrd.comPort + ', force closing serialport.');
|
||||||
|
setTimeout(() => {
|
||||||
|
serialport.close((err) => {
|
||||||
|
if (err) logError('Error closing serialport: ', err.message);
|
||||||
|
});
|
||||||
|
}, 1000);
|
||||||
|
} else logWarn('Communication lost from ' + serverConfig.xdrd.comPort + '.');
|
||||||
|
}
|
||||||
|
}, 2000);
|
||||||
|
|
||||||
|
// Serial Connection
|
||||||
|
function connectToSerial() {
|
||||||
|
if (serverConfig.xdrd.wirelessConnection === true) return;
|
||||||
|
|
||||||
|
serialport = new SerialPort({
|
||||||
|
path: serverConfig.xdrd.comPort,
|
||||||
|
baudRate: 115200,
|
||||||
|
autoOpen: false,
|
||||||
|
dtr: false, rts: false
|
||||||
|
});
|
||||||
|
|
||||||
|
serialport.open((err) => {
|
||||||
|
if (err) {
|
||||||
|
logError('Error opening port: ' + err.message);
|
||||||
|
setTimeout(() => {
|
||||||
|
connectToSerial();
|
||||||
|
}, 5000);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
logInfo('Using serial port: ' + serverConfig.xdrd.comPort);
|
||||||
|
dataHandler.state.isSerialportAlive = true;
|
||||||
|
pluginsApi.setOutput(serialport);
|
||||||
|
setTimeout(() => {
|
||||||
|
serialport.write('x\n');
|
||||||
|
}, 1000);
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
serialport.write('Q0\n');
|
||||||
|
serialport.write('M0\n');
|
||||||
|
serialport.write(`Z${serverConfig.antennaStartup}\n`); // Antenna on startup
|
||||||
|
|
||||||
|
if (serverConfig.defaultFreq && serverConfig.enableDefaultFreq === true) {
|
||||||
|
serialport.write('T' + Math.round(serverConfig.defaultFreq * 1000) + '\n');
|
||||||
|
dataHandler.initialData.freq = Number(serverConfig.defaultFreq).toFixed(3);
|
||||||
|
dataHandler.dataToSend.freq = Number(serverConfig.defaultFreq).toFixed(3);
|
||||||
|
} else if (dataHandler.state.lastFrequencyAlive && dataHandler.state.isSerialportRetrying) { // Serialport retry code when port is open but communication is lost
|
||||||
|
serialport.write('T' + (dataHandler.state.lastFrequencyAlive * 1000) + '\n');
|
||||||
|
} else serialport.write('T87500\n');
|
||||||
|
dataHandler.state.isSerialportRetrying = false;
|
||||||
|
|
||||||
|
if (serverConfig.device === 'si47xx') serialport.write('A0\n');
|
||||||
|
serialport.write('F-1\n');
|
||||||
|
serialport.write('W0\n');
|
||||||
|
serverConfig.webserver.rdsMode ? serialport.write('D1\n') : serialport.write('D0\n');
|
||||||
|
serialport.write(`G${serverConfig.ceqStartup}${serverConfig.imsStartup}\n`);
|
||||||
|
serialport.write(`B${serverConfig.stereoStartup}\n`);
|
||||||
|
serverConfig.audio.startupVolume
|
||||||
|
? serialport.write('Y' + (serverConfig.audio.startupVolume * 100).toFixed(0) + '\n')
|
||||||
|
: serialport.write('Y100\n');
|
||||||
|
}, 6000);
|
||||||
|
|
||||||
|
serialport.on('data', helpers.resolveDataBuffer);
|
||||||
|
serialport.on('error', (error) => logError(error.message));
|
||||||
|
});
|
||||||
|
|
||||||
|
// Handle port closure
|
||||||
|
serialport.on('close', () => {
|
||||||
|
pluginsApi.setOutput(null);
|
||||||
|
logWarn('Disconnected from ' + serverConfig.xdrd.comPort + '. Attempting to reconnect.');
|
||||||
|
setTimeout(() => {
|
||||||
|
dataHandler.state.isSerialportRetrying = true;
|
||||||
|
connectToSerial();
|
||||||
|
}, 5000);
|
||||||
|
});
|
||||||
|
return serialport;
|
||||||
|
}
|
||||||
|
|
||||||
|
// xdrd connection
|
||||||
|
let authFlags = {};
|
||||||
|
|
||||||
|
function connectToXdrd() {
|
||||||
|
const { xdrd } = serverConfig;
|
||||||
|
|
||||||
|
if (xdrd.wirelessConnection && configExists()) {
|
||||||
|
client.connect(xdrd.xdrdPort, xdrd.xdrdIp, () => {
|
||||||
|
logInfo('Connection to xdrd established successfully.');
|
||||||
|
pluginsApi.setOutput(client);
|
||||||
|
|
||||||
|
authFlags = {
|
||||||
|
authMsg: false,
|
||||||
|
firstClient: false,
|
||||||
|
receivedSalt: '',
|
||||||
|
receivedPassword: false,
|
||||||
|
messageCount: 0,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
client.on('data', (data) => {
|
||||||
|
const { xdrd } = serverConfig;
|
||||||
|
|
||||||
|
helpers.resolveDataBuffer(data);
|
||||||
|
if (authFlags.authMsg == true && authFlags.messageCount > 1) return;
|
||||||
|
|
||||||
|
authFlags.messageCount++;
|
||||||
|
const receivedData = data.toString();
|
||||||
|
const lines = receivedData.split('\n');
|
||||||
|
|
||||||
|
for (const line of lines) {
|
||||||
|
if (authFlags.receivedPassword === false) {
|
||||||
|
authFlags.receivedSalt = line.trim();
|
||||||
|
authFlags.receivedPassword = true;
|
||||||
|
helpers.authenticateWithXdrd(client, authFlags.receivedSalt, xdrd.xdrdPassword);
|
||||||
|
} else {
|
||||||
|
if (line.startsWith('a')) {
|
||||||
|
authFlags.authMsg = true;
|
||||||
|
logWarn('Authentication with xdrd failed. Is your password set correctly?');
|
||||||
|
} else if (line.startsWith('o1,')) authFlags.firstClient = true;
|
||||||
|
else if (line.startsWith('T') && line.length <= 7) {
|
||||||
|
const freq = line.slice(1) / 1000;
|
||||||
|
dataHandler.dataToSend.freq = freq.toFixed(3);
|
||||||
|
} else if (line.startsWith('OK')) {
|
||||||
|
authFlags.authMsg = true;
|
||||||
|
logInfo('Authentication with xdrd successful.');
|
||||||
|
} else if (line.startsWith('G')) {
|
||||||
|
dataHandler.initialData.eq = line.charAt(1);
|
||||||
|
dataHandler.dataToSend.eq = line.charAt(1);
|
||||||
|
dataHandler.initialData.ims = line.charAt(2);
|
||||||
|
dataHandler.dataToSend.ims = line.charAt(2);
|
||||||
|
} else if (line.startsWith('Z')) {
|
||||||
|
let modifiedLine = line.slice(1);
|
||||||
|
dataHandler.initialData.ant = modifiedLine;
|
||||||
|
dataHandler.dataToSend.ant = modifiedLine;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (authFlags.authMsg === true && authFlags.firstClient === true) {
|
||||||
|
client.write('x\n');
|
||||||
|
client.write(serverConfig.defaultFreq && serverConfig.enableDefaultFreq === true ? 'T' + Math.round(serverConfig.defaultFreq * 1000) + '\n' : 'T87500\n');
|
||||||
|
dataHandler.initialData.freq = serverConfig.defaultFreq && serverConfig.enableDefaultFreq === true ? Number(serverConfig.defaultFreq).toFixed(3) : (87.5).toFixed(3);
|
||||||
|
dataHandler.dataToSend.freq = serverConfig.defaultFreq && serverConfig.enableDefaultFreq === true ? Number(serverConfig.defaultFreq).toFixed(3) : (87.5).toFixed(3);
|
||||||
|
if (serverConfig.device === 'si47xx') serialport.write('A0\n');
|
||||||
|
client.write(serverConfig.audio.startupVolume ? 'Y' + (serverConfig.audio.startupVolume * 100).toFixed(0) + '\n' : 'Y100\n');
|
||||||
|
serverConfig.webserver.rdsMode ? client.write('D1\n') : client.write('D0\n');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
client.on('close', () => {
|
||||||
|
pluginsApi.setOutput(null);
|
||||||
|
if(serverConfig.autoShutdown === false) {
|
||||||
|
logWarn('Disconnected from xdrd. Attempting to reconnect.');
|
||||||
|
setTimeout(connectToXdrd, 2000);
|
||||||
|
} else logWarn('Disconnected from xdrd.');
|
||||||
|
});
|
||||||
|
|
||||||
|
client.on('error', (err) => {
|
||||||
|
switch (true) {
|
||||||
|
case err.message.includes("ECONNRESET"):
|
||||||
|
logError("Connection to xdrd lost. Reconnecting...");
|
||||||
|
break;
|
||||||
|
case err.message.includes("ETIMEDOUT"):
|
||||||
|
logError("Connection to xdrd @ " + serverConfig.xdrd.xdrdIp + ":" + serverConfig.xdrd.xdrdPort + " timed out.");
|
||||||
|
break;
|
||||||
|
case err.message.includes("ECONNREFUSED"):
|
||||||
|
logError("Connection to xdrd @ " + serverConfig.xdrd.xdrdIp + ":" + serverConfig.xdrd.xdrdPort + " failed. Is xdrd running?");
|
||||||
|
break;
|
||||||
|
case err.message.includes("EINVAL"):
|
||||||
|
logError("Attempts to reconnect are failing repeatedly. Consider checking your settings or restarting xdrd.");
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
logError("Unhandled error: ", err.message);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
});
|
||||||
+4
-6
@@ -14,8 +14,7 @@ const storage = require('./storage');
|
|||||||
const tunerProfiles = require('./tuner_profiles');
|
const tunerProfiles = require('./tuner_profiles');
|
||||||
const { logInfo, logs } = require('./console');
|
const { logInfo, logs } = require('./console');
|
||||||
const dataHandler = require('./datahandler');
|
const dataHandler = require('./datahandler');
|
||||||
const fmdxList = require('./fmdx_list');
|
const serverList = require('./server_list');
|
||||||
const allPluginConfigs = require('./plugins');
|
|
||||||
|
|
||||||
// Endpoints
|
// Endpoints
|
||||||
router.get('/', (req, res) => {
|
router.get('/', (req, res) => {
|
||||||
@@ -141,8 +140,7 @@ router.get('/setup', (req, res) => {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
SerialPort.list()
|
SerialPort.list().then((deviceList) => {
|
||||||
.then((deviceList) => {
|
|
||||||
serialPorts = deviceList.map(port => ({
|
serialPorts = deviceList.map(port => ({
|
||||||
path: port.path,
|
path: port.path,
|
||||||
friendlyName: port.friendlyName,
|
friendlyName: port.friendlyName,
|
||||||
@@ -162,7 +160,7 @@ router.get('/setup', (req, res) => {
|
|||||||
memoryHeap: (process.memoryUsage().heapUsed / 1024 / 1024).toFixed(1) + ' MB',
|
memoryHeap: (process.memoryUsage().heapUsed / 1024 / 1024).toFixed(1) + ' MB',
|
||||||
processUptime: formattedProcessUptime,
|
processUptime: formattedProcessUptime,
|
||||||
consoleOutput: logs,
|
consoleOutput: logs,
|
||||||
plugins: allPluginConfigs,
|
plugins: require('./plugins'),
|
||||||
enabledPlugins: updatedConfig.plugins,
|
enabledPlugins: updatedConfig.plugins,
|
||||||
onlineUsers: dataHandler.dataToSend.users,
|
onlineUsers: dataHandler.dataToSend.users,
|
||||||
connectedUsers: storage.connectedUsers,
|
connectedUsers: storage.connectedUsers,
|
||||||
@@ -308,7 +306,7 @@ router.post('/saveData', (req, res) => {
|
|||||||
let firstSetup;
|
let firstSetup;
|
||||||
if(req.session.isAdminAuthenticated || !configExists()) {
|
if(req.session.isAdminAuthenticated || !configExists()) {
|
||||||
configUpdate(data);
|
configUpdate(data);
|
||||||
fmdxList.update();
|
serverList.update();
|
||||||
|
|
||||||
if(!configExists()) firstSetup = true;
|
if(!configExists()) firstSetup = true;
|
||||||
logInfo('Server config changed successfully.');
|
logInfo('Server config changed successfully.');
|
||||||
|
|||||||
+7
-1
@@ -400,12 +400,18 @@ function startPluginsWithDelay(plugins, delay) {
|
|||||||
function findServerFiles(plugins) {
|
function findServerFiles(plugins) {
|
||||||
let results = [];
|
let results = [];
|
||||||
plugins.forEach(plugin => {
|
plugins.forEach(plugin => {
|
||||||
// Remove .js extension if present
|
|
||||||
if (plugin.endsWith('.js')) plugin = plugin.slice(0, -3);
|
if (plugin.endsWith('.js')) plugin = plugin.slice(0, -3);
|
||||||
|
|
||||||
const pluginPath = path.join(__dirname, '..', 'plugins', `${plugin}_server.js`);
|
const pluginPath = path.join(__dirname, '..', 'plugins', `${plugin}_server.js`);
|
||||||
if (fs.existsSync(pluginPath) && fs.statSync(pluginPath).isFile()) results.push(pluginPath);
|
if (fs.existsSync(pluginPath) && fs.statSync(pluginPath).isFile()) results.push(pluginPath);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
require("./plugins").forEach(config => {
|
||||||
|
if(config.backEndPath) {
|
||||||
|
const pluginPath = config.backEndPath;
|
||||||
|
if (fs.existsSync(pluginPath) && fs.statSync(pluginPath).isFile()) results.push(pluginPath);
|
||||||
|
}
|
||||||
|
});
|
||||||
return results;
|
return results;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+12
-215
@@ -1,229 +1,26 @@
|
|||||||
const { SerialPort } = require('serialport');
|
|
||||||
const tunnel = require('./tunnel');
|
|
||||||
const helpers = require('./helpers');
|
|
||||||
const dataHandler = require('./datahandler');
|
|
||||||
const { logError, logInfo, logWarn } = require('./console');
|
|
||||||
const { serverConfig, configExists } = require('./server_config');
|
|
||||||
const pluginsApi = require('./plugins_api');
|
|
||||||
const startServer = require("./web");
|
|
||||||
|
|
||||||
const client = new (require('net')).Socket();
|
|
||||||
|
|
||||||
// Get all plugins from config and find corresponding server files
|
|
||||||
const plugins = helpers.findServerFiles(serverConfig.plugins);
|
|
||||||
|
|
||||||
// Start the first plugin after 3 seconds, then the rest with 3 seconds delay
|
|
||||||
if (plugins.length > 0) {
|
|
||||||
setTimeout(() => {
|
|
||||||
helpers.startPluginsWithDelay(plugins, 3000); // Start plugins with 3 seconds interval
|
|
||||||
}, 4000); // Initial delay of 4 seconds for the first plugin
|
|
||||||
}
|
|
||||||
|
|
||||||
const terminalWidth = require('readline').createInterface({input: process.stdin, output: process.stdout}).output.columns;
|
const terminalWidth = require('readline').createInterface({input: process.stdin, output: process.stdout}).output.columns;
|
||||||
|
|
||||||
console.log('\x1b[32m' + require('figlet').textSync("FM-DX Webserver"));
|
console.log('\x1b[32m' + require('figlet').textSync("FM-DX Webserver"));
|
||||||
console.log('\x1b[32m\x1b[2mby Noobish @ \x1b[4mFMDX.org + KubaPro010\x1b[0m');
|
console.log('\x1b[2mby (Noobish @ \x1b[4mFMDX.org\x1b[0m\x1b[32m\x1b[2m) + KubaPro010\x1b[0m');
|
||||||
console.log("v" + require('../package.json').version)
|
console.log("v" + require('../package.json').version)
|
||||||
console.log('\x1b[90m' + '─'.repeat(terminalWidth - 1) + '\x1b[0m');
|
console.log('\x1b[90m' + '─'.repeat(terminalWidth - 1) + '\x1b[0m');
|
||||||
|
|
||||||
require('./plugins');
|
const { serverConfig } = require('./server_config');
|
||||||
|
const tunnel = require('./tunnel');
|
||||||
let serialport;
|
|
||||||
|
|
||||||
tunnel.download();
|
tunnel.download();
|
||||||
connectToXdrd();
|
|
||||||
connectToSerial();
|
|
||||||
|
|
||||||
// Serialport retry code when port is open but communication is lost (additional code in datahandler.js)
|
require("./device");
|
||||||
dataHandler.state.isSerialportRetrying = false;
|
|
||||||
|
|
||||||
setInterval(() => {
|
{
|
||||||
if (!dataHandler.state.isSerialportAlive && serverConfig.xdrd.wirelessConnection === false) {
|
const helpers = require('./helpers');
|
||||||
dataHandler.state.isSerialportAlive = true;
|
const plugins = helpers.findServerFiles(serverConfig.plugins);
|
||||||
dataHandler.state.isSerialportRetrying = true;
|
if (plugins.length > 0) setTimeout(helpers.startPluginsWithDelay, 4000, plugins, 3000);
|
||||||
if (serialport && serialport.isOpen) {
|
|
||||||
logWarn('Communication lost from ' + serverConfig.xdrd.comPort + ', force closing serialport.');
|
|
||||||
setTimeout(() => {
|
|
||||||
serialport.close((err) => {
|
|
||||||
if (err) logError('Error closing serialport: ', err.message);
|
|
||||||
});
|
|
||||||
}, 1000);
|
|
||||||
} else logWarn('Communication lost from ' + serverConfig.xdrd.comPort + '.');
|
|
||||||
}
|
|
||||||
}, 2000);
|
|
||||||
|
|
||||||
// Serial Connection
|
|
||||||
function connectToSerial() {
|
|
||||||
if (serverConfig.xdrd.wirelessConnection === true) return;
|
|
||||||
|
|
||||||
serialport = new SerialPort({
|
|
||||||
path: serverConfig.xdrd.comPort,
|
|
||||||
baudRate: 115200,
|
|
||||||
autoOpen: false,
|
|
||||||
dtr: false, rts: false
|
|
||||||
});
|
|
||||||
|
|
||||||
serialport.open((err) => {
|
|
||||||
if (err) {
|
|
||||||
logError('Error opening port: ' + err.message);
|
|
||||||
setTimeout(() => {
|
|
||||||
connectToSerial();
|
|
||||||
}, 5000);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
logInfo('Using serial port: ' + serverConfig.xdrd.comPort);
|
|
||||||
dataHandler.state.isSerialportAlive = true;
|
|
||||||
pluginsApi.setOutput(serialport);
|
|
||||||
setTimeout(() => {
|
|
||||||
serialport.write('x\n');
|
|
||||||
}, 1000);
|
|
||||||
|
|
||||||
setTimeout(() => {
|
|
||||||
serialport.write('Q0\n');
|
|
||||||
serialport.write('M0\n');
|
|
||||||
serialport.write(`Z${serverConfig.antennaStartup}\n`); // Antenna on startup
|
|
||||||
|
|
||||||
if (serverConfig.defaultFreq && serverConfig.enableDefaultFreq === true) {
|
|
||||||
serialport.write('T' + Math.round(serverConfig.defaultFreq * 1000) + '\n');
|
|
||||||
dataHandler.initialData.freq = Number(serverConfig.defaultFreq).toFixed(3);
|
|
||||||
dataHandler.dataToSend.freq = Number(serverConfig.defaultFreq).toFixed(3);
|
|
||||||
} else if (dataHandler.state.lastFrequencyAlive && dataHandler.state.isSerialportRetrying) { // Serialport retry code when port is open but communication is lost
|
|
||||||
serialport.write('T' + (dataHandler.state.lastFrequencyAlive * 1000) + '\n');
|
|
||||||
} else serialport.write('T87500\n');
|
|
||||||
dataHandler.state.isSerialportRetrying = false;
|
|
||||||
|
|
||||||
if (serverConfig.device === 'si47xx') serialport.write('A0\n');
|
|
||||||
serialport.write('F-1\n');
|
|
||||||
serialport.write('W0\n');
|
|
||||||
serverConfig.webserver.rdsMode ? serialport.write('D1\n') : serialport.write('D0\n');
|
|
||||||
serialport.write(`G${serverConfig.ceqStartup}${serverConfig.imsStartup}\n`);
|
|
||||||
serialport.write(`B${serverConfig.stereoStartup}\n`); // Mono
|
|
||||||
serverConfig.audio.startupVolume
|
|
||||||
? serialport.write('Y' + (serverConfig.audio.startupVolume * 100).toFixed(0) + '\n')
|
|
||||||
: serialport.write('Y100\n');
|
|
||||||
}, 6000);
|
|
||||||
|
|
||||||
serialport.on('data', (data) => helpers.resolveDataBuffer(data));
|
|
||||||
serialport.on('error', (error) => logError(error.message));
|
|
||||||
});
|
|
||||||
|
|
||||||
// Handle port closure
|
|
||||||
serialport.on('close', () => {
|
|
||||||
pluginsApi.setOutput(null);
|
|
||||||
logWarn('Disconnected from ' + serverConfig.xdrd.comPort + '. Attempting to reconnect.');
|
|
||||||
setTimeout(() => {
|
|
||||||
dataHandler.state.isSerialportRetrying = true;
|
|
||||||
connectToSerial();
|
|
||||||
}, 5000);
|
|
||||||
});
|
|
||||||
return serialport;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// xdrd connection
|
|
||||||
let authFlags = {};
|
|
||||||
|
|
||||||
function connectToXdrd() {
|
|
||||||
const { xdrd } = serverConfig;
|
|
||||||
|
|
||||||
if (xdrd.wirelessConnection && configExists()) {
|
|
||||||
client.connect(xdrd.xdrdPort, xdrd.xdrdIp, () => {
|
|
||||||
logInfo('Connection to xdrd established successfully.');
|
|
||||||
pluginsApi.setOutput(client);
|
|
||||||
|
|
||||||
authFlags = {
|
|
||||||
authMsg: false,
|
|
||||||
firstClient: false,
|
|
||||||
receivedSalt: '',
|
|
||||||
receivedPassword: false,
|
|
||||||
messageCount: 0,
|
|
||||||
};
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
client.on('data', (data) => {
|
|
||||||
const { xdrd } = serverConfig;
|
|
||||||
|
|
||||||
helpers.resolveDataBuffer(data);
|
|
||||||
if (authFlags.authMsg == true && authFlags.messageCount > 1) return;
|
|
||||||
|
|
||||||
authFlags.messageCount++;
|
|
||||||
const receivedData = data.toString();
|
|
||||||
const lines = receivedData.split('\n');
|
|
||||||
|
|
||||||
for (const line of lines) {
|
|
||||||
if (authFlags.receivedPassword === false) {
|
|
||||||
authFlags.receivedSalt = line.trim();
|
|
||||||
authFlags.receivedPassword = true;
|
|
||||||
helpers.authenticateWithXdrd(client, authFlags.receivedSalt, xdrd.xdrdPassword);
|
|
||||||
} else {
|
|
||||||
if (line.startsWith('a')) {
|
|
||||||
authFlags.authMsg = true;
|
|
||||||
logWarn('Authentication with xdrd failed. Is your password set correctly?');
|
|
||||||
} else if (line.startsWith('o1,')) authFlags.firstClient = true;
|
|
||||||
else if (line.startsWith('T') && line.length <= 7) {
|
|
||||||
const freq = line.slice(1) / 1000;
|
|
||||||
dataHandler.dataToSend.freq = freq.toFixed(3);
|
|
||||||
} else if (line.startsWith('OK')) {
|
|
||||||
authFlags.authMsg = true;
|
|
||||||
logInfo('Authentication with xdrd successful.');
|
|
||||||
} else if (line.startsWith('G')) {
|
|
||||||
dataHandler.initialData.eq = line.charAt(1);
|
|
||||||
dataHandler.dataToSend.eq = line.charAt(1);
|
|
||||||
dataHandler.initialData.ims = line.charAt(2);
|
|
||||||
dataHandler.dataToSend.ims = line.charAt(2);
|
|
||||||
} else if (line.startsWith('Z')) {
|
|
||||||
let modifiedLine = line.slice(1);
|
|
||||||
dataHandler.initialData.ant = modifiedLine;
|
|
||||||
dataHandler.dataToSend.ant = modifiedLine;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (authFlags.authMsg === true && authFlags.firstClient === true) {
|
|
||||||
client.write('x\n');
|
|
||||||
client.write(serverConfig.defaultFreq && serverConfig.enableDefaultFreq === true ? 'T' + Math.round(serverConfig.defaultFreq * 1000) + '\n' : 'T87500\n');
|
|
||||||
dataHandler.initialData.freq = serverConfig.defaultFreq && serverConfig.enableDefaultFreq === true ? Number(serverConfig.defaultFreq).toFixed(3) : (87.5).toFixed(3);
|
|
||||||
dataHandler.dataToSend.freq = serverConfig.defaultFreq && serverConfig.enableDefaultFreq === true ? Number(serverConfig.defaultFreq).toFixed(3) : (87.5).toFixed(3);
|
|
||||||
if (serverConfig.device === 'si47xx') serialport.write('A0\n');
|
|
||||||
client.write(serverConfig.audio.startupVolume ? 'Y' + (serverConfig.audio.startupVolume * 100).toFixed(0) + '\n' : 'Y100\n');
|
|
||||||
serverConfig.webserver.rdsMode ? client.write('D1\n') : client.write('D0\n');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
client.on('close', () => {
|
|
||||||
pluginsApi.setOutput(null);
|
|
||||||
if(serverConfig.autoShutdown === false) {
|
|
||||||
logWarn('Disconnected from xdrd. Attempting to reconnect.');
|
|
||||||
setTimeout(function () {
|
|
||||||
connectToXdrd();
|
|
||||||
}, 2000)
|
|
||||||
} else logWarn('Disconnected from xdrd.');
|
|
||||||
});
|
|
||||||
|
|
||||||
client.on('error', (err) => {
|
|
||||||
switch (true) {
|
|
||||||
case err.message.includes("ECONNRESET"):
|
|
||||||
logError("Connection to xdrd lost. Reconnecting...");
|
|
||||||
break;
|
|
||||||
case err.message.includes("ETIMEDOUT"):
|
|
||||||
logError("Connection to xdrd @ " + serverConfig.xdrd.xdrdIp + ":" + serverConfig.xdrd.xdrdPort + " timed out.");
|
|
||||||
break;
|
|
||||||
case err.message.includes("ECONNREFUSED"):
|
|
||||||
logError("Connection to xdrd @ " + serverConfig.xdrd.xdrdIp + ":" + serverConfig.xdrd.xdrdPort + " failed. Is xdrd running?");
|
|
||||||
break;
|
|
||||||
case err.message.includes("EINVAL"):
|
|
||||||
logError("Attempts to reconnect are failing repeatedly. Consider checking your settings or restarting xdrd.");
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
logError("Unhandled error: ", err.message);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
require('./stream/index');
|
require('./stream/index');
|
||||||
|
const startServer = require("./web");
|
||||||
|
|
||||||
startServer(serverConfig.webserver.webserverIp === '0.0.0.0' ? 'localhost' : serverConfig.webserver.webserverIp);
|
startServer(serverConfig.webserver.webserverIp === '0.0.0.0' ? 'localhost' : serverConfig.webserver.webserverIp);
|
||||||
|
|
||||||
tunnel.connect();
|
tunnel.connect();
|
||||||
require('./fmdx_list').update();
|
require('./server_list').update();
|
||||||
+12
-20
@@ -3,8 +3,7 @@ const path = require('path');
|
|||||||
const consoleCmd = require('./console');
|
const consoleCmd = require('./console');
|
||||||
|
|
||||||
function readJSFiles(dir) {
|
function readJSFiles(dir) {
|
||||||
const files = fs.readdirSync(dir);
|
return fs.readdirSync(dir).filter(file => file.endsWith('.js'));
|
||||||
return files.filter(file => file.endsWith('.js'));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function parsePluginConfig(filePath) {
|
function parsePluginConfig(filePath) {
|
||||||
@@ -44,7 +43,7 @@ function parsePluginConfig(filePath) {
|
|||||||
console.error(`Error creating symlink at ${destinationFile}: ${err.message}`);
|
console.error(`Error creating symlink at ${destinationFile}: ${err.message}`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else console.error(`Error: frontEndPath is not defined in ${filePath}`);
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error(`Error parsing plugin config from ${filePath}: ${err.message}`);
|
console.error(`Error parsing plugin config from ${filePath}: ${err.message}`);
|
||||||
}
|
}
|
||||||
@@ -52,27 +51,13 @@ function parsePluginConfig(filePath) {
|
|||||||
return pluginConfig;
|
return pluginConfig;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Main function to collect plugin configs from all .js files in the 'plugins' directory
|
|
||||||
function collectPluginConfigs() {
|
|
||||||
const pluginsDir = path.join(__dirname, '../plugins');
|
|
||||||
const jsFiles = readJSFiles(pluginsDir);
|
|
||||||
|
|
||||||
const pluginConfigs = [];
|
|
||||||
jsFiles.forEach(file => {
|
|
||||||
const filePath = path.join(pluginsDir, file);
|
|
||||||
const config = parsePluginConfig(filePath);
|
|
||||||
if (Object.keys(config).length > 0) pluginConfigs.push(config);
|
|
||||||
});
|
|
||||||
|
|
||||||
return pluginConfigs;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Ensure the web/js/plugins directory exists
|
// Ensure the web/js/plugins directory exists
|
||||||
const webJsPluginsDir = path.join(__dirname, '../web/js/plugins');
|
const webJsPluginsDir = path.join(__dirname, '../web/js/plugins');
|
||||||
if (!fs.existsSync(webJsPluginsDir)) fs.mkdirSync(webJsPluginsDir, { recursive: true });
|
if (!fs.existsSync(webJsPluginsDir)) fs.mkdirSync(webJsPluginsDir, { recursive: true });
|
||||||
|
|
||||||
|
const pluginsDir = path.join(__dirname, '../plugins');
|
||||||
|
|
||||||
if (process.platform === 'win32') {
|
if (process.platform === 'win32') {
|
||||||
const pluginsDir = path.join(__dirname, '../plugins');
|
|
||||||
const destinationPluginsDir = path.join(__dirname, '../web/js/plugins');
|
const destinationPluginsDir = path.join(__dirname, '../web/js/plugins');
|
||||||
|
|
||||||
// On Windows, create a junction
|
// On Windows, create a junction
|
||||||
@@ -84,4 +69,11 @@ if (process.platform === 'win32') {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = collectPluginConfigs();
|
const pluginConfigs = [];
|
||||||
|
readJSFiles(pluginsDir).forEach(file => {
|
||||||
|
const filePath = path.join(pluginsDir, file);
|
||||||
|
const config = parsePluginConfig(filePath);
|
||||||
|
if (Object.keys(config).length > 0) pluginConfigs.push(config);
|
||||||
|
});
|
||||||
|
|
||||||
|
module.exports = pluginConfigs;
|
||||||
|
|||||||
@@ -8,8 +8,6 @@ var os = require('os');
|
|||||||
let timeoutID = null;
|
let timeoutID = null;
|
||||||
|
|
||||||
function send(request) {
|
function send(request) {
|
||||||
const url = "https://servers.fmdx.org/api/";
|
|
||||||
|
|
||||||
const options = {
|
const options = {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: {
|
headers: {
|
||||||
@@ -19,7 +17,7 @@ function send(request) {
|
|||||||
body: JSON.stringify(request)
|
body: JSON.stringify(request)
|
||||||
};
|
};
|
||||||
|
|
||||||
fetch(url, options)
|
fetch("https://servers.fmdx.org/api/", options)
|
||||||
.then(response => response.json())
|
.then(response => response.json())
|
||||||
.then(data => {
|
.then(data => {
|
||||||
if (data.success && data.token) {
|
if (data.success && data.token) {
|
||||||
+11
-4
@@ -44,7 +44,7 @@ function xdr_auth(ws, salt) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
xdr.on('connection', async (ws, request) => {
|
xdr.on('connection', async (ws) => {
|
||||||
const { initialData } = require('./datahandler');
|
const { initialData } = require('./datahandler');
|
||||||
|
|
||||||
const salt = randomString(16);
|
const salt = randomString(16);
|
||||||
@@ -79,12 +79,19 @@ xdr.on('connection', async (ws, request) => {
|
|||||||
case "wL1":
|
case "wL1":
|
||||||
serverConfig.lockToAdmin = true;
|
serverConfig.lockToAdmin = true;
|
||||||
send_to_xdr("wL1\n")
|
send_to_xdr("wL1\n")
|
||||||
break;
|
return;
|
||||||
case "wL0":
|
case "wL0":
|
||||||
serverConfig.lockToAdmin = false;
|
serverConfig.lockToAdmin = false;
|
||||||
send_to_xdr("wL0\n")
|
send_to_xdr("wL0\n")
|
||||||
break;
|
return;
|
||||||
// TODO: Do the wT, but not rn because i don't feel like doing the tunerlockTracker
|
case "wT1":
|
||||||
|
serverConfig.publicTuner = false;
|
||||||
|
send_to_xdr("wT1\n")
|
||||||
|
return;
|
||||||
|
case "wT0":
|
||||||
|
serverConfig.publicTuner = true;
|
||||||
|
send_to_xdr("wT0\n")
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+4
-2
@@ -420,11 +420,13 @@
|
|||||||
<p>Any compatible <strong>.js</strong> plugin, which is in the "plugins" folder, will be listed here.<br>
|
<p>Any compatible <strong>.js</strong> plugin, which is in the "plugins" folder, will be listed here.<br>
|
||||||
Click on the individual plugins to enable/disable them.</p>
|
Click on the individual plugins to enable/disable them.</p>
|
||||||
<select id="plugins" class="multiselect" multiple>
|
<select id="plugins" class="multiselect" multiple>
|
||||||
<% plugins.forEach(function(plugin) { %>
|
<% plugins.forEach(function(plugin) {
|
||||||
|
if (plugin.frontEndPath) { %>
|
||||||
<option data-name="<%= plugin.frontEndPath %>" title="<%= plugin.name %> by <%= plugin.author %> [v<%= plugin.version %>]">
|
<option data-name="<%= plugin.frontEndPath %>" title="<%= plugin.name %> by <%= plugin.author %> [v<%= plugin.version %>]">
|
||||||
<%= plugin.name %> by <%= plugin.author %> [v<%= plugin.version %>]
|
<%= plugin.name %> by <%= plugin.author %> [v<%= plugin.version %>]
|
||||||
</option>
|
</option>
|
||||||
<% }); %>
|
<% }
|
||||||
|
}); %>
|
||||||
</select><br><br>
|
</select><br><br>
|
||||||
<a href="https://github.com/NoobishSVK/fm-dx-webserver/wiki/Plugin-List" target="_blank">Download new plugins here!</a>
|
<a href="https://github.com/NoobishSVK/fm-dx-webserver/wiki/Plugin-List" target="_blank">Download new plugins here!</a>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
+1
-1
@@ -21,7 +21,7 @@ class WebSocketAudioPlayer {
|
|||||||
const liveEdge = buffered.end(buffered.length - 1);
|
const liveEdge = buffered.end(buffered.length - 1);
|
||||||
const lag = liveEdge - this.audio.currentTime;
|
const lag = liveEdge - this.audio.currentTime;
|
||||||
|
|
||||||
if (lag > .9) this.audio.currentTime = liveEdge - 0.15; // snap to near live edge
|
if (lag > .9) this.audio.currentTime = liveEdge - 0.2; // snap to near live edge
|
||||||
}
|
}
|
||||||
|
|
||||||
Start() {
|
Start() {
|
||||||
|
|||||||
+1
-2
@@ -815,8 +815,7 @@ function findOnMaps() {
|
|||||||
|
|
||||||
frequency > 74 ? frequency = frequency.toFixed(1) : null;
|
frequency > 74 ? frequency = frequency.toFixed(1) : null;
|
||||||
|
|
||||||
var url = `https://maps.fmdx.org/#qth=${longitude},${latitude}&freq=${frequency}&findPi=${pi}`;
|
window.open(`https://maps.fmdx.org/#qth=${longitude},${latitude}&freq=${frequency}&findPi=${pi}`, "_blank");
|
||||||
window.open(url, "_blank");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function updateSignalUnits(parsedData, averageSignal) {
|
function updateSignalUnits(parsedData, averageSignal) {
|
||||||
|
|||||||
Reference in New Issue
Block a user