use crate::crtp_utils::crtp_channel_dispatcher;
use crate::{Error, Result};
use crate::crazyflie::SUPERVISOR_PORT;
use crazyflie_link::Packet;
use flume::{Receiver, Sender};
use futures::lock::Mutex;
use std::time::Instant;
use tokio::time::{timeout, Duration};
const SUPERVISOR_CH_INFO: u8 = 0;
pub(crate) const SUPERVISOR_CH_COMMAND: u8 = 1;
const CMD_GET_STATE_BITFIELD: u8 = 0x0C;
pub(crate) const CMD_ARM_SYSTEM: u8 = 0x01;
pub(crate) const CMD_RECOVER_SYSTEM: u8 = 0x02;
pub(crate) const CMD_EMERGENCY_STOP: u8 = 0x03;
pub(crate) const CMD_EMERGENCY_STOP_WATCHDOG: u8 = 0x04;
const CMD_RESPONSE: u8 = 0x80;
const BITFIELD_CACHE_TIMEOUT: Duration = Duration::from_millis(100);
const BIT_CAN_BE_ARMED: u8 = 0;
const BIT_IS_ARMED: u8 = 1;
const BIT_IS_AUTO_ARMED: u8 = 2;
const BIT_CAN_FLY: u8 = 3;
const BIT_IS_FLYING: u8 = 4;
const BIT_IS_TUMBLED: u8 = 5;
const BIT_IS_LOCKED: u8 = 6;
const BIT_IS_CRASHED: u8 = 7;
const BIT_HL_CONTROL_ACTIVE: u8 = 8;
const BIT_HL_TRAJ_FINISHED: u8 = 9;
const BIT_HL_CONTROL_DISABLED: u8 = 10;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct SupervisorInfo {
pub raw: u16,
}
impl SupervisorInfo {
pub fn from_bits(bits: u16) -> Self {
Self { raw: bits }
}
pub fn can_be_armed(&self) -> bool {
(self.raw >> BIT_CAN_BE_ARMED) & 0x01 != 0
}
pub fn is_armed(&self) -> bool {
(self.raw >> BIT_IS_ARMED) & 0x01 != 0
}
pub fn is_auto_armed(&self) -> bool {
(self.raw >> BIT_IS_AUTO_ARMED) & 0x01 != 0
}
pub fn can_fly(&self) -> bool {
(self.raw >> BIT_CAN_FLY) & 0x01 != 0
}
pub fn is_flying(&self) -> bool {
(self.raw >> BIT_IS_FLYING) & 0x01 != 0
}
pub fn is_tumbled(&self) -> bool {
(self.raw >> BIT_IS_TUMBLED) & 0x01 != 0
}
pub fn is_locked(&self) -> bool {
(self.raw >> BIT_IS_LOCKED) & 0x01 != 0
}
pub fn is_crashed(&self) -> bool {
(self.raw >> BIT_IS_CRASHED) & 0x01 != 0
}
pub fn hl_control_active(&self) -> bool {
(self.raw >> BIT_HL_CONTROL_ACTIVE) & 0x01 != 0
}
pub fn hl_traj_finished(&self) -> bool {
(self.raw >> BIT_HL_TRAJ_FINISHED) & 0x01 != 0
}
pub fn hl_control_disabled(&self) -> bool {
(self.raw >> BIT_HL_CONTROL_DISABLED) & 0x01 != 0
}
pub fn active_states(&self) -> Vec<&'static str> {
let states = [
("Can be armed", self.can_be_armed()),
("Is armed", self.is_armed()),
("Is auto armed", self.is_auto_armed()),
("Can fly", self.can_fly()),
("Is flying", self.is_flying()),
("Is tumbled", self.is_tumbled()),
("Is locked", self.is_locked()),
("Is crashed", self.is_crashed()),
("HL control active", self.hl_control_active()),
("HL trajectory finished", self.hl_traj_finished()),
("HL control disabled", self.hl_control_disabled()),
];
states
.iter()
.filter_map(|(name, active)| if *active { Some(*name) } else { None })
.collect()
}
}
pub struct Supervisor {
uplink: Sender<Packet>,
info_downlink: Mutex<Receiver<Packet>>,
cache_timeout: Duration,
cached_bitfield: std::sync::Mutex<Option<(Instant, u16)>>,
}
impl Supervisor {
pub(crate) fn new(uplink: Sender<Packet>, downlink: Receiver<Packet>) -> Self {
let (info_downlink, _cmd_downlink, _misc1, _misc2) = crtp_channel_dispatcher(downlink);
Self {
uplink,
info_downlink: Mutex::new(info_downlink),
cache_timeout: BITFIELD_CACHE_TIMEOUT,
cached_bitfield: std::sync::Mutex::new(None),
}
}
pub async fn read_bitfield(&self) -> Result<SupervisorInfo> {
if let Some(info) = self.cached_info() {
return Ok(info);
}
let downlink = self.info_downlink.lock().await;
if let Some(info) = self.cached_info() {
return Ok(info);
}
while downlink.try_recv().is_ok() {}
let pk = Packet::new(
SUPERVISOR_PORT,
SUPERVISOR_CH_INFO,
vec![CMD_GET_STATE_BITFIELD],
);
self.uplink.send_async(pk).await.map_err(|_| Error::Disconnected)?;
let bitfield = Self::wait_for_bitfield(&downlink).await?;
drop(downlink);
let mut cached = self.cached_bitfield.lock().unwrap();
*cached = Some((Instant::now(), bitfield));
Ok(SupervisorInfo::from_bits(bitfield))
}
fn cached_info(&self) -> Option<SupervisorInfo> {
let cached = self.cached_bitfield.lock().unwrap();
if let Some((fetched_at, bitfield)) = *cached
&& fetched_at.elapsed() < self.cache_timeout
{
return Some(SupervisorInfo::from_bits(bitfield));
}
None
}
async fn wait_for_bitfield(downlink: &Receiver<Packet>) -> Result<u16> {
loop {
let packet = timeout(Duration::from_millis(1000), downlink.recv_async())
.await
.map_err(|_| Error::Timeout)??;
let data = packet.get_data();
if data.len() < 3 {
continue;
}
let cmd = data[0];
if cmd != CMD_GET_STATE_BITFIELD && cmd != (CMD_GET_STATE_BITFIELD | CMD_RESPONSE) {
continue;
}
let bitfield = u16::from_le_bytes([data[1], data[2]]);
return Ok(bitfield);
}
}
pub async fn send_arming_request(&self, do_arm: bool) -> Result<()> {
let command = if do_arm { 1u8 } else { 0u8 };
let pk = Packet::new(
SUPERVISOR_PORT,
SUPERVISOR_CH_COMMAND,
vec![CMD_ARM_SYSTEM, command],
);
self.uplink.send_async(pk).await.map_err(|_| Error::Disconnected)?;
Ok(())
}
pub async fn send_crash_recovery_request(&self) -> Result<()> {
let pk = Packet::new(
SUPERVISOR_PORT,
SUPERVISOR_CH_COMMAND,
vec![CMD_RECOVER_SYSTEM],
);
self.uplink.send_async(pk).await.map_err(|_| Error::Disconnected)?;
Ok(())
}
pub async fn send_emergency_stop(&self) -> Result<()> {
let pk = Packet::new(
SUPERVISOR_PORT,
SUPERVISOR_CH_COMMAND,
vec![CMD_EMERGENCY_STOP],
);
self.uplink.send_async(pk).await.map_err(|_| Error::Disconnected)?;
Ok(())
}
pub async fn send_emergency_stop_watchdog(&self) -> Result<()> {
let pk = Packet::new(
SUPERVISOR_PORT,
SUPERVISOR_CH_COMMAND,
vec![CMD_EMERGENCY_STOP_WATCHDOG],
);
self.uplink.send_async(pk).await.map_err(|_| Error::Disconnected)?;
Ok(())
}
}