pub mod backend;
pub mod dac;
mod discovery;
pub mod error;
pub mod protocol;
pub use backend::LasercubeWifiBackend;
pub use discovery::LasercubeWifiDiscoverer;
use crate::device::{DacCapabilities, OutputModel};
use dac::buffer_estimator::LATENCY_POINT_ADJUSTMENT;
use protocol::{command, DeviceInfo, CMD_PORT, DEFAULT_BUFFER_CAPACITY};
pub fn capabilities_for_buffer(capacity: u16) -> DacCapabilities {
DacCapabilities {
pps_min: 1,
pps_max: 30_000,
max_points_per_chunk: capacity.saturating_sub(LATENCY_POINT_ADJUSTMENT) as usize,
output_model: OutputModel::NetworkFifo,
}
}
pub fn default_capabilities() -> DacCapabilities {
capabilities_for_buffer(DEFAULT_BUFFER_CAPACITY)
}
use socket2::{Domain, Protocol, SockAddr, Socket, Type};
use std::collections::HashSet;
use std::net::{Ipv4Addr, SocketAddr, SocketAddrV4, UdpSocket};
use std::time::Duration;
use std::{io, net};
const DEFAULT_DISCOVERY_TIMEOUT: Duration = Duration::from_secs(1);
pub struct DiscoverDacs {
socket: UdpSocket,
buffer: [u8; 1500],
seen_ips: HashSet<Ipv4Addr>,
}
pub fn discover_dacs() -> io::Result<DiscoverDacs> {
let socket = Socket::new(Domain::IPV4, Type::DGRAM, Some(Protocol::UDP))?;
socket.set_broadcast(true)?;
socket.set_reuse_address(true)?;
let bind_addr = SocketAddrV4::new(Ipv4Addr::UNSPECIFIED, 0);
socket.bind(&SockAddr::from(bind_addr))?;
socket.set_read_timeout(Some(DEFAULT_DISCOVERY_TIMEOUT))?;
let udp_socket: UdpSocket = socket.into();
send_discovery_broadcast(&udp_socket)?;
Ok(DiscoverDacs {
socket: udp_socket,
buffer: [0u8; 1500],
seen_ips: HashSet::new(),
})
}
fn send_discovery_broadcast(socket: &UdpSocket) -> io::Result<()> {
let discovery_cmd = command::get_full_info();
if let Ok(interfaces) = crate::net_utils::get_local_interfaces() {
for iface in &interfaces {
let broadcast_addr = SocketAddrV4::new(iface.broadcast_address(), CMD_PORT);
for _ in 0..2 {
let _ = socket.send_to(&discovery_cmd, broadcast_addr);
}
}
}
let standard_broadcast = SocketAddrV4::new(Ipv4Addr::BROADCAST, CMD_PORT);
for _ in 0..2 {
let _ = socket.send_to(&discovery_cmd, standard_broadcast);
}
Ok(())
}
impl DiscoverDacs {
pub fn set_timeout(&self, timeout: Option<Duration>) -> io::Result<()> {
self.socket.set_read_timeout(timeout)
}
pub fn next_device(&mut self) -> io::Result<(DeviceInfo, net::SocketAddr)> {
loop {
let (len, src_addr) = self.socket.recv_from(&mut self.buffer)?;
if let SocketAddr::V4(addr_v4) = src_addr {
if self.seen_ips.contains(addr_v4.ip()) {
continue;
}
self.seen_ips.insert(*addr_v4.ip());
}
match DeviceInfo::from_discovery_response(&self.buffer[..len]) {
Ok(info) => return Ok((info, src_addr)),
Err(_) => continue, }
}
}
}
impl Iterator for DiscoverDacs {
type Item = io::Result<(DeviceInfo, net::SocketAddr)>;
fn next(&mut self) -> Option<Self::Item> {
Some(self.next_device())
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_point_signed_conversion() {
use protocol::Point;
let p = Point::from_signed(0, 0, 1000, 2000, 3000);
assert_eq!(p.x, 2048);
assert_eq!(p.y, 2048);
let (x, y) = p.to_signed();
assert_eq!(x, 0);
assert_eq!(y, 0);
let p = Point::from_signed(2047, 2047, 0, 0, 0);
assert_eq!(p.x, 4095);
assert_eq!(p.y, 4095);
let (x, y) = p.to_signed();
assert_eq!(x, 2047);
assert_eq!(y, 2047);
let p = Point::from_signed(-2048, -2048, 0, 0, 0);
assert_eq!(p.x, 0);
assert_eq!(p.y, 0);
let (x, y) = p.to_signed();
assert_eq!(x, -2048);
assert_eq!(y, -2048);
}
#[test]
fn test_blank_point() {
use protocol::Point;
let blank = Point::blank();
assert_eq!(blank.x, Point::CENTER);
assert_eq!(blank.y, Point::CENTER);
assert_eq!(blank.r, 0);
assert_eq!(blank.g, 0);
assert_eq!(blank.b, 0);
}
#[test]
fn default_caps_are_network_fifo_with_safety_margin() {
let caps = default_capabilities();
assert_eq!(caps.output_model, OutputModel::NetworkFifo);
assert_eq!(caps.max_points_per_chunk, 5700);
}
#[test]
fn caps_from_discovery_derive_from_device_buffer() {
let caps = capabilities_for_buffer(4000);
assert_eq!(
caps.max_points_per_chunk,
(4000 - LATENCY_POINT_ADJUSTMENT) as usize
);
assert_eq!(caps.output_model, OutputModel::NetworkFifo);
}
}