node-aurora
Version:
Provides an interface to the Aurora Dreamband.
758 lines (540 loc) • 29.8 kB
JavaScript
'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;