Use fill level for jogging

This commit is contained in:
Frederik Menke 2023-12-09 21:37:44 +01:00
parent 821b15b599
commit edaaafbb15
4 changed files with 48 additions and 11 deletions

View file

@ -1,10 +1,11 @@
use crate::gamepad::Gamepad; use crate::gamepad::Gamepad;
use crate::printer::gcode::{G0Command, G91Command}; use crate::printer::gcode::{G0Command, G91Command};
use crate::printer::{Printer}; use crate::printer::Printer;
use euclid::{vec3, Vector3D}; use euclid::{vec3, Vector3D};
use futures::never::Never; use futures::never::Never;
use std::sync::Arc; use std::sync::Arc;
use std::time::Duration; use std::time::Duration;
use tokio::time::sleep;
/// Time that a single movement command should take /// Time that a single movement command should take
/// ///
@ -17,17 +18,22 @@ const FULL_SCALE_SPEED_XY: f64 = 1000.0;
/// Movement speed of gantry when going full throttle in z-direction /// Movement speed of gantry when going full throttle in z-direction
/// in units/min (mm/min) /// in units/min (mm/min)
const FULL_SCALE_SPEED_Z: f64 = 10.0; const FULL_SCALE_SPEED_Z: f64 = 10.0;
/// Amount of GCODE buffers that should be filled for optimal jogging performance.
/// More buffers increase the controller delay. Less buffers make it more difficult
/// for the motion planner on the printer to plan ahead.
const TARGET_BUFER_FILL_LEVEL: usize = 4;
/// Should be mm on most machine including the Leapfrog /// Should be mm on most machine including the Leapfrog
pub struct PrinterUnits; pub struct PrinterUnits;
pub type PrinterVec = Vector3D<f64, PrinterUnits>; pub type PrinterVec = Vector3D<f64, PrinterUnits>;
/// Jogging the gantry by pumping loads of gcode into the printer board /// Jog the gantry by pumping loads of gcode into the printer board
pub async fn jog(gamepad: Arc<Gamepad>, mut printer: Printer) -> Never { pub async fn jog(gamepad: Arc<Gamepad>, mut printer: Printer) -> Never {
printer.send_gcode(Box::new(G91Command)).await.unwrap(); printer.send_gcode(Box::new(G91Command)).await.unwrap();
println!("Sent G91Command"); println!("Sent G91Command");
loop { loop {
let (setpoint_x, setpoint_y, setpoint_z) = gamepad.speed_setpoint(); let (setpoint_x, setpoint_y, setpoint_z) = gamepad.speed_setpoint();
// We're bound by lower speed on z and have no way to go at separate speeds per axis // We're bound by lower speed on z and have no way to go at separate speeds per axis
let full_scale_speed = if setpoint_z == 0.0 { let full_scale_speed = if setpoint_z == 0.0 {
FULL_SCALE_SPEED_XY FULL_SCALE_SPEED_XY
@ -40,6 +46,7 @@ pub async fn jog(gamepad: Arc<Gamepad>, mut printer: Printer) -> Never {
full_scale_speed * (TIME_PER_MOVEMENT.as_secs_f64() / 60.0) * (setpoint_z as f64), full_scale_speed * (TIME_PER_MOVEMENT.as_secs_f64() / 60.0) * (setpoint_z as f64),
); );
if distance.length() == 0.0 { if distance.length() == 0.0 {
sleep(TIME_PER_MOVEMENT).await;
continue; continue;
} }
let velocity = distance.length() / (TIME_PER_MOVEMENT.as_secs_f64() / 60.0); let velocity = distance.length() / (TIME_PER_MOVEMENT.as_secs_f64() / 60.0);
@ -50,6 +57,20 @@ pub async fn jog(gamepad: Arc<Gamepad>, mut printer: Printer) -> Never {
e: None, e: None,
velocity: velocity.into(), velocity: velocity.into(),
}; };
printer.send_gcode(Box::new(command)).await; printer
.send_gcode(Box::new(command))
.await
.expect("Failed to send movement command!");
// Wait for one command time if buffer is overfull, wait for half that time if buffer is
// filled *just* right.
let fill_level = printer.maximum_capacity() - printer.remaining_capacity();
println!("remaining capacity: {}", printer.remaining_capacity());
println!("fill level: {}", fill_level);
if fill_level > TARGET_BUFER_FILL_LEVEL {
sleep(TIME_PER_MOVEMENT * 2).await;
} else if fill_level == TARGET_BUFER_FILL_LEVEL {
sleep(TIME_PER_MOVEMENT / 2).await;
}
} }
} }

