use crazyflie_link::Packet;
use flume::Sender;
use crate::{Error, Result};
use crate::crazyflie::HL_COMMANDER_PORT;
const COMMAND_SET_GROUP_MASK: u8 = 0;
const COMMAND_STOP: u8 = 3;
const COMMAND_DEFINE_TRAJECTORY: u8 = 6;
const COMMAND_TAKEOFF_2: u8 = 7;
const COMMAND_LAND_2: u8 = 8;
const COMMAND_SPIRAL: u8 = 11;
const COMMAND_GO_TO_2: u8 = 12;
const COMMAND_START_TRAJECTORY_2: u8 = 13;
pub const ALL_GROUPS: u8 = 0;
const TRAJECTORY_LOCATION_MEM: u8 = 1;
pub const TRAJECTORY_TYPE_POLY4D: u8 = 0;
pub const TRAJECTORY_TYPE_POLY4D_COMPRESSED: u8 = 1;
#[derive(Debug)]
pub struct HighLevelCommander {
uplink: Sender<Packet>,
}
impl HighLevelCommander {
pub fn new(uplink: Sender<Packet>) -> Self {
Self { uplink }
}
}
impl HighLevelCommander {
pub async fn set_group_mask(&self, group_mask: u8) -> Result<()> {
let mut payload = Vec::with_capacity(2);
payload.push(COMMAND_SET_GROUP_MASK);
payload.push(group_mask);
let pk = Packet::new(HL_COMMANDER_PORT, 0, payload);
self.uplink
.send_async(pk)
.await
.map_err(|_| Error::Disconnected)?;
Ok(())
}
}
impl HighLevelCommander {
pub async fn take_off(&self, height: f32, yaw: Option<f32>, duration: f32, group_mask: Option<u8>) -> Result<()> {
let use_current_yaw = yaw.is_none();
let target_yaw = yaw.unwrap_or(0.0);
let group_mask_value = group_mask.unwrap_or(ALL_GROUPS);
let mut payload = Vec::with_capacity(3 + 3 * 4);
payload.push(COMMAND_TAKEOFF_2);
payload.push(group_mask_value);
payload.extend_from_slice(&height.to_le_bytes());
payload.extend_from_slice(&target_yaw.to_le_bytes());
payload.push(use_current_yaw as u8);
payload.extend_from_slice(&duration.to_le_bytes());
let pk = Packet::new(HL_COMMANDER_PORT, 0, payload);
self.uplink
.send_async(pk)
.await
.map_err(|_| Error::Disconnected)?;
Ok(())
}
pub async fn land(&self, height: f32, yaw: Option<f32>, duration: f32, group_mask: Option<u8>) -> Result<()> {
let use_current_yaw = yaw.is_none();
let target_yaw = yaw.unwrap_or(0.0);
let group_mask_value = group_mask.unwrap_or(ALL_GROUPS);
let mut payload = Vec::with_capacity(3 + 3 * 4);
payload.push(COMMAND_LAND_2);
payload.push(group_mask_value);
payload.extend_from_slice(&height.to_le_bytes());
payload.extend_from_slice(&target_yaw.to_le_bytes());
payload.push(use_current_yaw as u8);
payload.extend_from_slice(&duration.to_le_bytes());
let pk = Packet::new(HL_COMMANDER_PORT, 0, payload);
self.uplink
.send_async(pk)
.await
.map_err(|_| Error::Disconnected)?;
Ok(())
}
pub async fn stop(&self, group_mask: Option<u8>) -> Result<()> {
let group_mask_value = group_mask.unwrap_or(ALL_GROUPS);
let mut payload = Vec::with_capacity(2);
payload.push(COMMAND_STOP);
payload.push(group_mask_value);
let pk = Packet::new(HL_COMMANDER_PORT, 0, payload);
self.uplink
.send_async(pk)
.await
.map_err(|_| Error::Disconnected)?;
Ok(())
}
pub async fn go_to(&self, x: f32, y: f32, z: f32, yaw: f32, duration: f32, relative: bool, linear: bool, group_mask: Option<u8>) -> Result<()> {
let group_mask_value = group_mask.unwrap_or(ALL_GROUPS);
let mut payload = Vec::with_capacity(4 + 5 * 4);
payload.push(COMMAND_GO_TO_2);
payload.push(group_mask_value);
payload.push(relative as u8);
payload.push(linear as u8);
payload.extend_from_slice(&x.to_le_bytes());
payload.extend_from_slice(&y.to_le_bytes());
payload.extend_from_slice(&z.to_le_bytes());
payload.extend_from_slice(&yaw.to_le_bytes());
payload.extend_from_slice(&duration.to_le_bytes());
let pk = Packet::new(HL_COMMANDER_PORT, 0, payload);
self.uplink
.send_async(pk)
.await
.map_err(|_| Error::Disconnected)?;
Ok(())
}
pub async fn spiral(&self, angle: f32, initial_radius: f32, final_radius: f32, altitude_gain: f32, duration: f32, sideways: bool, clockwise: bool, group_mask: Option<u8>) -> Result<()> {
if angle.abs() > 2.0 * std::f32::consts::PI {
return Err(Error::InvalidArgument("angle out of range".to_string()));
}
if initial_radius < 0.0 {
return Err(Error::InvalidArgument("initial_radius must be >= 0".to_string()));
}
if final_radius < 0.0 {
return Err(Error::InvalidArgument("final_radius must be >= 0".to_string()));
}
let group_mask_value = group_mask.unwrap_or(ALL_GROUPS);
let mut payload = Vec::with_capacity(4 + 5 * 4);
payload.push(COMMAND_SPIRAL);
payload.push(group_mask_value);
payload.push(sideways as u8);
payload.push(clockwise as u8);
payload.extend_from_slice(&angle.to_le_bytes());
payload.extend_from_slice(&initial_radius.to_le_bytes());
payload.extend_from_slice(&final_radius.to_le_bytes());
payload.extend_from_slice(&altitude_gain.to_le_bytes());
payload.extend_from_slice(&duration.to_le_bytes());
let pk = Packet::new(HL_COMMANDER_PORT, 0, payload);
self.uplink
.send_async(pk)
.await
.map_err(|_| Error::Disconnected)?;
Ok(())
}
}
impl HighLevelCommander {
pub async fn define_trajectory(&self, trajectory_id: u8, memory_offset: u32, num_pieces: u8, trajectory_type: Option<u8>) -> Result<()> {
let trajectory_type_value = trajectory_type.unwrap_or(TRAJECTORY_TYPE_POLY4D);
let mut payload = Vec::with_capacity(5 + 1 * 4);
payload.push(COMMAND_DEFINE_TRAJECTORY);
payload.push(trajectory_id);
payload.push(TRAJECTORY_LOCATION_MEM);
payload.push(trajectory_type_value);
payload.extend_from_slice(&memory_offset.to_le_bytes());
payload.push(num_pieces);
let pk = Packet::new(HL_COMMANDER_PORT, 0, payload);
self.uplink
.send_async(pk)
.await
.map_err(|_| Error::Disconnected)?;
Ok(())
}
pub async fn start_trajectory(&self, trajectory_id: u8, time_scale: f32, relative_position: bool, relative_yaw: bool, reversed: bool, group_mask: Option<u8>) -> Result<()> {
let group_mask_value = group_mask.unwrap_or(ALL_GROUPS);
let mut payload = Vec::with_capacity(5 + 1 * 4);
payload.push(COMMAND_START_TRAJECTORY_2);
payload.push(group_mask_value);
payload.push(relative_position as u8);
payload.push(relative_yaw as u8);
payload.push(reversed as u8);
payload.push(trajectory_id);
payload.extend_from_slice(&time_scale.to_le_bytes());
let pk = Packet::new(HL_COMMANDER_PORT, 0, payload);
self.uplink
.send_async(pk)
.await
.map_err(|_| Error::Disconnected)?;
Ok(())
}
}