use std::collections::HashMap;
use std::fmt::Debug;
use std::sync::{Arc, Mutex};
use crate::core::utils::TryUpdateFrom;
use crate::error::FrameError;
use crate::protocol::{CrcExtra, Frame, MavFrame, MaybeVersioned};
pub trait ProcessFrame: Debug + Send + Sync {
fn process(
&mut self,
frame: &mut MavFrame,
case: ProcessFrameCase,
crc_extra: Option<CrcExtra>,
) -> Result<(), FrameError>;
}
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
pub enum ProcessFrameCase {
IncomingBefore,
IncomingAfter,
OutgoingBefore,
OutgoingAfter,
}
#[derive(Clone, Debug, Default)]
pub struct CustomFrameProcessors {
inner: HashMap<&'static str, Arc<Mutex<dyn ProcessFrame>>>,
sorted_keys: Vec<&'static str>,
sorted_keys_rev: Vec<&'static str>,
}
impl CustomFrameProcessors {
#[inline(always)]
pub fn is_empty(&self) -> bool {
self.inner.is_empty()
}
pub fn add(&mut self, name: &'static str, processor: impl ProcessFrame + 'static) {
self.inner.insert(name, Arc::new(Mutex::new(processor)));
self.resort_keys();
}
pub fn process<V: MaybeVersioned>(
&self,
frame: &mut Frame<V>,
case: ProcessFrameCase,
crc_extra: Option<CrcExtra>,
) -> Result<(), FrameError> {
if self.inner.is_empty() {
return Ok(());
}
let mut mav_frame = frame.clone().into_mav_frame();
let keys = match case {
ProcessFrameCase::IncomingBefore | ProcessFrameCase::OutgoingBefore => {
self.sorted_keys.iter()
}
ProcessFrameCase::IncomingAfter | ProcessFrameCase::OutgoingAfter => {
self.sorted_keys_rev.iter()
}
};
for name in keys {
let processor = self.inner.get(name).unwrap();
if let Ok(mut processor) = processor.lock() {
processor.process(&mut mav_frame, case, crc_extra)?;
if let Err(err) = frame.try_update_from(&mav_frame) {
log::error!("[frame processor] invalid output from custom processor '{name}' for {case:?}: {err:?}");
return Err(FrameError::from(err));
}
}
}
Ok(())
}
pub(super) fn extend(&mut self, other: &Self) {
for (name, processor) in &other.inner {
self.inner.insert(name, processor.clone());
}
self.resort_keys();
}
fn resort_keys(&mut self) {
self.sorted_keys = self.inner.keys().copied().collect();
self.sorted_keys.sort();
self.sorted_keys_rev = self.sorted_keys.iter().rev().copied().collect();
}
}