mirror of
https://github.com/OrcaSlicer/OrcaSlicer.git
synced 2026-10-08 16:21:14 +00:00
196 lines
11 KiB
Markdown
196 lines
11 KiB
Markdown
# Multi-extruder and multi-AMS device control
|
|
|
|
`DeviceManager`/`MachineObject` model the toolheads, AMS units, and filament slots of a connected
|
|
printer and drive the device-control UI from that model. The model represents the counts as
|
|
device-reported runtime values rather than compile-time caps, so a printer with an arbitrary number
|
|
of toolheads, several AMS units, and a non-uniform number of slots per unit is handled by the same
|
|
code paths. The printer agent is responsible for translating its printer's state into the shared
|
|
JSON dialect this model consumes; the agent boundary is described in
|
|
[printer-agent.md](printer-agent.md), and this document describes the dialect and the model.
|
|
|
|
## Model layout
|
|
|
|
`MachineObject` owns the device subsystems that this document covers:
|
|
|
|
- `DevExtderSystem` — the toolheads (extruders) and their per-toolhead filament/temperature state.
|
|
- `DevFilaSystem` — the AMS units (`DevAms`) and their trays (`DevAmsTray`).
|
|
- `DevNozzleSystem` — the nozzles and, on rack printers, `DevNozzleRack`.
|
|
- `DevFilaSwitch` — a filament switch that can feed several AMS units to more than one extruder.
|
|
|
|
Each subsystem exposes its own count — `DevExtderSystem::GetTotalExtderCount()`,
|
|
`DevFilaSystem::GetAmsCount()`, `DevAms::GetSlotCount()` — and the rest of the application derives
|
|
its layout from those values rather than from fixed assumptions.
|
|
|
|
## Wire dialect
|
|
|
|
The dialect is the Bambu-shaped JSON that `DeviceManager` and `MachineObject` already consume. An
|
|
agent whose printer speaks something else is expected to translate into it; the agent is not free
|
|
to invent a new shape. Two representations coexist for extruders, and one for AMS/slots.
|
|
|
|
### Extruders
|
|
|
|
The generic representation is `print.device.extruder`, parsed by
|
|
`ExtderSystemParser::ParseV2_0` (`DevExtruderSystem.cpp`). It has a packed `state` integer and an
|
|
`info` array with one object per toolhead:
|
|
|
|
```text
|
|
device.extruder.state packed integer
|
|
bits 0..3 total extruder count
|
|
bits 4..7 current extruder id
|
|
bits 8..11 target extruder id
|
|
bits 12..14 switch state
|
|
bits 15..18 currently loading extruder id
|
|
bit 19 busy for loading
|
|
|
|
device.extruder.info[] one entry per toolhead
|
|
id extruder id
|
|
filam_bak array of backup-group bits
|
|
info bit 1 has filament, bit 2 buffer has filament, bit 3 nozzle present
|
|
temp bits 0..15 current temperature, bits 16..31 target temperature
|
|
spre slot_id bits 0..7, ams_id bits 8..15 (previous)
|
|
snow same packing (current)
|
|
star same packing (target)
|
|
stat bit 0..15 AMS status, bits 16..31 RFID status
|
|
hnow current nozzle id
|
|
```
|
|
|
|
The legacy single/dual representation is the pair of scalar fields `nozzle_temper` /
|
|
`nozzle_target_temper`, plus the `print.ams.tray_tar` / `tray_now` tray pointer, parsed by
|
|
`ExtderSystemParser::ParseV1_0`. That parser is a fallback and only fills the main extruder when the
|
|
reported toolhead count is one.
|
|
|
|
Physical extruder ids are fixed (`MAIN_EXTRUDER_ID = 0`, `DEPUTY_EXTRUDER_ID = 1`). Bambu printers
|
|
present the two toolheads as a left/right pair and invert the physical-to-logical mapping; generic
|
|
printers use the toolhead id itself as the logical id. `DevNozzle::GetLogicExtruderId()` /
|
|
`GetExtruderId()` centralise that distinction.
|
|
|
|
### AMS units and slots
|
|
|
|
AMS units arrive as `print.ams.ams[]` and are parsed by `DevFilaSystemParser::ParseV1_0`. Each unit
|
|
carries an `id` and an `info` bitfield:
|
|
|
|
```text
|
|
info bits 0..3 type: 0 dummy, 1 AMS, 2 AMS-Lite, 3 N3F, 4 N3S, 5 AMS-Lite-mixed (N9)
|
|
info bits 8..11 bound extruder id (0xE marks a switch-bound unit)
|
|
info bits 24..27 switch inlet when the unit is switch-bound
|
|
```
|
|
|
|
`DevAmsType` (`DevDefs.h`) enumerates the resulting unit kinds: `EXT_SPOOL`, `AMS`, `AMS_LITE`,
|
|
`N3F`, `N3S`, and the `AMS_LITE_MIXED` N9 variant. `AMS_LITE_MIXED` reports itself as `AMS_LITE` to
|
|
generic callers; code that needs the distinction uses `IsAmsLiteMixed()`.
|
|
|
|
A unit whose bound-extruder nibble is `0xE` is switch-bound: it is kept only when a `DevFilaSwitch`
|
|
is installed, in which case its extruder id is pinned to the main extruder and its binding set
|
|
becomes `{0, 1}`; otherwise the unit is dropped from the AMS list.
|
|
|
|
Each unit owns a map of `DevAmsTray` keyed by the tray id from the payload. The slot count is a
|
|
property of the unit kind and the reported trays:
|
|
|
|
- `AMS`, `AMS_LITE`, and `N3F` report four slots on Bambu printers, matching the firmware contract;
|
|
- `N3S` reports one slot;
|
|
- `EXT_SPOOL` and any non-Bambu unit report the number of trays actually present.
|
|
|
|
The tray ids are the payload's slot indices; they are expected to be unique, 0-based, and contiguous
|
|
within a unit, because the generic tray-index mapping below is cumulative.
|
|
|
|
Bulk/extended spools are carried by `print.vir_slot`, which `DeviceManager` parses into
|
|
`MachineObject::vt_slot`. Two ids are fixed: `VIRTUAL_TRAY_MAIN_ID = 255` (extruder 0) and
|
|
`VIRTUAL_TRAY_DEPUTY_ID = 254` (extruder 1); the id convention is `vt_id = 255 - extruder_id`, so
|
|
further extruders use 253, 252, … . The `vir_slot` parser recognises the 255/254 pair; the agent
|
|
filament descriptor path `DevFilaSystemParser::ParseAgentFilament` is the entry that stores a fuller
|
|
descending set in `vt_slot`.
|
|
|
|
## Mapping
|
|
|
|
Several subsystems relate AMS units, trays, and extruders, and more than one place derives a tray
|
|
index. The Bambu mapping path (`DevMappingUtil`) computes `ams_id * 4 + slot_id` and knows only the
|
|
Bambu unit kinds; `DevFilaSystem` provides the device model's own mapping below. The two agree for
|
|
Bambu units and diverge for a non-Bambu unit whose slot count is not four.
|
|
|
|
### AMS id to extruder id
|
|
|
|
`DevFilaSystem::GetExtruderIdByAmsId()` resolves an AMS id to the extruder it feeds:
|
|
|
|
- an AMS unit returns its bound extruder (`DevAms::GetExtruderId()`);
|
|
- a virtual tray returns `VIRTUAL_TRAY_MAIN_ID - vt_id`;
|
|
- Bambu's virtual AMS ids map to the main/deputy extruder.
|
|
|
|
A unit may be bound to more than one extruder when a `DevFilaSwitch` feeds it; the binding set
|
|
(`DevAms::m_binded_extruder_set`) and switch inlet are read by switch-aware code while the
|
|
single-extruder id stays pinned for legacy callers.
|
|
|
|
### Tray index
|
|
|
|
`DevFilaSystem::GetTrayIndexMap()` maps a global tray index to an `(ams_id, slot_id)` pair and is
|
|
used for backup-slot translation, calibration addressing, and the mapping popups.
|
|
|
|
- Bambu AMS: `ams_id * 4 + slot_id`; N3S: the AMS id; AMS-Lite-mixed (N9): `24 + slot_id`.
|
|
- Generic (non-Bambu): a running lane index — the count of trays in preceding units plus the slot
|
|
id. This depends on each unit's tray ids being 0-based and contiguous.
|
|
|
|
### Virtual-slot recognition
|
|
|
|
`devPrinterUtil::IsVirtualSlot()` decides whether an id denotes a virtual tray. On Bambu printers it
|
|
is the fixed pair `{255, 254}`. On generic printers it is the descending range
|
|
`[255 - count + 1, 255]`, where `count` is the selected printer preset's extruder count. The preset
|
|
is expected to match the connected device; when the two disagree the virtual range is wrong, which
|
|
is why a mismatch is reported rather than silently accepted.
|
|
|
|
## Device-control UI
|
|
|
|
The device-control widgets follow the same device-reported counts.
|
|
|
|
- `AMSModel` (`AMSItem.hpp`) mirrors `DevAmsType` numerically: `EXT_AMS`, `GENERIC_AMS`, `AMS_LITE`,
|
|
`N3F_AMS`, `N3S_AMS`. A plain `AMS` maps to `GENERIC_AMS`.
|
|
- On non-Bambu printers `use_generic_ams_layout()` routes `GENERIC_AMS` units through a variable-lane
|
|
rendering that draws one lane per reported tray; Bambu keeps its fixed four-slot rendering.
|
|
- `AMSControl` picks its layout by vendor: Bambu keeps the left/right presentation
|
|
(`CreateAmsSingleNozzle` for one toolhead, `CreateAmsDoubleNozzle` for two), while non-Bambu
|
|
printers use `CreateAmsMultiNozzle`, which builds one preview and one AMS simplebook per toolhead
|
|
for any count.
|
|
- The status panel's `ExtruderImage` renders one per-toolhead state per nozzle and is resized to
|
|
`GetTotalExtderCount()`. Non-Bambu printers choose the active toolhead through
|
|
`m_generic_nozzle_selector` (shown when more than one toolhead exists); Bambu keeps its left/right
|
|
`m_nozzle_btn_panel`. Nozzle temperature controls are created on demand per toolhead.
|
|
|
|
## Constraints
|
|
|
|
- **Single dialect.** The agent translates its protocol into this JSON; the model does not learn
|
|
vendor protocols. Extending the model with a new unit kind or field means extending the dialect.
|
|
- **Agent-produced counts.** Toolhead, AMS, and slot counts come from the device payload. Nothing
|
|
caps them at two toolheads or four slots except the Bambu contract described above.
|
|
- **Extruder dialect invariants.** `device.extruder.state`'s count field must equal the length of
|
|
`info[]`, and `info[]` must be ordered by extruder id with `id` equal to the array index: the
|
|
parser places toolheads by array position while `GetExtderById()` indexes by id.
|
|
- **Preset/device agreement.** The generic layout and virtual-slot range derive their count from the
|
|
selected printer preset. A printer preset must therefore declare the same number of
|
|
`nozzle_diameter` entries as the device has toolheads; a mismatch is surfaced because the sidebar
|
|
and extruder logic would otherwise disagree.
|
|
- **Bambu compatibility.** Bambu-specific presentation (left/right pair, fixed 4-slot units, fixed
|
|
virtual ids, physical/logical inversion) is preserved behind the vendor check. Generic paths do
|
|
not alter it.
|
|
- **Tray-index invariant.** The generic tray-index map is cumulative, so payload tray ids must be
|
|
unique, 0-based, and contiguous per unit. A malformed id is a producer error.
|
|
- **Protocol markers.** The dialect has no neutral equivalent for some Bambu state, so agents that
|
|
lack it send empty `print.cfg`, `print.fun`, `print.aux`, and `print.stat`. Their presence selects
|
|
the new-protocol parse path; they are compatibility markers, not feature switches.
|
|
|
|
## Main implementation locations
|
|
|
|
- [`DevExtruderSystem`](../../src/slic3r/GUI/DeviceCore/DevExtruderSystem.h) — toolhead model and the
|
|
`device.extruder` parser
|
|
- [`DevFilaSystem`](../../src/slic3r/GUI/DeviceCore/DevFilaSystem.h) — AMS/tray model, the AMS parser,
|
|
and the mapping helpers
|
|
- [`DevDefs.h`](../../src/slic3r/GUI/DeviceCore/DevDefs.h) — `DevAmsType`, extruder ids, and virtual
|
|
tray ids
|
|
- [`DeviceManager.cpp`](../../src/slic3r/GUI/DeviceManager.cpp) — message dispatch, `vir_slot`
|
|
parsing, and device state assembly
|
|
- [`AMSControl.cpp`](../../src/slic3r/GUI/Widgets/AMSControl.cpp) — per-toolhead AMS panes
|
|
(`CreateAmsMultiNozzle`) and the generic layout
|
|
- [`AMSItem.hpp`](../../src/slic3r/GUI/Widgets/AMSItem.hpp) — `AMSModel` and the variable-lane AMS
|
|
rendering
|
|
- [`StatusPanel.cpp`](../../src/slic3r/GUI/StatusPanel.cpp) — `ExtruderImage`, the toolhead selector,
|
|
and per-toolhead temperature controls
|
|
- [`MoonrakerPrinterAgent.cpp`](../../src/slic3r/Utils/MoonrakerPrinterAgent.cpp) — producer example
|
|
(AMS payload and the `device.extruder` encoding)
|