UNPKG

swarms

Version:

The ultimate node.js library for controlling Bitcraze Crazyflie 2.0 drones

201 lines 6.9 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const constants_1 = require("../constants"); const packet_1 = require("../packet"); const utils_1 = require("../utils"); const toc_1 = require("./toc"); const events_1 = require("events"); const fs = require("fs-extra"); const path = require("path"); class TOCFetcher extends events_1.EventEmitter { /** * Fetches TOC from the Crazyflie. Specify port because both parameters and logging use the same system * (https://wiki.bitcraze.io/doc:crazyflie:crtp:log#table_of_content_access) * (https://wiki.bitcraze.io/doc:crazyflie:crtp:param#toc_access) */ constructor(crazyflie, type) { super(); this.crazyflie = crazyflie; this.type = type; this.fetched = false; this.toc = new toc_1.TOC(); switch (this.type) { case 0 /* PARAM */: this.port = constants_1.PORTS.PARAMETERS; break; case 1 /* LOG */: this.port = constants_1.PORTS.LOGGING; break; default: throw new Error(`Invalid TOC type "${type}"!`); } } get cachePath() { let cacheFile; switch (this.type) { case 0 /* PARAM */: cacheFile = 'param-toc.json'; break; case 1 /* LOG */: cacheFile = 'log-toc.json'; break; default: return null; } return path.join(this.crazyflie.options.cacheDir, cacheFile); } async start() { const packet = new packet_1.Packet(); packet.port = this.port; packet.channel = constants_1.CHANNELS.TOC; packet.write('int8', constants_1.COMMANDS.TOC.GET_INFO); return this.crazyflie.radio.sendPacket(packet) .then(utils_1.waitUntilEvent(this, 'toc ready')); } async handleTOCInfo(data) { const types = constants_1.BUFFER_TYPES(data); this.length = types.uInt8.read(0); this.crc = types.int32.read(1); if (this.type === 1 /* LOG */) { this.maxPackets = types.int8.read(5); this.maxOperations = types.int8.read(6); } // See if TOC is cached first const cache = await this.getTOCFromCache(this.crc); if (cache) { this.toc = cache; this.fetched = true; this.emit('toc ready', this.toc); } else { // Bombard the Crazyflie with TOC item requests until all TOC items are received. // While this is contrary to what most other libraries do, it saves a bit of time instead of // sending a request for TOC item, waiting for response, then send request for next TOC item. while (this.toc.items.length < this.length) { this.fetchRemainingTOCItems(); await utils_1.wait(100); } } } /** * Loop through all TOC ids and see if we already have it. If not, retrieve it. */ async fetchRemainingTOCItems() { for (let i = 0; i < this.length; i++) { if (!this.toc.getItemById(i)) { try { await this.fetchTOCItem(i); } catch (err) { this.emit('error', err); } } } } /** * Fetch TOC item from the Crazyflie * (https://wiki.bitcraze.io/doc:crazyflie:crtp:log#get_toc_item) */ fetchTOCItem(id) { if (0 > id || id >= this.length) { return Promise.reject(`Id "${id}" is out of range! (0-${this.length - 1} inclusive)`); } const packet = new packet_1.Packet(); packet.port = this.port; packet.channel = constants_1.CHANNELS.TOC; packet .write('int8', constants_1.COMMANDS.TOC.GET_ITEM) .write('uInt8', id); return this.crazyflie.radio.sendPacket(packet); } /** * Handle TOC item response * (https://wiki.bitcraze.io/doc:crazyflie:crtp:log#get_toc_item) */ async handleTOCItem(data) { const types = constants_1.BUFFER_TYPES(data); const id = types.uInt8.read(0); const metadata = types.int8.read(1); let type; let readOnly = false; switch (this.type) { case 0 /* PARAM */: type = constants_1.GET_PARAM_TYPE(metadata & 0x0F); // If param type begins with 0x4_, then it's read only. readOnly = ((metadata & 0xF0) === 0x40); break; case 1 /* LOG */: type = constants_1.GET_LOGGING_TYPE(metadata); break; } const [group, name] = data.slice(2).toString().split('\u0000'); const item = { id, type, group, name }; if (readOnly) { item.readOnly = true; } // Add TOC item if it isn't a duplicate if (this.toc.addItem(item)) { // We should tell somebody this.emit('toc item', item); // If that was the last item, cache TOC and alert the others! if (this.toc.items.length === this.length) { this.fetched = true; await this.cacheTOC(this.crc, this.toc.items); this.emit('toc ready', this.toc); } } } /** * Save a TOC to cache */ cacheTOC(crc, items) { return this.getTOCCache() .then(existingCache => { if (!existingCache) { existingCache = {}; } existingCache[crc] = items; // `fs as any` because Typescript picks the wrong type definition in the overloaded method return fs.outputJson(this.cachePath, existingCache, { spaces: '\t' }); }); } /** * Get TOC from cache according to cyclic redundancy check (checksum) value * Will return null if no crc in cache */ getTOCFromCache(crc) { return this.getTOCCache() .then(cache => { if (cache && typeof cache[crc] !== 'undefined') { return new toc_1.TOC(cache[crc]); } return null; }); } /** * Gets the complete TOC cache * Will return null if file doesn't exist or invalid JSON */ getTOCCache() { return fs.pathExists(this.cachePath) .then(exists => { if (!exists) { return null; } return fs.readJson(this.cachePath, { throws: false }); }); } /** * Deletes cache */ clearCache() { return fs.remove(this.cachePath); } } exports.TOCFetcher = TOCFetcher; //# sourceMappingURL=toc-fetcher.js.map