diff --git a/red/src/printer/mod.rs b/red/src/printer/mod.rs index b10fe5e..2173ffb 100644 --- a/red/src/printer/mod.rs +++ b/red/src/printer/mod.rs @@ -1,14 +1,14 @@ pub mod gcode; +use futures::AsyncWriteExt; use lazy_static::lazy_static; -use bytes::BytesMut; use core::panic; use regex::Regex; use serialport::SerialPort; use serialport::TTYPort; use std::io::Read; use std::io::Write; -use std::ops::{Deref, DerefMut, Index}; +use std::ops::{Deref, DerefMut}; use std::os::fd::{FromRawFd, RawFd}; use std::sync::mpsc; use std::sync::mpsc::Receiver; @@ -18,7 +18,6 @@ use std::sync::Arc; use std::sync::Mutex; use std::time::{Duration, Instant}; use std::{io, str}; -use tokio; pub use gcode::{GcodeCommand, GcodeReply}; @@ -65,6 +64,7 @@ pub struct PrinterPosition { pub z: f64, } +#[derive(Debug, Clone, Copy)] pub enum MovementMode { AbsoluteMovements, RelativeMovements, @@ -87,12 +87,7 @@ pub struct Printer { impl Printer { /// Send gcode to the printer and parse its reply - fn send_gcode<T: GcodeCommand>( - &mut self, - command: T, - ignore_pos: bool, - timeout: Duration, - ) -> Result<T::Reply, PrinterError> { + fn send_gcode<T: GcodeCommand>(&mut self, command: T) -> Result<T::Reply, PrinterError> { let command_text = command.command() + "\n"; self.to_io_thread .send(command_text.clone()) @@ -146,7 +141,6 @@ impl Printer { last_buffer_capacity: 0, // this is updated on the next call to `send_gcode()` })); - //TODO: Spawn IO-Thread let state_for_io = state.clone(); std::thread::spawn(move || { Self::io_thread_work(to_user_thread, from_user_thread, port, state_for_io) @@ -281,15 +275,14 @@ impl Printer { e: None, // Machine has no e velocity, }; - if let MovementMode::AbsoluteMovements = - self.state.lock().unwrap().deref_mut().movement_mode - { + let movement_mode = self.state.lock().unwrap().deref_mut().movement_mode; + if let MovementMode::AbsoluteMovements = movement_mode { self.use_relative_movements()?; - let res = self.send_gcode(command, true, DEFAULT_COMMAND_TIMEOUT); + let res = self.send_gcode(command); self.use_absolute_movements()?; res } else { - self.send_gcode(command, true, DEFAULT_COMMAND_TIMEOUT) + self.send_gcode(command) }?; self.state.lock().unwrap().deref_mut().position.x += x; @@ -321,13 +314,14 @@ impl Printer { e: None, // Machine has no e velocity, }; - if let MovementMode::RelativeMovements = self.state.lock().unwrap().deref().movement_mode { + let movement_mode = self.state.lock().unwrap().deref().movement_mode; + if let MovementMode::RelativeMovements = movement_mode { self.use_absolute_movements()?; - let res = self.send_gcode(command, true, DEFAULT_COMMAND_TIMEOUT); + let res = self.send_gcode(command); self.use_relative_movements()?; res } else { - self.send_gcode(command, true, DEFAULT_COMMAND_TIMEOUT) + self.send_gcode(command) }?; self.state.lock().unwrap().deref_mut().position.x = x; @@ -350,7 +344,12 @@ impl Printer { ) { loop { match from_user_thread.try_recv() { - Ok(user_command) => handle_user_command(&mut port, user_command, state.clone()), + Ok(user_command) => handle_user_command( + &mut port, + user_command, + state.clone(), + to_user_thread.clone(), + ), Err(TryRecvError::Disconnected) => break, Err(TryRecvError::Empty) => handle_printer_autoreport(&mut port, state.clone()), } @@ -368,21 +367,21 @@ 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` -/// * `timeout` - Time to wait before raising a timeout error +/// * `timeout` - Time to wait for new bytes from the `port` before raising a timeout error /// /// Returns -/// `(potential_line, rest)`, where -/// - `potential_line` is `None` if no complete line was read or `Some(line)` if a whole -/// line was received from the port -/// - `rest` contains the bytes that were read since the last occurence of `\n` +/// +/// * `Ok(Some(line))` if a complete line was read from the port +/// * `Ok(None)` if there was no `\n` received +/// * `Err(e)` in case reading failed fn read_line( port: &mut TTYPort, - mut already_read: Vec<u8>, + already_read: &mut Vec<u8>, timeout: Duration, -) -> io::Result<(Option<Vec<u8>>, Vec<u8>)> { +) -> io::Result<Option<Vec<u8>>> { let deadline = Instant::now() + timeout; while Instant::now() < deadline { - match port.read(&mut already_read) { + match port.read(already_read) { Err(e) => { if let io::ErrorKind::TimedOut = e.kind() { continue; @@ -393,10 +392,7 @@ fn read_line( Ok(0) => panic!("TTYPort returned 0 bytes!"), Ok(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()), - already_read[line_break_idx..].into(), - )); + return Ok(Some(already_read[..line_break_idx].into())); } } } @@ -407,32 +403,41 @@ fn read_line( )) } -fn handle_user_command(port: &mut TTYPort, user_command: String, state: Arc<Mutex<State>>) { +fn handle_user_command( + port: &mut TTYPort, + user_command: String, + state: Arc<Mutex<State>>, + to_user_thread: Sender<Vec<u8>>, +) { port.write(user_command.as_bytes()) - .expect("Printer IO-Thread hung up its incoming mpsc"); + .expect("Failed to write to printer serial port"); port.flush().unwrap(); - let ignore_pos = false; - let mut reply = String::with_capacity(RECV_BUFFER_CAPACITY); - let mut already_read = Vec::new(); + + let mut already_read_lines = Vec::new(); + let mut rest = Vec::new(); loop { - let (mut line, mut rest) = read_line(port, Vec::new(), DEFAULT_COMMAND_TIMEOUT).unwrap(); - let timeout = Instant::now() + DEFAULT_COMMAND_TIMEOUT; - while line.is_none() && Instant::now() < timeout { - let buf = Vec::new(); - _ = port.read(&mut buf); - if line.is_none() { + 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)); } } - if let Some(line) = line { - let line = String::from_utf8(line).unwrap(); - if line.starts_with("ok") { - (*state.lock().unwrap()).last_buffer_capacity = Printer::parse_ok(&line)?; - let reply = command_parser(&reply); - return reply.map_err(PrinterError::GcodeReply); - } else { - reply.push_str(&line); - } + let line = line.expect("Line must be set as otherwise we don't leave the loop"); + + 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 = + Printer::parse_ok(&str_line).expect("Couldn't parse line as 'ok'-message"); + to_user_thread + .send(already_read_lines.join(&b'\n')) + .expect("Failed to send read bytes to user thread"); + } else { + already_read_lines.push(line); } } }