Working on #9
- Can join and leave from voice channels - Will check to make sure that the bot is in a given system or no system before joining - Cleaned up the socket client with wrappers - Added a new module to handle subprocesses for the client - Beginning workings on OP25 handler - Added OP25 config object generator with config exporter
This commit is contained in:
114
client/discordAudioBot/dabWrappers.mjs
Normal file
114
client/discordAudioBot/dabWrappers.mjs
Normal file
@@ -0,0 +1,114 @@
|
|||||||
|
import { connectToChannel, checkIfConnectedToVC, initDiscordBotClient, getVoiceChannelFromID, getVoiceConnectionFromGuild } from './dab.mjs';
|
||||||
|
import { openOP25, closeOP25 } from '../op25Handler/op25Handler.mjs';
|
||||||
|
|
||||||
|
let activeDiscordClient = undefined;
|
||||||
|
const activeDiscordVoiceConnections = {};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Join the requested server VC and listen to the requested system
|
||||||
|
* @param {object} joinData The object containing all the information to join the server
|
||||||
|
*/
|
||||||
|
export const joinDiscordVC = async (joinData) => {
|
||||||
|
console.log("Join requested: ", joinData)
|
||||||
|
const connection = await new Promise((res) => {
|
||||||
|
// Check if a client already exists
|
||||||
|
if (!activeDiscordClient) {
|
||||||
|
// Open a new client and join the requested channel with the requested ID
|
||||||
|
initDiscordBotClient(joinData.clientID, joinData.system, client => {
|
||||||
|
// Open an instance of OP25
|
||||||
|
openOP25(joinData.system);
|
||||||
|
|
||||||
|
getVoiceChannelFromID(client, joinData.channelID).then(vc => {
|
||||||
|
// Add the client object to the IO instance
|
||||||
|
activeDiscordClient = client;
|
||||||
|
const connection = connectToChannel(vc);
|
||||||
|
activeDiscordVoiceConnections[vc.guild.id] = connection;
|
||||||
|
console.log("Bot Connected to VC");
|
||||||
|
res(connection);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
// Join the requested channel with the requested ID
|
||||||
|
getVoiceChannelFromID(activeDiscordClient, joinData.channelID).then(vc => {
|
||||||
|
// Add the client object to the IO instance
|
||||||
|
const connection = connectToChannel(vc);
|
||||||
|
activeDiscordVoiceConnections[vc.guild.id] = connection;
|
||||||
|
console.log("Bot Connected to VC");
|
||||||
|
res(connection);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return connection;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Leave VC on the requested server
|
||||||
|
* @param {string} guildId The guild ID to disconnect from VC
|
||||||
|
*/
|
||||||
|
export const leaveDiscordVC = async (guildId) => {
|
||||||
|
console.log("Leave requested");
|
||||||
|
if (await checkIfConnectedToVC(guildId)) {
|
||||||
|
const connection = await getVoiceConnectionFromGuild(guildId);
|
||||||
|
if (connection) {
|
||||||
|
console.log("There is an open VC connection, closing it now");
|
||||||
|
// Destroy the open VC connection
|
||||||
|
connection.destroy();
|
||||||
|
|
||||||
|
// Remove the connection from the object
|
||||||
|
delete activeDiscordVoiceConnections[guildId];
|
||||||
|
|
||||||
|
// Check if this was the last open VC connection
|
||||||
|
if (Object.keys(activeDiscordVoiceConnections).length == 0) {
|
||||||
|
console.log("No more open VC connections, closing the client-side discord client and OP25")
|
||||||
|
// Close the active client if there are no open VCs after this one
|
||||||
|
activeDiscordClient.destroy();
|
||||||
|
activeDiscordClient = undefined;
|
||||||
|
|
||||||
|
// Close OP25
|
||||||
|
await closeOP25();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if the bot is connected to a discord VC in the given server
|
||||||
|
* @param {string} guildId The guild id to check the connection status in
|
||||||
|
* @returns {boolean} If the node is connected to VC in the given guild
|
||||||
|
*/
|
||||||
|
export const checkIfDiscordVCConnected = async (guildId) => {
|
||||||
|
console.log("Requested status check");
|
||||||
|
if (await checkIfConnectedToVC(guildId)) {
|
||||||
|
console.log("There is an open VC connection");
|
||||||
|
return (true);
|
||||||
|
} else {
|
||||||
|
return (false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the username of the bot in a given guild
|
||||||
|
* (there may be a server nickname given to the bot in a certain guild)
|
||||||
|
* @param {string} guildId The guild id to check the connection status in
|
||||||
|
* @returns {string} The username of the bot in the given guild's CV
|
||||||
|
*/
|
||||||
|
export const getDiscordUsername = async (guildId) => {
|
||||||
|
console.log("Requested username");
|
||||||
|
if (activeDiscordClient) return (activeDiscordClient.user.username);
|
||||||
|
else return (undefined);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if there is an open discord client
|
||||||
|
* @returns {boolean} If the client is open or not
|
||||||
|
*/
|
||||||
|
export const checkIfClientIsOpen = async () => {
|
||||||
|
if (activeDiscordClient) {
|
||||||
|
return (true);
|
||||||
|
}
|
||||||
|
return (false);
|
||||||
|
}
|
||||||
@@ -1,6 +1,5 @@
|
|||||||
import { io } from "socket.io-client";
|
import { io } from "socket.io-client";
|
||||||
import { connectToChannel, initDiscordBotClient, getVoiceChannelFromID, checkIfConnectedToVC, getVoiceConnectionFromGuild } from '../discordAudioBot/dab.mjs';
|
import { logIntoServerWrapper, nodeCheckStatus, nodeJoinServer, nodeLeaveServer, nodeGetUsername, nodeCheckDiscordClientStatus, nodeCheckCurrentSystem } from "./socketClientWrappers.mjs";
|
||||||
import { logIntoServerWrapper, sendNodeUpdateWrapper, nodeCheckStatus } from "./socketClientWrappers.mjs";
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Initialize the socket connection with the server, this will handle disconnects within itself
|
* Initialize the socket connection with the server, this will handle disconnects within itself
|
||||||
@@ -12,53 +11,36 @@ export const initSocketConnection = async (localNodeConfig) => {
|
|||||||
|
|
||||||
const socket = io.connect(serverEndpoint);
|
const socket = io.connect(serverEndpoint);
|
||||||
|
|
||||||
const discordClients = {};
|
// Socket Events ('system' events persay)
|
||||||
|
// When the socket connects to the node server
|
||||||
socket.on('connect', async () => {
|
socket.on('connect', async () => {
|
||||||
console.log('Connected to the server');
|
console.log('Connected to the server');
|
||||||
await logIntoServerWrapper(socket, localNodeConfig);
|
await logIntoServerWrapper(socket, localNodeConfig);
|
||||||
});
|
});
|
||||||
|
|
||||||
socket.on('node-join', async (joinData) => {
|
// When the socket disconnects from the node server
|
||||||
console.log("Join requested: ", joinData)
|
|
||||||
// TODO - Implement logic to control OP25 for the requested channel/system
|
|
||||||
|
|
||||||
// Join the requested channel with the requested ID
|
|
||||||
initDiscordBotClient(joinData.clientID, joinData.system, client => {
|
|
||||||
getVoiceChannelFromID(client, joinData.channelID).then(vc => {
|
|
||||||
// Add the client object to the IO instance
|
|
||||||
discordClients[vc.guild.id] = client;
|
|
||||||
const connection = connectToChannel(vc);
|
|
||||||
console.log("Bot Connected to VC");
|
|
||||||
})
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
socket.on('node-leave', async (guildId) => {
|
|
||||||
console.log("Leave requested");
|
|
||||||
if (await checkIfConnectedToVC(guildId)) {
|
|
||||||
const connection = await getVoiceConnectionFromGuild(guildId);
|
|
||||||
if (connection) {
|
|
||||||
console.log("There is an open VC connection, closing it now");
|
|
||||||
// Destroy the open VC connection
|
|
||||||
connection.destroy();
|
|
||||||
|
|
||||||
// Remove the client from the socket connection
|
|
||||||
delete discordClients[guildId];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
socket.on('node-get-discord-username', async (guildId, socketCallback) => {
|
|
||||||
console.log("Requested username");
|
|
||||||
socketCallback(discordClients[guildId].user.username);
|
|
||||||
});
|
|
||||||
|
|
||||||
socket.on('node-check-connected-status', nodeCheckStatus);
|
|
||||||
|
|
||||||
socket.on('disconnect', () => {
|
socket.on('disconnect', () => {
|
||||||
console.log('Disconnected from the server');
|
console.log('Disconnected from the server');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Node events/commands
|
||||||
|
// Requested to join a discord guild and listen to a system
|
||||||
|
socket.on('node-join', nodeJoinServer);
|
||||||
|
|
||||||
|
// Requested to leave a discord guild
|
||||||
|
socket.on('node-leave', nodeLeaveServer);
|
||||||
|
|
||||||
|
// Requested to get the discord username in a given guild
|
||||||
|
socket.on('node-get-discord-username', nodeGetUsername);
|
||||||
|
|
||||||
|
// Requested to check if the node is connected to VC in a given guild
|
||||||
|
socket.on('node-check-connected-status', nodeCheckStatus);
|
||||||
|
|
||||||
|
// Requested to check if the node has an open discord client
|
||||||
|
socket.on('node-check-discord-open-client', nodeCheckDiscordClientStatus);
|
||||||
|
|
||||||
|
// Requested to get the current listening system
|
||||||
|
socket.on('node-check-current-system', nodeCheckCurrentSystem);
|
||||||
|
|
||||||
return socket;
|
return socket;
|
||||||
}
|
}
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
import { checkIfConnectedToVC } from '../discordAudioBot/dab.mjs';
|
import { checkIfDiscordVCConnected, joinDiscordVC, leaveDiscordVC, getDiscordUsername, checkIfClientIsOpen } from '../discordAudioBot/dabWrappers.mjs';
|
||||||
|
import { getCurrentSystem } from '../op25Handler/op25Handler.mjs';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Wrapper to log into the server
|
* Wrapper to log into the server
|
||||||
@@ -27,19 +28,20 @@ export const sendNodeUpdateWrapper = async (socket, localNodeConfig) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
export const nodeJoinServer = async (joinData) => {
|
/**
|
||||||
console.log("Join requested: ", joinData)
|
* Join the requested server VC and listen to the requested system
|
||||||
// TODO - Implement logic to control OP25 for the requested channel/system
|
* @param {object} joinData The object containing all the information to join the server
|
||||||
|
*/
|
||||||
|
export const nodeJoinServer = async (joinData) => {
|
||||||
|
await joinDiscordVC(joinData);
|
||||||
|
}
|
||||||
|
|
||||||
// Join the requested channel with the requested ID
|
/**
|
||||||
initDiscordBotClient(joinData.clientID, joinData.system, client => {
|
* Leave VC on the requested server
|
||||||
getVoiceChannelFromID(client, joinData.channelID).then(vc => {
|
* @param {string} guildId The guild ID to disconnect from VC
|
||||||
// Add the client object to the IO instance
|
*/
|
||||||
discordClients[vc.guild.id] = client;
|
export const nodeLeaveServer = async (guildId) => {
|
||||||
const connection = connectToChannel(vc);
|
await leaveDiscordVC(guildId);
|
||||||
console.log("Bot Connected to VC");
|
|
||||||
})
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -47,14 +49,37 @@ export const nodeJoinServer = async (joinData) => {
|
|||||||
* Check if the bot is connected to a discord VC in the given server
|
* Check if the bot is connected to a discord VC in the given server
|
||||||
* @param {string} guildId The guild id to check the connection status in
|
* @param {string} guildId The guild id to check the connection status in
|
||||||
* @param {any} socketCallback The callback function to return the result to
|
* @param {any} socketCallback The callback function to return the result to
|
||||||
* @returns {any}
|
* @callback {boolean} If the node is connected to VC in the given guild
|
||||||
*/
|
*/
|
||||||
export const nodeCheckStatus = async (guildId, socketCallback) => {
|
export const nodeCheckStatus = async (guildId, socketCallback) => {
|
||||||
console.log("Requested status check");
|
socketCallback(await checkIfDiscordVCConnected(guildId));
|
||||||
if (await checkIfConnectedToVC(guildId)) {
|
}
|
||||||
console.log("There is an open VC connection");
|
|
||||||
socketCallback(true);
|
|
||||||
} else {
|
/**
|
||||||
socketCallback(false);
|
* Get the username of the bot in a given guild
|
||||||
}
|
* (there may be a server nickname given to the bot in a certain guild)
|
||||||
|
* @param {string} guildId The guild id to check the connection status in
|
||||||
|
* @param {any} socketCallback The callback function to return the result to
|
||||||
|
* @callback {any}
|
||||||
|
*/
|
||||||
|
export const nodeGetUsername = async (guildId, socketCallback) => {
|
||||||
|
socketCallback(await getDiscordUsername(guildId));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if the local node has an open discord client in any server
|
||||||
|
* @callback {boolean} If the node has an open discord client or not
|
||||||
|
*/
|
||||||
|
export const nodeCheckDiscordClientStatus = async (socketCallback) => {
|
||||||
|
socketCallback(await checkIfClientIsOpen());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check what system the local node is currently listening to
|
||||||
|
* @callback {boolean} If the node has an open discord client or not
|
||||||
|
*/
|
||||||
|
export const nodeCheckCurrentSystem = async (socketCallback) => {
|
||||||
|
socketCallback(await getCurrentSystem());
|
||||||
}
|
}
|
||||||
66
client/modules/subprocessHandler.mjs
Normal file
66
client/modules/subprocessHandler.mjs
Normal file
@@ -0,0 +1,66 @@
|
|||||||
|
import { spawn } from "child_process";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Object to store references to spawned processes.
|
||||||
|
* @type {Object.<string, import('child_process').ChildProcess>}
|
||||||
|
*/
|
||||||
|
const runningProcesses = {};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Launches a new process if it's not already running.
|
||||||
|
* @param {string} processName - The name of the process to launch.
|
||||||
|
* @param {string[]} args - The arguments to pass to the process.
|
||||||
|
*/
|
||||||
|
export const launchProcess = (processName, args) => {
|
||||||
|
if (!runningProcesses[processName]) {
|
||||||
|
const childProcess = spawn(processName, args);
|
||||||
|
|
||||||
|
// Store reference to the spawned process
|
||||||
|
runningProcesses[processName] = childProcess;
|
||||||
|
|
||||||
|
childProcess.on('exit', (code, signal) => {
|
||||||
|
// Remove reference to the process when it exits
|
||||||
|
delete runningProcesses[processName];
|
||||||
|
console.log(`${processName} process exited with code ${code} and signal ${signal}`);
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log(`${processName} process started.`);
|
||||||
|
} else {
|
||||||
|
console.log(`${processName} process is already running.`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks the status of a process.
|
||||||
|
* @param {string} processName - The name of the process to check.
|
||||||
|
* @returns {string} A message indicating whether the process is running or not.
|
||||||
|
*/
|
||||||
|
export const checkProcessStatus = (processName) => {
|
||||||
|
const childProcess = runningProcesses[processName];
|
||||||
|
if (childProcess) {
|
||||||
|
// Check if the process is running
|
||||||
|
if (!childProcess.killed) {
|
||||||
|
return `${processName} process is running.`;
|
||||||
|
} else {
|
||||||
|
return `${processName} process is not running.`;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return `${processName} process is not running.`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Kills a running process.
|
||||||
|
* @param {string} processName - The name of the process to kill.
|
||||||
|
*/
|
||||||
|
export const killProcess = (processName) => {
|
||||||
|
const childProcess = runningProcesses[processName];
|
||||||
|
if (childProcess) {
|
||||||
|
childProcess.kill();
|
||||||
|
console.log(`${processName} process killed.`);
|
||||||
|
} else {
|
||||||
|
console.log(`${processName} process is not running.`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const getRunningProcesses = () => runningProcesses;
|
||||||
182
client/op25Handler/modules/op25ConfigGenerators.mjs
Normal file
182
client/op25Handler/modules/op25ConfigGenerators.mjs
Normal file
@@ -0,0 +1,182 @@
|
|||||||
|
import { promises as fs } from 'fs';
|
||||||
|
|
||||||
|
class OP25ConfigObject {
|
||||||
|
constructor() { }
|
||||||
|
|
||||||
|
async exportToFile(filename) {
|
||||||
|
try {
|
||||||
|
const jsonConfig = JSON.stringify(this, null, 2);
|
||||||
|
await fs.writeFile(filename, jsonConfig);
|
||||||
|
console.log(`Config exported to ${filename}`);
|
||||||
|
} catch (error) {
|
||||||
|
console.error(`Error exporting config to ${filename}: ${error}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class P25ConfigGenerator extends OP25ConfigObject {
|
||||||
|
constructor({ systemName, controlChannels, tagsFile, whitelistFile = undefined }) {
|
||||||
|
super();
|
||||||
|
const controlChannelsString = controlChannels.join(',');
|
||||||
|
this.channels = new channelConfig({
|
||||||
|
"channelName": systemName,
|
||||||
|
"systemName": systemName,
|
||||||
|
"enableAnalog": "off",
|
||||||
|
"demodType": "cqpsk",
|
||||||
|
"cqpskTracking": true,
|
||||||
|
"filterType": "rc"
|
||||||
|
});
|
||||||
|
this.devices = new deviceConfig({
|
||||||
|
"gain": "LNA:36"
|
||||||
|
});
|
||||||
|
this.trunking = new trunkingConfig({
|
||||||
|
"module": "tk_p25.py",
|
||||||
|
"systemName": systemName,
|
||||||
|
"controlChannelsString": controlChannelsString,
|
||||||
|
"tagsFile": tagsFile,
|
||||||
|
"whitelist": whitelistFile
|
||||||
|
});
|
||||||
|
this.audio = new audioConfig({});
|
||||||
|
this.terminal = new terminalConfig({});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class NBFMConfigGenerator extends OP25ConfigObject {
|
||||||
|
constructor({ systemName, frequency, nbfmSquelch = -70 }) {
|
||||||
|
super();
|
||||||
|
this.channels = new channelConfig({
|
||||||
|
"channelName": systemName,
|
||||||
|
"enableAnalog": "on",
|
||||||
|
"nbfmSquelch": nbfmSquelch,
|
||||||
|
"frequency": frequency,
|
||||||
|
"demodType": "fsk4",
|
||||||
|
"filterType": "widepulse"
|
||||||
|
});
|
||||||
|
this.devices = new deviceConfig({
|
||||||
|
"gain": "LNA:32"
|
||||||
|
});
|
||||||
|
this.audio = new audioConfig({});
|
||||||
|
this.terminal = new terminalConfig({});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class channelConfig {
|
||||||
|
constructor({
|
||||||
|
channelName = "Voice_ch1",
|
||||||
|
device = "sdr0",
|
||||||
|
systemName,
|
||||||
|
metaStreamName,
|
||||||
|
demodType, // cqpsk: P25; fsk4: everything else
|
||||||
|
cqpskTracking,
|
||||||
|
trackingThreshold = 120,
|
||||||
|
trackingFeedback = 0.75,
|
||||||
|
destination = "udp://127.0.0.1:23456",
|
||||||
|
excess_bw = 0.2,
|
||||||
|
filterType = "rc", // rc: P25; widepulse: analog
|
||||||
|
ifRate = 24000,
|
||||||
|
plot = "",
|
||||||
|
symbolRate = 4800,
|
||||||
|
enableAnalog, //[on, off, auto]
|
||||||
|
nbfmDeviation = 4000, // only needed if analog is enabled
|
||||||
|
nbfmSquelch = -50, // only needed if analog is enabled
|
||||||
|
frequency, // only needed if analog is enabled
|
||||||
|
blacklist,
|
||||||
|
whitelist,
|
||||||
|
cryptKeys
|
||||||
|
}) {
|
||||||
|
// Core Configs
|
||||||
|
this.name = channelName;
|
||||||
|
this.device = device;
|
||||||
|
this.demod_type = demodType;
|
||||||
|
this.destination = destination;
|
||||||
|
this.excess_bw = excess_bw;
|
||||||
|
this.filter_type = filterType;
|
||||||
|
this.if_rate = ifRate;
|
||||||
|
this.plot = plot;
|
||||||
|
this.symbol_rate = symbolRate;
|
||||||
|
this.enable_analog = enableAnalog;
|
||||||
|
|
||||||
|
// P25 config
|
||||||
|
if (!enableAnalog || enableAnalog === "off" || systemName) this.trunking_sysname = systemName;
|
||||||
|
if (!enableAnalog || enableAnalog === "off" || systemName && metaStreamName) this.meta_stream_name = metaStreamName ?? "";
|
||||||
|
if (!enableAnalog || enableAnalog === "off" || systemName) this.cqpsk_tracking = cqpskTracking;
|
||||||
|
if (!enableAnalog || enableAnalog === "off" || systemName) this.tracking_threshold = trackingThreshold;
|
||||||
|
if (!enableAnalog || enableAnalog === "off" || systemName) this.tracking_feedback = trackingFeedback;
|
||||||
|
if (!enableAnalog || enableAnalog === "off" || systemName && blacklist) this.blacklist = blacklist ?? "";
|
||||||
|
if (!enableAnalog || enableAnalog === "off" || systemName && whitelist) this.whitelist = whitelist ?? "";
|
||||||
|
if (!enableAnalog || enableAnalog === "off" || systemName && cryptKeys) this.crypt_keys = cryptKeys ?? "";
|
||||||
|
|
||||||
|
// Analog config
|
||||||
|
if (enableAnalog === "on" || enableAnalog === "auto") this.nbfm_deviation = nbfmDeviation;
|
||||||
|
if (enableAnalog === "on" || enableAnalog === "auto") this.nbfm_squelch = nbfmSquelch;
|
||||||
|
if (enableAnalog === "on" || enableAnalog === "auto") this.frequency = frequency;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class deviceConfig {
|
||||||
|
constructor({ args = "rtl", gain = "LNA:32", gainMode = false, name = "sdr0", offset = 0, ppm = 0.0, sampleRate = 1920000, tunable = true }) {
|
||||||
|
this.args = args
|
||||||
|
this.gains = gain
|
||||||
|
this.gain_mode = gainMode
|
||||||
|
this.name = name
|
||||||
|
this.offset = offset
|
||||||
|
this.ppm = ppm
|
||||||
|
this.rate = sampleRate
|
||||||
|
this.usable_bw_pct = 0.85
|
||||||
|
this.tunable = tunable
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class trunkingConfig {
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param {object} *
|
||||||
|
*/
|
||||||
|
constructor({ module, systemName, controlChannelsString, tagsFile = "", nac = "0x0", wacn = "0x0", cryptBehavior = 2, whitelist = "", blacklist = "" }) {
|
||||||
|
this.module = module;
|
||||||
|
this.chans = [{
|
||||||
|
"nac": nac,
|
||||||
|
"wacn": wacn,
|
||||||
|
"sysname": systemName,
|
||||||
|
"control_channel_list": controlChannelsString,
|
||||||
|
"whitelist": whitelist,
|
||||||
|
"blacklist": blacklist,
|
||||||
|
"tgid_tags_file": tagsFile,
|
||||||
|
"tdma_cc": false,
|
||||||
|
"crypt_behavior": cryptBehavior
|
||||||
|
}];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class audioConfig {
|
||||||
|
constructor({ module = "sockaudio.py", port = 23456, deviceName = "default" }) {
|
||||||
|
this.module = module;
|
||||||
|
this.instances = [{
|
||||||
|
"instance_name": "audio0",
|
||||||
|
"device_name": deviceName,
|
||||||
|
"udp_port": port,
|
||||||
|
"audio_gain": 2.0,
|
||||||
|
"number_channels": 1
|
||||||
|
}];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class metadataStreamConfig {
|
||||||
|
constructor({ }) {
|
||||||
|
this.module = "";
|
||||||
|
this.streams = [];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class terminalConfig {
|
||||||
|
constructor({ module = "terminal.py", terminalType = "http:0.0.0.0:8080" }) {
|
||||||
|
this.module = module;
|
||||||
|
this.terminal_type = terminalType;
|
||||||
|
this.curses_plot_interval = 0.1;
|
||||||
|
this.http_plot_interval = 1.0;
|
||||||
|
this.http_plot_directory = "../www/images";
|
||||||
|
this.tuning_step_large = 1200;
|
||||||
|
this.tuning_step_small = 100;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
14
client/op25Handler/op25Handler.mjs
Normal file
14
client/op25Handler/op25Handler.mjs
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
let currentSystem = undefined;
|
||||||
|
|
||||||
|
|
||||||
|
export const openOP25 = async (systemName) => {
|
||||||
|
currentSystem = systemName;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const closeOP25 = async () => {
|
||||||
|
currentSystem = undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const getCurrentSystem = async () => {
|
||||||
|
return currentSystem;
|
||||||
|
}
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
import { SlashCommandBuilder, ActionRowBuilder, ButtonBuilder, ButtonStyle } from 'discord.js';
|
import { SlashCommandBuilder, ActionRowBuilder, ButtonBuilder, ButtonStyle } from 'discord.js';
|
||||||
import { requestNodeJoinSystem, checkIfNodeIsConnectedToVC } from '../../modules/socketServerWrappers.mjs';
|
import { requestNodeJoinSystem, checkIfNodeIsConnectedToVC, checkIfNodeHasOpenDiscordClient, getNodeCurrentListeningSystem } from '../../modules/socketServerWrappers.mjs';
|
||||||
import { getSystemsByNuid, getAllSystems, getSystemByName } from '../../modules/mongoSystemsWrappers.mjs';
|
import { getSystemsByNuid, getAllSystems, getSystemByName } from '../../modules/mongoSystemsWrappers.mjs';
|
||||||
|
|
||||||
// Exporting data property
|
// Exporting data property
|
||||||
@@ -63,11 +63,22 @@ export async function execute(nodeIo, interaction) {
|
|||||||
// Get all open socket nodes
|
// Get all open socket nodes
|
||||||
const openSockets = [...await nodeIo.allSockets()]; // TODO - Filter the returned nodes to only nodes that have the radio capability
|
const openSockets = [...await nodeIo.allSockets()]; // TODO - Filter the returned nodes to only nodes that have the radio capability
|
||||||
console.log("All open sockets: ", openSockets);
|
console.log("All open sockets: ", openSockets);
|
||||||
|
|
||||||
var availableNodes = [];
|
var availableNodes = [];
|
||||||
// Check each open socket to see if the node has the requested system
|
// Check each open socket to see if the node has the requested system
|
||||||
await Promise.all(openSockets.map(async openSocket => {
|
await Promise.all(openSockets.map(async openSocket => {
|
||||||
openSocket = await nodeIo.sockets.sockets.get(openSocket);
|
openSocket = await nodeIo.sockets.sockets.get(openSocket);
|
||||||
|
// Check if the node has an existing open client (meaning the radio is already being listened to)
|
||||||
|
const hasOpenClient = await checkIfNodeHasOpenDiscordClient(openSocket);
|
||||||
|
if (hasOpenClient) {
|
||||||
|
let currentSystem = await getNodeCurrentListeningSystem(openSocket);
|
||||||
|
if (currentSystem != system.name) {
|
||||||
|
console.log("Node is listening to a different system than requested", openSocket.node.name);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if the bot has an open voice connection in the requested server already
|
||||||
const connected = await checkIfNodeIsConnectedToVC(nodeIo, interaction.guild.id, openSocket.node.nuid);
|
const connected = await checkIfNodeIsConnectedToVC(nodeIo, interaction.guild.id, openSocket.node.nuid);
|
||||||
console.log("Connected:", connected);
|
console.log("Connected:", connected);
|
||||||
if (!connected) {
|
if (!connected) {
|
||||||
@@ -76,6 +87,7 @@ export async function execute(nodeIo, interaction) {
|
|||||||
availableNodes.push(openSocket);
|
availableNodes.push(openSocket);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}));
|
}));
|
||||||
|
|
||||||
console.log("Availble nodes:", availableNodes.map(socket => socket.node.name));
|
console.log("Availble nodes:", availableNodes.map(socket => socket.node.name));
|
||||||
|
|||||||
@@ -182,6 +182,51 @@ export const getAllSocketsConnectedToVC = async (nodeIo, guildId) => {
|
|||||||
return socketsConnectedToVC;
|
return socketsConnectedToVC;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if the given node has an open discord client
|
||||||
|
* @param {any} openSocket The open socket connection with the node to check
|
||||||
|
* @returns {boolean} If the given node has an open discord client or not
|
||||||
|
*/
|
||||||
|
export const checkIfNodeHasOpenDiscordClient = async (openSocket) => {
|
||||||
|
// Check the open socket to see if the node has an open discord client
|
||||||
|
let hasOpenDiscordClient = false;
|
||||||
|
await new Promise((res) => {
|
||||||
|
openSocket.emit('node-check-discord-open-client', (status) => {
|
||||||
|
if (status) {
|
||||||
|
console.log("Socket has an open discord client:", openSocket.node.name, status);
|
||||||
|
hasOpenDiscordClient = true;
|
||||||
|
} else {
|
||||||
|
console.log("Socket does NOT have an open discord client:", openSocket.node.name);
|
||||||
|
}
|
||||||
|
res();
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
return hasOpenDiscordClient;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const getNodeCurrentListeningSystem = async (openSocket) => {
|
||||||
|
const hasOpenClient = checkIfNodeHasOpenDiscordClient(openSocket);
|
||||||
|
if (!hasOpenClient) return undefined;
|
||||||
|
|
||||||
|
// check what system the socket is listening to
|
||||||
|
let currentSystem = undefined;
|
||||||
|
await new Promise((res) => {
|
||||||
|
openSocket.emit('node-check-current-system', (system) => {
|
||||||
|
if (system) {
|
||||||
|
console.log("Socket is listening to system:", openSocket.node.name, system);
|
||||||
|
currentSystem = system;
|
||||||
|
} else {
|
||||||
|
console.log("Socket is not currently listening to a system:", openSocket.node.name);
|
||||||
|
}
|
||||||
|
res();
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
return currentSystem;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Wrapper to check if the given NUID is connected to a VC
|
* Wrapper to check if the given NUID is connected to a VC
|
||||||
* @param {any} nodeIo The nodeIo object that contains the IO server
|
* @param {any} nodeIo The nodeIo object that contains the IO server
|
||||||
|
|||||||
Reference in New Issue
Block a user