UNPKG

incyclist-ant-plus

Version:

Incyclist library for ANT+ - originally forked from longhorn/ant-plus

379 lines (378 loc) 16.4 kB
"use strict"; var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.MAX_CHANNEL_COLLISION = exports.CLOSE_TIMEOUT = exports.START_TIMEOUT = void 0; const consts_1 = require("./consts"); const events_1 = __importDefault(require("events")); const messages_1 = require("./messages"); exports.START_TIMEOUT = 5000; exports.CLOSE_TIMEOUT = 2000; exports.MAX_CHANNEL_COLLISION = 10; class Channel extends events_1.default { constructor(channelNo, device, props) { super(); this.isScanner = false; this.isSensor = false; this.isWriting = false; this.messageQueue = []; this.ackErrorCount = 0; this.channelNo = channelNo; this.device = device; this.props = props || {}; } onDeviceData(profile, deviceID, deviceState) { if (this.isScanner) { this.emit('detected', profile, deviceID); } this.emit('data', profile, deviceID, deviceState); } getChannelNo() { return this.channelNo; } getDevice() { return this.device; } setProps(props) { this.props = props || {}; } getProps() { return this.props; } startScanner() { return __awaiter(this, void 0, void 0, function* () { if (this.isSensor) yield this.stopAllSensors(); return new Promise((done) => __awaiter(this, void 0, void 0, function* () { let to; let starting = true; try { const send = (data, opts) => __awaiter(this, void 0, void 0, function* () { if (!starting) return; return yield this.sendMessage(data, opts); }); to = setTimeout(() => { done(false); starting = false; }, exports.START_TIMEOUT); yield send(messages_1.Messages.assignChannel(this.channelNo, 'receive')); yield send(messages_1.Messages.setDevice(this.channelNo, 0, 0, 0)); yield send(messages_1.Messages.setFrequency(this.channelNo, 57)); yield send(messages_1.Messages.setRxExt()); yield send(messages_1.Messages.libConfig(this.channelNo, 0xE0)); yield send(messages_1.Messages.openRxScan()); if (to) clearTimeout(to); this.isScanner = true; done(true); } catch (err) { if (to) clearTimeout(to); done(false); } })); }); } stopScanner() { return __awaiter(this, void 0, void 0, function* () { if (!this.isScanner) return true; yield this.closeChannel(); this.isScanner = false; }); } startSensor(sensor) { return __awaiter(this, void 0, void 0, function* () { if (this.isScanner) yield this.stopScanner(); return new Promise((done) => __awaiter(this, void 0, void 0, function* () { let to; try { let isStarting = true; const send = (data, opts) => __awaiter(this, void 0, void 0, function* () { if (!isStarting) return; return yield this.sendMessage(data, opts); }); to = setTimeout(() => { if (isStarting) { isStarting = false; done(false); } }, exports.START_TIMEOUT); const { type, transmissionType, timeout, frequency, period } = sensor.getChannelConfiguration(); const deviceID = sensor.getDeviceID(); const deviceType = sensor.getDeviceType(); yield send(messages_1.Messages.assignChannel(this.channelNo, type)); yield send(messages_1.Messages.setDevice(this.channelNo, deviceID, deviceType, transmissionType)); yield send(messages_1.Messages.searchChannel(this.channelNo, timeout)); yield send(messages_1.Messages.setFrequency(this.channelNo, frequency)); yield send(messages_1.Messages.setPeriod(this.channelNo, period)); yield send(messages_1.Messages.libConfig(this.channelNo, 0xE0)); yield send(messages_1.Messages.openChannel(this.channelNo)); isStarting = false; if (to) clearTimeout(to); this.attach(sensor); this.isSensor = true; done(this.isSensor); } catch (err) { if (to) clearTimeout(to); done(false); } })); }); } stopAllSensors() { return __awaiter(this, void 0, void 0, function* () { if (!this.isSensor) return true; this.detach(this.attachedSensor); yield this.closeChannel(); this.isSensor = false; }); } stopSensor(sensor) { return __awaiter(this, void 0, void 0, function* () { return yield this.stopAllSensors(); }); } restartSensor() { return __awaiter(this, void 0, void 0, function* () { if (!this.isSensor || !this.attachedSensor) return true; this.flush(); yield this.closeChannel({ restart: true }); const sensor = this.attachedSensor; return new Promise((done) => __awaiter(this, void 0, void 0, function* () { let to; let isStarting = true; try { const send = (data, opts) => __awaiter(this, void 0, void 0, function* () { if (!isStarting) return; return yield this.sendMessage(data, opts); }); to = setTimeout(() => { if (isStarting) { isStarting = false; done(false); } }, exports.START_TIMEOUT); const { type, transmissionType, timeout, frequency, period } = sensor.getChannelConfiguration(); const deviceID = sensor.getDeviceID(); const deviceType = sensor.getDeviceType(); yield this.sendMessage(messages_1.Messages.assignChannel(this.channelNo, type)); yield this.sendMessage(messages_1.Messages.setDevice(this.channelNo, deviceID, deviceType, transmissionType)); yield this.sendMessage(messages_1.Messages.searchChannel(this.channelNo, timeout)); yield this.sendMessage(messages_1.Messages.setFrequency(this.channelNo, frequency)); yield this.sendMessage(messages_1.Messages.setPeriod(this.channelNo, period)); yield this.sendMessage(messages_1.Messages.libConfig(this.channelNo, 0xE0)); yield this.sendMessage(messages_1.Messages.openChannel(this.channelNo)); isStarting = false; if (to) clearTimeout(to); this.attach(sensor); return done(true); } catch (err) { if (to) clearTimeout(to); return done(false); } })); }); } closeChannel() { return __awaiter(this, arguments, void 0, function* (props = {}) { const { restart } = props || {}; return new Promise(resolve => { let isClosing = true; const close = () => { isClosing = false; if (!restart) this.device.freeChannel(this); this.off('status', onStatusUpdate); resolve(); }; const onStatusUpdate = (status) => __awaiter(this, void 0, void 0, function* () { if (status.msg === 1 && status.code === consts_1.Constants.EVENT_CHANNEL_CLOSED) { yield this.sendMessage(messages_1.Messages.unassignChannel(this.channelNo)); close(); } }); this.removeAllListeners(); this.on('status', onStatusUpdate); setTimeout(() => { if (isClosing) { close(); } }, exports.CLOSE_TIMEOUT); this.device.write(messages_1.Messages.closeChannel(this.channelNo)); }); }); } attach(sensor) { this.attachedSensor = sensor; sensor.setChannel(this); this.on('message', (data) => { sensor.onMessage(data); }); this.on('status', (status, data) => { sensor.onEvent(data); }); } detach(sensor) { this.attachedSensor = null; sensor.setChannel(null); this.off('message', (data) => { sensor.onMessage(data); }); this.off('status', (status, data) => { sensor.onEvent(data); }); } onMessage(data) { try { const messageID = data.readUInt8(messages_1.Messages.BUFFER_INDEX_MSG_TYPE); const channel = data.readUInt8(messages_1.Messages.BUFFER_INDEX_CHANNEL_NUM); const prevMsgId = this.messageQueue.length > 0 ? this.messageQueue[0].msgId : undefined; const resolve = (value) => { if (this.messageQueue.length === 0) return; const msg = this.messageQueue[0]; this.messageQueue.splice(0, 1); this.isWriting = false; this.ackErrorCount = 0; msg.resolve(value); }; const next = () => { if (this.messageQueue.length === 0) return; const msg = this.messageQueue[0]; if (msg.timeout) { setTimeout(() => { resolve(false); next(); }, msg.timeout); } this.isWriting = true; this.ackErrorCount = 0; this.device.write(msg.data); msg.data = undefined; }; if (messageID === consts_1.Constants.MESSAGE_CHANNEL_EVENT && channel === this.channelNo) { const status = { msg: data.readUInt8(4), code: data.readUInt8(5), }; this.emit('status', status, data); if (status.msg !== 1) { if (prevMsgId === status.msg) { const success = status.code === 0x00 || status.code === consts_1.Constants.EVENT_TRANSFER_TX_COMPLETED; resolve(success); next(); return; } } else { switch (status.code) { case consts_1.Constants.EVENT_TRANSFER_TX_COMPLETED: resolve(true); next(); return; case consts_1.Constants.EVENT_TRANSFER_TX_FAILED: case consts_1.Constants.TRANSFER_IN_PROGRESS: case consts_1.Constants.TRANSFER_SEQUENCE_NUMBER_ERROR: case consts_1.Constants.TRANSFER_IN_ERROR: case consts_1.Constants.MESSAGE_SIZE_EXCEEDS_LIMIT: case consts_1.Constants.INVALID_MESSAGE: case consts_1.Constants.EVENT_CHANNEL_CLOSED: resolve(false); next(); return; case consts_1.Constants.EVENT_RX_SEARCH_TIMEOUT: this.stopScanner(); return; case consts_1.Constants.EVENT_RX_FAIL: break; case consts_1.Constants.EVENT_RX_FAIL_GO_TO_SEARCH: break; case consts_1.Constants.EVENT_CHANNEL_COLLISION: if (this.isWriting) { this.ackErrorCount++; } break; } } } else if (channel === this.channelNo) this.emit('message', data); } catch (err) { console.log(err); } } sendMessage(data, opts) { const props = opts || {}; const msgId = data.readUInt8(messages_1.Messages.BUFFER_INDEX_MSG_TYPE); const channel = data.readUInt8(messages_1.Messages.BUFFER_INDEX_CHANNEL_NUM); const { timeout } = props; return new Promise((resolve, reject) => { if (channel !== this.channelNo) reject(new Error('invalid channel')); let to; const done = (res) => { resolve(res); clearTimeout(to); to = undefined; this.isWriting = false; }; const error = (err) => { reject(err); clearTimeout(to); to = undefined; this.isWriting = false; }; if (this.isWriting) { let found = -1; do { found = this.messageQueue.findIndex(qi => qi.msgId === msgId && qi.data); if (found !== -1) { const message = this.messageQueue[found]; this.messageQueue.splice(found, 1); message.resolve(false); } } while (found !== -1); this.messageQueue.push({ msgId, resolve: done, reject: error, data, timeout }); } else { this.messageQueue.push({ msgId, resolve: done, reject: error }); this.isWriting = true; this.device.write(data); if (timeout && !to) to = setTimeout(() => { if (!this.isWriting) return; const found = this.messageQueue.findIndex(qi => qi.msgId === msgId); if (found !== -1) { this.messageQueue.splice(found, 1); } error(new Error('timeout')); }, timeout); } }); } flush() { this.messageQueue.forEach(msg => { msg.resolve(false); }); this.messageQueue = []; this.isWriting = false; } } exports.default = Channel;