Add pushall + print_status cache; fix envelope, race, and matching bugs
PrintReport now flattens CommandEnvelope (sequence_id/command) - push_status.schema.json composes the shared envelope like every other Bambu message; the struct's old doc comment concluded otherwise from a capture that turned out to be an incomplete example (also missing dozens of other real fields), not proof the message lacks them. Added BambuMqttSession's PrintStatusCache: the in-memory print.push_status object, replaced wholesale on v2 (always full) or on a v1 message with msg != 1 (forced/full), merged field-by-field otherwise (v1's organic partial updates) via PrintReport::merge_from. Tracks last_sequence_id to warn on a gap in the organic stream. BambuPrinter gets pushall() (v1-only; pushing.pushall doesn't exist in v2's schema at all) and print_status(). Three real bugs found and fixed via live testing against an actual P1P, none of them guessable from the schema alone: - pushall's reply doesn't echo the sequence_id we send - the printer stamps its own free-running counter regardless of what triggered it. request()'s ID correlation silently can't work here; first fix attempt added request_next_of_category (wait for the next message under a category, no ID needed). - That undershot too: matching by category alone let an unrelated "print" message (that category carries many commands - gcode_line, pause, push_status, ...) satisfy the wait before the real push_status reply arrived, so pushall() returned Ok having consumed the wrong message - now keyed by (category, command). - A real ordering race: handle_incoming_report woke a waiting oneshot before updating the print_status cache, so a caller could resume and check print_status() before the write landed - pushall() returned Ok immediately followed by print_status() still showing None. Fixed by applying the cache update before sending on any matched oneshot. Superseded by real protocol knowledge before any of the above needed to ship, though: push_status carries its own msg field (1 = automatic/ partial, 0 = forced/full) - the actual, reliable, self-describing signal for which kind a message is, regardless of timing or which client (if any) triggered it. PrintStatusCache now reads that directly instead of inferring it from request correlation, and pushall() is plain fire-and- forget (publish, no wait) since there's nothing left to correlate for - confirmed live: msg reads correctly off a real P1P, and the cache populates/updates correctly with no gap warnings across several organic pushes. request_next_of_category is kept (a future case may still need "no correlation ID at all" for real), just no longer used here. Also: the poll loop now logs when it exits instead of returning silently - that silence is exactly what made a real dropped MQTT connection to a live H2C indistinguishable from "nothing happening" during testing.
This commit is contained in:
@@ -402,6 +402,57 @@ impl BambuPrinter {
|
||||
serde_json::from_value(response)
|
||||
.map_err(|err| PrinterError(format!("failed to parse get_version response: {err}")))
|
||||
}
|
||||
|
||||
/// Bambu-only, same reasoning as `get_version` above. Publishes
|
||||
/// `pushing.pushall`, which - unlike `get_version` - doesn't reply
|
||||
/// under its own category: it triggers/refreshes the ongoing
|
||||
/// `print.push_status` stream instead. Fire-and-forget, same as
|
||||
/// `send_gcode_line`: there's nothing to wait for here, since the
|
||||
/// forced full update this triggers self-describes as such via its
|
||||
/// own `msg` field once it arrives (see `PrintStatusCache::apply`),
|
||||
/// whenever that turns out to be - waiting on it added two real bugs
|
||||
/// (a race, and matching the wrong "print" message) for no benefit
|
||||
/// once that was understood.
|
||||
///
|
||||
/// Call this periodically for v1 printers specifically - v2 has no
|
||||
/// `pushing` category at all (checked: `schemas/bambulab/v2/
|
||||
/// request.schema.json` only wires camera/info/print/system/upgrade/
|
||||
/// xcam). Whether v2 needs some *other* trigger to get
|
||||
/// `print.push_status` flowing at all is still an open question (a
|
||||
/// live test meant to check that lost its connection entirely, an
|
||||
/// unrelated failure, not real evidence either way) - if it does,
|
||||
/// that's a different method to add once identified, not a variant
|
||||
/// of this one. A v2 connection makes this call a no-op rather than
|
||||
/// an error, since `pushing.pushall` specifically is confirmed not to
|
||||
/// exist for it regardless.
|
||||
pub async fn pushall(&self) -> Result<(), PrinterError> {
|
||||
let (session, version) =
|
||||
self.connection.as_ref().ok_or_else(|| PrinterError(format!("{} is not connected", self.base.name)))?;
|
||||
|
||||
if matches!(version, BambuProtocolVersion::V2) {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let mut body = continuum_schema_instance::build_instance_from_file(&get_schema_for_command(
|
||||
*version,
|
||||
"pushing".into(),
|
||||
"pushall".into(),
|
||||
))
|
||||
.map_err(|err| PrinterError(format!("failed to build pushall request: {err}")))?;
|
||||
body["push_target"] = serde_json::Value::from(1);
|
||||
|
||||
session.publish("pushing", &body).await
|
||||
}
|
||||
|
||||
/// The current in-memory `print.push_status` object - `None` until at
|
||||
/// least one has arrived (which, on v1, requires having called
|
||||
/// `pushall` at least once since connecting; v2 pushes on its own).
|
||||
pub async fn print_status(&self) -> Option<super::PrintReport> {
|
||||
match self.connection.as_ref() {
|
||||
Some((session, _)) => session.print_status().await,
|
||||
None => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl GenericPrinter for BambuPrinter {
|
||||
|
||||
Reference in New Issue
Block a user