use crate::buffer_estimate::BufferEstimator;
use crate::device::{DacCapabilities, DacType};
use crate::point::LaserPoint;
pub use crate::error::{Error, Result};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum WriteOutcome {
Written,
WouldBlock,
}
pub trait DacBackend: Send + 'static {
fn dac_type(&self) -> DacType;
fn caps(&self) -> &DacCapabilities;
fn connect(&mut self) -> Result<()>;
fn disconnect(&mut self) -> Result<()>;
fn is_connected(&self) -> bool;
fn stop(&mut self) -> Result<()>;
fn set_shutter(&mut self, open: bool) -> Result<()>;
}
pub trait FifoBackend: DacBackend {
fn try_write_points(&mut self, pps: u32, points: &[LaserPoint]) -> Result<WriteOutcome>;
fn estimator(&self) -> &dyn BufferEstimator;
}
pub trait FrameSwapBackend: DacBackend {
fn frame_capacity(&self) -> usize;
fn is_ready_for_frame(&mut self) -> bool;
fn write_frame(&mut self, pps: u32, points: &[LaserPoint]) -> Result<WriteOutcome>;
}
pub enum BackendKind {
Fifo(Box<dyn FifoBackend>),
FrameSwap(Box<dyn FrameSwapBackend>),
}
impl BackendKind {
pub fn dac_type(&self) -> DacType {
match self {
BackendKind::Fifo(b) => b.dac_type(),
BackendKind::FrameSwap(b) => b.dac_type(),
}
}
pub fn caps(&self) -> &DacCapabilities {
match self {
BackendKind::Fifo(b) => b.caps(),
BackendKind::FrameSwap(b) => b.caps(),
}
}
pub fn connect(&mut self) -> Result<()> {
match self {
BackendKind::Fifo(b) => b.connect(),
BackendKind::FrameSwap(b) => b.connect(),
}
}
pub fn disconnect(&mut self) -> Result<()> {
match self {
BackendKind::Fifo(b) => b.disconnect(),
BackendKind::FrameSwap(b) => b.disconnect(),
}
}
pub fn is_connected(&self) -> bool {
match self {
BackendKind::Fifo(b) => b.is_connected(),
BackendKind::FrameSwap(b) => b.is_connected(),
}
}
pub fn stop(&mut self) -> Result<()> {
match self {
BackendKind::Fifo(b) => b.stop(),
BackendKind::FrameSwap(b) => b.stop(),
}
}
pub fn set_shutter(&mut self, open: bool) -> Result<()> {
match self {
BackendKind::Fifo(b) => b.set_shutter(open),
BackendKind::FrameSwap(b) => b.set_shutter(open),
}
}
pub fn try_write(&mut self, pps: u32, points: &[LaserPoint]) -> Result<WriteOutcome> {
match self {
BackendKind::Fifo(b) => b.try_write_points(pps, points),
BackendKind::FrameSwap(b) => b.write_frame(pps, points),
}
}
pub fn estimator(&self) -> Option<&dyn BufferEstimator> {
match self {
BackendKind::Fifo(b) => Some(b.estimator()),
BackendKind::FrameSwap(_) => None,
}
}
pub fn is_frame_swap(&self) -> bool {
self.caps().output_model == crate::device::OutputModel::UsbFrameSwap
}
pub fn is_ready_for_frame(&mut self) -> bool {
match self {
BackendKind::Fifo(_) => true,
BackendKind::FrameSwap(b) => b.is_ready_for_frame(),
}
}
pub fn frame_capacity(&self) -> Option<usize> {
match self {
BackendKind::Fifo(_) => None,
BackendKind::FrameSwap(b) => Some(b.frame_capacity()),
}
}
}
#[cfg(feature = "helios")]
pub use crate::protocols::helios::HeliosBackend;
#[cfg(feature = "ether-dream")]
pub use crate::protocols::ether_dream::EtherDreamBackend;
#[cfg(feature = "idn")]
pub use crate::protocols::idn::IdnBackend;
#[cfg(feature = "lasercube-wifi")]
pub use crate::protocols::lasercube_wifi::LasercubeWifiBackend;
#[cfg(feature = "lasercube-usb")]
pub use crate::protocols::lasercube_usb::LasercubeUsbBackend;
#[cfg(feature = "oscilloscope")]
pub use crate::protocols::oscilloscope::OscilloscopeBackend;
#[cfg(feature = "avb")]
pub use crate::protocols::avb::AvbBackend;
#[cfg(test)]
mod tests {
use super::*;
use crate::device::{DacCapabilities, DacType, OutputModel};
struct StubFifo {
caps: DacCapabilities,
estimator: crate::buffer_estimate::SoftwareDecayEstimator,
}
impl DacBackend for StubFifo {
fn dac_type(&self) -> DacType {
DacType::Custom("stub".into())
}
fn caps(&self) -> &DacCapabilities {
&self.caps
}
fn connect(&mut self) -> Result<()> {
Ok(())
}
fn disconnect(&mut self) -> Result<()> {
Ok(())
}
fn is_connected(&self) -> bool {
true
}
fn stop(&mut self) -> Result<()> {
Ok(())
}
fn set_shutter(&mut self, _open: bool) -> Result<()> {
Ok(())
}
}
impl FifoBackend for StubFifo {
fn try_write_points(&mut self, _pps: u32, _points: &[LaserPoint]) -> Result<WriteOutcome> {
Ok(WriteOutcome::Written)
}
fn estimator(&self) -> &dyn BufferEstimator {
&self.estimator
}
}
struct StubFrameSwap;
impl DacBackend for StubFrameSwap {
fn dac_type(&self) -> DacType {
DacType::Custom("stub-fs".into())
}
fn caps(&self) -> &DacCapabilities {
static CAPS: std::sync::OnceLock<DacCapabilities> = std::sync::OnceLock::new();
CAPS.get_or_init(|| DacCapabilities {
output_model: OutputModel::UsbFrameSwap,
..DacCapabilities::default()
})
}
fn connect(&mut self) -> Result<()> {
Ok(())
}
fn disconnect(&mut self) -> Result<()> {
Ok(())
}
fn is_connected(&self) -> bool {
true
}
fn stop(&mut self) -> Result<()> {
Ok(())
}
fn set_shutter(&mut self, _open: bool) -> Result<()> {
Ok(())
}
}
impl FrameSwapBackend for StubFrameSwap {
fn frame_capacity(&self) -> usize {
4096
}
fn is_ready_for_frame(&mut self) -> bool {
true
}
fn write_frame(&mut self, _pps: u32, _points: &[LaserPoint]) -> Result<WriteOutcome> {
Ok(WriteOutcome::Written)
}
}
#[test]
fn is_frame_swap_matches_output_model_usb_frame_swap() {
for model in [
OutputModel::NetworkFifo,
OutputModel::UdpTimed,
OutputModel::UsbFrameSwap,
] {
let caps = DacCapabilities {
output_model: model.clone(),
..DacCapabilities::default()
};
let kind = BackendKind::Fifo(Box::new(StubFifo {
caps,
estimator: crate::buffer_estimate::SoftwareDecayEstimator::new(),
}));
assert_eq!(
kind.is_frame_swap(),
model == OutputModel::UsbFrameSwap,
"Fifo wrapper with model {:?}",
model
);
}
let fs = BackendKind::FrameSwap(Box::new(StubFrameSwap));
assert!(fs.is_frame_swap(), "FrameSwap wrapper should report true");
assert_eq!(fs.caps().output_model, OutputModel::UsbFrameSwap);
}
}