UNPKG

node-aurora

Version:

Provides an interface to the Aurora Dreamband.

758 lines (540 loc) 29.8 kB
'use strict'; Object.defineProperty(exports, "__esModule", { value: true }); var _regenerator = require('babel-runtime/regenerator'); var _regenerator2 = _interopRequireDefault(_regenerator); var _promise = require('babel-runtime/core-js/promise'); var _promise2 = _interopRequireDefault(_promise); var _asyncToGenerator2 = require('babel-runtime/helpers/asyncToGenerator'); var _asyncToGenerator3 = _interopRequireDefault(_asyncToGenerator2); var _getPrototypeOf = require('babel-runtime/core-js/object/get-prototype-of'); var _getPrototypeOf2 = _interopRequireDefault(_getPrototypeOf); var _getIterator2 = require('babel-runtime/core-js/get-iterator'); var _getIterator3 = _interopRequireDefault(_getIterator2); var _classCallCheck2 = require('babel-runtime/helpers/classCallCheck'); var _classCallCheck3 = _interopRequireDefault(_classCallCheck2); var _possibleConstructorReturn2 = require('babel-runtime/helpers/possibleConstructorReturn'); var _possibleConstructorReturn3 = _interopRequireDefault(_possibleConstructorReturn2); var _createClass2 = require('babel-runtime/helpers/createClass'); var _createClass3 = _interopRequireDefault(_createClass2); var _inherits2 = require('babel-runtime/helpers/inherits'); var _inherits3 = _interopRequireDefault(_inherits2); var _events = require('events'); var _events2 = _interopRequireDefault(_events); var _serialport = require('serialport'); var _serialport2 = _interopRequireDefault(_serialport); var _AuroraSerialParser = require('./AuroraSerialParser'); var _AuroraSerialParser2 = _interopRequireDefault(_AuroraSerialParser); var _AuroraConstants = require('./AuroraConstants'); var _AuroraConstants2 = _interopRequireDefault(_AuroraConstants); var _util = require('./util'); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } var CONNECT_RETRY_DELAY_MS = 1500; var DISCONNECT_RETRY_DELAY_MS = 3000; var AuroraUsb = function (_EventEmitter) { (0, _inherits3.default)(AuroraUsb, _EventEmitter); (0, _createClass3.default)(AuroraUsb, null, [{ key: 'discoverAuroraPorts', value: function discoverAuroraPorts() { return (0, _util.promisify)(_serialport2.default.list, _serialport2.default)().then(function (ports) { var auroraPorts = []; var _iteratorNormalCompletion = true; var _didIteratorError = false; var _iteratorError = undefined; try { for (var _iterator = (0, _getIterator3.default)(ports), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) { var port = _step.value; //simplify these conditions when old //units are no longer in circulation if (port.pnpId && port.pnpId.indexOf('5740') !== -1 || port.pnpId && port.pnpId.indexOf('0483') !== -1 || port.productId && port.productId.indexOf('5740') !== -1 || port.vendorId && port.vendorId.indexOf(_AuroraConstants2.default.AURORA_USB_VID) !== -1 || port.manufacturer == "iWinks") { auroraPorts.push(port.comName.replace('cu.', 'tty.')); } } } catch (err) { _didIteratorError = true; _iteratorError = err; } finally { try { if (!_iteratorNormalCompletion && _iterator.return) { _iterator.return(); } } finally { if (_didIteratorError) { throw _iteratorError; } } } return auroraPorts; }); } }]); function AuroraUsb() { (0, _classCallCheck3.default)(this, AuroraUsb); var _this = (0, _possibleConstructorReturn3.default)(this, (AuroraUsb.__proto__ || (0, _getPrototypeOf2.default)(AuroraUsb)).call(this)); _this._onSerialDisconnect = function () { _this._setConnectionState(AuroraUsb.ConnectionStates.DISCONNECTED); }; _this._onSerialData = function (chunk) { _this._serialParser.parseChunk(chunk); }; _this._onParseLog = function (log) { log.origin = 'usb'; _this.emit('log', log); }; _this._onParseAuroraEvent = function (auroraEvent) { auroraEvent.origin = 'usb'; _this.emit('auroraEvent', auroraEvent); }; _this._onParseStreamData = function (streamData) { streamData.origin = 'usb'; _this.emit('streamData', streamData); }; _this._onParseCmdInputRequested = function () { _this.emit('cmdInputRequested'); }; _this._onParseCmdOutputReady = function (output) { _this.emit('cmdOutputReady', output); }; _this._onParseStreamTimestamp = function (streamTimestamp) { streamTimestamp.origin = 'usb'; _this.emit('streamTimestamp', streamTimestamp); }; _this._onParseError = function (error) { _this.emit('usbError', 'Parse Error: ' + error); }; _this._onSerialError = function (error) { _this.emit('usbError', 'Serial error: ' + error); }; _this._serialParser = new _AuroraSerialParser2.default(); _this._connectionState = AuroraUsb.ConnectionStates.DISCONNECTED; _this._disconnectPending = false; _this._serialParser.on('auroraEvent', _this._onParseAuroraEvent); _this._serialParser.on('log', _this._onParseLog); _this._serialParser.on('streamData', _this._onParseStreamData); _this._serialParser.on('streamTimestamp', _this._onParseStreamTimestamp); _this._serialParser.on('cmdInputRequested', _this._onParseCmdInputRequested); _this._serialParser.on('cmdOutputReady', _this._onParseCmdOutputReady); _this._serialParser.on('parseError', _this._onParseError); return _this; } (0, _createClass3.default)(AuroraUsb, [{ key: 'isConnected', value: function isConnected() { return this._connectionState == AuroraUsb.ConnectionStates.CONNECTED_IDLE || this._connectionState == AuroraUsb.ConnectionStates.CONNECTED_BUSY; } }, { key: 'isConnecting', value: function isConnecting() { return this._connectionState == AuroraUsb.ConnectionStates.CONNECTING; } }, { key: 'connect', value: function () { var _ref = (0, _asyncToGenerator3.default)( /*#__PURE__*/_regenerator2.default.mark(function _callee() { var port = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 'detect'; var retryCount = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 3; var connectionAttempts, auroraPorts, _iteratorNormalCompletion2, _didIteratorError2, _iteratorError2, _iterator2, _step2, auroraPort; return _regenerator2.default.wrap(function _callee$(_context) { while (1) { switch (_context.prev = _context.next) { case 0: if (!(this._connectionState != AuroraUsb.ConnectionStates.DISCONNECTED)) { _context.next = 7; break; } _context.t0 = this._connectionState; _context.next = _context.t0 === AuroraUsb.ConnectionStates.CONNECTING ? 4 : _context.t0 === AuroraUsb.ConnectionStates.CONNECTED_BUSY ? 5 : _context.t0 === AuroraUsb.ConnectionStates.CONNECTED_IDLE ? 5 : 6; break; case 4: return _context.abrupt('return', _promise2.default.reject('Already connecting...')); case 5: return _context.abrupt('return', _promise2.default.reject('Already connected.')); case 6: return _context.abrupt('return', _promise2.default.reject('Unknown USB connection state.')); case 7: this._setConnectionState(AuroraUsb.ConnectionStates.CONNECTING); connectionAttempts = 0; case 9: if (!connectionAttempts) { _context.next = 12; break; } _context.next = 12; return (0, _util.sleep)(CONNECT_RETRY_DELAY_MS); case 12: connectionAttempts++; _context.prev = 13; if (!(port == 'detect')) { _context.next = 59; break; } _context.next = 17; return AuroraUsb.discoverAuroraPorts(); case 17: auroraPorts = _context.sent; if (auroraPorts.length) { _context.next = 20; break; } throw new Error('No Aurora devices found.'); case 20: _iteratorNormalCompletion2 = true; _didIteratorError2 = false; _iteratorError2 = undefined; _context.prev = 23; _iterator2 = (0, _getIterator3.default)(auroraPorts); case 25: if (_iteratorNormalCompletion2 = (_step2 = _iterator2.next()).done) { _context.next = 42; break; } auroraPort = _step2.value; if (!this._disconnectPending) { _context.next = 29; break; } return _context.abrupt('break', 42); case 29: _context.prev = 29; _context.next = 32; return this._connect(auroraPort); case 32: this._serialPort = _context.sent; this._setConnectionState(AuroraUsb.ConnectionStates.CONNECTED_IDLE); return _context.abrupt('return', auroraPort); case 37: _context.prev = 37; _context.t1 = _context['catch'](29); case 39: _iteratorNormalCompletion2 = true; _context.next = 25; break; case 42: _context.next = 48; break; case 44: _context.prev = 44; _context.t2 = _context['catch'](23); _didIteratorError2 = true; _iteratorError2 = _context.t2; case 48: _context.prev = 48; _context.prev = 49; if (!_iteratorNormalCompletion2 && _iterator2.return) { _iterator2.return(); } case 51: _context.prev = 51; if (!_didIteratorError2) { _context.next = 54; break; } throw _iteratorError2; case 54: return _context.finish(51); case 55: return _context.finish(48); case 56: throw new Error('Failed connecting to Aurora on port(s): ' + auroraPorts.join(',')); case 59: _context.next = 61; return this._connect(port); case 61: this._serialPort = _context.sent; this._setConnectionState(AuroraUsb.ConnectionStates.CONNECTED_IDLE); return _context.abrupt('return', port); case 64: _context.next = 69; break; case 66: _context.prev = 66; _context.t3 = _context['catch'](13); return _context.abrupt('continue', 69); case 69: if (connectionAttempts <= retryCount && this._connectionState == AuroraUsb.ConnectionStates.CONNECTING && !this._disconnectPending) { _context.next = 9; break; } case 70: //if we are here, all connection attempts failed this._setConnectionState(AuroraUsb.ConnectionStates.DISCONNECTED); return _context.abrupt('return', _promise2.default.reject('Failed connecting to Aurora on port "' + port + '".')); case 72: case 'end': return _context.stop(); } } }, _callee, this, [[13, 66], [23, 44, 48, 56], [29, 37], [49,, 51, 55]]); })); function connect() { return _ref.apply(this, arguments); } return connect; }() }, { key: 'disconnect', value: function () { var _ref2 = (0, _asyncToGenerator3.default)( /*#__PURE__*/_regenerator2.default.mark(function _callee2() { var _this2 = this; return _regenerator2.default.wrap(function _callee2$(_context2) { while (1) { switch (_context2.prev = _context2.next) { case 0: if (!(this._connectionState == AuroraUsb.ConnectionStates.DISCONNECTED || this._disconnectPending)) { _context2.next = 2; break; } return _context2.abrupt('return'); case 2: this._disconnectPending = true; //check if we are in the process of connecting, or are processing a command if (!(this._connectionState == AuroraUsb.ConnectionStates.CONNECTING || this._connectionState == AuroraUsb.ConnectionStates.CONNECTED_BUSY)) { _context2.next = 8; break; } _context2.next = 6; return (0, _util.sleep)(DISCONNECT_RETRY_DELAY_MS); case 6: if (!(this._connectionState == AuroraUsb.ConnectionStates.DISCONNECTED)) { _context2.next = 8; break; } return _context2.abrupt('return'); case 8: return _context2.abrupt('return', (0, _util.promisify)(this._serialPort.close, this._serialPort)().catch(console.log).then(function () { //in case disconnected event hasn't fired yet, we fire it here _this2._setConnectionState(AuroraUsb.ConnectionStates.DISCONNECTED); })); case 9: case 'end': return _context2.stop(); } } }, _callee2, this); })); function disconnect() { return _ref2.apply(this, arguments); } return disconnect; }() }, { key: 'writeCmd', value: function () { var _ref3 = (0, _asyncToGenerator3.default)( /*#__PURE__*/_regenerator2.default.mark(function _callee3(cmd) { var _this3 = this; return _regenerator2.default.wrap(function _callee3$(_context3) { while (1) { switch (_context3.prev = _context3.next) { case 0: if (!(this._connectionState != AuroraUsb.ConnectionStates.CONNECTED_IDLE)) { _context3.next = 7; break; } _context3.t0 = this._connectionState; _context3.next = _context3.t0 === AuroraUsb.ConnectionStates.DISCONNECTED ? 4 : _context3.t0 === AuroraUsb.ConnectionStates.CONNECTING ? 4 : _context3.t0 === AuroraUsb.ConnectionStates.CONNECTED_BUSY ? 5 : 6; break; case 4: return _context3.abrupt('return', _promise2.default.reject('No idle serial connection.')); case 5: return _context3.abrupt('return', _promise2.default.reject('Another command is already in progress.')); case 6: return _context3.abrupt('return', _promise2.default.reject('Unknown USB connection state.')); case 7: if (!this._disconnectPending) { _context3.next = 9; break; } return _context3.abrupt('return', _promise2.default.reject('Serial port currently disconnecting.')); case 9: this._setConnectionState(AuroraUsb.ConnectionStates.CONNECTED_BUSY); return _context3.abrupt('return', new _promise2.default(function (resolve, reject) { var onDisconnect = function onDisconnect(connectionState) { if (connectionState == AuroraUsb.ConnectionStates.DISCONNECTED) { reject('Usb disconnected while processing command response.'); } }; _this3._serialParser.once('cmdResponse', function (cmdResponse) { _this3.removeListener('connectionStateChange', onDisconnect); if (_this3._connectionState == AuroraUsb.ConnectionStates.CONNECTED_BUSY) { _this3._setConnectionState(AuroraUsb.ConnectionStates.CONNECTED_IDLE); } cmdResponse.origin = 'usb'; resolve(cmdResponse); }); _this3.once('connectionStateChange', onDisconnect); cmd = cmd.trim() + '\r'; _this3._write(cmd).catch(function (error) { _this3._serialParser.removeAllListeners('cmdResponse'); _this3.removeListener('connectionStateChange', onDisconnect); if (_this3._connectionState == AuroraUsb.ConnectionStates.CONNECTED_BUSY) { _this3._setConnectionState(AuroraUsb.ConnectionStates.CONNECTED_IDLE); } reject(error); }); })); case 11: case 'end': return _context3.stop(); } } }, _callee3, this); })); function writeCmd(_x3) { return _ref3.apply(this, arguments); } return writeCmd; }() }, { key: 'writeCmdInput', value: function () { var _ref4 = (0, _asyncToGenerator3.default)( /*#__PURE__*/_regenerator2.default.mark(function _callee4(data) { var i, packet; return _regenerator2.default.wrap(function _callee4$(_context4) { while (1) { switch (_context4.prev = _context4.next) { case 0: if (!(this._connectionState != AuroraUsb.ConnectionStates.CONNECTED_BUSY)) { _context4.next = 7; break; } _context4.t0 = this._connectionState; _context4.next = _context4.t0 === AuroraUsb.ConnectionStates.DISCONNECTED ? 4 : _context4.t0 === AuroraUsb.ConnectionStates.CONNECTING ? 4 : _context4.t0 === AuroraUsb.ConnectionStates.CONNECTED_IDLE ? 5 : 6; break; case 4: return _context4.abrupt('return', _promise2.default.reject('No idle serial connection.')); case 5: return _context4.abrupt('return', _promise2.default.reject('Command input can only be written during a command.')); case 6: return _context4.abrupt('return', _promise2.default.reject('Unknown USB connection state.')); case 7: i = 0; case 8: if (!(i < data.length + 128)) { _context4.next = 17; break; } packet = data.slice(i, i + 128); //if this slice is empty, nothing to do if (packet.length) { _context4.next = 12; break; } return _context4.abrupt('break', 17); case 12: _context4.next = 14; return this._write(packet); case 14: i += 128; _context4.next = 8; break; case 17: return _context4.abrupt('return', data); case 18: case 'end': return _context4.stop(); } } }, _callee4, this); })); function writeCmdInput(_x4) { return _ref4.apply(this, arguments); } return writeCmdInput; }() }, { key: '_setConnectionState', value: function _setConnectionState(connectionState) { //don't fire or respond to events when the //state doesn't actually change if (this._connectionState == connectionState) { return; } var previousConnectionState = this._connectionState; this._connectionState = connectionState; if (connectionState == AuroraUsb.ConnectionStates.DISCONNECTED) { this._disconnectPending = false; if (this._serialPort) { this._serialPort.removeAllListeners(); } } else if (connectionState == AuroraUsb.ConnectionStates.CONNECTED_IDLE && previousConnectionState == AuroraUsb.ConnectionStates.CONNECTING) { this._serialParser.reset(); this._serialPort.removeAllListeners(); this._serialPort.on('data', this._onSerialData); this._serialPort.on('close', this._onSerialDisconnect); this._serialPort.on('error', this._onSerialError); } this.emit('connectionStateChange', connectionState, previousConnectionState); } }, { key: '_connect', value: function () { var _ref5 = (0, _asyncToGenerator3.default)( /*#__PURE__*/_regenerator2.default.mark(function _callee5(port) { var _this4 = this; return _regenerator2.default.wrap(function _callee5$(_context5) { while (1) { switch (_context5.prev = _context5.next) { case 0: return _context5.abrupt('return', new _promise2.default(function (resolve, reject) { var serialPort = new _serialport2.default(port, function (error) { if (error) return reject(error); //flush any bytes in buffer that haven't been read serialPort.flush(function (error) { if (error) return reject(error); if (_this4._disconnectPending) return reject('Serial disconnect pending, cancelling connection.'); resolve(serialPort); }); }); })); case 1: case 'end': return _context5.stop(); } } }, _callee5, this); })); function _connect(_x5) { return _ref5.apply(this, arguments); } return _connect; }() }, { key: '_write', value: function () { var _ref6 = (0, _asyncToGenerator3.default)( /*#__PURE__*/_regenerator2.default.mark(function _callee6(data) { var _this5 = this; return _regenerator2.default.wrap(function _callee6$(_context6) { while (1) { switch (_context6.prev = _context6.next) { case 0: return _context6.abrupt('return', new _promise2.default(function (resolve, reject) { _this5._serialPort.write(data, function (writeError) { if (writeError) return reject(writeError); _this5._serialPort.drain(function (drainError) { if (drainError) return reject(drainError); resolve(data); }); }); })); case 1: case 'end': return _context6.stop(); } } }, _callee6, this); })); function _write(_x6) { return _ref6.apply(this, arguments); } return _write; }() }]); return AuroraUsb; }(_events2.default); AuroraUsb.ConnectionStates = { DISCONNECTED: 'disconnected', CONNECTING: 'connecting', CONNECTED_IDLE: 'idle', CONNECTED_BUSY: 'busy' }; exports.default = AuroraUsb;