diff --git a/red/src/printer/mod.rs b/red/src/printer/mod.rs index 2173ffb..30946ea 100644 --- a/red/src/printer/mod.rs +++ b/red/src/printer/mod.rs @@ -1,5 +1,4 @@ pub mod gcode; -use futures::AsyncWriteExt; use lazy_static::lazy_static; use core::panic; @@ -28,7 +27,7 @@ use self::gcode::{G0Command, G28Command, G90Command, GcodeReplyError}; /// Recv buffer string will be initialized with this capacity. /// This should fit a simple "OK Pnn Bn" reply for GCODE commands that /// do not return any data. -const RECV_BUFFER_CAPACITY: usize = 32; +const BUF_SIZE_READ_LINE: usize = 1024; const BAUD_RATE: u32 = 115200; const DEFAULT_COMMAND_TIMEOUT: Duration = Duration::from_secs(2); @@ -70,6 +69,7 @@ pub enum MovementMode { RelativeMovements, } +#[derive(Debug, Clone)] pub struct State { pub position: PrinterPosition, pub movement_mode: MovementMode, @@ -174,8 +174,8 @@ impl Printer { Ok(res) } - pub fn printer_state(&self) -> &State { - &self.state.lock().unwrap().deref().clone() + pub fn printer_state(&self) -> State { + self.state.lock().unwrap().deref().clone() } /// The maximum capacity of the machines GCODE buffer. @@ -209,7 +209,7 @@ impl Printer { /// Update the internal position by asking the printer for it fn update_position(&mut self) -> Result<PrinterPosition, PrinterError> { - let res = self.send_gcode(M114Command, false, DEFAULT_COMMAND_TIMEOUT)?; + let res = self.send_gcode(M114Command)?; self.state.lock().unwrap().deref_mut().position = res; Ok(res) } @@ -222,7 +222,7 @@ impl Printer { /// itself. (See its documentation) pub fn use_absolute_movements(&mut self) -> Result<(), PrinterError> { self.state.lock().unwrap().deref_mut().movement_mode = MovementMode::AbsoluteMovements; - self.send_gcode(G90Command, true, DEFAULT_COMMAND_TIMEOUT) + self.send_gcode(G90Command) } /// Switch the printer to relative movement mode. @@ -233,16 +233,16 @@ impl Printer { /// itself. (See its documentation) pub fn use_relative_movements(&mut self) -> Result<(), PrinterError> { self.state.lock().unwrap().deref_mut().movement_mode = MovementMode::RelativeMovements; - self.send_gcode(G91Command, true, DEFAULT_COMMAND_TIMEOUT) + self.send_gcode(G91Command) } /// Home the printer using the hardware endstops /// /// # Arguments /// * `x, y, z` - Whether the axis should be homed. Axis that are set to `false` will not be - /// homed., + /// homed. pub fn auto_home(&mut self, x: bool, y: bool, z: bool) -> Result<(), PrinterError> { - self.send_gcode(G28Command::new(x, y, z), true, DEFAULT_COMMAND_TIMEOUT)?; + self.send_gcode(G28Command::new(x, y, z))?; self.state.lock().unwrap().deref_mut().position = PrinterPosition { x: 0.0, y: 0.0, @@ -366,7 +366,8 @@ fn handle_printer_autoreport(port: &mut TTYPort, state: Arc<Mutex<State>>) { /// /// Parameters /// * `port` - The port to read from -/// * `already_read` - Read bytes from a previous call. May not contain `\n` +/// * `already_read` - Read bytes from a previous call. May not contain `\n`. +/// On a successful read, the buffer will be cleared and then filled with the new residual bytes /// * `timeout` - Time to wait for new bytes from the `port` before raising a timeout error /// /// Returns @@ -378,10 +379,11 @@ fn read_line( port: &mut TTYPort, already_read: &mut Vec<u8>, timeout: Duration, -) -> io::Result<Option<Vec<u8>>> { +) -> io::Result<Vec<u8>> { let deadline = Instant::now() + timeout; + let mut buf = [0; BUF_SIZE_READ_LINE]; while Instant::now() < deadline { - match port.read(already_read) { + match port.read(&mut buf) { Err(e) => { if let io::ErrorKind::TimedOut = e.kind() { continue; @@ -391,9 +393,14 @@ fn read_line( } Ok(0) => panic!("TTYPort returned 0 bytes!"), Ok(n) => { + already_read.extend_from_slice(&buf[..n]); if let Some(line_break_idx) = already_read[..n].iter().position(|x| *x == b'\n') { - return Ok(Some(already_read[..line_break_idx].into())); + let res = Ok(already_read[..line_break_idx].into()); + *already_read = already_read[line_break_idx..].into(); + return res; } + + std::thread::sleep(Duration::from_millis(5)); } } } @@ -409,29 +416,21 @@ fn handle_user_command( state: Arc<Mutex<State>>, to_user_thread: Sender<Vec<u8>>, ) { - port.write(user_command.as_bytes()) + // TODO: Add timeout? + port.write_all(user_command.as_bytes()) .expect("Failed to write to printer serial port"); port.flush().unwrap(); let mut already_read_lines = Vec::new(); let mut rest = Vec::new(); loop { - let mut line; - loop { - line = read_line(port, &mut rest, DEFAULT_COMMAND_TIMEOUT) - .expect("Failed to read from printer"); - if line.is_some() { - break; - } else { - std::thread::sleep(Duration::from_millis(5)); - } - } - let line = line.expect("Line must be set as otherwise we don't leave the loop"); + let line = read_line(port, &mut rest, DEFAULT_COMMAND_TIMEOUT) + .expect("Failed to read from printer"); let str_line = String::from_utf8(line.clone()).expect("Read line was no valid utf8"); if str_line.starts_with("ok") { - (*state.lock().unwrap()).last_buffer_capacity = + state.lock().unwrap().last_buffer_capacity = Printer::parse_ok(&str_line).expect("Couldn't parse line as 'ok'-message"); to_user_thread .send(already_read_lines.join(&b'\n'))