UNPKG

@jcoreio/iron-pi-device-client

Version:

Client library for reading and writing Iron Pi input and output states

190 lines (157 loc) 6.84 kB
'use strict'; Object.defineProperty(exports, "__esModule", { value: true }); exports.IronPiDeviceClient = exports.EVENT_DEVICES_DETECTED = exports.EVENT_DEVICE_INPUT_STATES = undefined; var _log4jcore = require('log4jcore'); var _log4jcore2 = _interopRequireDefault(_log4jcore); var _typedEventEmitter = require('@jcoreio/typed-event-emitter'); var _typedEventEmitter2 = _interopRequireDefault(_typedEventEmitter); var _socketIpc = require('socket-ipc'); var _verror = require('verror'); var _verror2 = _interopRequireDefault(_verror); var _ipcTypes = require('./ipcTypes'); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } const log = (0, _log4jcore2.default)('iron-pi-device-client'); const EVENT_DEVICE_INPUT_STATES = exports.EVENT_DEVICE_INPUT_STATES = 'deviceInputStates'; const EVENT_DEVICES_DETECTED = exports.EVENT_DEVICES_DETECTED = 'devicesDetected'; class IronPiDeviceClient extends _typedEventEmitter2.default { /** * @param unixSocketPath Optional override of default UNIX socket path, which is '/tmp/socket-iron-pi'. */ /** mapping from request serial number to request state */ constructor({ unixSocketPath, host, port }) { super(); this._inFlightRequests = new Map(); this._curMethodSerial = 0; this.connected = false; this.deviceInputStates = undefined; this._onIPCMessage = event => { try { const msg = JSON.parse(event.data); log.trace('rx from driver:', msg); const { hardwareInfo, deviceInputStates, methodSerial, methodResult, methodError } = msg; if (methodSerial) { const request = this._inFlightRequests.get(methodSerial); if (request) { clearTimeout(request.timeout); if (methodError) { request.reject(Error(`method error: ${methodError}`)); } else { request.resolve(methodResult); } } else { log.error(`could not find method serial: ${methodSerial}`); } } if (hardwareInfo) { this._hardwareInfo = hardwareInfo; this.connected = true; this.emit(EVENT_DEVICES_DETECTED, hardwareInfo); } if (deviceInputStates) { this.deviceInputStates = deviceInputStates; this.emit(EVENT_DEVICE_INPUT_STATES, deviceInputStates); } } catch (err) { log.error('could not process an incoming IPC message', err); } }; const socketOpts = port ? { host, port } : { path: unixSocketPath || _ipcTypes.UNIX_SOCKET_PATH }; const ipcClient = this._ipcClient = new _socketIpc.MessageClient(socketOpts); ipcClient.on('message', this._onIPCMessage); ipcClient.on('error', err => this.emit('error', new _verror2.default(err, 'IronPiDeviceClient socket error'))); ipcClient.on('connection', () => this.emit('connection')); ipcClient.on('close', () => { this.connected = false; this.emit('close'); }); } start() { this._ipcClient.start().catch(err => { log.error('could not connect to iron pi hardware agent', err); }); } hardwareInfo() { return this._hardwareInfo; } setOutputs(setOutputs) { const { outputs } = setOutputs; if (!Array.isArray(outputs) || outputs.find(out => typeof out !== 'object')) throw Error('outputs property must be an array of Objects with the format {address: number, levels: Array<boolean>}'); this._send({ setOutputs }); } setLEDs(setLEDs) { const { leds } = setLEDs; if (!Array.isArray(leds) || leds.find(cmd => typeof cmd !== 'object')) throw Error('leds property must be an array of Objects with the format {address: number, colors: string, onTime: number, offTime: number, idleTime: number}'); this._send({ setLEDs }); } async getNetworkState() { return await this._callMethod(_ipcTypes.METHOD_GET_NETWORK_STATE); } async getNetworkSettings() { return await this._callMethod(_ipcTypes.METHOD_GET_NETWORK_SETTINGS); } async setNetworkSettings(settings) { await this._callMethod(_ipcTypes.METHOD_SET_NETWORK_SETTINGS, settings); } async isSSHEnabled() { return await this._callMethod(_ipcTypes.METHOD_IS_SSH_ENABLED); } async setSSHEnabled(enabled) { await this._callMethod(_ipcTypes.METHOD_SET_SSH_ENABLED, enabled); } async setSystemPassword(password) { await this._callMethod(_ipcTypes.METHOD_SET_SYSTEM_PASSWORD, password); } /** * Checks that a unit has no issues that need to be resolved before shipping * @return {string[]|[]} list of problems, or an empty array if there are no problems to report */ checkDeviceForShipping() { if (!this.connected) return ['not connected to the Iron Pi hardware agent']; const { deviceInputStates } = this; if (!deviceInputStates) return ['Iron Pi hardware agent has not reported input states']; const { hasError, inputStates } = deviceInputStates; const issues = []; if (hasError !== false) issues.push('Iron Pi hardware agent has errors'); const [mainBoardInputStates] = inputStates; if (mainBoardInputStates) { const { digitalInputs, analogInputs, connectButtonPressed } = mainBoardInputStates; for (let inputIdx = 0; inputIdx < 8; ++inputIdx) { const inputName = `Input ${inputIdx + 1}`; const digitalValue = digitalInputs[inputIdx]; const analogValue = analogInputs[inputIdx]; if (typeof digitalValue !== 'boolean') issues.push(`${inputName} digital input is missing`);else if (digitalValue) issues.push(`${inputName} digital input is high`); if (!Number.isFinite(analogValue)) issues.push(`${inputName} analog input is missing`);else if (Math.abs(analogValue) > 0.1) issues.push(`${inputName} analog reading is out of range: ${analogValue.toFixed(2)}V`); } if (connectButtonPressed) issues.push('Connect button is pressed'); } else { issues.push('Missing input states for main board'); } return issues; } _callMethod(methodName, methodArg = {}) { return new Promise((resolve, reject) => { if (this._ipcClient.isConnected()) { const methodSerial = ++this._curMethodSerial; const timeout = setTimeout(() => { this._inFlightRequests.delete(methodSerial); reject(Error('method call timed out')); }, 20000); this._inFlightRequests.set(methodSerial, { timeout, resolve, reject }); this._send({ methodName, methodSerial, methodArg }); } else { reject(Error('not connected to iron pi hardware agent')); } }); } _send(msg) { this._ipcClient.send(JSON.stringify(msg)); } } exports.IronPiDeviceClient = IronPiDeviceClient; exports.default = IronPiDeviceClient;