Wire fleet::load into main.rs; fix request() reply unwrapping
main.rs never actually loaded printers.toml or connected anything -
fleet/printer were only ever exercised by standalone examples. Now
main() loads the fleet (path configurable via CONTINUUM_PRINTERS_PATH,
default "printers.toml") and connects every printer concurrently at
startup, logging success/failure per printer rather than failing
startup over one bad printer.
That surfaced a real bug the instant it ran against a live printer:
complete_pending() was sending the whole {"info": {...}} wrapper through
the pending oneshot, not the unwrapped body - BambuPrinter::get_version
deserializing that into GetVersionResponse failed with "missing field
module" even though module was right there, just one level deeper than
expected. Fixed by unwrapping the single category key before completing
the pending request, symmetric with how a request gets wrapped on the
way out.
Verified live against a real H2C (MQTT v5/BambuMQTT v2): connect,
get_version, and the full pending-request correlation all worked
end-to-end, returning real firmware versions for every module (main
board 01.01.05.00, both AMS units, AMS HT, etc). The other configured
printer (P1P, forced v1) failed with a real, expected TLS error - it
doesn't chain to the bundled CA, matching the already-documented P1P
quirk; needs the ca_cert_path/fetch_bambu_cert workflow, not a code fix.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MWQVtBGxHYLkVw2kBupd5g
This commit is contained in:
+18
-14
@@ -262,24 +262,28 @@ async fn poll_loop_v2(mut event_loop: rumqttc::v5::EventLoop, pending: PendingTa
|
||||
}
|
||||
|
||||
/// Parses an incoming report and, if its `sequence_id` matches something
|
||||
/// `request` is waiting on, hands it the parsed JSON. A report with no
|
||||
/// `sequence_id`, or one nothing's waiting on (an unsolicited status
|
||||
/// `request` is waiting on, hands it the *unwrapped* body. A report with
|
||||
/// no `sequence_id`, or one nothing's waiting on (an unsolicited status
|
||||
/// push, not a reply to anything), is silently ignored - most Bambu
|
||||
/// reports aren't replies to a specific request at all.
|
||||
///
|
||||
/// A report is `{category: {"sequence_id": "...", ...}}` - one key, same
|
||||
/// shape a request is published as. `request` publishes `body` and wants
|
||||
/// `body`-shaped data back, not the wrapper around it, so this looks
|
||||
/// inside whichever single category key is present (rather than
|
||||
/// requiring the caller to already know it) and sends only that inner
|
||||
/// value through - confirmed against a real printer's `get_version`
|
||||
/// reply, which failed to parse as `GetVersionResponse` before this
|
||||
/// unwrapped it (real device, real bug: `serde_json::from_value` on the
|
||||
/// still-wrapped `{"info": {...}}` reported "missing field `module`",
|
||||
/// since `module` lives one level deeper than that).
|
||||
async fn complete_pending(pending: &PendingTable, payload: &[u8]) {
|
||||
let Ok(value) = serde_json::from_slice::<Value>(payload) else { return };
|
||||
let Some(sequence_id) = find_sequence_id(&value) else { return };
|
||||
let Some(object) = value.as_object() else { return };
|
||||
let Some((_, body)) = object.iter().next() else { return };
|
||||
let Some(sequence_id) = body.get("sequence_id").and_then(Value::as_str) else { return };
|
||||
|
||||
if let Some(sender) = pending.lock().await.remove(&sequence_id) {
|
||||
let _ = sender.send(value);
|
||||
if let Some(sender) = pending.lock().await.remove(sequence_id) {
|
||||
let _ = sender.send(body.clone());
|
||||
}
|
||||
}
|
||||
|
||||
/// A report is `{category: {"sequence_id": "...", ...}}` - one key, same
|
||||
/// shape as a request. Look inside whichever single category key is
|
||||
/// present rather than requiring the caller to already know it.
|
||||
fn find_sequence_id(report: &Value) -> Option<String> {
|
||||
let object = report.as_object()?;
|
||||
let (_, category_body) = object.iter().next()?;
|
||||
category_body.get("sequence_id")?.as_str().map(str::to_string)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user