Files
bambuddy/backend
maziggy f648439536 Resolve the H2C rack nozzle at dispatch instead of letting firmware pick (#2800)
An H2C ran its startup clean and bed levelling on one hotend, switched,
    and then printed several millimetres above the plate. The same job from
    Bambu Studio was fine.

    The H2C is the only model that mounts its nozzle from a rack of six, and
    a print command names that nozzle by physical rack position -- the
    firmware reports those as 16 to 21 -- not by the extruder index, 0 or 1,
    every other dual-nozzle printer uses. Bambuddy only ever had a rack
    position when a job arrived through the Virtual Printer, which captures
    Bambu Studio's pick and replays it (#1780). Anything queued from the
    library, an archive, the webhook or a slicer pipeline carried none, so
    the field was omitted and the firmware chose -- and its choice need not
    match what the file was sliced for.

    The scheduler now derives the per-slot extruder assignment from the file
    it is about to send, and the MQTT layer resolves it against the rack
    position the printer reports live. Both are needed: the file knows which
    side a slot prints from, only the printer knows which hotend is in the
    carriage, and it can be swapped from the touchscreen between queueing a
    job and printing it.

    Derived at dispatch rather than at creation because that is the first
    point knowing both the real printer and the real file -- an item can be
    created unassigned, reassigned later, or have its file swapped for a
    G-code-injected copy. One call therefore covers the print dialog, bulk
    library adds, the webhook and pipeline runs, and no column is needed.

    extract_nozzle_mapping_from_3mf is deliberately untouched. Its output
    feeds the AMS matcher, where nozzle_id is compared against a tray's
    extruder_id as a hard filter, and physical_extruder_map is what makes
    that comparison correct -- on an H2D it is [1, 0] and flips the two.
    Dropping the translation to suit the rack would send every dual-nozzle
    AMS match to the wrong extruder. The dense per-slot form is a separate
    function reusing the same output.

    Nothing here can fail a dispatch. The command is built and published with
    no exception handler above it, and the queue item is already committed as
    printing by then, so a bad input has to degrade to "firmware picks"
    rather than wedge the item. resolve_rack_nozzle_mapping validates every
    input and raises nothing; an unresolvable mapping, an unparseable value
    or an unknown rack position all omit the field, which is the behaviour
    that existed before. Slot IDs are bounded before the dense list is built:
    they come from the file, and one declaring filament id="50000000" would
    otherwise allocate a fifty-million-entry list on the dispatch path.

    Two things are not guessed. A job printing only from the fixed hotend is
    still left to the firmware, because that nozzle's physical ID is not
    confirmed by a known-good capture. And the rack is taken to feed extruder
    0 from a single hardware observation -- if that is flipped, a one-sided
    job matches nothing and falls back to the old behaviour, so only a job
    using both nozzles at once could be harmed, which is what a second
    capture needs to confirm.

    Confined to the H2C throughout. Building the print command for 21 model
    spellings with and without the new argument changes exactly three of them
    -- H2C, O1C and O1C2. The other 18, including H2D and X2D, are identical.

    Reported by @tru3l3gend, who diagnosed it on real hardware against a
    working Bambu Studio dispatch, established the rack ID range and supplied
    a patch.
2026-08-15 14:49:43 +02:00
..