UNPKG

barnacles

Version:

Efficient processor of ambient RF decodings enabling real-time hyperlocal context. We believe in an open Internet of Things.

268 lines (224 loc) 8.22 kB
/** * Copyright reelyActive 2020-2024 * We believe in an open Internet of Things */ const Raddec = require('raddec'); const Device = require('./device'); const DEFAULT_DELAY_MILLISECONDS = 1000; const DEFAULT_DECODING_COMPILATION_MILLISECONDS = 2000; const DEFAULT_PACKET_COMPILATION_MILLISECONDS = 5000; const DEFAULT_KEEP_ALIVE_MILLISECONDS = 5000; const DEFAULT_HISTORY_MILLISECONDS = DEFAULT_KEEP_ALIVE_MILLISECONDS + DEFAULT_DELAY_MILLISECONDS + DEFAULT_DECODING_COMPILATION_MILLISECONDS; const DEFAULT_DISAPPEARANCE_MILLISECONDS = 15000; const DEFAULT_OBSERVED_EVENTS = [ Raddec.events.APPEARANCE, Raddec.events.DISPLACEMENT, Raddec.events.PACKETS, Raddec.events.KEEPALIVE ]; /** * MapManager Class * Manages a JavaScript Map instance. */ class MapManager { /** * MapManager constructor * @param {Object} options The options as a JSON object. * @constructor */ constructor(options) { options = options || {}; this.parameters = createParameters(options); this.store = new Map(); } /** * Retrieve the most recent data regarding all active/specified devices. * @param {String} deviceId The device identifier. * @param {Number} deviceIdType The device identifier type. * @param {Array} properties The optional properties to include. * @param {callback} callback Function to call on completion. */ retrieveDevices(deviceId, deviceIdType, properties, callback) { let self = this; let devices = {}; let isSpecificDevice = (deviceId && deviceIdType); let isSpecificProperties = Array.isArray(properties) && (properties.length > 0); if(isSpecificDevice) { let signature = deviceId + Raddec.identifiers.SIGNATURE_SEPARATOR + deviceIdType; let isDevicePresent = self.store.has(signature); if(isDevicePresent) { let device = self.store.get(signature); devices[signature] = device.assemble(properties); return callback(devices); } } else { for(const signature of self.store.keys()) { if(isSpecificProperties) { let device = self.store.get(signature); devices[signature] = device.assemble(properties); } else { devices[signature] = {}; } } return callback(devices); } return callback(null); } /** * Retrieve the most recent context regarding all active/specified devices. * @param {Array} signatures The deviceId signatures to query. * @param {Number} depth The (optional) depth of context to retrieve. * @param {callback} callback Function to call on completion. */ retrieveContext(signatures, depth, callback) { let self = this; let devices = {}; let deviceNetwork = new Map(); let currentDepth = 0; let isAllDevices = !signatures || (Array.isArray(signatures) && (signatures.length === 0)); // Step 1: iterate ALL the devices, collecting active first-level devices for(const signature of self.store.keys()) { let device = self.store.get(signature); if(isAllDevices || device.hasIdOrNearest(signatures)) { devices[signature] = device.assembleContext(); if(devices[signature].nearest) { devices[signature].nearest.forEach(function(item) { deviceNetwork.set(item.device, currentDepth); }); } } } // Step 2: retrieve the devices nearest to the active first-level devices deviceNetwork.forEach(function(value, signature) { let isIncluded = devices.hasOwnProperty(signature); if(!isIncluded) { let isDevicePresent = self.store.has(signature); if(isDevicePresent) { let device = self.store.get(signature); devices[signature] = device.assembleContext(); } else { devices[signature] = {}; } } }); // TODO: implement depth let isNoneFound = (Object.keys(devices).length === 0); if(!isAllDevices && isNoneFound) { return callback(null); } return callback(devices); } /** * Insert the given raddec. * @param {Raddec} raddec The given Raddec instance. * @param {function} handleEvent The function to call if event is triggered. */ insertRaddec(raddec, handleEvent) { let self = this; let device; let isDevicePresent = self.store.has(raddec.signature); if(isDevicePresent) { device = self.store.get(raddec.signature); } else { device = new Device(raddec.transmitterId, raddec.transmitterIdType, self.parameters); self.store.set(raddec.signature, device); } device.handleRaddec(raddec, self.parameters); } /** * Insert the given dynamb. * @param {Object} dynamb The given dynamb. */ insertDynamb(dynamb) { let self = this; let device; let signature = dynamb.deviceId + Raddec.identifiers.SIGNATURE_SEPARATOR + dynamb.deviceIdType; let isDevicePresent = self.store.has(signature); if(isDevicePresent) { device = self.store.get(signature); } else { device = new Device(dynamb.deviceId, dynamb.deviceIdType, self.parameters); self.store.set(signature, device); } device.handleDynamb(dynamb, self.parameters); } /** * Insert the given statid. * @param {Object} statid The given statid. */ insertStatid(statid) { let self = this; let signature = statid.deviceId + Raddec.identifiers.SIGNATURE_SEPARATOR + statid.deviceIdType; let isDevicePresent = self.store.has(signature); if(isDevicePresent) { let device = self.store.get(signature); device.handleStatid(statid); } } /** * Determine if there are events to handle. * @param {function} handleEvent The function to call for each event. * @param {function} callback The function to call on completion. */ determineEvents(handleEvent, callback) { let self = this; let currentTime = new Date().getTime(); let nextTimeout = currentTime + this.parameters.delayMilliseconds; this.store.forEach(function(device, transmitterSignature) { let timeout = device.determineEvents(self.parameters, handleEvent); let isDisappearance = (timeout < 0); if(isDisappearance) { self.store.delete(transmitterSignature); } else if(timeout < nextTimeout) { nextTimeout = timeout; } }); return callback(nextTimeout); } } /** * Create from the given options the parameters for determining and preparing * events. * @param {Object} options The options as a JSON object. */ function createParameters(options) { let delayMilliseconds = options.delayMilliseconds || DEFAULT_DELAY_MILLISECONDS; let decodingCompilationMilliseconds = options.decodingCompilationMilliseconds || DEFAULT_DECODING_COMPILATION_MILLISECONDS; let packetCompilationMilliseconds = options.packetCompilationMilliseconds || DEFAULT_PACKET_COMPILATION_MILLISECONDS; let historyMilliseconds = options.historyMilliseconds || DEFAULT_HISTORY_MILLISECONDS; let keepAliveMilliseconds = options.keepAliveMilliseconds || DEFAULT_KEEP_ALIVE_MILLISECONDS; let disappearanceMilliseconds = options.disappearanceMilliseconds || DEFAULT_DISAPPEARANCE_MILLISECONDS; let observedEvents = options.observedEvents || DEFAULT_OBSERVED_EVENTS; return { delayMilliseconds: delayMilliseconds, decodingCompilationMilliseconds: decodingCompilationMilliseconds, packetCompilationMilliseconds: packetCompilationMilliseconds, historyMilliseconds: historyMilliseconds, keepAliveMilliseconds: keepAliveMilliseconds, disappearanceMilliseconds: disappearanceMilliseconds, observedEvents: observedEvents }; } module.exports = MapManager;