swarms
Version:
The ultimate node.js library for controlling Bitcraze Crazyflie 2.0 drones
300 lines • 12.3 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
const constants_1 = require("../constants");
const packet_1 = require("../packet");
const utils_1 = require("../utils");
const toc_fetcher_1 = require("./toc-fetcher");
const events_1 = require("events");
class Logging extends events_1.EventEmitter {
/**
* Class for dealing with the 'logging' port (telemetry)
* (https://wiki.bitcraze.io/doc:crazyflie:crtp:log)
*/
constructor(crazyflie) {
super();
this.crazyflie = crazyflie;
// Table of Contents
this.tocFetcher = new toc_fetcher_1.TOCFetcher(this.crazyflie, 1 /* LOG */);
// Keep track of blocks
// (A block is a set of variables that the Crazyflie sends back at certain intervals)
this.blocks = [];
// Counter for assigning block ids
this.nextBlockId = 0;
// Emit all logging variables from the `data` property
this.data = new events_1.EventEmitter();
this.crazyflie.radio.on('logging', (ackPack) => {
try {
// Route the packet to the correct handler function
switch (ackPack.channel) {
case constants_1.CHANNELS.TOC:
// Find out which command
switch (ackPack.data[0]) {
case constants_1.COMMANDS.TOC.GET_ITEM:
this.tocFetcher.handleTOCItem(ackPack.data.slice(1));
break;
case constants_1.COMMANDS.TOC.GET_INFO:
this.tocFetcher.handleTOCInfo(ackPack.data.slice(1));
break;
}
break;
case constants_1.CHANNELS.LOG.CTRL:
this.handleBlock(ackPack.data);
break;
case constants_1.CHANNELS.LOG.DATA:
this.handleLogData(ackPack.data);
break;
default:
this.emit('error', `Unrecognized logging channel "${ackPack.data[0]}"!`);
break;
}
}
catch (err) {
this.emit('error', err);
}
});
}
/**
* Retrieve logging TOC from the Crazyflie.
* Required before getting any logging data!
*/
getTOC() {
return this.tocFetcher.start();
}
/**
* Start receiving data of TOC items. Creates a block of variables and activates it.
* You can access these variables using various events; check the documentation.
* You can optionally specify the interval in milliseconds for the Crazyflie to ping data back to the computer.
* (Floors to the nearest 10ms interval)
*/
async start(variables, millisecondInterval = 100) {
const period = Math.floor(millisecondInterval / 10);
if (0 >= period || period > 0xFF) {
return Promise.reject(`Interval "${millisecondInterval}ms" is out of range 10ms and 255000ms!`);
}
const block = {
id: this.nextBlockId++,
variables
};
this.blocks.push(block);
return this.createBlock(block)
.then(() => this.startBlock(block.id, period));
}
/**
* Gets a block with a specified id from the `block` array. Not Crazyflie.
*/
getBlock(id) {
let targetBlock = null;
for (const block of this.blocks) {
if (block.id === id) {
targetBlock = block;
break;
}
}
return targetBlock;
}
/**
* Create a log block
* (https://wiki.bitcraze.io/projects:crazyflie:firmware:comm_protocol#log_settings_access_port_5_channel_1)
*/
createBlock(block) {
const packet = new packet_1.Packet();
packet.port = constants_1.PORTS.LOGGING;
packet.channel = constants_1.CHANNELS.LOG.CTRL;
packet
.write('int8', constants_1.COMMANDS.LOG_CTRL.CREATE_BLOCK)
.write('int8', block.id);
for (const variable of block.variables) {
const type = constants_1.LOGGING_TYPES[variable.type];
packet
.write('int8', type << 4 | type)
.write('uInt8', variable.id);
}
return this.crazyflie.radio.sendPacket(packet)
.then(utils_1.waitUntilEvent(this, 'create block'));
}
/**
* Appends variables to a block
* (https://wiki.bitcraze.io/projects:crazyflie:firmware:comm_protocol#log_settings_access_port_5_channel_1)
*/
appendToBlock(blockId, variables) {
const block = this.getBlock(blockId);
if (!block) {
return Promise.reject(`Invalid block id "${blockId}"!`);
}
const packet = new packet_1.Packet();
packet.port = constants_1.PORTS.LOGGING;
packet.channel = constants_1.CHANNELS.LOG.CTRL;
packet
.write('int8', constants_1.COMMANDS.LOG_CTRL.APPEND_BLOCK)
.write('int8', block.id);
for (const variable of variables) {
// @TODO test if this works...
block.variables.push(variable);
const type = constants_1.LOGGING_TYPES[variable.type];
packet
.write('int8', type << 4 | type)
.write('int8', variable.id);
}
return this.crazyflie.radio.sendPacket(packet)
.then(utils_1.waitUntilEvent(this, 'append block'));
}
/**
* Deletes a block
* (https://wiki.bitcraze.io/projects:crazyflie:firmware:comm_protocol#log_settings_access_port_5_channel_1)
*/
deleteBlock(blockId) {
let deleted = false;
for (let i = 0; i < this.blocks.length; i++) {
if (this.blocks[i].id === blockId) {
this.blocks.splice(i--, 1);
deleted = true;
}
}
if (!deleted) {
return Promise.resolve();
}
// If we deleted something, delete on Crazyflie too
const packet = new packet_1.Packet();
packet.port = constants_1.PORTS.LOGGING;
packet.channel = constants_1.CHANNELS.LOG.CTRL;
packet
.write('int8', constants_1.COMMANDS.LOG_CTRL.DELETE_BLOCK)
.write('int8', blockId);
return this.crazyflie.radio.sendPacket(packet)
.then(utils_1.waitUntilEvent(this, 'delete block'));
}
/**
* Activate a block so logging data is sent back to computer at a certain interval.
* Interval is specified in increments of 10ms (1 = 10ms, 2 = 20ms, etc...)
* (https://wiki.bitcraze.io/projects:crazyflie:firmware:comm_protocol#log_settings_access_port_5_channel_1)
*/
startBlock(blockId, interval) {
if (0 >= interval || interval > 0xFF) {
return Promise.reject(`Interval "${interval}" is out of range 1 to 255!`);
}
if (!this.getBlock(blockId)) {
return Promise.reject(`Invalid block id "${blockId}"!`);
}
const packet = new packet_1.Packet();
packet.port = constants_1.PORTS.LOGGING;
packet.channel = constants_1.CHANNELS.LOG.CTRL;
packet
.write('int8', constants_1.COMMANDS.LOG_CTRL.START_BLOCK)
.write('int8', blockId)
.write('int8', interval);
return this.crazyflie.radio.sendPacket(packet)
.then(utils_1.waitUntilEvent(this, 'start block'));
}
/**
* Deactivates a block so no more logging data is sent from that block
* (https://wiki.bitcraze.io/projects:crazyflie:firmware:comm_protocol#log_settings_access_port_5_channel_1)
*/
stopBlock(blockId) {
if (!this.getBlock(blockId)) {
return Promise.reject(`Invalid block id "${blockId}"!`);
}
const packet = new packet_1.Packet();
packet.port = constants_1.PORTS.LOGGING;
packet.channel = constants_1.CHANNELS.LOG.CTRL;
packet
.write('int8', constants_1.COMMANDS.LOG_CTRL.DELETE_BLOCK)
.write('int8', blockId);
return this.crazyflie.radio.sendPacket(packet)
.then(utils_1.waitUntilEvent(this, 'delete block'));
}
/**
* Stop and delete all blocks
* (https://wiki.bitcraze.io/projects:crazyflie:firmware:comm_protocol#log_settings_access_port_5_channel_1)
*/
reset() {
this.blocks = [];
const packet = new packet_1.Packet();
packet.port = constants_1.PORTS.LOGGING;
packet.channel = constants_1.CHANNELS.LOG.CTRL;
packet.write('int8', constants_1.COMMANDS.LOG_CTRL.RESET_LOG);
return this.crazyflie.radio.sendPacket(packet)
.then(utils_1.waitUntilEvent(this, 'reset log'));
}
/**
* Handle block response
* (https://wiki.bitcraze.io/projects:crazyflie:firmware:comm_protocol#log_settings_access_port_5_channel_1)
*/
handleBlock(data) {
const types = constants_1.BUFFER_TYPES(data);
const command = types.int8.read(0);
const blockId = types.int8.read(1);
const status = types.int8.read(2);
let error = null;
if (status !== 0) {
error = new Error(constants_1.BLOCK_ERRORS[status].message);
error.name = constants_1.BLOCK_ERRORS[status].name;
this.emit('error', error);
}
switch (command) {
case constants_1.COMMANDS.LOG_CTRL.CREATE_BLOCK:
this.emit('create block', { error, blockId });
break;
case constants_1.COMMANDS.LOG_CTRL.APPEND_BLOCK:
this.emit('append block', { error, blockId });
break;
case constants_1.COMMANDS.LOG_CTRL.DELETE_BLOCK:
this.emit('delete block', { error, blockId });
break;
case constants_1.COMMANDS.LOG_CTRL.START_BLOCK:
this.emit('start block', { error, blockId });
break;
case constants_1.COMMANDS.LOG_CTRL.STOP_BLOCK:
this.emit('stop block', { error, blockId });
break;
case constants_1.COMMANDS.LOG_CTRL.RESET_LOG:
this.emit('reset log', { error });
break;
default:
this.emit('error', `Unrecognized block command "${command}"!`);
break;
}
}
/**
* Handle incoming log data
* (https://wiki.bitcraze.io/projects:crazyflie:firmware:comm_protocol#log_data_access_port_5_channel_2)
*/
handleLogData(data) {
const types = constants_1.BUFFER_TYPES(data);
const blockId = types.int8.read(0);
// Timestamp is different because it's a 3-byte integer
// const timestamp = data.readIntLE(1, 3);
// However, we have no use for this at the moment
// Get block so we know what variables are and their data types
const block = this.getBlock(blockId);
if (!block) {
// If drone hasn't initialized yet, we're about to reset the log so any previous blocks should be deleted
if (this.crazyflie.initialized) {
this.emit('error', `Received data for block id "${blockId}" but we don't have that block!`);
}
return;
}
const logData = {};
let pointer = 4;
for (const variable of block.variables) {
const type = types[variable.type];
const logDatum = type.read(pointer);
if (typeof logData[variable.group] === 'undefined') {
logData[variable.group] = {};
}
logData[variable.group][variable.name] = logDatum;
pointer += type.size;
}
// Global `*` event
this.data.emit('*', logData);
// Group-wide event
for (const group of Object.keys(logData)) {
this.data.emit(group, logData[group]);
// Specific `group.name` event
for (const name of Object.keys(logData[group])) {
this.data.emit(`${group}.${name}`, logData[group][name]);
}
}
}
}
exports.Logging = Logging;
//# sourceMappingURL=logging.js.map