kopia lustrzana https://github.com/manuelkasper/sotlas-api
159 wiersze
4.0 KiB
JavaScript
159 wiersze
4.0 KiB
JavaScript
const reconnect = require('reconnect-net');
|
|
const wsManager = require('./ws-manager');
|
|
const carrier = require('carrier');
|
|
const config = require('./config');
|
|
const db = require('./db');
|
|
const utils = require('./utils');
|
|
|
|
const rbnSpotRegex = /^DX de (\S+):\s+(\S+)\s+(\S+)\s+(\S+)\s+(\S+)\s+dB\s+(\S+)\s+\S+\s+(CQ|DX)\s+(\d+)Z$/;
|
|
|
|
class RbnReceiver {
|
|
start() {
|
|
this.restartConnection();
|
|
|
|
wsManager.on('message', (ws, message) => {
|
|
if (message.rbnFilter !== undefined) {
|
|
console.log("Set RBN filter to " + JSON.stringify(message.rbnFilter));
|
|
ws.rbnFilter = message.rbnFilter;
|
|
|
|
this.sendSpotHistory(ws)
|
|
}
|
|
});
|
|
}
|
|
|
|
restartConnection() {
|
|
if (this.re)
|
|
this.re.disconnect();
|
|
|
|
this.resetTimer();
|
|
this.re = reconnect((stream) => {
|
|
console.log("Connected to RBN");
|
|
stream.write(config.rbn.login + "\r\n");
|
|
if (config.rbn.server.commands) {
|
|
config.rbn.server.commands.forEach(command => {
|
|
stream.write(command + "\r\n");
|
|
});
|
|
}
|
|
|
|
carrier.carry(stream, (line) => {
|
|
this.handleLine(line);
|
|
});
|
|
});
|
|
|
|
this.re.on('error', (err) => {
|
|
console.error(`RBN connection error: ${err}`);
|
|
});
|
|
|
|
this.re.connect(config.rbn.server);
|
|
}
|
|
|
|
resetTimer() {
|
|
if (this.timeout) {
|
|
clearTimeout(this.timeout);
|
|
}
|
|
|
|
this.timeout = setTimeout(() => {
|
|
console.error("RBN: timeout, reconnecting");
|
|
this.restartConnection();
|
|
}, config.rbn.timeout);
|
|
}
|
|
|
|
handleLine(line) {
|
|
this.resetTimer();
|
|
let matches = rbnSpotRegex.exec(line);
|
|
if (matches) {
|
|
let spot = {
|
|
timeStamp: new Date(),
|
|
callsign: matches[3],
|
|
homeCallsign: this.homeCallsign(matches[3]),
|
|
spotter: matches[1].replace("-#", ""),
|
|
frequency: parseFloat((matches[2]/1000).toFixed(4)),
|
|
mode: matches[4],
|
|
snr: parseInt(matches[5]),
|
|
speed: parseInt(matches[6])
|
|
};
|
|
|
|
// Check if this is a known SOTA activator
|
|
db.getDb().collection('activators').countDocuments({callsign: { $in: utils.makeCallsignVariations(spot.homeCallsign) }}, (error, result) => {
|
|
if (result > 0) {
|
|
spot.isActivator = true;
|
|
}
|
|
db.getDb().collection('rbnspots').insertOne(spot, (error, result) => {
|
|
// _id has now been added, but not in our preferred format
|
|
spot._id = spot._id.toHexString()
|
|
wsManager.broadcast({'rbnSpot': spot}, (ws) => {
|
|
if (!ws.rbnFilter) {
|
|
return false;
|
|
}
|
|
|
|
if (ws.rbnFilter.homeCallsign && ws.rbnFilter.homeCallsign.includes(spot.homeCallsign)) {
|
|
return true;
|
|
}
|
|
|
|
if (ws.rbnFilter.isActivator && spot.isActivator) {
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
});
|
|
});
|
|
});
|
|
}
|
|
}
|
|
|
|
sendSpotHistory(ws) {
|
|
// Send the spot history for the currently defined RBN filter
|
|
if (!ws.rbnFilter.homeCallsign && !ws.rbnFilter.isActivator) {
|
|
return;
|
|
}
|
|
|
|
let query = {};
|
|
|
|
if (ws.rbnFilter.homeCallsign) {
|
|
query.homeCallsign = ws.rbnFilter.homeCallsign;
|
|
}
|
|
if (ws.rbnFilter.isActivator) {
|
|
query.isActivator = true;
|
|
}
|
|
|
|
let maxAge = parseInt(ws.rbnFilter.maxAge) || 3600000;
|
|
query.timeStamp = {$gte: new Date(new Date().getTime() - maxAge)};
|
|
|
|
db.getDb().collection('rbnspots').find(query).sort({timeStamp: -1}).limit(config.rbn.maxSpotHistory).toArray((err, rbnSpots) => {
|
|
if (err) {
|
|
console.error(err);
|
|
return;
|
|
}
|
|
|
|
rbnSpots.forEach(spot => {
|
|
spot._id = spot._id.toHexString();
|
|
});
|
|
|
|
let response = {rbnSpotHistory: rbnSpots};
|
|
if (ws.rbnFilter.viewId) {
|
|
response.viewId = ws.rbnFilter.viewId;
|
|
}
|
|
wsManager.unicast(response, ws);
|
|
});
|
|
}
|
|
|
|
homeCallsign(callsign) {
|
|
let parts = callsign.split('/');
|
|
let longestPart = '';
|
|
parts.forEach(part => {
|
|
if (part.length > longestPart.length) {
|
|
longestPart = part;
|
|
}
|
|
})
|
|
|
|
// For UK callsigns, normalize them all to 2E/G/M for the sake of comparison
|
|
let matches = longestPart.match(/^(2[DEIJMUW]|G[DIJMUW]?|M[DIJMUW]?)(\d[A-Z]{2,3})$/)
|
|
if (matches) {
|
|
longestPart = matches[1].replace(/^2./, '2E').replace(/^G[DIJMUW]/, 'G').replace(/^M[DIJMUW]/, 'M') + matches[2]
|
|
}
|
|
return longestPart;
|
|
}
|
|
}
|
|
|
|
module.exports = RbnReceiver;
|