Add GenericPrinter::set_fan_speed() across all five printer kinds

Same override pattern as connect(): required, no default body, five
completely different bodies.

- BambuGenericPrinter::set_fan_speed_impl() is shared by BambuV1Printer and
  BambuV2Printer (composition reuse, same as connect_impl) - both speak
  the same MQTT gcode-injection mechanism.
- PrusaLinkPrinter and PrusaSerialPrinter both send the same M106 gcode
  but over different transports (HTTP command injection vs. raw serial
  bytes) - they share the percent->PWM conversion despite having unrelated
  connect() implementations.
- KlipperPrinter uses a hypothetical Moonraker-native endpoint that takes
  a percentage directly - no PWM conversion at all, since that math only
  applies to the gcode-speaking vendors.

percent_to_pwm() lives in mod.rs as a private free function rather than a
trait default: it's genuine shared logic, but only for the subset of
vendors that need it, which is exactly the case a trait default can't
express cleanly. Visible to bambu.rs/prusa.rs via super:: because Rust's
module privacy reaches into child modules.

Verified with cargo check --all-targets and a full run of
printer_polymorphism: 50% converges on the same PWM value (127) across all
three gcode-based printers, Klipper's native path takes 0.50 directly.
This commit is contained in:
2026-08-28 21:40:11 +00:00
parent aa471ff906
commit 6e18215af5
5 changed files with 80 additions and 6 deletions
+25 -6
View File
@@ -67,6 +67,12 @@ pub trait GenericPrinter {
/// into the multi-threaded daemon — safe to ignore for now.
async fn connect(&mut self) -> Result<(), PrinterError>;
/// REQUIRED, no default body: `percent` is 0-100, but how it actually
/// gets sent — an MQTT gcode-injection command, a REST call, raw bytes
/// on a serial port — is different for every vendor, same as
/// `connect()`.
async fn set_fan_speed(&mut self, percent: u8) -> Result<(), PrinterError>;
/// OPTIONAL: a default body every printer gets for free unless it
/// writes its own. None of ours do below, so all three vendors share
/// this exact implementation — the trait-method equivalent of
@@ -76,14 +82,17 @@ pub trait GenericPrinter {
}
}
pub enum PrinterFlavour {
BambuV1Printer,
BambuV2Printer,
PrusaLinkPrinter,
PrusaSerialPrinter,
KlipperPrinter,
/// M106's `S` parameter is a raw 0-255 PWM value, not a percentage. Shared
/// by anything that ultimately sends gcode (Bambu, PrusaLink, PrusaSerial)
/// — *not* a trait default, since Klipper's hypothetical native fan API
/// below takes a percent directly and has no use for this at all. Private,
/// but still visible to bambu.rs/prusa.rs: Rust's module privacy reaches
/// into child modules, not just the exact file it's defined in.
fn percent_to_pwm(percent: u8) -> u8 {
((percent.min(100) as u16 * 255) / 100) as u8
}
/// The closed set of printer kinds this fleet can talk to.
pub enum PrinterHandle {
BambuV1(BambuV1Printer),
@@ -117,4 +126,14 @@ impl GenericPrinter for PrinterHandle {
PrinterHandle::Klipper(p) => p.connect().await,
}
}
async fn set_fan_speed(&mut self, percent: u8) -> Result<(), PrinterError> {
match self {
PrinterHandle::BambuV1(p) => p.set_fan_speed(percent).await,
PrinterHandle::BambuV2(p) => p.set_fan_speed(percent).await,
PrinterHandle::PrusaLink(p) => p.set_fan_speed(percent).await,
PrinterHandle::PrusaSerial(p) => p.set_fan_speed(percent).await,
PrinterHandle::Klipper(p) => p.set_fan_speed(percent).await,
}
}
}