"use strict";
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
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) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
Object.defineProperty(exports, "__esModule", { value: true });
const child_process_1 = require("child_process");
const events_1 = require("events");
const logger_1 = require("./logger");
const webServer_1 = require("../servers/webServer");
const telemetry_1 = require("../servers/telemetry");
const osdData_1 = require("../model/osdData");
const dgram_1 = require("dgram");
const opn = require("opn");
/**
*
*/
class Tello {
constructor() {
this.PORT = 8889;
this.HOST = '192.168.10.1';
this.PORT2 = 8890;
this.HOST2 = '0.0.0.0';
this.localPort = 50602;
this.isMock = false;
this.isStreming = false;
this.hasTelemetry = false;
this.myEmitter = new events_1.EventEmitter();
this.osdData = new osdData_1.OSDData();
this.deviceIP = this.HOST;
this.telloWebServer = new webServer_1.TelloWebServer();
this.telloTelemetry = new telemetry_1.TelloTelemetry();
}
/**
*
* @param {number} time in ms
* @return {Promise<Tello>}
*/
wait(time) {
return __awaiter(this, void 0, void 0, function* () {
return new Promise(resolve => {
setTimeout(() => {
resolve(this);
}, time);
});
});
}
;
/**
*
* @return {Promise<Tello>}
*/
stopStream() {
return new Promise(resolve => {
this.sendCmd('streamoff').then(() => {
if (this.tello_video) {
this.tello_video.close();
this.tello_video = undefined;
}
if (this.h264encoder) {
this.h264encoder.kill();
}
this.isStreming = false;
resolve(this);
});
});
}
/**
*
* @return {Promise<Tello>}
*/
startStream() {
return new Promise(resolve => {
this.sendCmd('streamon').then(() => {
this.tello_video = dgram_1.createSocket('udp4');
this.h264encoder_spawn = {
"command": 'mplayer',
"args": ['-gui', '-nolirc', '-fps', '35', '-really-quiet', '-']
};
this.h264encoder = child_process_1.spawn(this.h264encoder_spawn.command, this.h264encoder_spawn.args);
/* this.h264encoder.on('close', (code) => {
Logger.error('[Tello]', `child process exited with code ${code}`);
});*/
this.h264encoder.stderr.on('data', data => {
logger_1.Logger.error('[Tello]', 'mplayer error', data.toString());
});
/* this.h264encoder.stdout.on('data', data => {
const idx = data.indexOf(this.headers['h264_baseline']);
if (idx > -1 && this.h264chunks.length > 0) {
this.h264chunks.push(data.slice(0, idx));
if (this.hasTelemetry) {
try {
TelloTelemetry.sendVideo(Buffer.concat(this.h264chunks).toString('binary'));
} catch (e) {
}
}
this.h264chunks = [];
this.h264chunks.push(data.slice(idx));
} else {
this.h264chunks.push(data);
}
});*/
this.tello_video.on('message', msg => this.h264encoder.stdin.write(msg));
this.tello_video.on('listening', () => {
logger_1.Logger.info('[Tello]', `tello_video listening ${this.tello_video.address().address}:${this.tello_video.address().port}`);
this.isStreming = true;
resolve(this);
});
this.tello_video.bind(11111, this.HOST2);
});
});
}
/**
*
* @return {Tello}
*/
mock() {
this.deviceIP = '127.0.0.1';
this.isMock = true;
return this;
}
/**
*
* @return {Promise<Tello>}
*/
startTelemetry() {
return new Promise(resolve => {
if (!this.isMock) {
this.telloWebServer.start().then(() => {
this.runTelemetry().then(() => resolve(this));
});
}
});
}
/**
*
* @return {Promise<Tello>}
*/
runTelemetry() {
return new Promise(resolve => {
this.telloTelemetry.start().then(() => {
this.hasTelemetry = true;
opn('http://127.0.0.1:3000/telemetry.html').then(() => {
logger_1.Logger.info('[Tello]', `Telemetry started`);
resolve(this);
});
});
});
}
/**
*
* @return {Promise<Tello>}
*/
stopTelemetry() {
return new Promise(resolve => {
this.shutDownTelemetry().then(() => {
this.telloTelemetry.stop().then(() => {
this.hasTelemetry = true;
logger_1.Logger.info('[Tello]', `Stopping telemetry`);
resolve(this);
});
});
});
}
/**
*
* @return {Promise<Tello>}
*/
shutDownTelemetry() {
return new Promise(resolve => {
if (!this.isMock) {
this.telloWebServer.stop().then(() => resolve(this));
}
else {
resolve(this);
}
});
}
/**
*
* @param {string} value
* @return {Promise<{value: string, tello: Tello}>}
*/
get(value) {
return Promise.resolve({ value: this.osdData[value], tello: this });
}
/**
*
*/
listenState() {
this.UDPServer.on('message', msg => {
const strMsg = msg.toString().trim();
const fieldList = strMsg.split(';');
fieldList.forEach(field => {
const fields = field.split(':');
this.osdData[fields[0]] = fields[1];
if (this.hasTelemetry) {
this.telloTelemetry.send(this.osdData);
}
});
/* Logger.info('fieldList', fieldList)
Logger.info('osdData', osdData)*/
});
this.UDPServer.on('listening', () => {
const address = this.UDPServer.address();
logger_1.Logger.info('[Tello]', 'server listening', address.address, address.port);
});
this.UDPServer.bind(this.PORT2, this.HOST2);
}
/**
*
* @param {string} cmd
* @return {Promise<Tello>}
*/
sendMethod(cmd) {
return new Promise((resolve, reject) => {
const message = new Buffer(cmd);
logger_1.Logger.info('[Tello]', 'send:', cmd);
this.UDPClient.send(message, 0, message.length, this.PORT, this.deviceIP, err => {
if (err) {
reject(err);
}
else {
resolve(this);
}
});
});
}
/**
*
* @param {string} cmd
* @return {Promise<Tello>}
*/
sendCmd(cmd) {
return new Promise(((resolve, reject) => {
this.sendMethod(cmd).then(() => {
this.myEmitter.on('status', () => {
resolve(this);
});
}).catch(err => {
reject(err);
});
}));
}
/**
*
* @return {Promise<Tello>}
*/
emergencyStop() {
return __awaiter(this, void 0, void 0, function* () {
return this.sendCmd('emergency');
});
}
/**
*
* @return {Promise<Tello>}
*/
takeoff() {
return this.sendCmd('takeoff');
}
/**
*
* @param {number} x1 in cm
* @param {number} y1 in cm
* @param {number} z1 in cm
* @param {number} x2 in cm
* @param {number} y2 in cm
* @param {number} z2 in cm
* @param {number} speed in cm/s
* @return {Promise<Tello>}
*/
curve(x1 = 20, y1 = 20, z1 = 20, x2 = 60, y2 = 40, z2 = 0, speed = 60) {
return this.sendCmd('curve ' + x1 + ' ' + y1 + ' ' + z1 + ' ' + x2 + ' ' + y2 + ' ' + z2 + ' ' + speed + ' ');
}
/**
*
* @param {number} distance in cm
* @return {Promise<Tello>}
*/
forward(distance = 50) {
return this.sendCmd('forward ' + distance);
}
/**
*
* @return {Promise<Tello>}
*/
start() {
this.myEmitter.setMaxListeners(20);
return new Promise(resolve => {
this.UDPClient = dgram_1.createSocket('udp4');
this.UDPServer = dgram_1.createSocket('udp4');
this.UDPClient.bind(this.localPort, '0.0.0.0', () => {
logger_1.Logger.info('[Tello]', 'connected');
this.sendCmd('command').then(() => {
resolve(this);
});
});
process.on('SIGINT', () => __awaiter(this, void 0, void 0, function* () {
yield this.stop();
}));
this.UDPClient.on('message', msg => {
if (msg.toString() === 'ok') {
logger_1.Logger.info('[Tello]', 'Data received from server : ', msg.toString());
}
else {
logger_1.Logger.error('[Tello]', 'not ok', msg);
}
this.myEmitter.emit('status');
});
this.listenState();
});
}
/**
*
* @param {number} distance in cm
* @return {Promise<Tello>}
*/
right(distance = 50) {
return this.sendCmd('right ' + distance);
}
/**
*
* @param {number} height in cm
* @return {Promise<Tello>}
*/
down(height = 50) {
return this.sendCmd('down ' + height);
}
/**
*
* @param {number} speed in cm/s
* @return {Promise<Tello>}
*/
speed(speed = 50) {
return this.sendCmd('speed ' + speed);
}
/**
* Ends the process
*/
stop() {
return __awaiter(this, void 0, void 0, function* () {
this.UDPClient.close();
this.UDPServer.close();
if (this.tello_video) {
yield this.tello_video.close();
}
if (this.h264encoder) {
yield this.h264encoder.kill();
}
if (!this.isMock && this.hasTelemetry) {
yield this.stopTelemetry();
}
logger_1.Logger.info(new Date(), '[Tello]', 'Goodbye !');
process.exit(0);
});
}
/**
*
* @param {number} distance in cm
* @return {Promise<Tello>}
*/
left(distance = 50) {
return this.sendCmd('left ' + distance);
}
/**
*
* @param {number} angle in degree
* @return {Promise<Tello>}
*/
rotateCW(angle = 90) {
return this.sendCmd('cw ' + angle);
}
/**
*
* @return {Promise<Tello>}
*/
land() {
return this.sendCmd('land');
}
/**
*
* @param {number} distance in cm
* @return {Promise<Tello>}
*/
backward(distance = 50) {
return this.sendCmd('back ' + distance);
}
/**
*
* @param {number} angle in degree
* @return {Promise<Tello>}
*/
rotateCCW(angle = 90) {
return this.sendCmd('ccw ' + angle);
}
/**
*
* @param {number} height in cm
* @return {Promise<Tello>}
*/
up(height = 50) {
return this.sendCmd('up ' + height);
}
/**
*
* @param {string} orientation values: f, b, l, r
* @return {Promise<Tello>}
*/
flip(orientation = 'f') {
return this.sendCmd('flip ' + orientation);
}
/**
*
* @param {number} x in cm
* @param {number} y in cm
* @param {number} z in cm
* @param {number} speed in cm/s
* @return {Promise<Tello>}
*/
flyTo(x = 50, y = 50, z = 0, speed = 100) {
return this.sendCmd('go ' + x + ' ' + y + ' ' + z + ' ' + speed + ' ');
}
}
exports.Tello = Tello;
//# sourceMappingURL=tello.js.map