use crate::error::{Error, Result};
#[derive(Debug, Clone, Copy)]
pub struct Attitude {
pub pitch: f32,
pub roll: f32,
pub yaw: f32,
}
pub trait GimbalDevice: Send {
fn protocol_name(&self) -> &'static str;
fn set_attitude(&mut self, pitch: f32, roll: f32, yaw: f32) -> Result<()>;
fn get_attitude(&mut self) -> Result<Attitude>;
fn center(&mut self) -> Result<()> {
self.set_attitude(0.0, 0.0, 0.0)
}
fn set_pan_mode(&mut self, _mode: u8) -> Result<()> {
Err(Error::Unsupported {
protocol: self.protocol_name(),
op: "set_pan_mode",
})
}
fn set_standby(&mut self, _enabled: bool) -> Result<()> {
Err(Error::Unsupported {
protocol: self.protocol_name(),
op: "set_standby",
})
}
fn get_version(&mut self) -> Result<String> {
Err(Error::Unsupported {
protocol: self.protocol_name(),
op: "get_version",
})
}
fn as_any(&self) -> Option<&dyn std::any::Any> {
None
}
fn as_any_mut(&mut self) -> Option<&mut dyn std::any::Any> {
None
}
}
pub fn detect_gimbal(device_path: &str) -> Result<Box<dyn GimbalDevice>> {
use crate::protocols::storm32_rc::Storm32RC;
match Storm32RC::try_detect(device_path) {
Ok(gimbal) => Ok(Box::new(gimbal)),
Err(e) => {
if is_resource_busy(&e) {
Err(Error::DeviceBusy {
device: device_path.to_string(),
})
} else {
Err(Error::NotDetected {
device: device_path.to_string(),
tried: "STorM32 RC".to_string(),
})
}
}
}
}
fn is_resource_busy(err: &Error) -> bool {
let mut current: Option<&(dyn std::error::Error + 'static)> = Some(err);
while let Some(e) = current {
if let Some(io_err) = e.downcast_ref::<std::io::Error>() {
if io_err.kind() == std::io::ErrorKind::ResourceBusy {
return true;
}
}
let display = e.to_string();
if display.contains("Device or resource busy") || display.contains("Resource busy") {
return true;
}
current = e.source();
}
false
}