use super::Operation;
use crate::{
error::AUTDDriverError,
geometry::{Device, Geometry},
};
use autd3_core::link::{MsgId, TxMessage};
#[doc(hidden)]
pub struct OperationHandler {}
impl OperationHandler {
#[must_use]
pub fn is_done<'a, O1, O2>(operations: &[Option<(O1, O2)>]) -> bool
where
O1: Operation<'a>,
O2: Operation<'a>,
{
operations.iter().all(|op| {
op.as_ref()
.is_none_or(|(op1, op2)| op1.is_done() && op2.is_done())
})
}
#[allow(unused_variables)]
pub fn pack<'a, O1, O2>(
msg_id: MsgId,
operations: &mut [Option<(O1, O2)>],
geometry: &'a Geometry,
tx: &mut [TxMessage],
parallel: bool,
) -> Result<(), AUTDDriverError>
where
O1: Operation<'a>,
O2: Operation<'a>,
AUTDDriverError: From<O1::Error> + From<O2::Error>,
{
#[cfg(feature = "parallel")]
{
use rayon::prelude::*;
if parallel {
geometry
.iter()
.zip(tx.iter_mut())
.zip(operations.iter_mut())
.par_bridge()
.try_for_each(|((dev, tx), op)| {
if let Some((op1, op2)) = op {
Self::pack_op2(msg_id, op1, op2, dev, tx)
} else {
Ok(())
}
})
} else {
geometry
.iter()
.zip(tx.iter_mut())
.zip(operations.iter_mut())
.try_for_each(|((dev, tx), op)| {
if let Some((op1, op2)) = op {
Self::pack_op2(msg_id, op1, op2, dev, tx)
} else {
Ok(())
}
})
}
}
#[cfg(not(feature = "parallel"))]
{
geometry
.iter()
.zip(tx.iter_mut())
.zip(operations.iter_mut())
.try_for_each(|((dev, tx), op)| {
if let Some((op1, op2)) = op {
Self::pack_op2(msg_id, op1, op2, dev, tx)
} else {
Ok(())
}
})
}
}
fn pack_op2<'a, O1, O2>(
msg_id: MsgId,
op1: &mut O1,
op2: &mut O2,
dev: &'a Device,
tx: &mut TxMessage,
) -> Result<(), AUTDDriverError>
where
O1: Operation<'a>,
O2: Operation<'a>,
AUTDDriverError: From<O1::Error> + From<O2::Error>,
{
let res = match (op1.is_done(), op2.is_done()) {
(true, true) => 0,
(true, false) => Self::pack_op(msg_id, op2, dev, tx)?,
(false, true) => Self::pack_op(msg_id, op1, dev, tx)?,
(false, false) => {
let op1_size = Self::pack_op(msg_id, op1, dev, tx)?;
debug_assert!(op1_size % 2 == 0);
let req_size = op2.required_size(dev);
debug_assert!(req_size % 2 == 0);
if tx.payload().len() - op1_size >= req_size {
let op2_size = op2.pack(dev, &mut tx.payload_mut()[op1_size..])?;
tx.header.slot_2_offset = op1_size as u16;
op1_size + op2_size
} else {
op1_size
}
}
};
debug_assert!(res % 2 == 0 && res <= autd3_core::ethercat::EC_OUTPUT_FRAME_SIZE);
Ok(())
}
fn pack_op<'a, O>(
msg_id: MsgId,
op: &mut O,
dev: &'a Device,
tx: &mut TxMessage,
) -> Result<usize, AUTDDriverError>
where
O: Operation<'a>,
AUTDDriverError: From<O::Error>,
{
tx.header.msg_id = msg_id;
tx.header.slot_2_offset = 0;
Ok(op.pack(dev, tx.payload_mut())?)
}
}
#[cfg(test)]
pub(crate) mod tests {
use std::mem::size_of;
use crate::{ethercat::EC_OUTPUT_FRAME_SIZE, link::Header};
use super::*;
struct TestOperation {
pub pack_size: usize,
pub required_size: usize,
pub num_frames: usize,
pub broken: bool,
}
impl Operation<'_> for TestOperation {
type Error = AUTDDriverError;
fn required_size(&self, _: &Device) -> usize {
self.required_size
}
fn pack(&mut self, _: &Device, _: &mut [u8]) -> Result<usize, AUTDDriverError> {
if self.broken {
return Err(AUTDDriverError::NotSupportedTag);
}
self.num_frames -= 1;
Ok(self.pack_size)
}
fn is_done(&self) -> bool {
self.num_frames == 0
}
}
#[rstest::rstest]
#[case::serial(false)]
#[case::parallel(true)]
fn operation_handler(#[case] parallel: bool) {
let geometry = Geometry::new(vec![crate::tests::create_device()]);
let mut op = vec![Some((
TestOperation {
pack_size: 2,
required_size: 4,
num_frames: 3,
broken: false,
},
TestOperation {
pack_size: 2,
required_size: 4,
num_frames: 3,
broken: false,
},
))];
assert!(!OperationHandler::is_done(&op));
let msg_id = MsgId::new(0);
let mut tx = vec![TxMessage::new(); 1];
assert!(OperationHandler::pack(msg_id, &mut op, &geometry, &mut tx, parallel).is_ok());
assert_eq!(op[0].as_ref().unwrap().0.num_frames, 2);
assert_eq!(op[0].as_ref().unwrap().1.num_frames, 2);
assert!(!OperationHandler::is_done(&op));
op[0].as_mut().unwrap().0.pack_size =
EC_OUTPUT_FRAME_SIZE - size_of::<Header>() - op[0].as_ref().unwrap().1.required_size;
assert!(OperationHandler::pack(msg_id, &mut op, &geometry, &mut tx, parallel).is_ok());
assert_eq!(op[0].as_ref().unwrap().0.num_frames, 1);
assert_eq!(op[0].as_ref().unwrap().1.num_frames, 1);
assert!(!OperationHandler::is_done(&op));
op[0].as_mut().unwrap().0.pack_size =
EC_OUTPUT_FRAME_SIZE - size_of::<Header>() - op[0].as_ref().unwrap().1.required_size
+ 2;
assert!(OperationHandler::pack(msg_id, &mut op, &geometry, &mut tx, parallel).is_ok());
assert_eq!(op[0].as_ref().unwrap().0.num_frames, 0);
assert_eq!(op[0].as_ref().unwrap().1.num_frames, 1);
assert!(!OperationHandler::is_done(&op));
assert!(OperationHandler::pack(msg_id, &mut op, &geometry, &mut tx, parallel).is_ok());
assert_eq!(op[0].as_ref().unwrap().0.num_frames, 0);
assert_eq!(op[0].as_ref().unwrap().1.num_frames, 0);
assert!(OperationHandler::is_done(&op));
}
#[rstest::rstest]
#[case::serial(false)]
#[case::parallel(true)]
fn operation_handler_none(#[case] parallel: bool) {
let geometry = Geometry::new(vec![crate::tests::create_device()]);
let mut op: Vec<Option<(TestOperation, TestOperation)>> = vec![None, None];
assert!(OperationHandler::is_done(&op));
let msg_id = MsgId::new(0);
let mut tx = vec![TxMessage::new(); 1];
assert!(OperationHandler::pack(msg_id, &mut op, &geometry, &mut tx, parallel).is_ok());
assert!(OperationHandler::is_done(&op));
}
#[test]
fn first() {
let geometry = Geometry::new(vec![crate::tests::create_device()]);
let mut op = vec![Some((
TestOperation {
pack_size: 0,
required_size: 0,
num_frames: 1,
broken: false,
},
TestOperation {
pack_size: 0,
required_size: 0,
num_frames: 0,
broken: false,
},
))];
assert!(!op[0].as_ref().unwrap().0.is_done());
assert!(op[0].as_ref().unwrap().1.is_done());
assert!(!OperationHandler::is_done(&op));
let msg_id = MsgId::new(0);
let mut tx = vec![TxMessage::new(); 1];
assert!(OperationHandler::pack(msg_id, &mut op, &geometry, &mut tx, false).is_ok());
assert!(op[0].as_ref().unwrap().0.is_done());
assert!(op[0].as_ref().unwrap().1.is_done());
assert!(OperationHandler::is_done(&op));
}
#[test]
fn second() {
let geometry = Geometry::new(vec![crate::tests::create_device()]);
let mut op = vec![Some((
TestOperation {
pack_size: 0,
required_size: 0,
num_frames: 0,
broken: false,
},
TestOperation {
pack_size: 0,
required_size: 0,
num_frames: 1,
broken: false,
},
))];
assert!(op[0].as_ref().unwrap().0.is_done());
assert!(!op[0].as_ref().unwrap().1.is_done());
assert!(!OperationHandler::is_done(&op));
let msg_id = MsgId::new(0);
let mut tx = vec![TxMessage::new(); 1];
assert!(OperationHandler::pack(msg_id, &mut op, &geometry, &mut tx, false).is_ok());
assert!(op[0].as_ref().unwrap().0.is_done());
assert!(op[0].as_ref().unwrap().1.is_done());
assert!(OperationHandler::is_done(&op));
}
#[test]
fn broken_pack() {
let geometry = Geometry::new(vec![crate::tests::create_device()]);
let mut op = vec![Some((
TestOperation {
pack_size: 0,
required_size: 0,
num_frames: 1,
broken: true,
},
TestOperation {
pack_size: 0,
required_size: 0,
num_frames: 1,
broken: false,
},
))];
let msg_id = MsgId::new(0);
let mut tx = vec![TxMessage::new(); 1];
assert_eq!(
Err(AUTDDriverError::NotSupportedTag),
OperationHandler::pack(msg_id, &mut op, &geometry, &mut tx, false)
);
op[0].as_mut().unwrap().0.broken = false;
op[0].as_mut().unwrap().1.broken = true;
assert_eq!(
Err(AUTDDriverError::NotSupportedTag),
OperationHandler::pack(msg_id, &mut op, &geometry, &mut tx, false)
);
op[0].as_mut().unwrap().0.num_frames = 0;
assert_eq!(
Err(AUTDDriverError::NotSupportedTag),
OperationHandler::pack(msg_id, &mut op, &geometry, &mut tx, false)
);
op[0].as_mut().unwrap().0.broken = true;
op[0].as_mut().unwrap().1.broken = false;
op[0].as_mut().unwrap().0.num_frames = 1;
op[0].as_mut().unwrap().1.num_frames = 0;
assert_eq!(
Err(AUTDDriverError::NotSupportedTag),
OperationHandler::pack(msg_id, &mut op, &geometry, &mut tx, false)
);
}
#[test]
fn finished() {
let geometry = Geometry::new(vec![crate::tests::create_device()]);
let mut op = vec![Some((
TestOperation {
pack_size: 0,
required_size: 0,
num_frames: 0,
broken: false,
},
TestOperation {
pack_size: 0,
required_size: 0,
num_frames: 0,
broken: false,
},
))];
assert!(OperationHandler::is_done(&op));
let msg_id = MsgId::new(0);
let mut tx = vec![TxMessage::new(); 1];
assert!(OperationHandler::pack(msg_id, &mut op, &geometry, &mut tx, false).is_ok());
assert!(OperationHandler::is_done(&op));
}
}