tello 0.6.3

SDK for intel DJI Tello drone using the native api
Documentation
use super::PackageData;
use byteorder::{LittleEndian, ReadBytesExt};
use std::io::{BufRead, Cursor, Seek, SeekFrom};

/// Represents the last received meta data from the drone
///
#[derive(Debug, Clone, Default)]
pub struct DroneMeta {
    flight: Option<FlightData>,
    wifi: Option<WifiInfo>,
    light: Option<LightInfo>,
}

impl DroneMeta {
    /// returns an option of the FlightData.
    ///
    /// this will always represent the last state event if the network connection is dropped
    pub fn get_flight_data(&self) -> Option<FlightData> {
        self.flight.clone()
    }
    /// returns an option of the WifiInfo.
    /// stang 90% is max in the AP mode
    ///
    /// this will always represent the last state event if the network connection is dropped
    pub fn get_wifi_info(&self) -> Option<WifiInfo> {
        self.wifi.clone()
    }
    /// returns an option of the LightInfo.
    ///
    /// this will always represent the last state event if the network connection is dropped
    pub fn get_light_info(&self) -> Option<LightInfo> {
        self.light.clone()
    }
    /// applies the package to the current data.
    /// It ignore non Meta package data and just overwrite the current metadata
    pub fn update(&mut self, package: &PackageData) {
        match package {
            PackageData::FlightData(fd) => self.flight = Some(fd.clone()),
            PackageData::WifiInfo(wifi) => self.wifi = Some(wifi.clone()),
            PackageData::LightInfo(li) => self.light = Some(li.clone()),
            _ => (),
        };
    }
}

fn int16(val0: u8, val1: u8) -> i16 {
    if val1 != 0 {
        (((val0 as i32) | ((val1 as i32) << 8)) - 0x10000) as i16
    } else {
        (val0 as i16) | ((val1 as i16) << 8)
    }
}

#[derive(Clone)]
pub struct FlightData {
    pub height: i16,
    pub north_speed: i16,
    pub east_speed: i16,
    pub ground_speed: i16,
    pub fly_time: i16,
    pub imu_state: bool,
    pub pressure_state: bool,
    pub down_visual_state: bool,
    pub power_state: bool,
    pub battery_state: bool,
    pub gravity_state: bool,
    pub wind_state: bool,
    pub imu_calibration_state: u8,
    pub battery_percentage: u8,
    pub drone_battery_left: i16,
    pub drone_fly_time_left: i16,

    pub em_sky: bool,
    pub em_ground: bool,
    pub em_open: bool,
    pub drone_hover: bool,
    pub outage_recording: bool,
    pub battery_low: bool,
    pub battery_lower: bool,
    pub factory_mode: bool,

    pub fly_mode: u8,
    pub throw_fly_timer: u8,
    pub camera_state: u8,
    pub electrical_machinery_state: u8,
    pub front_in: bool,
    pub front_out: bool,
    pub front_lsc: bool,
    pub temperature_height: bool,
}

impl std::fmt::Debug for FlightData {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(
            f,
            "FlightData {{ alt: {}, north_sp: {}, east_sp: {}, ground_sp: {}, fly_time: {}, imu_cal: {}, battery: {}, battery_left: {}, fly_time_left: {}, fly_mode: {}, throw_fly_timer: {}, camera: {}, em: {}}}",
            self.height, self.north_speed, self.east_speed, self.ground_speed, self.fly_time, self.imu_calibration_state, self.battery_percentage,
            self.drone_battery_left, self.drone_fly_time_left, self.fly_mode, self.throw_fly_timer, self.camera_state, self.electrical_machinery_state
        )
    }
}

impl From<Vec<u8>> for FlightData {
    fn from(data: Vec<u8>) -> FlightData {
        FlightData {
            height: int16(data[0], data[1]),
            north_speed: int16(data[2], data[3]),
            east_speed: int16(data[4], data[5]),
            ground_speed: int16(data[6], data[7]),
            fly_time: int16(data[8], data[9]),

            imu_state: ((data[10]) & 0x1) != 0,
            pressure_state: ((data[10] >> 1) & 0x1) != 0,
            down_visual_state: ((data[10] >> 2) & 0x1) != 0,
            power_state: ((data[10] >> 3) & 0x1) != 0,
            battery_state: ((data[10] >> 4) & 0x1) != 0,
            gravity_state: ((data[10] >> 5) & 0x1) != 0,
            wind_state: ((data[10] >> 7) & 0x1) != 0,

            imu_calibration_state: data[11],
            battery_percentage: data[12],
            drone_battery_left: int16(data[13], data[14]),
            drone_fly_time_left: int16(data[15], data[16]),

            em_sky: ((data[17]) & 0x1) != 0,
            em_ground: ((data[17] >> 1) & 0x1) != 0,
            em_open: ((data[17] >> 2) & 0x1) != 0,
            drone_hover: ((data[17] >> 3) & 0x1) != 0,
            outage_recording: ((data[17] >> 4) & 0x1) != 0,
            battery_low: ((data[17] >> 5) & 0x1) != 0,
            battery_lower: ((data[17] >> 6) & 0x1) != 0,
            factory_mode: ((data[17] >> 7) & 0x1) != 0,

            fly_mode: data[18],
            throw_fly_timer: data[19],
            camera_state: data[20],
            electrical_machinery_state: data[21],

            front_in: ((data[22]) & 0x1) != 0,
            front_out: ((data[22] >> 1) & 0x1) != 0,
            front_lsc: ((data[22] >> 2) & 0x1) != 0,

            temperature_height: ((data[23]) & 0x1) != 0,
        }
    }
}

/// current strength of the wifi signal and distortion.
/// When the drone is in the AP mode, the max strength value is 90
#[derive(Debug, Clone)]
pub struct WifiInfo {
    strength: u8,
    disturb: u8,
}
impl From<Vec<u8>> for WifiInfo {
    /// parse the incoming network package
    fn from(data: Vec<u8>) -> WifiInfo {
        WifiInfo {
            strength: data[0],
            disturb: data[1],
        }
    }
}

/// some features like a flip or bouncing is only available when the battery is charged and there is enough light
/// check the FlightData for the battery state
#[derive(Debug, Clone)]
pub struct LightInfo {
    good: bool,
}
impl From<Vec<u8>> for LightInfo {
    /// parse the incoming network package
    fn from(data: Vec<u8>) -> LightInfo {
        LightInfo { good: data[0] == 0 }
    }
}

/// not complete parse log message. This message is send frequently from the drone
#[derive(Debug, Clone)]
pub struct LogMessage {
    pub id: u16,
    pub message: String,
}
impl From<Vec<u8>> for LogMessage {
    /// parse the incoming network package
    fn from(data: Vec<u8>) -> LogMessage {
        let mut cur = Cursor::new(data);
        let id: u16 = cur.read_u16::<LittleEndian>().unwrap();
        cur.seek(SeekFrom::Start(19)).unwrap();
        let mut msg: Vec<u8> = Vec::new();
        cur.read_until(0, &mut msg).unwrap();
        LogMessage {
            id,
            message: String::from_utf8(msg)
                .unwrap()
                .trim_matches('\u{0}')
                .to_string(),
        }
    }
}