Files
bambuddy/backend/app/core
maziggy 8e553289db Choose which rack nozzle each filament prints from on an H2C (#1784)
The Vortek rack holds six hotends, and a multi-colour plate is sliced to
    use a different one per colour so it can skip the purge. Which of the six
    each colour takes is not in the 3MF. The same plate, sliced and sent twice
    from Bambu Studio with a different choice each time, produces two files
    that differ only in rounding in the last digit of a few extrusion figures
    -- the filament grouping, the toolchange stream, the 120 nozzle-change
    markers and project_settings.config are all identical. The choice travels
    only in the dispatched nozzle_mapping.

    Bambuddy had no way to state it, so those plates went out with no nozzle
    assignment at all and the printer chose for itself. That is what levelled
    on one hotend and printed with another, millimetres above the plate.

    Every rack-bound filament now carries a position picker beside its AMS
    slot dropdown, listing all six with the nozzle each holds. An empty
    position, or one holding the wrong diameter or flow type, is shown greyed
    out with the reason rather than hidden, so someone looking for position 4
    finds it. The choice is per filament *group* rather than per slot, because
    a group is one hotend: two filaments the slicer grouped together share it
    and cannot point at different positions.

    Nothing has to be picked. Positions are assigned automatically, preferring
    one already loaded with that colour, which on the plate this was built
    against reproduces Bambu Studio's own pick exactly.

    A nozzle currently picked up onto the carriage is offered too. The
    firmware drops its rack position from the report entirely rather than
    sending a placeholder (#943), and refusing it would rule out the position
    most likely to be wanted -- the one the last print left mounted. Only
    recoverable when exactly one position is missing; two gaps are genuinely
    ambiguous and stay unavailable.

    Positions are re-checked at dispatch, not just when queued, because the
    rack can be re-loaded in between. The two failure modes differ on purpose:
    an explicitly chosen position that no longer fits stops the print, names
    what the position now holds, and deletes the uploaded file from the SD
    card so it cannot be started by hand either -- an operator who named a
    hotend must not silently get a different one. An automatic assignment that
    cannot be made instead falls back to letting the firmware choose, which is
    what happened before any of this existed.

    The pick is stored as {group: position} rather than as the expanded
    nozzle_mapping, though that is what goes on the wire. That column means
    "Bambu Studio decided, forward verbatim", and only the group-and-position
    form can be re-checked against what is actually mounted at dispatch.

    The existing multi-rack refusal in extract_nozzle_mapping_from_3mf stays.
    It still guards the #2800 fallback, which can only ever name one rack id.

    Measured on the maintainer's H2C: rack position n is physical nozzle id
    15 + n, confirmed by cross-referencing two captured dispatches against
    Bambu Studio's own dialog. extruder_max_nozzle_count names which carriage
    is the rack straight from the file, and is read rather than assumed -- a
    fourth independent confirmation of the carriage indices fixed in 45dc139.

    The print dialog is also wider, on every printer. Its filament rows carry
    the most horizontal content in it and adding a picker truncated names to
    "Bamb...". The column widths themselves only change on a rack machine.

    Tests: 44 unit covering the plan, the resolver, the mounted-nozzle
    recovery and every refusal; 9 dispatch integration asserting the two real
    captures end to end; 7 API round-trip; 33 frontend. The API ones exist
    because two integration bugs got through a green suite that tested the
    pieces and not the seams -- the group data reached only one of the three
    filament-requirements routes, and the field was declared on every schema
    except the create one, where Pydantic dropped it in silence.
2026-08-15 14:59:41 +02:00
..
…
.
2026-08-15 14:37:27 +02:00
…
…
…