Files
bambuddy/backend
maziggy 3bc96254ef Charge the tray the printer said it used, not the first one loaded (issue #2953)
A sliced file numbers its filaments 1..4; which AMS tray each came from is
    decided when the job is sent. #2768 gave the Spoolman writer two ways to
    recover that decision when the print did not come through Bambuddy: the
    printer's own mapping field, and a colour match of the 3MF's slots against the
    loaded trays. An A1 satisfies neither. It publishes no mapping field, and it
    drops the MQTT connection when we subscribe to its request topic, so the
    slicer's instruction never arrives either. That leaves the colour match, and it
    compares hex strings exactly.

    The reporter sliced with a generic black profile against a tray they had set to
    charged slot 1 to whatever sat in the first tray -- 2.17 g onto a grey PLA+
    spool, while the print was fed from tray 3. Their bundle carries the printer's
    own answer: "Tray change during print: tray=3 at layer=0", recorded 90 seconds
    in, and read further down the same completion pass by _print_used_tray_keys to
    decide which slots the print had touched. The same pass then charged tray 0 on
    a guess, and logged "AMS0-T3: remain% did not fall over the print" about the
    tray that had actually done the work.

    _single_slot_tray_from_state adds the third rung. For a print with exactly one
    slot carrying usage, the one slot came from the one tray, so the printer's tray
    reporting answers the question directly: the mid-print tray-change log, then
    the tray loaded at print start, then the current one, then the last real tray
    seen. That is the ladder usage_tracker.on_print_complete has consulted since it
    started resolving mappings at completion -- Spoolman users were the only ones
    not getting it, which is why an install running the built-in inventory has
    never shown this. On this printer only the first and last rungs can fire:
    tray_now_at_start is 255 because print start runs before the filament is
    loaded, and the A1 parks tray_now back at 255 the moment a print ends.

    Gated on exactly one slot with usage, like the internal writer: a multi-colour
    print moves tray_now on every change, so one reading cannot then be attributed
    to one slot. It also declines when the log holds more than one switch, because
    an AMS-backup runout is split per segment (#1793) and a single-tray mapping
    would land the whole print on one spool.

    That gate needed the guess warning to exclude the split path too. The split
    never reads slot_to_tray at all -- it charges each segment to the tray the
    printer announced switching to, which is the same evidence this fallback is
    built on -- so a declined mapping there is not a guess, and calling it one
    suppressed the archive rewrite for exactly the prints whose attribution is best
    supported. Nothing covered that combination; a test does now.

    Where nothing names a tray the positional default still stands, because it is
    right for an AMS loaded in slicer order. It now says so at warning level so a
    support bundle carries the reason, and it no longer restamps the archive's
    filament colour and material from a spool it picked by position. That restamp
    is what made the fault read as data loss: the grams can be put back, whereas
    overwriting what the slicer recorded leaves nothing to compare against, and the
    reporter's archive had already been rewritten from #000000 to the wrong spool's
    grey. A slot that consumed nothing also stops claiming a tray in the handled
    set -- it was never charged, so the remain-delta path should stay free to cover
    it rather than be suppressed by an estimate of zero.

    The request-topic probe is the same failure reached from the other side. A
    printer that refuses kills the TCP connection instead of returning a SUBACK
    failure, so the only signal is "we subscribed, then got disconnected", and that
    was believed the first time it happened. Every other reason a connection drops
    inside the same window looks identical -- a network blip, the printer
    rebooting, the container stopped mid-probe -- and the verdict was cached per
    serial with no re-probe anywhere, so on a printer that supports the topic one
    unlucky drop cost mapping capture for the rest of the process and every slicer
    print after it was charged by tray position. It now takes two consecutive
    drops, and a disconnect we asked for is not counted. A printer that genuinely
    refuses answers the same way every time and pays one extra reconnect; one
    already known to refuse still skips the subscription outright rather than
    reopening a reconnect loop.
2026-08-26 10:20:45 +02:00
..