UNPKG

incyclist-ant-plus

Version:

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

339 lines (338 loc) 12 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.getSticks = void 0; const events_1 = __importDefault(require("events")); const usb_1 = require("usb"); const consts_1 = require("./consts"); const messages_1 = require("./messages"); const types_1 = require("./types"); function getSticks(filterFn) { const info = usb_1.getDeviceList(); if (filterFn === undefined) return info; return info .filter(filterFn); } exports.getSticks = getSticks; class Stick extends events_1.default { constructor(idVendor, idProduct, props = {}) { super(); this.idVendor = idVendor; this.idProduct = idProduct; this.detachedKernelDriver = false; this.usedChannels = 0; this.attachedSensors = []; this.maxChannels = 0; this.canScan = false; this.waitingForStartup = false; this.setMaxListeners(50); this.props = props; } getDevices() { try { const allDevices = usb_1.getDeviceList(); return allDevices .filter((d) => d.deviceDescriptor.idVendor === this.idVendor && d.deviceDescriptor.idProduct === this.idProduct) .filter(d => Stick.deviceInUse.indexOf(d) === -1); } catch (err) { const logger = this.props.logger; logger.log('getDevices ERROR :', err); return []; } } static listDevices(filterFn) { const allDevices = usb_1.getDeviceList(); const info = [...allDevices]; info.forEach(d => d.inUse = Stick.deviceInUse.indexOf(d) !== -1); if (filterFn === undefined) return info; return info .filter(filterFn); } logEvent(event) { if (this.props && this.props.logger) { const logger = this.props.logger; if (logger.logEvent) logger.logEvent(event); else if (logger.log) { const str = Object.keys(event).map(k => ({ key: k, data: event[k] })) .reduce((p, d, i) => i === 0 ? `${d.key}:${d.data}` : `${p}, ${d.key}:${d.data}`, ''); logger.log(str); } } } blockDevice(device) { Stick.deviceInUse.push(device); } unblockDevice(device) { let deviceIdx = undefined; for (var i = 0; i < Stick.deviceInUse.length; i++) { let blocked = Stick.deviceInUse[i]; if (blocked.deviceAddress == device.deviceAddress) { deviceIdx = i; } } if (deviceIdx != undefined) { Stick.deviceInUse.splice(deviceIdx, 1); } } is_present() { return this.getDevices().length > 0; } open() { return __awaiter(this, void 0, void 0, function* () { const device = usb_1.findByIds(this.idVendor, this.idProduct); this.device = device; if (!this.device) return false; try { this.device.open(); this.iface = this.device.interfaces[0]; try { if (this.iface.isKernelDriverActive()) { this.detachedKernelDriver = true; this.iface.detachKernelDriver(); } } catch (_a) { } this.iface.claim(); } catch (_b) { this.device.close(); this.device = undefined; this.iface = undefined; } Stick.deviceInUse.push(this.device); this.waitingForStartup = true; this.inEp = this.iface.endpoints[0]; this.inEp.on('data', (data) => { if (!data.length) { return; } if (this.leftover) { data = Buffer.concat([this.leftover, data]); this.leftover = undefined; } if (data.readUInt8(0) !== 0xA4) { this.emit('error', { error: 'SYNC missing', context: data }); } const len = data.length; let beginBlock = 0; while (beginBlock < len) { if (beginBlock + 1 === len) { this.leftover = data.slice(beginBlock); break; } const blockLen = data.readUInt8(beginBlock + 1); const endBlock = beginBlock + blockLen + 4; if (endBlock > len) { this.leftover = data.slice(beginBlock); break; } const readData = data.slice(beginBlock, endBlock); this.read(readData); beginBlock = endBlock; } }); this.inEp.on('error', (err) => { if (this.props.debug) { const logger = this.props.logger || console; logger.log('ERROR RECV: ', err); } }); this.inEp.on('end', () => { if (this.props.debug) { const logger = this.props.logger || console; logger.log('STOP RECV: '); } }); this.inEp.startPoll(); this.outEp = this.iface.endpoints[1]; let startupCnt = 0; let tsStart = Date.now(); let tsTimeout = tsStart + 1000; this.reset(); let startupTimer = setInterval(() => { if (!this.waitingForStartup) { clearInterval(startupTimer); return; } if (Date.now() > tsTimeout) { startupCnt++; if (this.props.maxAttempts && startupCnt > this.props.maxAttempts) { clearInterval(startupTimer); this.waitingForStartup = false; this.emit('error', { error: 'Startup timeout', context: 'timeout' }); return; } else { tsStart = Date.now(); tsTimeout = tsStart + 1000; this.reset(); } } }, 100); return true; }); } openAsync(cb) { let ct; const doOpen = () => { try { const result = this.open(); if (result) { ct._completed = true; try { cb(undefined); } catch (_a) { } } else { return false; } } catch (err) { cb(err); } return true; }; const fn = (d) => { if (!d || (d.deviceDescriptor.idVendor === this.idVendor && d.deviceDescriptor.idProduct === this.idProduct)) { if (doOpen()) { usb_1.usb.removeListener('attach', fn); } } }; usb_1.usb.on('attach', fn); if (this.is_present()) { setImmediate(() => usb_1.usb.emit('attach', this.device)); } return ct = new types_1.CancellationTokenListener(fn, cb); } close() { this.detach_all(); this.inEp.stopPoll(() => { this.iface.release(true, () => { if (this.detachedKernelDriver) { this.detachedKernelDriver = false; try { this.iface.attachKernelDriver(); } catch (_a) { } } this.iface = undefined; this.device.reset(() => { this.device.close(); this.emit('shutdown'); const devIdx = Stick.deviceInUse.indexOf(this.device); if (devIdx >= 0) { Stick.deviceInUse.splice(devIdx, 1); } if (usb_1.usb.listenerCount('attach')) { usb_1.usb.emit('attach', this.device); } this.device = undefined; }); }); }); } reset() { this.detach_all(); this.maxChannels = 0; this.usedChannels = 0; this.write(messages_1.Messages.resetSystem()); } isScanning() { return this.usedChannels === -1; } attach(sensor, forScan) { if (this.usedChannels < 0) { return false; } if (forScan) { if (this.usedChannels !== 0) { return false; } this.usedChannels = -1; } else { if (this.maxChannels <= this.usedChannels) { return false; } ++this.usedChannels; } this.attachedSensors.push(sensor); return true; } stopScan() { if (this.usedChannels === -1) this.usedChannels = 0; } detach(sensor) { const idx = this.attachedSensors.indexOf(sensor); if (idx < 0) { return false; } if (this.usedChannels < 0) { this.usedChannels = 0; } else { --this.usedChannels; } this.attachedSensors.splice(idx, 1); return true; } detach_all() { const copy = [...this.attachedSensors]; copy.forEach((sensor) => sensor.detach()); } write(data) { if (this.props.debug) { const logger = this.props.logger || console; this.logEvent({ message: 'ANT+ SEND', data: data.toString('hex') }); } this.outEp.transfer(data, (error) => { if (error) { } }); } read(data) { if (this.props.debug) { this.logEvent({ message: 'ANT+ RECV', data: data.toString('hex'), handlers: this.listenerCount('read') }); } const messageID = data.readUInt8(2); if (messageID === consts_1.Constants.MESSAGE_STARTUP) { this.waitingForStartup = false; this.write(messages_1.Messages.requestMessage(0, consts_1.Constants.MESSAGE_CAPABILITIES)); } else if (messageID === consts_1.Constants.MESSAGE_CAPABILITIES) { this.maxChannels = data.readUInt8(3); this.canScan = (data.readUInt8(7) & 0x06) === 0x06; this.write(messages_1.Messages.setNetworkKey()); } else if (messageID === consts_1.Constants.MESSAGE_CHANNEL_EVENT && data.readUInt8(4) === consts_1.Constants.MESSAGE_NETWORK_KEY) { this.emit('startup', data); } else { this.emit('read', data); } } } exports.default = Stick; Stick.deviceInUse = [];