swarms
Version:
The ultimate node.js library for controlling Bitcraze Crazyflie 2.0 drones
201 lines • 6.9 kB
JavaScript
"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