UNPKG

nsolid-manager

Version:

A manager that sets up and runs N|Solid for you

214 lines (178 loc) 5.19 kB
module.exports = createGraph // create a width * height 2d array function Array2D (width, height) { var out = [] for (var i = 0; i < width; i++) { out[i] = [] for (var j = 0; j < height; j++) { out[i][j] = 0 } } return out } var graphs = {} function createGraph (obj, storeTime) { var iso8601Time = (new Date(Math.round(Date.now() / 1000) * 1000)).toISOString() return { // update line chart add (graphName, value, longestGraph, now) { var key = toKey(graphName) if (!graphs[key]) { graphs[key] = [] } var graph = graphs[key] now = now || Date.now() var minDate = now - storeTime // TODO: handle the case where the previous record is > 2*interval var insert = [value, now] graph.push(insert) var l = graph.length var i = 0 // flush the latest value for this graph into leveldb createGraph.db && createGraph.db.put(toLevelKey(key), { type: 'graph', value: insert, graphKey: key, graphName: graphName }) // Instead of relying on pollInterval, find the first result that is // out of the allowed time range. Using that index we can slice off // collected metrics before it and maintain a consistent array length. for (; i < l; i++) { if (graph[0][1] >= minDate) { break } graph.shift() } if (longestGraph) { var diff = longestGraph.length - graph.length if (diff > 0) { var j = diff while (j--) { var sampleTime = longestGraph[j][1] // avoid adding 2 records for a single data point if (sampleTime < now) { graph.unshift([null, sampleTime]) } } } } }, get (graphName) { return graphs[toKey(graphName)] }, // spectrogram & spiffygram grid (graphName, width, height, fn) { var key = toKey(graphName) if (!graphs[key]) { graphs[key] = Array2D(width, height) } var graph = graphs[key] // wrap grid updates since they can be done in place, and // allow them to return updates (spectrogram) var update = fn(graph) || graph // flush the latest value for this graph into leveldb createGraph.db && createGraph.db.put(toLevelKey(key), { type: 'grid', value: update, graphKey: key, graphName: graphName, width: width, height: height }) } } function toLevelKey (key) { return 'graph-' + iso8601Time + '-' + key } function toKey (graphName) { return JSON.stringify(Object.assign({}, obj, { graph: graphName })) } } function arrayRemoved (a, b) { if (!b || !b.length) { return a } var ret = [] var l = a.length for (var i = 0; i < l; i++) { if (b.indexOf(a[i]) === -1) { ret.push(a[i]) } } return ret } function arrayAdded (a, b) { if (!a || !a.length) { return b } var ret = [] var l = b.length for (var i = 0; i < l; i++) { if (a.indexOf(b[i]) === -1) { ret.push(b[i]) } } return ret } createGraph.updateGraphSubscriptions = function updateGraphSubscriptions (socket, subscriptions, fn) { var added, removed var activeSubscriptions = socket.graphSubscriptions if (activeSubscriptions) { removed = arrayRemoved(activeSubscriptions, subscriptions) removed.forEach(function (graphName) { activeSubscriptions.splice(activeSubscriptions.indexOf(graphName), 1) }) added = arrayAdded(activeSubscriptions, subscriptions) } else { added = subscriptions } // if there were any changes, send the whole cache if (added.length || (removed && removed.length)) { socket.graphSubscriptions = subscriptions fn(null, createGraph.buildGraphPayload(socket)) } else { fn() } } createGraph.buildGraphPayload = function buildGraphPayload (socket) { if (!socket.graphSubscriptions) return null var payload = { graphs: {} } socket.graphSubscriptions.forEach(graphName => { payload.graphs[graphName] = graphs[graphName] }) return payload } createGraph.init = function init (db, pastSeconds, fn) { if (!db) return fn() createGraph.db = db var since = (new Date(Date.now() - pastSeconds * 1000 * 60)).toISOString() db.createReadStream({ gte: 'graph-' + since, keys: false }) .on('data', function onGraphData (d) { if (d.type === 'graph') { if (!graphs[d.graphKey]) { graphs[d.graphKey] = [] } graphs[d.graphKey].push(d.value) } else if (d.type === 'grid') { if (d.graphName.indexOf('pectrogram') > -1) { // spectrograms need to be exactly the right dimensions or they will break the UI. // So start off with an empty 2d array if (!graphs[d.graphKey]) { graphs[d.graphKey] = Array2D(d.width, d.height) } graphs[d.graphKey].push(d.value) while (graphs[d.graphKey].length > d.width) { graphs[d.graphKey].shift() } } else { graphs[d.graphKey] = d.value } } }).on('end', fn) }