UNPKG

web-remote-control

Version:

Fast, real-time, remote control of devices (drones, boats, etc) from the web.

291 lines (238 loc) 8.87 kB
/********************************************************************* * * * Copyright 2016 Simon M. Werner * * * * Licensed to the Apache Software Foundation (ASF) under one * * or more contributor license agreements. See the NOTICE file * * distributed with this work for additional information * * regarding copyright ownership. The ASF licenses this file * * to you under the Apache License, Version 2.0 (the * * "License"); you may not use this file except in compliance * * with the License. You may obtain a copy of the License at * * * * http://www.apache.org/licenses/LICENSE-2.0 * * * * Unless required by applicable law or agreed to in writing, * * software distributed under the License is distributed on an * * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * * KIND, either express or implied. See the License for the * * specific language governing permissions and limitations * * under the License. * * * *********************************************************************/ 'use strict'; var makeUID = require('shortid').generate; /** * The DeviceManager manages toys and controllers (devices). Devices operate on * a given channel, both the toy and controller require the same channel. There * can be multiple toys and controllers on the same channel. */ function DeviceManager(settings) { settings = settings ? settings : {}; this.onlyOneControllerPerChannel = settings.onlyOneControllerPerChannel === true ? true : false; this.onlyOneToyPerChannel = settings.onlyOneToyPerChannel === true ? true : false; this.allowObservers = settings.allowObservers === true ? true : false; this.list = {}; } /** * Add a new toy/controller to the device manager. * @param {string} deviceType 'toy' or 'controller' or 'observer'. * @param {string} channel The channel to operate on (this is provided by the device). * @param {object} socket The socket details. * @param {number} seqNum The sequence number from the device. * @param {string} protocol 'tcp' or 'udp4' * @return {uid} The UID of the device. */ DeviceManager.prototype.add = function(deviceType, channel, socket, seqNum) { switch (undefined) { // eslint-disable-line default-case case deviceType: case channel: case socket: case seqNum: console.error('DeviceManager.add(): one of the inputs are undefined.'); return undefined; } if (!this.validDeviceType(deviceType)) { console.error('DeviceManager.getAll(): "deviceType" should be "toy" or "controller" or "observer" (if allowObservers is true), not: ', deviceType); return undefined; } // Remove the controller / toy that already exists if ((deviceType === 'controller' && this.onlyOneControllerPerChannel) || (deviceType === 'toy' && this.onlyOneToyPerChannel)) { var devUID = this.findByDeviceTypeAndChannel(deviceType, channel); if (devUID) { this.remove(devUID); } } var uid = makeUID(); this.list[uid] = { deviceType: deviceType, channel: channel, socket: socket, seqNum: seqNum }; return uid; }; /** * Remove a device from the device manager. * @param {string} uid The UID * @return {boolean} true if successful, otherwise false. */ DeviceManager.prototype.remove = function(uid) { var dev = this.list[uid]; if (typeof dev === 'undefined') { return false; } try { dev.socket.close(); } catch (ex) { // okay if there is an error - socket may already be closed. } try { delete this.list[uid]; } catch (ex) { return false; } return true; }; /** * Checks if the device is allowed the given action * @param {string} uid The UID of the device * @param {string} actionType The action to test * @return {boolean} true if action is allowed, otherwise false. */ DeviceManager.prototype.isAllowedAction = function(uid, actionType) { var dev = this.list[uid]; if (typeof dev === 'undefined') { return false; } if (dev.deviceType === 'toy' && actionType === 'status') return true; if (dev.deviceType === 'controller' && actionType === 'command') return true; return false; }; /** * Remove a device from the device manager. * @param {string} uid The UID * @return {boolean} true if successful, otherwise false. */ DeviceManager.prototype.removeBySocketId = function(socketId) { var uid = this.findBySocketId(socketId); return this.remove(uid); }; DeviceManager.prototype.findByDeviceTypeAndChannel = function(deviceType, channel) { var self = this; var foundUid = null; Object.keys(this.list).forEach( function(uid) { var dev = self.list[uid]; if (dev.deviceType === deviceType && dev.channel === channel) { foundUid = uid; } }); return foundUid; }; DeviceManager.prototype.findBySocketId = function(socketId) { var self = this; var foundUid = null; Object.keys(this.list).forEach( function(uid) { var dev = self.list[uid]; if (dev.socket.socketId === socketId) { foundUid = uid; } }); return foundUid; }; /** * Get a device from a UID. * @param {string} uid The UID. * @return {object} The device details. */ DeviceManager.prototype.get = function(uid) { return this.list[uid]; }; /** * Get a socket from the device with the given UID. * @param {string} uid The UID. * @return {object} The socket details. */ DeviceManager.prototype.getSocket = function(uid) { return this.list[uid].socket; }; /** * Get all devices on a channel. But only by device type (toy/controller). * @param {string} deviceType 'toy' or 'controllers' * @param {string} channel The channel. * @return {array} An array of UID strings. */ DeviceManager.prototype.getAll = function(deviceType, channel) { if (!this.validDeviceType(deviceType)) { console.error('DeviceManager.getAll(): "deviceType" should be "toy" or "controller" or "observer" (if allowObservers is true), not: ', deviceType); return []; } var devList = []; var self = this; Object.keys(this.list).forEach( function(uid) { var dev = self.list[uid]; if (dev.deviceType === deviceType && dev.channel === channel) { devList.push(uid); } }); return devList; }; /** * Update the device with the given ip and port parameters. Then return the * device. * @param {string} uid The UID for the device. * @param {string} ip The IP the device was last seen on. * @param {number} port The port the device was last seen on. * @return {object} The updated device details. */ DeviceManager.prototype.update = function(uid, socket, seqNum) { if (isNaN(seqNum)) { console.error('DeviceManager.update(): provided "seqNum" is not a number'); return undefined; } var device = this.list[uid]; if (!device) { return undefined; } device.socket = socket || device.socket; device.seqNum = seqNum; return device; }; /** * Make sure the sequence number is okay. * @param {string} uid UID for the device * @param {number} seqNum The seqNum to check * @return {boolean} True or false */ DeviceManager.prototype.isLatestSeqNum = function(uid, seqNum) { if (isNaN(seqNum)) { console.error('DeviceManager.isLatestSeqNum(): provided "seqNum" is not a number'); return false; } var device = this.list[uid]; if (!device) { return false; } if (device.seqNum > seqNum) { return false; } return true; }; /** * Confirm the device type is correct. * @param {string} type The name of the device to check. * @return {boolean} True if the device type is valid. */ DeviceManager.prototype.validDeviceType = function(deviceType) { switch (deviceType) { case 'toy': case 'controller': return true; case 'observer': // observer only valid if allowObserver === true return this.allowObservers; default: return false; } }; module.exports = DeviceManager;