use autd3_core::{
ethercat::DcSysTime,
geometry::Geometry,
link::{Link, LinkError, MsgId, RxMessage, TxBufferPoolSync, TxMessage},
};
use autd3_firmware_emulator::CPUEmulator;
#[derive(Default, Clone, Copy)]
#[doc(hidden)]
pub struct AuditOption {
pub initial_msg_id: Option<MsgId>,
pub broken: bool,
}
#[doc(hidden)]
pub struct Audit {
option: AuditOption,
cpus: Vec<CPUEmulator>,
broken: bool,
buffer_pool: TxBufferPoolSync,
}
impl std::ops::Deref for Audit {
type Target = [CPUEmulator];
fn deref(&self) -> &Self::Target {
&self.cpus
}
}
impl std::ops::DerefMut for Audit {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.cpus
}
}
impl Audit {
pub const fn new(option: AuditOption) -> Self {
Self {
option,
cpus: Vec::new(),
broken: false,
buffer_pool: TxBufferPoolSync::new(),
}
}
pub const fn break_down(&mut self) {
self.broken = true;
}
pub const fn repair(&mut self) {
self.broken = false;
}
}
impl Link for Audit {
fn open(&mut self, geometry: &Geometry) -> Result<(), LinkError> {
self.cpus = geometry
.iter()
.enumerate()
.map(|(i, dev)| {
let mut cpu = CPUEmulator::new(i, dev.num_transducers());
if let Some(msg_id) = self.option.initial_msg_id {
cpu.set_last_msg_id(msg_id);
}
cpu
})
.collect();
self.broken = self.option.broken;
self.buffer_pool.init(geometry);
Ok(())
}
fn close(&mut self) -> Result<(), LinkError> {
self.cpus.clear();
Ok(())
}
fn alloc_tx_buffer(&mut self) -> Result<Vec<TxMessage>, LinkError> {
Ok(self.buffer_pool.borrow())
}
fn send(&mut self, tx: Vec<TxMessage>) -> Result<(), LinkError> {
if self.broken {
return Err(LinkError::new("broken"));
}
self.cpus.iter_mut().for_each(|cpu| {
cpu.send(&tx);
});
self.buffer_pool.return_buffer(tx);
Ok(())
}
fn receive(&mut self, rx: &mut [RxMessage]) -> Result<(), LinkError> {
if self.broken {
return Err(LinkError::new("broken"));
}
self.cpus.iter_mut().for_each(|cpu| {
cpu.update_with_sys_time(DcSysTime::ZERO);
rx[cpu.idx()] = cpu.rx();
});
Ok(())
}
fn is_open(&self) -> bool {
!self.cpus.is_empty()
}
}