Implement handle_user_command

This commit is contained in:
zaubentrucker 2025-02-11 22:17:31 +01:00
parent 62816b749e
commit c616279c12

View file

@ -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);
}
}
}