use std::{ io, sync::{Arc, Mutex}, thread, time::{Duration, Instant}, }; use anyhow::{anyhow, Result}; use serialport::SerialPort; use crate::fixtures::{DMXFixture, MovingHead}; const FPS: u32 = 50; enum MCUResponse { Sync, Ack, #[allow(dead_code)] Info { num_pkts: u32, }, } fn poll_response(ser: &mut dyn SerialPort) -> Result { let mut read_buffer = vec![0u8; 32]; let bytes_read; loop { match ser.read(read_buffer.as_mut_slice()) { Ok(t) => { bytes_read = t; break; } Err(ref e) if e.kind() == io::ErrorKind::TimedOut => continue, Err(e) => Err(e), }? } let response = std::str::from_utf8(&read_buffer[..bytes_read])?; match response.trim() { "Sync." => Ok(MCUResponse::Sync), "Ack." => Ok(MCUResponse::Ack), s if s.starts_with("Info") => Ok(MCUResponse::Info { num_pkts: 69 }), s => Err(anyhow!("Unknown response: \"{}\"", s)), } } pub fn controller_thread( running: Arc>, movingheads: Arc>, ) -> Result<()> { let frame_time = Duration::from_secs_f64(1.0 / FPS as f64); let mut dmx_buffer = [0u8; 512]; let mut ser = serialport::new("/dev/ttyUSB0", 500_000) .timeout(Duration::from_millis(10)) .open()?; // wait for initial sync loop { match poll_response(&mut *ser) { Ok(MCUResponse::Sync) => break, _ => continue, } } 'main: loop { { let running = running.lock().unwrap(); if !*running { break Ok(()); } } let loop_start = Instant::now(); { let movingheads = movingheads.lock().unwrap(); for head in movingheads.iter() { head.render(&mut dmx_buffer); } } let write_result = ser.write(&dmx_buffer); if write_result.is_err() { loop { match poll_response(&mut *ser) { Ok(MCUResponse::Sync) => continue 'main, _ => continue, } } } loop { match poll_response(&mut *ser) { Ok(MCUResponse::Ack) => break, Ok(MCUResponse::Info { .. }) | Err(_) => continue, Ok(MCUResponse::Sync) => continue 'main, } } let loop_time = loop_start.elapsed(); if loop_time < frame_time { thread::sleep(frame_time - loop_time); } else { println!("loop took too long!"); } } }