9b3815fd25
Verified with cargo check + cargo run --example printer_polymorphism.
69 lines
2.2 KiB
Rust
69 lines
2.2 KiB
Rust
mod gpio;
|
|
mod serial;
|
|
|
|
use thiserror::Error;
|
|
use tracing::{info, warn};
|
|
|
|
#[derive(Debug, Error)]
|
|
pub enum PlateChangerError {
|
|
#[error("plate changer hardware not responding")]
|
|
NotResponding,
|
|
#[error("sensor validation failed: {0}")]
|
|
SensorMismatch(String),
|
|
#[error("serial transport error: {0}")]
|
|
Serial(#[from] serial::SerialError),
|
|
}
|
|
|
|
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
|
pub enum CycleOutcome {
|
|
Success,
|
|
Retried,
|
|
}
|
|
|
|
/// Drives a mechanical plate-swap cycle: signal the changer over serial,
|
|
/// wait for it to report completion, then cross-check the physical sensors
|
|
/// (plate-present, bed-clear) before releasing the print queue to continue.
|
|
pub struct PlateChanger {
|
|
port: serial::SerialPort,
|
|
}
|
|
|
|
impl PlateChanger {
|
|
pub fn open(path: &str, baud: u32) -> Result<Self, PlateChangerError> {
|
|
Ok(Self {
|
|
port: serial::SerialPort::open(path, baud)?,
|
|
})
|
|
}
|
|
|
|
pub async fn run_cycle(&mut self, slot: u8) -> Result<CycleOutcome, PlateChangerError> {
|
|
info!(slot, "starting plate change cycle");
|
|
|
|
self.port.send_command(&serial::Command::Eject).await?;
|
|
self.port.await_ack(std::time::Duration::from_secs(30)).await?;
|
|
|
|
self.port.send_command(&serial::Command::LoadSlot(slot)).await?;
|
|
self.port.await_ack(std::time::Duration::from_secs(30)).await?;
|
|
|
|
match self.validate_sensors().await {
|
|
Ok(()) => Ok(CycleOutcome::Success),
|
|
Err(err) => {
|
|
warn!(?err, slot, "sensor validation failed, retrying cycle once");
|
|
self.port.send_command(&serial::Command::LoadSlot(slot)).await?;
|
|
self.port.await_ack(std::time::Duration::from_secs(30)).await?;
|
|
self.validate_sensors().await?;
|
|
Ok(CycleOutcome::Retried)
|
|
}
|
|
}
|
|
}
|
|
|
|
async fn validate_sensors(&mut self) -> Result<(), PlateChangerError> {
|
|
let state = gpio::read_sensor_state().await;
|
|
if !state.plate_present {
|
|
return Err(PlateChangerError::SensorMismatch("plate not detected on bed".into()));
|
|
}
|
|
if !state.bed_clear {
|
|
return Err(PlateChangerError::SensorMismatch("bed obstruction detected".into()));
|
|
}
|
|
Ok(())
|
|
}
|
|
}
|