use std::io;
use std::sync::Arc;
use std::sync::atomic::{self, AtomicBool};
use std::sync::mpsc;
use std::time::Duration;
pub trait DetectedDacCallback: FnMut(io::Result<DetectedDac>) {}
impl<F> DetectedDacCallback for F where F: FnMut(io::Result<DetectedDac>) {}
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
pub enum Id {
EtherDream { mac_address: [u8; 6] },
}
#[derive(Clone, Debug)]
pub enum DetectedDac {
EtherDream {
broadcast: ether_dream::protocol::DacBroadcast,
source_addr: std::net::SocketAddr,
},
}
pub struct DetectDacs {
pub(crate) dac_broadcasts: ether_dream::RecvDacBroadcasts,
}
enum DetectorThreadMsg {
Close,
Tick,
}
pub struct DetectDacsAsync {
msg_tx: mpsc::Sender<DetectorThreadMsg>,
thread: Option<std::thread::JoinHandle<()>>,
}
impl DetectedDac {
pub fn max_point_hz(&self) -> u32 {
match self {
DetectedDac::EtherDream { broadcast, .. } => broadcast.max_point_rate as _,
}
}
pub fn buffer_capacity(&self) -> u32 {
match self {
DetectedDac::EtherDream { broadcast, .. } => broadcast.buffer_capacity as _,
}
}
pub fn id(&self) -> Id {
match self {
DetectedDac::EtherDream { broadcast, .. } => Id::EtherDream {
mac_address: broadcast.mac_address,
},
}
}
}
impl DetectDacs {
pub fn set_timeout(&self, duration: Option<std::time::Duration>) -> io::Result<()> {
self.dac_broadcasts.set_timeout(duration)
}
pub fn set_nonblocking(&self, nonblocking: bool) -> io::Result<()> {
self.dac_broadcasts.set_nonblocking(nonblocking)
}
}
impl DetectDacsAsync {
pub fn close(mut self) {
self.close_inner()
}
fn close_inner(&mut self) {
if let Some(thread) = self.thread.take() {
if self.msg_tx.send(DetectorThreadMsg::Close).is_ok() {
thread.join().ok();
}
}
}
}
impl Iterator for DetectDacs {
type Item = io::Result<DetectedDac>;
fn next(&mut self) -> Option<Self::Item> {
let res = self.dac_broadcasts.next()?;
match res {
Err(err) => Some(Err(err)),
Ok((broadcast, source_addr)) => Some(Ok(DetectedDac::EtherDream {
broadcast,
source_addr,
})),
}
}
}
impl Drop for DetectDacsAsync {
fn drop(&mut self) {
self.close_inner();
}
}
pub(crate) fn detect_dacs() -> io::Result<DetectDacs> {
let dac_broadcasts = ether_dream::recv_dac_broadcasts()?;
Ok(DetectDacs { dac_broadcasts })
}
pub(crate) fn detect_dacs_async<F>(
timeout: Option<Duration>,
callback: F,
) -> io::Result<DetectDacsAsync>
where
F: 'static + DetectedDacCallback + Send,
{
detect_dacs_async_inner(timeout, Box::new(callback) as Box<_>)
}
fn detect_dacs_async_inner(
timeout: Option<Duration>,
mut callback: Box<dyn 'static + DetectedDacCallback + Send>,
) -> io::Result<DetectDacsAsync> {
let mut detect_dacs = detect_dacs()?;
detect_dacs.set_nonblocking(true)?;
let (msg_tx, msg_rx) = mpsc::channel();
let msg_tx2 = msg_tx.clone();
let thread = std::thread::Builder::new()
.name("nannou_laser-dac-detection".to_string())
.spawn(move || {
let is_closed = Arc::new(AtomicBool::new(false));
let is_closed2 = is_closed.clone();
std::thread::spawn(move || {
let tick_interval = timeout.unwrap_or(std::time::Duration::from_secs(1));
while !is_closed2.load(atomic::Ordering::Relaxed) {
std::thread::sleep(tick_interval);
if msg_tx2.send(DetectorThreadMsg::Tick).is_err() {
break;
}
}
});
'msgs: for msg in msg_rx {
if let DetectorThreadMsg::Close = msg {
is_closed.store(true, atomic::Ordering::Relaxed);
break;
}
for res in detect_dacs.by_ref() {
if let Err(ref e) = res {
match e.kind() {
io::ErrorKind::TimedOut | io::ErrorKind::WouldBlock => continue 'msgs,
_ => (),
}
}
callback(res);
}
}
})
.expect("failed to spawn DAC detection thread");
Ok(DetectDacsAsync {
msg_tx,
thread: Some(thread),
})
}