Handle temperature auto-reports

This commit is contained in:
zaubentrucker 2025-02-22 21:48:02 +01:00
parent df88b5a40c
commit 13d716924d
4 changed files with 104 additions and 44 deletions
red/src

View file

@ -39,6 +39,7 @@ fn jog() -> Result<()> {
.with_context(|| anyhow!("Initializing printer connection"))?;
printer.set_position_auto_report(AutoReportSetting::EverySeconds(1))?;
printer.set_temperature_auto_report(AutoReportSetting::EverySeconds(1))?;
jogger::jog(&mut gamepad, printer).with_context(|| anyhow!("Running jog mode"))
}

View file

@ -0,0 +1,19 @@
use super::*;
/// Set up auto reporting of axis positions
#[derive(Debug, Default)]
pub struct M155Command {
/// Seconds between the auto reports. Disable auto report by setting this to 0
pub interval: u64,
}
impl GcodeCommand for M155Command {
type Reply = ();
fn command(&self) -> String {
format!("M155 S{}", self.interval)
}
fn parse_reply(&self, reply: &str) -> Result<Self::Reply> {
super::parse_empty_reply(self.command(), reply)
}
}

View file

@ -15,6 +15,7 @@ mod g90;
mod g91;
mod m114;
mod m154;
mod m155;
mod m997;
use crate::printer::PrinterPosition;
@ -25,6 +26,7 @@ pub use g91::G91Command;
use lazy_static::lazy_static;
pub use m114::M114Command;
pub use m154::M154Command;
pub use m155::M155Command;
pub use m997::M997Command;
use regex::Regex;
use std::fmt::Debug;
@ -88,9 +90,15 @@ pub fn parse_autoreport_line(line: &str) -> AutoReport {
/// Can the line be interpreted as an auto-report by the printer
fn is_auto_report(line: &str) -> bool {
matches!(parse_autoreport_line(line), AutoReport::NotRecognized)
matches!(parse_autoreport_line(line), AutoReport::NotRecognized) && is_temperature_report(line)
}
pub fn is_temperature_report(line: &str) -> bool {
lazy_static! {
static ref RE: Regex = Regex::new(r" T:(-?\d+(?:\.\d+)) /").unwrap();
}
RE.is_match(line)
}
fn parse_position_line(line: &str) -> Result<(Option<f64>, Option<f64>, Option<f64>)> {
lazy_static! {
static ref RE_SET: Vec<Regex> = vec![
@ -114,7 +122,7 @@ fn parse_position_line(line: &str) -> Result<(Option<f64>, Option<f64>, Option<f
})
})
.collect();
if fields.iter().all(|r| r.is_err()) {
return Err(fields[0].clone().unwrap_err());
}

View file

@ -1,7 +1,12 @@
pub mod gcode;
use lazy_static::lazy_static;
use crate::printer::gcode::{AutoReport, G91Command, M114Command};
use self::gcode::{
G0Command, G28Command, G90Command, GcodeReplyError, M154Command, parse_autoreport_line,
};
use crate::printer::gcode::{
AutoReport, G91Command, M114Command, M155Command, is_temperature_report,
};
pub use gcode::GcodeCommand;
use regex::Regex;
use serialport::SerialPort;
@ -15,12 +20,11 @@ use std::sync::Arc;
use std::sync::Mutex;
use std::sync::mpsc;
use std::sync::mpsc::Receiver;
use std::sync::mpsc::RecvTimeoutError;
use std::sync::mpsc::Sender;
use std::sync::mpsc::TryRecvError;
use std::time::{Duration, Instant};
use std::{io, str};
use std::sync::mpsc::RecvTimeoutError;
use self::gcode::{G0Command, G28Command, G90Command, GcodeReplyError, M154Command, parse_autoreport_line};
/// Recv buffer string will be initialized with this capacity.
/// This should fit a simple "OK Pnn Bn" reply for GCODE commands that
@ -111,9 +115,7 @@ impl Printer {
Err(RecvTimeoutError::Timeout) => {
Err(PrinterError::NoResponseFromPrinter(command_text))
}
Err(RecvTimeoutError::Disconnected) => {
Err(PrinterError::OutputChannelDropped)
}
Err(RecvTimeoutError::Disconnected) => Err(PrinterError::OutputChannelDropped),
}
}
@ -351,7 +353,22 @@ impl Printer {
/// Set an interval at which to report the printer position or disable automatic position
/// updates
pub fn set_position_auto_report(&mut self, report: AutoReportSetting) -> Result<(), PrinterError> {
pub fn set_temperature_auto_report(
&mut self,
report: AutoReportSetting,
) -> Result<(), PrinterError> {
match report {
AutoReportSetting::Disabled => self.send_gcode(M155Command { interval: 0 }),
AutoReportSetting::EverySeconds(n) => self.send_gcode(M155Command { interval: n }),
}
}
/// Set an interval at which to report the printer position or disable automatic position
/// updates
pub fn set_position_auto_report(
&mut self,
report: AutoReportSetting,
) -> Result<(), PrinterError> {
match report {
AutoReportSetting::Disabled => self.send_gcode(M154Command { interval: 0 }),
AutoReportSetting::EverySeconds(n) => self.send_gcode(M154Command { interval: n }),
@ -381,38 +398,9 @@ impl Printer {
to_user_thread.clone(),
),
Err(TryRecvError::Disconnected) => break,
Err(TryRecvError::Empty) => handle_printer_autoreport(&mut port, &mut partial_reads, state.clone()),
}
}
}
}
/// Check for auto-report messages coming in from the printer and update the `state` accordingly
fn handle_printer_autoreport(port: &mut TTYPort, partial_reads: &mut Vec<u8>, state: Arc<Mutex<State>>) {
return;
if port
.bytes_to_read()
.expect("`handle_printer_autoreport`: Failed to check for available data")
> 0
{
let mut buf = [0; BUF_SIZE_READ_LINE];
let bytes_read = port
.read(&mut buf)
.expect("`handle_printer_autoreport`: Failed to read data");
assert!(
bytes_read > 0,
"The port returned 0 bytes after a read. Is the port closed?"
);
match String::from_utf8(buf[..bytes_read].to_vec()) {
Ok(s) => {
println!("Received autoreport:");
println!("<< {}", s);
}
Err(e) => {
panic!(
"`handle_printer_autoreport` Failed to parse received bytes `{}` as UTF8: {}",
bytes_read, e
);
Err(TryRecvError::Empty) => {
handle_printer_autoreport(&mut port, &mut partial_reads, state.clone())
}
}
}
}
@ -466,6 +454,48 @@ fn read_line(
))
}
/// Check for auto-report messages coming in from the printer and update the `state` accordingly
fn handle_printer_autoreport(
port: &mut TTYPort,
partial_reads: &mut Vec<u8>,
state: Arc<Mutex<State>>,
) {
if port
.bytes_to_read()
.expect("Unable to read available byte count")
== 0
{
return;
}
// If we have any byte available, we should also get a whole line
let line = read_line(port, partial_reads, DEFAULT_COMMAND_TIMEOUT)
.expect("Failed to read from printer");
let str_line = String::from_utf8(line.clone()).expect("Read line was no valid utf8");
println!("<< {:?}", str_line);
if is_temperature_report(&str_line) {
// Temperature reports are not handled
// If you need this, implement it analogous to `parse_autoreport_line`
} else if let AutoReport::Position(x, y, z) = parse_autoreport_line(&str_line) {
let mut position_guard = state.lock().unwrap();
if let Some(x) = x {
position_guard.position.x = x;
}
if let Some(y) = y {
position_guard.position.y = y;
}
if let Some(z) = z {
position_guard.position.z = z;
}
} else {
eprintln!(
"ERROR: Got line from printer outside of command!: {}",
str_line
)
}
}
fn handle_user_command(
port: &mut TTYPort,
user_command: String,
@ -491,7 +521,10 @@ fn handle_user_command(
let str_line = String::from_utf8(line.clone()).expect("Read line was no valid utf8");
println!("<< {:?}", str_line);
if let AutoReport::Position(x, y, z) = parse_autoreport_line(&str_line) {
if is_temperature_report(&str_line) {
// Temperature reports are not handled
// If you need this, implement it analogous to `parse_autoreport_line`
} else if let AutoReport::Position(x, y, z) = parse_autoreport_line(&str_line) {
let mut position_guard = state.lock().unwrap();
if let Some(x) = x {
position_guard.position.x = x;
@ -502,8 +535,7 @@ fn handle_user_command(
if let Some(z) = z {
position_guard.position.z = z;
}
}
else if str_line.starts_with("ok") {
} else 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