const {PhysicalTrafficLight} = require('../physical/physical-traffic-light');
const EventEmitter = require('events');
////////////////////////////////////////////////
/**
* A physical device that can turn lights on or off.
* @memberof physical
* @abstract
* @extends EventEmitter
*/
class Device extends EventEmitter {
/**
* @param {(string|number)} serialNum - The unique serial number of the
* device.
* @param {boolean} [isConnected=true] - If the device is connected.
*/
constructor(serialNum, isConnected = true) {
super();
/**
* The unique serial number of the device.
* @type {(string|number)}
*/
this.serialNum = serialNum;
/**
* If the device is connected.
* @type {boolean}
*/
this.isConnected = isConnected;
}
/* istanbul ignore next */
/**
* Turns a light on or off.
* @abstract
* @param {number} lightID - Zero-based index of the light to turn:
* 0 is red, 1 is yellow and 2 is green.
* @param {number} onOff - On (1) or off (0).
*/
turn(lightID, onOff) {
throw new Error('Device#turn is abstract');
}
/**
* The physical traffic light associated with this device.
* @type {physical.PhysicalTrafficLight}
*/
get trafficLight() {
if (this._tl) return this._tl;
return this._tl = new PhysicalTrafficLight(this);
}
/**
* Marks this device as connected.
* @fires physical.Device#connected
*/
connect() {
if (this.isConnected) return;
this.isConnected = true;
this.trafficLight.sync();
/**
* Connected event.
* @event physical.Device#connected
*/
this.emit('connected');
}
/**
* Marks this device as disconnected.
* @fires physical.Device#disconnected
*/
disconnect() {
if (!this.isConnected) return;
this.isConnected = false;
/**
* Disconnected event.
* @event physical.Device#disconnected
*/
this.emit('disconnected');
}
}
////////////////////////////////////////////////
/**
* A Device Manager.
* @memberof physical
* @abstract
* @extends EventEmitter
*/
class DeviceManager extends EventEmitter {
/**
* Starts monitoring for devices. No-op by default.
* @fires physical.DeviceManager#added
* @fires physical.DeviceManager#removed
*/
startMonitoring() { }
/**
* Stops monitoring for devices. No-op by default.
*/
stopMonitoring() { }
/**
* If this device manager supports monitoring, `true` by default.
* @returns {boolean} If this device manager supports monitoring.
*/
supportsMonitoring() {
return true;
}
/**
* @param {string} type - The type of device managed.
*/
constructor(type) {
super();
this.type = type;
}
/* istanbul ignore next */
/**
* All devices being managed.
* @abstract
* @return {physical.Device[]} All devices being managed.
*/
allDevices() {
throw new Error('DeviceManager#allDevices is abstract');
}
/**
* Returns information about known devices.
* Known devices are either connected devices or
* devices that were once connected and then got disconnected.
* @returns {physical.DeviceInfo[]} Device info list.
*/
info() {
let devices = this.allDevices();
return devices.map(d => ({
type: this.type,
serialNum: d.serialNum,
status: d.isConnected ? 'connected' : 'disconnected'
}));
}
/**
* Logs information about known devices.
* @param {object} [logger=console] - A Console-like object for logging,
* with a log function.
* @see physical.DeviceManager#info
*/
logInfo(logger = console) {
let devicesInfo = this.info();
if (devicesInfo.length === 0) {
logger.log('No devices found');
} else {
logger.log('Known devices:');
devicesInfo.forEach(info =>
logger.log(`device ${info.serialNum}: ${info.status}`));
}
}
}
////////////////////////////////////////////////
/**
* Device added event.
* A monitoring Device Manager should fire this event
* when a device is added (connected).
* @event physical.DeviceManager#added
*/
/**
* Device removed event.
* A monitoring Device Manager should fire this event
* when a device is removed (disconnected).
* @event physical.DeviceManager#removed
*/
////////////////////////////////////////////////
/**
* @typedef {object} DeviceInfo
* @memberof physical
* @property {string} type - The type of the device.
* @property {(string|number)} serialNum - The serial number of the device.
* @property {string} status - The status of the device, either
* 'connected' or 'disconnected'.
*/
////////////////////////////////////////////////
module.exports = {
Device,
DeviceManager
};