View file

@ -1,4 +1,3 @@
#![warn(missing_docs)]
pub mod gamepad; pub mod gamepad;
pub mod jogger; pub mod jogger;
pub mod printer; pub mod printer;

View file

@ -1,5 +1,4 @@
#![warn(rust_2018_idioms)] #![warn(rust_2018_idioms)]
#![warn(missing_docs)]
use futures::never::Never; use futures::never::Never;
use i2c_linux::I2c; use i2c_linux::I2c;
use red::gamepad; use red::gamepad;

View file

@ -1,4 +1,5 @@
pub mod gcode; pub mod gcode;
use lazy_static::lazy_static;
use bytes::BytesMut; use bytes::BytesMut;
use futures::sink::SinkExt; use futures::sink::SinkExt;
@ -13,7 +14,7 @@ use tokio_util::codec::{Decoder, Encoder, Framed};
pub use gcode::{GcodeCommand, GcodeReply}; pub use gcode::{GcodeCommand, GcodeReply};
use crate::printer::gcode::M114Command; use crate::printer::gcode::{G91Command, M114Command};
use self::gcode::GcodeReplyError; use self::gcode::GcodeReplyError;
@ -42,6 +43,7 @@ pub struct Printer {
serial_tx: SplitSink<Framed<SerialStream, LineCodec>, String>, serial_tx: SplitSink<Framed<SerialStream, LineCodec>, String>,
serial_rx: SplitStream<Framed<SerialStream, LineCodec>>, serial_rx: SplitStream<Framed<SerialStream, LineCodec>>,
last_buffer_capacity: usize, last_buffer_capacity: usize,
maximum_buffer_capacity: usize,
} }
impl Printer { impl Printer {
@ -88,7 +90,7 @@ impl Printer {
// The printer will send some info after connecting for the first time. We need to wait for this // The printer will send some info after connecting for the first time. We need to wait for this
// to be received as it will otherwise stop responding for some reason: // to be received as it will otherwise stop responding for some reason:
loop { loop {
if let Ok(message) = timeout(Duration::from_secs(10), serial_rx.next()).await { if let Ok(message) = timeout(Duration::from_secs(2), serial_rx.next()).await {
match message { match message {
Some(Ok(reply)) => { Some(Ok(reply)) => {
println!("got stuff: {:?}", reply); println!("got stuff: {:?}", reply);
@ -111,7 +113,6 @@ impl Printer {
break; break;
} }
} }
println!("Sending M114 command");
let mut res = Printer { let mut res = Printer {
serial_rx, serial_rx,
@ -120,23 +121,40 @@ impl Printer {
position: (0.0, 0.0, 0.0, 0.0), position: (0.0, 0.0, 0.0, 0.0),
}), }),
last_buffer_capacity: 0, // this is updated on the next call to `send_gcode()` last_buffer_capacity: 0, // this is updated on the next call to `send_gcode()`
maximum_buffer_capacity: 0, // this is updated on the next call to `send_gcode()`
}; };
res.send_gcode(Box::new(M114Command)) res.send_gcode(Box::new(G91Command))
.await .await
.expect("Could not ask for current position!"); .expect("Could not ask for current position!");
// since we never sent any positioning GCODE, we should be at max-capacity now.
res.maximum_buffer_capacity = res.last_buffer_capacity;
Ok(res) Ok(res)
} }
/// The maximum capacity of the machines GCODE buffer.
pub fn maximum_capacity(&self) -> usize {
self.maximum_buffer_capacity
}
/// The remaining capacity of the machines GCODE buffer.
/// This value is refreshed after each sent command.
pub fn remaining_capacity(&self) -> usize {
self.last_buffer_capacity
}
/// Parse the "Ok" confirmation line that the printer sends after every successfully received /// Parse the "Ok" confirmation line that the printer sends after every successfully received
/// command. /// command.
fn parse_ok(line: &str) -> Result<usize, PrinterError> { fn parse_ok(line: &str) -> Result<usize, PrinterError> {
let make_err = || PrinterError::ConfirmationError { let make_err = || PrinterError::ConfirmationError {
parsed_string: line.to_string(), parsed_string: line.to_string(),
}; };
let re = Regex::new(r"ok P(\d+) B(\d+)").unwrap(); lazy_static! {
let captures = re.captures(line).ok_or_else(make_err)?; static ref RE: Regex = Regex::new(r"ok P(\d+) B(\d+)").unwrap();
}
let captures = RE.captures(line).ok_or_else(make_err)?;
captures captures
.get(1) .get(1)
.unwrap() .unwrap()