From ae75092d340047d4e9caacd219f90f293100ca47 Mon Sep 17 00:00:00 2001 From: alban Date: Wed, 19 Jul 2023 01:28:45 +0200 Subject: [PATCH] feat: the etherdream device should work --- examples/etherdream.rs | 150 +++++++++++++++++++++++++++++++++++ src/device.rs | 3 +- src/device/etherdream.rs | 163 ++++++++++++++++++++++++++++++--------- src/main.rs | 39 ++++++---- src/point.rs | 18 ++++- 5 files changed, 316 insertions(+), 57 deletions(-) create mode 100644 examples/etherdream.rs diff --git a/examples/etherdream.rs b/examples/etherdream.rs new file mode 100644 index 0000000..e174a88 --- /dev/null +++ b/examples/etherdream.rs @@ -0,0 +1,150 @@ +extern crate ether_dream; + +use ether_dream::dac; + +fn main() { + println!("Listening for an Ether Dream DAC..."); + + let (dac_broadcast, source_addr) = ether_dream::recv_dac_broadcasts() + .expect("failed to bind to UDP socket") + .filter_map(Result::ok) + .next() + .unwrap(); + let mac_address = dac::MacAddress(dac_broadcast.mac_address); + + println!( + "Discovered DAC \"{}\" at \"{}\"! Connecting...", + mac_address, source_addr + ); + + // Establish the TCP connection. + let mut stream = dac::stream::connect(&dac_broadcast, source_addr.ip().clone()).unwrap(); + + // If we want to create an animation (in our case a moving sine wave) we need a frame rate. + let frames_per_second = 60.0; + // Lets use the DAC at an eighth the maximum scan rate. + let points_per_second = stream.dac().max_point_rate / 32; + // Determine the number of points per frame given our target frame and point rates. + let points_per_frame = (points_per_second as f32 / frames_per_second) as u16; + + println!( + "Preparing for playback:\n\tframe_hz: {}\n\tpoint_hz: {}\n\tpoints_per_frame: {}\n", + frames_per_second, points_per_second, points_per_frame + ); + + // Prepare the DAC's playback engine and await the repsonse. + stream + .queue_commands() + .prepare_stream() + .submit() + .err() + .map(|err| { + eprintln!( + "err occurred when submitting PREPARE_STREAM \ + command and listening for response: {}", + err + ); + }); + + println!("Beginning playback!"); + + // The sine wave used to generate points. + let mut sine_wave = SineWave { + point: 0, + points_per_frame, + frames_per_second, + }; + + // Queue the initial frame and tell the DAC to begin producing output. + let n_points = points_to_generate(stream.dac()); + stream + .queue_commands() + .data(sine_wave.by_ref().take(n_points)) + .begin(0, points_per_second) + .submit() + .err() + .map(|err| { + eprintln!( + "err occurred when submitting initial DATA and BEGIN \ + commands and listening for response: {}", + err + ); + }); + + // Loop and continue to send points forever. + loop { + // Determine how many points the DAC can currently receive. + let n_points = points_to_generate(stream.dac()); + if let Err(err) = stream + .queue_commands() + .data(sine_wave.by_ref().take(n_points)) + .submit() + { + eprintln!( + "err occurred when submitting DATA command and listening \ + for response: {}", + err + ); + break; + } + } + + // Tell the DAC to stop producing output and return to idle. Wait for the response. + // + // Note that the DAC is commanded to stop on `Drop` if this is not called and any errors + // produced are ignored. + stream + .queue_commands() + .stop() + .submit() + .expect("err occurred when submitting STOP command and listening for response"); +} + +// Determine the number of points needed to fill the DAC. +fn points_to_generate(dac: ðer_dream::dac::Dac) -> usize { + dac.buffer_capacity as usize - 1 - dac.status.buffer_fullness as usize +} + +// An iterator that endlessly generates a sine wave of DAC points. +// +// The sine wave oscillates at a rate of once per second. +struct SineWave { + point: u32, + points_per_frame: u16, + frames_per_second: f32, +} + +impl Iterator for SineWave { + type Item = ether_dream::protocol::DacPoint; + fn next(&mut self) -> Option { + let coloured_points_per_frame = self.points_per_frame - 1; + let i = (self.point % self.points_per_frame as u32) as u16; + let hz = 1.0; + let fract = i as f32 / coloured_points_per_frame as f32; + let phase = (self.point as f32 / coloured_points_per_frame as f32) / self.frames_per_second; + let amp = (hz * (fract + phase) * 2.0 * std::f32::consts::PI).sin(); + let (r, g, b) = match i { + i if i == coloured_points_per_frame || i < 13 => (0, 0, 0), + _ => (std::u16::MAX, std::u16::MAX, std::u16::MAX), + }; + let x_min = std::i16::MIN; + let x_max = std::i16::MAX; + let x = (x_min as f32 + fract * (x_max as f32 - x_min as f32)) as i16; + let y = (amp * x_max as f32) as i16; + let control = 0; + let (u1, u2) = (0, 0); + let p = ether_dream::protocol::DacPoint { + control, + x, + y, + i, + r, + g, + b, + u1, + u2, + }; + self.point += 1; + Some(p) + } +} \ No newline at end of file diff --git a/src/device.rs b/src/device.rs index fe28f74..6cbf0d5 100644 --- a/src/device.rs +++ b/src/device.rs @@ -28,8 +28,7 @@ self.point_count pub enum PlaybackState { IDLE = 0, PREPARE = 1, - PLAYING = 2, - UNKNOWN = 99, + PLAYING = 2 } impl fmt::Display for PlaybackState { diff --git a/src/device/etherdream.rs b/src/device/etherdream.rs index d1aaac6..d5bc4e8 100644 --- a/src/device/etherdream.rs +++ b/src/device/etherdream.rs @@ -1,14 +1,14 @@ use std::time; use std::net::SocketAddr; use ether_dream::dac::stream::connect; -use ether_dream::dac::Stream; +use ether_dream::dac::{Playback, Stream}; use crate::conf::EtherDreamConf; use crate::device::{Device, Status, PlaybackState}; use crate::errors::{LJError, LJResult}; use crate::point::{Color, Point}; -use ether_dream::protocol::{DacBroadcast, DacStatus}; -use log::{info, warn}; +use ether_dream::protocol::{DacBroadcast, DacPoint}; +use log::{debug, info, warn}; #[warn(dead_code)] pub struct EtherdreamDevice { @@ -21,10 +21,11 @@ pub struct EtherdreamDevice { last_traced_at: String, } + impl EtherdreamDevice { pub fn new(conf: &EtherDreamConf) -> LJResult { let (dac, _source_address, stream) = EtherdreamDevice::get_dac(conf)?; - // let (dac, source_address) = EtherdreamDevice::get_dac(conf)?; + Ok(Self { conf: (*conf).clone(), dac, @@ -41,17 +42,18 @@ impl EtherdreamDevice { dac_broadcast.set_timeout(Some(time::Duration::new(10, 0)))?; info!("Attempting to get DAC broadcast..."); let broadcast = dac_broadcast + .take(3) .filter_map(|result| { match result { Err(err) => { warn!( "Failed to find a valid DAC via broadcast. Error: {:?}", err); info!( "Retrying..."); None - }, + } Ok((dac, source_addr)) => { + info!("Valid broadcast, source_addr: {}", source_addr); if source_addr.is_ipv6() { return None; } if &source_addr.ip().to_string() != ip { return None; } - info!("Valid broadcast"); Some(Ok((dac, source_addr))) } } @@ -64,6 +66,7 @@ impl EtherdreamDevice { } Ok((dac, source_addr)) => { let stream = EtherdreamDevice::get_tcp_stream(&dac, &source_addr)?; + info!("Finished configuring DAC and TCP stream."); Ok((dac, source_addr, stream)) } } @@ -72,22 +75,61 @@ impl EtherdreamDevice { pub fn get_tcp_stream(dac: &DacBroadcast, source_address: &SocketAddr) -> LJResult { // Establish the TCP connection. let mut stream = connect(dac, source_address.ip())?; + EtherdreamDevice::prepare_tcp_stream(&mut stream).unwrap(); + Ok(stream) + } + + fn prepare_tcp_stream(stream: &mut Stream) -> LJResult<()> { // Prepare stream - stream + match stream .queue_commands() .prepare_stream() - .submit() - .err() - .map(|err| { - eprintln!( - "err occurred when submitting PREPARE_STREAM \ - command and listening for response: {}", + .submit() { + Err(err) => { + warn!( + "err occurred when submitting PREPARE_STREAM command and listening for response: {}", err ); - }); + } + Ok(_) => { + info!("Prepared Stream.") + } + } - Ok(stream) + let control = 0; + let (u1, u2) = (0, 0); + let i = 255; + let point = DacPoint { + control, + x: 0, + y: 0, + i, + r: 0, + g: 0, + b: 0, + u1, + u2, + }; + let begin_list = vec![point]; + let points_per_second = stream.dac().max_point_rate / 32; + match stream + .queue_commands() + .data(begin_list.into_iter().take(1 as usize)) + .begin(0, points_per_second) + .submit() { + Err(err) => { + warn!( + "err occurred when submitting first data: {}", + err + ); + } + Ok(_) => { + info!("Sent first data to Etherdream.") + } + } + + Ok(()) } @@ -98,8 +140,10 @@ impl EtherdreamDevice { } // Determine the number of points needed to fill the DAC. - fn points_to_generate(&self) -> usize { - self.dac.buffer_capacity as usize - 1 - self.dac.dac_status.buffer_fullness as usize + fn points_capacity(&self) -> u16 { + // Fixme thread 'main' panicked at 'attempt to subtract with overflow', src/device/etherdream.rs:144:24 + let n_points = self.dac.buffer_capacity as u16 - self.stream.dac().dac.status.buffer_fullness as u16 - 1; + n_points } } @@ -108,46 +152,93 @@ impl Device for EtherdreamDevice { let _ = self.check_tcp_stream(); // "a": ACK "F": Full "I": invalid. 64 or 35 for no connection. - let playback_state = match self.dac.dac_status.playback_state { - DacStatus::PLAYBACK_IDLE => PlaybackState::IDLE, - DacStatus::PLAYBACK_PREPARED => PlaybackState::PREPARE, - DacStatus::PLAYBACK_PLAYING => PlaybackState::PLAYING, - _ => PlaybackState::UNKNOWN + let playback_state = match self.stream.dac().dac.status.playback { + Playback::Idle => PlaybackState::IDLE, + Playback::Prepared => PlaybackState::PREPARE, + Playback::Playing => PlaybackState::PLAYING, }; - Status { + let status = Status { last_traced_at: self.last_traced_at.clone(), properties: vec!["foo".to_string()], playback_state, - capacity: self.dac.dac_status.buffer_fullness, + capacity: self.points_capacity(), lack: String::from(&self.lack), - } + }; + // info!("Dac Status: {:?} ", status ); + // info!("Etherdream Dac {:?} ", self.dac ); + // info!("Stream dac{:?}", self.stream.dac()); + + status } fn draw(&mut self, line: Vec, _speed: u32, ) -> LJResult<()> { - let n_points = self.points_to_generate(); - self.stream + let n_points = self.points_capacity(); + // let n_points = &line.len(); + debug!("Etherdream::device draw Generating {:?} points", n_points); + return match self.stream .queue_commands() - .data(line.into_iter().map(|point| point.into()).take(n_points)) - .submit()?; - Ok(()) + .data( + line.into_iter() + .map(|point| point.into()) + // .take(line.len() as usize) + .take(n_points as usize ) + ) + .submit() { + Err(err) => { + // We should account for + // 'Broken pipe (os error 32)' + // Connection reset by peer (os error 104) + warn!("Draw error: '{}'",err); + Ok(()) + } + Ok(_) => { + debug!("Draw is ok"); + Ok(()) + } + }; } fn stop(&mut self) -> LJResult<()> { - self.stream + info!("Stopping Etherdream device..."); + match self.stream .queue_commands() .stop() .submit() - .expect("err occurred when submitting STOP command and listening for response"); - Ok(()) + { + Err(err) => { + warn!("Failed to stop EtherDream device with error {:?}", err); + Err(Box::new(err)) + } + Ok(_) => { + info!("Sucessfully closed EtherDream device."); + Ok(()) + } + } } fn grid(&mut self) -> Vec { - vec!( - Point { x: 0.0, y: 0.0, color: Color { r: 255, g: 255, b: 255 } } - ) + let dim_mid = 16000 as f32; + let dim_max = 32000 as f32; + let col_min = Color { r: 0, g: 0, b: 0 }; + let col_max = Color { r: 255, g: 255, b: 255 }; + + vec![ + Point { x: -dim_max, y: dim_max, color: col_min }, + Point { x: -dim_max, y: dim_max, color: col_max }, + Point { x: dim_max, y: dim_max, color: col_max }, + Point { x: dim_max, y: -dim_max, color: col_max }, + Point { x: -dim_max, y: -dim_max, color: col_max }, + Point { x: -dim_max, y: -dim_mid, color: col_min }, + Point { x: -dim_mid, y: dim_mid, color: col_min }, + Point { x: -dim_mid, y: dim_mid, color: col_max }, + Point { x: dim_mid, y: dim_mid, color: col_max }, + Point { x: dim_mid, y: -dim_mid, color: col_max }, + Point { x: -dim_mid, y: -dim_mid, color: col_max }, + Point { x: -dim_mid, y: -dim_mid, color: col_min }, + ] } } diff --git a/src/main.rs b/src/main.rs index 501d8cd..2624273 100644 --- a/src/main.rs +++ b/src/main.rs @@ -35,6 +35,13 @@ pub fn main() { fn run_all() -> LJResult<()> { + // Setup handler for interrupt Signals + let running = Arc::new(AtomicBool::new(true)); + let r = running.clone(); + ctrlc::set_handler(move || { + r.store(false, Ordering::SeqCst); + })?; + // Setup configuration file and set up logs let filename = std::env::args().nth(1).unwrap_or_else(|| { DEFAULT_CONF_FILE.to_string() @@ -53,12 +60,6 @@ fn run_all() -> LJResult<()> { let mut world_state = rs.init_world_state().unwrap(); info!("WorldState: {:?}", world_state); - // Setup handler for interrupt Signals - let running = Arc::new(AtomicBool::new(true)); - let r = running.clone(); - ctrlc::set_handler(move || { - r.store(false, Ordering::SeqCst); - })?; // Setup Laser Device based on conf let mut tracer = device_factory(&config)?; @@ -108,7 +109,7 @@ fn run_all() -> LJResult<()> { } Order::ClientKey => { world_state.client_key = rs.get_client_key()?; - }, + } // Order::ColorBalance => {}, _ => { // 4 : Resampler Change (longs and shorts lsteps) @@ -127,14 +128,18 @@ fn run_all() -> LJResult<()> { fn init_logging(config: &LJResult) { if let Ok(ref config) = config { - if config.debug { - let mut builder = Builder::from_default_env(); - builder - .filter(None, LevelFilter::Info) - .init(); - info!("Debug mode enabled from configuration file"); - return; - } + + let level = if config.debug { + LevelFilter::Debug + } else { + LevelFilter::Info + }; + let mut builder = Builder::from_default_env(); + builder + .filter(None, level) + .init(); + info!("Debug mode enabled from configuration file"); + return; } info!("Logging level inherited from env"); env_logger::init(); @@ -165,8 +170,8 @@ fn get_next_frame( line = transformer.apply(&line, world_state); } - info!("Draw Black -> {}", world_state.draw_black); - info!("Draw Grid -> {}", world_state.draw_grid); + // info!("Draw Black -> {}", world_state.draw_black); + // info!("Draw Grid -> {}", world_state.draw_grid); // LIMITER and BLACK line = line.into_iter() diff --git a/src/point.rs b/src/point.rs index b64a01d..d060fd5 100644 --- a/src/point.rs +++ b/src/point.rs @@ -1,5 +1,15 @@ use ether_dream::protocol::DacPoint; +fn clamp(val: f32, min: f32, max: f32) -> f32 { + if val < min { + return min; + } + if val > max { + return max; + } + val +} + #[derive(Debug, Clone, Copy, Default, PartialEq)] pub struct Point { pub x: f32, @@ -45,14 +55,18 @@ impl From for helios_dac::Point { } impl From for DacPoint { + fn from(pt: Point) -> DacPoint { + print!("."); let control = 0; let (u1, u2) = (0, 0); let i = 255; + let x = clamp(pt.x, -32000 as f32, 32000 as f32); + let y = clamp(pt.y, -32000 as f32, 32000 as f32); DacPoint { control, - x: pt.x as i16, - y: pt.y as i16, + x: x as i16, + y: y as i16, i, r: pt.color.r.into(), g: pt.color.g.into(),