diff --git a/backend/config/express.js b/backend/config/express.js index ea9afd9..7c1e107 100644 --- a/backend/config/express.js +++ b/backend/config/express.js @@ -2,7 +2,6 @@ var path = require("path"); var express = require("express"); var bodyParser = require("body-parser"); -var cookie_domain = process.env['REACT_APP_COOKIE_DOMAIN'] != null ? process.env['REACT_APP_COOKIE_DOMAIN'] : '.openmhz.com'; //'https://s3.amazonaws.com/robotastic'; var backend_server = process.env['REACT_APP_BACKEND_SERVER'] != null ? process.env['REACT_APP_BACKEND_SERVER'] : 'https://api.openmhz.com'; var frontend_server = process.env['REACT_APP_FRONTEND_SERVER'] != null ? process.env['REACT_APP_FRONTEND_SERVER'] : 'https://openmhz.com'; var socket_server = process.env['REACT_APP_SOCKET_SERVER'] != null ? process.env['REACT_APP_SOCKET_SERVER'] : 'wss://socket.openmhz.com'; //'https://s3.amazonaws.com/robotastic'; diff --git a/backend/config/secrets.js b/backend/config/secrets.js deleted file mode 100644 index a7d2c54..0000000 --- a/backend/config/secrets.js +++ /dev/null @@ -1,15 +0,0 @@ -/** Important **/ -/** You should not be committing this file to GitHub **/ -/** Repeat: DO! NOT! COMMIT! THIS! FILE! TO! YOUR! REPO! **/ -var host = process.env['MONGO_NODE_DRIVER_HOST'] != null ? process.env['MONGO_NODE_DRIVER_HOST'] : 'localhost'; -var port = process.env['MONGO_NODE_DRIVER_PORT'] != null ? process.env['MONGO_NODE_DRIVER_PORT'] : 27017; -var mongoUrl = 'mongodb://' + host + ':' + port + '/scanner'; - - - -const secrets = { - db: mongoUrl, - sessionSecret: "letthisbeyoursecret" -} - -module.exports = secrets diff --git a/backend/controllers/calls.js b/backend/controllers/calls.js index c1889cc..8d70978 100644 --- a/backend/controllers/calls.js +++ b/backend/controllers/calls.js @@ -1,16 +1,12 @@ const { ObjectId } = require('mongodb'); -var db = require('../db'); -var mongoose = require("mongoose"); var { callModel: Call } = require("../models/call"); -var Star = require("../models/star"); +const Group = require("../models/group"); var defaultNumResults = 50; - var channels = {}; - async function get_calls(query, numResults, res) { var calls = []; @@ -27,12 +23,9 @@ async function get_calls(query, numResults, res) { url: true }; - const transCollection = db.get().collection('calls'); const sort = { length: -1 }; try { - let cursor = transCollection.find(query.filter, fields).sort(sort).limit(numResults); - await cursor.forEach(function (item) { - + for await (const item of Call.find(query.filter, fields).sort(sort).limit(numResults)) { call = { _id: item._id.toHexString(), talkgroupNum: item.talkgroupNum, @@ -45,8 +38,7 @@ async function get_calls(query, numResults, res) { len: Math.round(item.len) }; calls.push(call); - - }); + } res.contentType('json'); res.send(JSON.stringify({ @@ -105,13 +97,10 @@ async function build_filter(filter_type, code, start_time, direction, shortName, if (filter_type) { if ((filter_type == "group") && code && (code.indexOf(',') == -1)) { - const groupCollection = db.get().collection("groups"); + - const group = await groupCollection.findOne({ - 'shortName': shortName, - '_id': ObjectId.createFromHexString(code) - }); + const group = await Group.findOne({ 'shortName': shortName, '_id': ObjectId.createFromHexString(code)}).exec(); if (!group) { console.warn("[" + shortName + "] Error - build_filter() group is null " + err); res.contentType('json'); @@ -173,8 +162,8 @@ exports.get_card = async function (req, res) { })); return; } - const transCollection = db.get().collection('calls'); - const item = await transCollection.findOne({ '_id': o_id }); + + const item = await Call.findById(o_id).exec(); //console.log(util.inspect(item)); if (item) { @@ -297,8 +286,7 @@ exports.get_call = async function (req, res) { })); return; } - const transCollection = db.get().collection('calls'); - const item = await transCollection.findOne({ '_id': o_id }); + const item = await Call.findById(o_id).exec(); if (item) { var call = package_call(item); diff --git a/backend/controllers/groups.js b/backend/controllers/groups.js index 602c66f..8db33e6 100644 --- a/backend/controllers/groups.js +++ b/backend/controllers/groups.js @@ -1,10 +1,7 @@ -var db = require('../db'); +const Group = require("../models/group"); exports.get_groups = async function (req, res) { - - const grp_coll = db.get().collection('groups'); - const cursor = grp_coll.find({ 'shortName': req.params.shortName.toLowerCase() }); - const grp_results = await cursor.toArray(); + const grp_results = await Group.find({ shortName: req.params.shortName.toLowerCase()}).sort("position").exec(); res.contentType('json'); res.send(JSON.stringify(grp_results)); diff --git a/backend/controllers/talkgroups.js b/backend/controllers/talkgroups.js index c4189fd..c5475f5 100644 --- a/backend/controllers/talkgroups.js +++ b/backend/controllers/talkgroups.js @@ -1,9 +1,7 @@ -var db = require('../db'); +const Talkgroup = require("../models/talkgroup"); exports.get_talkgroups = async function (req, res) { - const tg_coll = db.get().collection('talkgroups'); - const cursor = tg_coll.find({ 'shortName': req.params.shortName.toLowerCase() }) - const tg_results = await cursor.toArray(); + const tg_results = await Talkgroup.find({ 'shortName': req.params.shortName.toLowerCase()}) var talkgroups = {}; for (var tg in tg_results) { talkgroups[tg_results[tg].num] = tg_results[tg]; diff --git a/backend/db.js b/backend/db.js index 4306aa3..3f8bc28 100644 --- a/backend/db.js +++ b/backend/db.js @@ -1,11 +1,8 @@ var MongoClient = require('mongodb').MongoClient; -var config = require('./config/config.json'); -var test = require('assert'); + var host = process.env['MONGO_NODE_DRIVER_HOST'] != null ? process.env['MONGO_NODE_DRIVER_HOST'] : 'localhost'; var port = process.env['MONGO_NODE_DRIVER_PORT'] != null ? process.env['MONGO_NODE_DRIVER_PORT'] : 27017; -var dbUser = process.env['MONGO_TRUNK_FRONTEND_USER'] != null ? process.env['MONGO_TRUNK_FRONTEND_USER'] : null; -var dbPass = process.env['MONGO_TRUNK_FRONTEND_PASS'] != null ? process.env['MONGO_TRUNK_FRONTEND_PASS'] : null; var state = { db: null, diff --git a/backend/index.js b/backend/index.js index 4e0896c..e5fa8a5 100644 --- a/backend/index.js +++ b/backend/index.js @@ -1,9 +1,5 @@ var express = require("express"); -var passport = require("passport"); -var util = require("util"); - -var secrets = require("./config/secrets"); var configureExpress = require("./config/express"); var calls = require("./controllers/calls"); @@ -17,9 +13,8 @@ var config = require('./config/config.json'); var mongoose = require("mongoose"); const { ObjectId } = require('mongodb'); -var db = require('./db'); +const Group = require("./models/group"); -var System = require("./models/system"); var multer = require('multer'); // ------------------------------------------- @@ -51,9 +46,13 @@ configureExpress(app) // ------------------------------------------- +var host = process.env['MONGO_NODE_DRIVER_HOST'] != null ? process.env['MONGO_NODE_DRIVER_HOST'] : 'localhost'; +var port = process.env['MONGO_NODE_DRIVER_PORT'] != null ? process.env['MONGO_NODE_DRIVER_PORT'] : 27017; +var mongoUrl = 'mongodb://' + host + ':' + port + '/scanner'; + const connect = () => { - mongoose.connect(secrets.db).then((result) => { // Successfully connected + mongoose.connect(mongoUrl).then((result) => { // Successfully connected console.log("connected to Mongo"); }) .catch((err) => { @@ -258,32 +257,18 @@ io.sockets.on('connection', function (client) { clients[client.id].talkgroupNums = []; clients[client.id].timestamp = new Date(); if ((data.filterType == "group") && (data.filterCode.indexOf(',') == -1)) { - const groupCollection = db.get().collection("groups"); - if (!groupCollection) { - console.error("Error - unable to open groups collection: " + err); - delete clients[client.id]; - return; - } if (!ObjectId.isValid(data.filterCode)) { console.error("Error - Socket - Invalid Group ID: " + data.filterCode); delete clients[client.id]; return; } - let group; - try { - group = await groupCollection.findOne({ - 'shortName': data.shortName.toLowerCase(), - '_id': ObjectId.createFromHexString(data.filterCode) - }); - } catch (err) { - console.warn("[" + data.shortName.toLowerCase() + "] Error - WebSocket Group ID not Found! Error: " + err + " Group ID: " + data.filterCode); - delete clients[client.id]; - return; - } - if (group && clients[client.id]) { + + const group = await Group.findOne({ 'shortName': data.shortName.toLowerCase(), '_id': ObjectId.createFromHexString(data.filterCode) }); + + if (group) { clients[client.id].talkgroupNums = group.talkgroups; } else { - console.error("Error - Socket: Invalid group or Client: " + data.filterCode + " Shortname: " + data.shortName + " ClientID: " + client.id); + console.error("Error - Socket: Invalid group " + data.filterCode + " Shortname: " + data.shortName + " ClientID: " + client.id); delete clients[client.id]; return; } @@ -309,24 +294,14 @@ io.sockets.on('connection', function (client) { }); -//console.log("stats: " + util.inspect(call_stats)); -// Connect to Mongo on start -db.connect(function (err) { + +stats.init_stats(); + +server.listen(app.get("port"), (err) => { if (err) { - console.log('Unable to connect to Mongo.') - process.exit(1) + console.err(err.stack) } else { - stats.init_stats(); - - server.listen(app.get("port"), (err) => { - if (err) { - console.err(err.stack) - } else { - console.log(`App listening on port ${app.get("port")} [${process.env.NODE_ENV} mode]`) - } - }) - - + console.log(`App listening on port ${app.get("port")} [${process.env.NODE_ENV} mode]`) } }) diff --git a/backend/models/system_stat.js b/backend/models/system_stat.js new file mode 100644 index 0000000..edb586a --- /dev/null +++ b/backend/models/system_stat.js @@ -0,0 +1,27 @@ +var mongoose = require('mongoose'); + +let decodeErrorSchema = mongoose.Schema({ + errorHistory: [Number], + spikeHistory: [Number], +}) + +let talkgroupStatsSchema = mongoose.Schema({ + callCountHistory: [Number], + callAvgLenHistory: [Number] +}) +var systemStatSchema = mongoose.Schema({ + callTotals: [Number], + uploadErrors: [Number], + decodeErrorsFreq: { + type: Map, + of: decodeErrorSchema + }, + talkgroupStats: { + type: Map, + of: talkgroupStatsSchema + }, + shortName: String +}, { collection: 'system_stats' }); + + +module.exports = mongoose.model('SystemStat', systemStatSchema); diff --git a/backend/sys_stats.js b/backend/sys_stats.js index 4dedee0..2134961 100644 --- a/backend/sys_stats.js +++ b/backend/sys_stats.js @@ -1,76 +1,65 @@ var util = require("util"); var db = require('./db'); +const SystemStat = require("./models/system_stat"); +var System = require("./models/system"); var talkgroupStats = {}; var callTotals = {}; var uploadErrors = {}; let decodeErrorsFreq = {}; const timePeriod = 15; // in minutes -var spots = (24*60) / timePeriod; // the number of spots needed to keep track of 24 hours of stats +var spots = (24 * 60) / timePeriod; // the number of spots needed to keep track of 24 hours of stats async function updateActiveSystems() { // Go through all of the Systems - const sysCollection = db.get().collection('systems'); - var cursor = sysCollection.find(); - await cursor.forEach(function(item) { - // go through all the systems - // if you have recieved some calls during that last period, make the system active - if ((callTotals[item.shortName] != undefined) && (callTotals[item.shortName][0] > 0)) { - const active = true; - const callAvg = callTotals[item.shortName][0] / timePeriod; - const query = { shortName: item.shortName}; - const update = { $set: {"active": active,"callAvg": callAvg,"lastActive": new Date() }}; - const options = {upsert: true}; - sysCollection.updateOne(query, update, options); - - } else { - const query = { shortName: item.shortName}; - const update = { $set: {"active": false, "callAvg": 0}}; - const options = {upsert: true}; + for await (const item of System.find()) { + // go through all the systems + // if you have recieved some calls during that last period, make the system active + if ((callTotals[item.shortName] != undefined) && (callTotals[item.shortName][0] > 0)) { + item.active = true; + item.callAvg = callTotals[item.shortName][0] / timePeriod; + item.lastActive = new Date(); + await item.Save(); + } else { + item.active = false; + item.callAvg = 0; + await item.save(); + } - sysCollection.updateOne(query, update, options); - } - - }); + }; } -exports.initStats = async function() { +exports.initStats = async function () { // get the System Stats collection - - db.get().createCollection("system_stats").catch((err)=>{console.log("System Stats collection is ready")}); - const statsCollection = db.get().collection('system_stats'); - var cursor = statsCollection.find(); - // go through each of the Systems in Stats - await cursor.forEach(function(item) { - - // Talkgroup Stats - if (item.talkgroupStats != undefined) { - talkgroupStats[item.shortName] = item.talkgroupStats; - } + for await (const item of SystemStat.find()) { + // Talkgroup Stats + if (item.talkgroupStats != undefined) { + talkgroupStats[item.shortName] = item.talkgroupStats; + } - // Decode Errors - if (item.decodeErrorsFreq != undefined) { - decodeErrorsFreq[item.shortName] = item.decodeErrorsFreq; - } + // Decode Errors + if (item.decodeErrorsFreq != undefined) { + decodeErrorsFreq[item.shortName] = item.decodeErrorsFreq; + } - // Upload Error Totals - if (item.uploadErrors != undefined) { - uploadErrors[item.shortName] = item.uploadErrors; - } + // Upload Error Totals + if (item.uploadErrors != undefined) { + uploadErrors[item.shortName] = item.uploadErrors; + } - if (item.callTotals != undefined) { - callTotals[item.shortName] = item.callTotals; - } - }); + if (item.callTotals != undefined) { + callTotals[item.shortName] = item.callTotals; + } + }; } // keeps track of the number of calls that get uploaded with an audio file -exports.addError = function(call) { +exports.addError = function (call) { // if there is no Array associated with the system. if (uploadErrors[call.shortName] == undefined) { @@ -85,10 +74,7 @@ exports.addError = function(call) { } // Keeps track of the number of calls for each talkgroup for a system -exports.addCall = function(call) { - - - +exports.addCall = function (call) { // if you haven't started keeping track of stats for the System yet if (talkgroupStats[call.shortName] == undefined) { talkgroupStats[call.shortName] = {}; @@ -139,147 +125,147 @@ exports.addCall = function(call) { // This gets called when a Time Period is up -exports.shiftStats = async function() { - const statsCollection = db.get().collection('system_stats'); +exports.shiftStats = async function () { - // for all the systems in Error Stats - for (var shortName in uploadErrors) { - if (uploadErrors.hasOwnProperty(shortName)) { - // Update the DB with Error Stats and Error Totals - const query = { shortName: shortName}; - const update = { $set: { "uploadErrors": uploadErrors[shortName]}}; - const options = {upsert: true}; + // for all the systems in Error Stats + for (var shortName in uploadErrors) { + if (uploadErrors.hasOwnProperty(shortName)) { - statsCollection.updateOne(query, update, options); - // move everything back one after updating - for (var j = spots - 1; j > 0; j--) { - uploadErrors[shortName][j] = uploadErrors[shortName][j - 1]; - } - // reset the first spot back to 0, so you can count the next period - uploadErrors[shortName][0] = 0; + // Update the DB with Error Stats and Error Totals + const query = { shortName: shortName }; + const update = { $set: { "uploadErrors": uploadErrors[shortName] } }; + const options = { upsert: true }; + + await SystemStat.updateOne(query, update, options); + // move everything back one after updating + for (var j = spots - 1; j > 0; j--) { + uploadErrors[shortName][j] = uploadErrors[shortName][j - 1]; } + // reset the first spot back to 0, so you can count the next period + uploadErrors[shortName][0] = 0; } + } - - // for each system in decodeErrorsFreq - for (let shortName in decodeErrorsFreq) { - // if the system is in decodeErrorsFreq - if (decodeErrorsFreq.hasOwnProperty(shortName)) { - var sysErrors = decodeErrorsFreq[shortName]; + // for each system in decodeErrorsFreq + for (let shortName in decodeErrorsFreq) { - // for each freq in that systems stats - for (var freqNum in sysErrors ) { - if (sysErrors.hasOwnProperty(freqNum)) { - var freqErrors = sysErrors[freqNum]; + // if the system is in decodeErrorsFreq + if (decodeErrorsFreq.hasOwnProperty(shortName)) { + var sysErrors = decodeErrorsFreq[shortName]; - // move the history for that freq back - for (let j = spots - 1; j > 0; j--) { - let i = j - 1; - freqErrors.errorHistory[j] = freqErrors.errorHistory[i]; - freqErrors.spikeHistory[j] = freqErrors.spikeHistory[i]; - } + // for each freq in that systems stats + for (var freqNum in sysErrors) { + if (sysErrors.hasOwnProperty(freqNum)) { + var freqErrors = sysErrors[freqNum]; - if (freqErrors.totalLen > 0) { - freqErrors.errorHistory[0] = (freqErrors.errors / freqErrors.totalLen); - freqErrors.spikeHistory[0] = (freqErrors.spikes / freqErrors.totalLen); - } else { - freqErrors.errorHistory[0] = 0; - freqErrors.spikeHistory[0] = 0; - } - freqErrors.totalLen = 0; - freqErrors.errors = 0; - freqErrors.spikes = 0; + // move the history for that freq back + for (let j = spots - 1; j > 0; j--) { + let i = j - 1; + freqErrors.errorHistory[j] = freqErrors.errorHistory[i]; + freqErrors.spikeHistory[j] = freqErrors.spikeHistory[i]; + } + + if (freqErrors.totalLen > 0) { + freqErrors.errorHistory[0] = (freqErrors.errors / freqErrors.totalLen); + freqErrors.spikeHistory[0] = (freqErrors.spikes / freqErrors.totalLen); + } else { + freqErrors.errorHistory[0] = 0; + freqErrors.spikeHistory[0] = 0; + } + freqErrors.totalLen = 0; + freqErrors.errors = 0; + freqErrors.spikes = 0; + } + } + + const query = { shortName: shortName }; + const update = { $set: { "decodeErrorsFreq": decodeErrorsFreq[shortName] } }; + const options = { upsert: true }; + + await SystemStat.updateOne(query, update, options); + + } + } + + + + // for each system in talkgroupStats + for (let shortName in talkgroupStats) { + let callTotal = 0; + + // if the system is in stats + if (talkgroupStats.hasOwnProperty(shortName)) { + var sysTalkgroupStats = talkgroupStats[shortName]; + + // for each talkgroup in that systems stats + for (var talkgroupNum in sysTalkgroupStats) { + if (sysTalkgroupStats.hasOwnProperty(talkgroupNum)) { + var tg = sysTalkgroupStats[talkgroupNum]; + var tgHistoryTotal = 0; + // move the history for that talkgroup back + for (let j = spots - 1; j > 0; j--) { + let i = j - 1; + tgHistoryTotal += tg.callCountHistory[i]; + tg.callCountHistory[j] = tg.callCountHistory[i]; + tg.callAvgLenHistory[j] = tg.callAvgLenHistory[i]; + } + + // add to the total for that group + callTotal += tg.calls; + + + // figure out the history for current period in the history for this talkgroup; + tg.callCountHistory[0] = tg.calls; + tgHistoryTotal += tg.calls; + if (tg.calls > 0) { + tg.callAvgLenHistory[0] = Math.floor(tg.totalLen / tg.calls); + } else { + tg.callAvgLenHistory[0] = 0; + } + tg.calls = 0; + tg.totalLen = 0; + if (tgHistoryTotal == 0) { + // there has been no recent activity on this talkgroup. remove it from the stats. + delete stat[talkgroupNum]; } } - - const query = { shortName: shortName}; - const update = { $set: { "decodeErrorsFreq": decodeErrorsFreq[shortName]}}; - const options = {upsert: true}; - - statsCollection.updateOne(query, update, options); - } - } - - - // for each system in talkgroupStats - for (let shortName in talkgroupStats) { - let callTotal = 0; - - // if the system is in stats - if (talkgroupStats.hasOwnProperty(shortName)) { - var sysTalkgroupStats = talkgroupStats[shortName]; - - // for each talkgroup in that systems stats - for (var talkgroupNum in sysTalkgroupStats ) { - if (sysTalkgroupStats.hasOwnProperty(talkgroupNum)) { - var tg = sysTalkgroupStats[talkgroupNum]; - var tgHistoryTotal = 0; - // move the history for that talkgroup back - for (let j = spots - 1; j > 0; j--) { - let i = j - 1; - tgHistoryTotal += tg.callCountHistory[i]; - tg.callCountHistory[j] = tg.callCountHistory[i]; - tg.callAvgLenHistory[j] = tg.callAvgLenHistory[i]; - } - - // add to the total for that group - callTotal += tg.calls; - - - // figure out the history for current period in the history for this talkgroup; - tg.callCountHistory[0] = tg.calls; - tgHistoryTotal += tg.calls; - if (tg.calls > 0) { - tg.callAvgLenHistory[0] = Math.floor(tg.totalLen / tg.calls); - } else { - tg.callAvgLenHistory[0] = 0; - } - tg.calls = 0; - tg.totalLen = 0; - if (tgHistoryTotal ==0 ) { - // there has been no recent activity on this talkgroup. remove it from the stats. - delete stat[talkgroupNum]; - } - } + // figure out call totals for this shortName/Sys + if (callTotals[shortName] == undefined) { + callTotals[shortName] = new Array(); + for (var j = 0; j < spots; j++) { + callTotals[shortName][j] = 0; } - - // figure out call totals for this shortName/Sys - if (callTotals[shortName] == undefined) { - callTotals[shortName] = new Array(); - for (var j = 0; j < spots; j++) { - callTotals[shortName][j] = 0; - } - } - - for (var j = spots - 1; j > 0; j--) { - callTotals[shortName][j] = callTotals[shortName][j - 1]; - } - callTotals[shortName][0] = callTotal; - - - - const query = { shortName: shortName}; - const update = { $set: { "talkgroupStats": talkgroupStats[shortName], "callTotals": callTotals[shortName] }}; - const options = {upsert: true}; - - statsCollection.updateOne(query, update, options); } + + for (var j = spots - 1; j > 0; j--) { + callTotals[shortName][j] = callTotals[shortName][j - 1]; + } + callTotals[shortName][0] = callTotal; + + + + const query = { shortName: shortName }; + const update = { $set: { "talkgroupStats": talkgroupStats[shortName], "callTotals": callTotals[shortName] } }; + const options = { upsert: true }; + + await SystemStat.updateOne(query, update, options); } + } updateActiveSystems(); } -exports.callTotals = function(shortName) { +exports.callTotals = function (shortName) { return callTotals[shortName]; } -exports.talkgroupStats = function(shortName) { +exports.talkgroupStats = function (shortName) { return talkgroupStats[shortName]; } -exports.uploadErrors = function(shortName) { +exports.uploadErrors = function (shortName) { return uploadErrors[shortName]; } -exports.decodeErrorsFreq = function(shortName) { +exports.decodeErrorsFreq = function (shortName) { return decodeErrorsFreq[shortName]; } \ No newline at end of file