| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960 |
- """Is there a spool in this AMS slot?
- Three backend decisions turn on the answer -- whether to push
- ``ams_filament_setting`` when a spool is assigned, whether a pre-assigned slot
- has just been filled, and whether an assignment has gone stale -- and all three
- used to read it off the tray's ``state`` field. That field cannot carry it.
- ## Why ``state`` is the wrong source
- ``state`` is firmware-variant. The A1 Mini BMCU and the P1S Standard AMS report
- 3 for a loaded slot and never emit 11; the AMS-HT's codes differ again (#2670).
- Bambuddy already works around that on the printer card, where
- ``getEmptySlotKind`` (``PrintersPage.tsx``) reads the presence bit first and
- only falls back to the 9/10 heuristic when there is no bit to read.
- Worse, ``state`` is partly Bambuddy's own writing. ``apply_tray_exist_bits``
- sets ``state = 9`` on every slot whose presence bit is 0 -- and when the bit
- comes back it leaves the 9 exactly where it was, because the "slot occupied"
- branch only annotates ``exists`` and moves on. So a slot the firmware says is
- full can sit in the cache reading ``exists=True, state=9`` indefinitely. That
- is #3084: a non-Bambu spool is swapped in, Assign Spool reads the stale 9,
- calls the slot empty, sends no MQTT, and the printer keeps showing ``?``. The
- deferred-configuration replay could not rescue it either, because its own
- "loaded" test was the same 9/10 heuristic -- the exact deadlock #1322 removed
- elsewhere. #3100 is the same stale 9 one step further on: the replay does not
- fire, the assignment keeps the empty fingerprint it was stored with, and the
- first real tray report is read as a spool swap and deleted.
- ## What this module answers
- ``tray_exist_bits`` is the firmware's own "which slots have a spool" bitmask --
- the one BambuStudio draws its ``?`` from -- and ``apply_tray_exist_bits``
- records it per tray as ``exists``. That bit is authoritative where it exists,
- and absent otherwise; it is never a guess. Callers that want a decision for a
- payload carrying no bit at all keep their own fallback, because the right
- fallback differs per caller: the assign path wants to know whether the push is
- doomed, the unlink pass wants to know whether a spool was removed, and a state
- of 26 ("unloaded", mid-runout) answers those two questions differently.
- """
- from collections.abc import Mapping
- from typing import Any
- def spool_present(tray: Mapping[str, Any] | None) -> bool | None:
- """Does firmware's presence bit say a spool is in this slot?
- ``True`` / ``False`` straight from ``tray_exist_bits``; ``None`` when the
- tray carries no presence annotation, which means the caller has to decide
- on its own terms rather than assume either way.
- Only the internal AMS path annotates ``exists`` (``apply_tray_exist_bits``
- is called with ``annotate_exists=True`` there and False for the VP bridge),
- so the external spool's ``vt_tray`` entries answer ``None`` -- they have no
- bit in the mask.
- """
- if not isinstance(tray, Mapping):
- return None
- exists = tray.get("exists")
- return exists if isinstance(exists, bool) else None
|