mirror of
https://github.com/OrcaSlicer/OrcaSlicer.git
synced 2026-08-05 09:07:39 +00:00
206 lines
11 KiB
Markdown
206 lines
11 KiB
Markdown
# Built-in printer agents
|
|
|
|
*Owns the per-vendor behavior of the built-in agents: what each subclass
|
|
changes and what it inherits unchanged. Defers the interface every agent
|
|
implements to [Architecture](architecture.md) and
|
|
[Python plugin agents](plugin-agents.md).*
|
|
|
|
This chapter covers the built-in Moonraker family: the general
|
|
`MoonrakerPrinterAgent` and the Qidi and Snapmaker variants. Creality
|
|
(`CrealityPrintAgent`) is also a member of this family and inherits the base
|
|
behavior, but has no section here; see the capability matrix for its
|
|
per-feature coverage. They share the same connection and status machinery.
|
|
Change the base class only when the behavior is valid for all of them.
|
|
|
|
Each subclass is thin. `MoonrakerPrinterAgent` holds the HTTP connection,
|
|
the WebSocket status subscription, the REST command worker, thumbnail
|
|
lookup, the chamber-light heuristic, and the upload-and-start path.
|
|
`QidiPrinterAgent` overrides filament discovery and adds multi-color box
|
|
mapping; `SnapmakerPrinterAgent` overrides filament discovery and camera
|
|
setup; `CrealityPrintAgent` overrides filament refresh. Each derives from
|
|
`MoonrakerPrinterAgent` and is `final`, which is why the guard rule below
|
|
must be type-based.
|
|
|
|
## Moonraker family
|
|
|
|
### Connection and commands
|
|
|
|
Moonraker-family agents use plain HTTP for the LAN connection. The connection
|
|
path deliberately ignores a TLS request because the supported printer stacks
|
|
serve Moonraker or a reverse proxy over HTTP. Restoring the caller's TLS
|
|
default can send a connection to an unavailable HTTPS endpoint.
|
|
|
|
Status is a Moonraker WebSocket subscription. Commands use REST. Command
|
|
translation happens immediately, but the resulting HTTP work runs through one
|
|
agent-owned FIFO worker. Each queued operation captures the current base URL
|
|
and API key before it is queued, so a later printer switch does not redirect
|
|
an earlier command. Keep this separation: network work on the UI path makes
|
|
controls feel stalled, and allowing a queued command to reread connection
|
|
state can send it to the wrong printer.
|
|
|
|
Pause, resume, and cancel use the dedicated Moonraker print endpoints. Do not
|
|
replace them with queued `PAUSE`, `RESUME`, or `CANCEL_PRINT` G-code. The
|
|
endpoints interrupt the print directly; a G-code command can wait behind the
|
|
active print or macro.
|
|
|
|
The request router accepts the Bambu-shaped JSON used by the native device
|
|
tab. Supply object-shaped namespaces such as `print` and `system`. A malformed
|
|
but parseable payload with a scalar where the router expects an object can
|
|
still fail before the unsupported-command fallback. The supported generic fan
|
|
status is the standard `fan` object, which represents the part fan only.
|
|
Ordinary part-fan control also works through the legacy `gcode_line` path,
|
|
which sends `M106` while `is_enable_np` is false. Auxiliary and chamber fans
|
|
are neither reported nor controlled.
|
|
|
|
Do not add `cfg`, `fun`, `aux`, and `stat` to the Moonraker status payload just
|
|
to make it look more complete. Together those fields set `is_enable_np` and
|
|
make the UI choose its structured fan and extruder commands instead. The
|
|
Moonraker agent does not translate those commands, so working controls become
|
|
unsupported no-ops. This is a UI-routing constraint, not a reason to expose
|
|
structured fan support.
|
|
|
|
### Status shown by the native device tab
|
|
|
|
The agent translates Moonraker status into the Bambu-shaped status payload the
|
|
existing Device tab understands. Some fields are necessarily synthetic:
|
|
|
|
- The virtual SD-card readiness bit and a basic software-version row make the
|
|
native UI consider the printer ready. Each pull payload also ensures
|
|
`m_push_count` and `m_full_msg_count` are at least one and refreshes
|
|
`last_push_time`. Together with the normal-storage state and a placeholder
|
|
module version, this satisfies the native `is_info_ready()` and printing
|
|
gates. These are compatibility scaffolding, not reports of physical storage
|
|
or OTA support.
|
|
- Current and total layers are emitted only when `print_stats.info` contains
|
|
numeric values. Moonraker may send `null`, and many profiles do not emit the
|
|
`SET_PRINT_STATS_INFO` data needed to populate them. Do not turn that gap
|
|
into a JSON conversion exception.
|
|
- Remaining time is estimated from elapsed print time and virtual-SD progress.
|
|
It is omitted below two percent progress because the early estimate is too
|
|
unstable. Do not derive an ETA by subtracting Moonraker duration counters:
|
|
both are elapsed counters, so their difference is overhead, not remaining
|
|
time.
|
|
- Temperature readings are available, but nozzle diameter and nozzle type are
|
|
not supplied in the status payload. The UI can therefore show an unknown
|
|
nozzle. Do not make print submission depend on those missing fields.
|
|
|
|
### Camera thumbnails and lights
|
|
|
|
For a running job, the agent asks Moonraker for thumbnails and chooses the
|
|
widest usable entry, rather than assuming the first entry is useful. It accepts
|
|
both thumbnail path spellings used by Moonraker versions, encodes each path
|
|
segment, and caches the result by filename. A failed transient lookup is tried
|
|
again only a bounded number of times; a clean response without a thumbnail is
|
|
cached as a negative result. The response shape handling is source-derived,
|
|
not hardware-verified.
|
|
|
|
> **Do not perform this HTTP lookup while holding `payload_mutex`.** The
|
|
> WebSocket thread builds the status payload under that mutex and the UI
|
|
> path also needs it, so a thumbnail timeout taken under the lock would
|
|
> stall status delivery or the UI. The lookup still blocks the WebSocket
|
|
> thread briefly, so move it to a worker if that becomes measurable.
|
|
|
|
Chamber-light control searches Moonraker objects for names that look like a
|
|
light or a standalone LED, then writes the first matching pin, LED, or macro.
|
|
The filter exists to avoid treating unrelated objects, such as a beeper, as a
|
|
lamp. It remains a heuristic. The incoming `led_node` is validated, but only
|
|
`chamber_light` is acted on; `chamber_light2` is deliberately ignored. A
|
|
printer with more than one lamp therefore has no reliable node-to-object map.
|
|
|
|
### Common maintenance limits
|
|
|
|
The same cache is reused for a selected agent ID, not per physical printer.
|
|
Qidi and Snapmaker inherit this behavior. A stateful feature added
|
|
to the base class must be reset carefully when a preset switches hosts.
|
|
|
|
> **Keep guards for this family type-based** - check whether an agent
|
|
> derives from `MoonrakerPrinterAgent` rather than comparing its ID to
|
|
> `moonraker`. An ID-based guard silently excludes Qidi, Snapmaker, and
|
|
> Creality, even though they share the base behavior.
|
|
|
|
The family has no generic implementation for firmware-specific AMS write
|
|
commands. Keep unsupported commands unsupported until the printer-side macro
|
|
or API is known. Reporting success for an untranslated command makes the
|
|
native UI claim that an action happened when it did not.
|
|
|
|
## Qidi
|
|
|
|
Qidi inherits the Moonraker connection, status, camera, and local-print path.
|
|
Its differences are Qidi filament discovery and the pre-print multi-color-box
|
|
mapping.
|
|
|
|
### Filament discovery
|
|
|
|
Discovery first reads the printer's device information to infer a Qidi series
|
|
identifier, then falls back to the configured Orca model if needed. Series
|
|
inference intentionally recognizes only a narrow set of known names. An
|
|
unknown model still produces usable generic filament data, but not a
|
|
series-specific preset identifier.
|
|
|
|
The agent reads a Qidi filament dictionary and the `save_variables` plus
|
|
slot-runout data. Failing to fetch the dictionary is non-fatal: slot discovery
|
|
continues with fallback material and colour values. Failing to fetch or parse
|
|
slot data is fatal to the refresh. A missing runout value means the agent
|
|
cannot prove filament is loaded, so it reports that slot as empty. This is an
|
|
ambiguity in the firmware data, not proof that the box is empty.
|
|
|
|
`save_variables.variables` must be an object. Qidi firmware can return `null`
|
|
there, and generic JSON value access can throw on a present null. The parser
|
|
rejects that shape without throwing. Preserve the null-slot tests whenever the
|
|
response parser changes.
|
|
|
|
### Multi-color mapping before a print
|
|
|
|
Before every Qidi print-start wrapper, the agent writes `enable_box` and, for
|
|
mapped tools, persistent `value_t<tool>` variables. These writes survive the
|
|
job. Invalid mapping JSON is checked only after `enable_box` has been written.
|
|
When the mapping is enabled, that failure can therefore leave `enable_box=1`.
|
|
There is no rollback for this or for a later per-tool write failure, so a
|
|
partial mapping can remain on the printer. An empty mapping is accepted when
|
|
the box is enabled. Single-colour jobs disable the box but leave old per-tool
|
|
assignments in place.
|
|
|
|
`enable_box` currently follows `task_use_ams`. That meaning has not been
|
|
verified against all Qidi firmware: if firmware treats it as "a box exists"
|
|
rather than "use the box for this job", this gate is wrong and needs hardware
|
|
evidence before it changes.
|
|
|
|
Only `start_local_print` reaches Moonraker's real upload-and-start path. The
|
|
other Qidi mapping wrappers currently return success stubs after applying the
|
|
mapping. Do not describe those wrappers as confirmed print paths.
|
|
|
|
Because the agent cache is keyed by agent type, a Qidi mapping can also become
|
|
stale when switching between Qidi printers. This is a generic Moonraker-family
|
|
state risk, made more consequential by Qidi's persistent firmware variables.
|
|
The configured `printer_type` can also be stale, so treat it as a fallback
|
|
hint rather than device truth.
|
|
|
|
## Snapmaker
|
|
|
|
Snapmaker uses the Moonraker base and overrides filament discovery and camera
|
|
setup. Neither path is hardware-verified in the current documentation set.
|
|
|
|
Filament information comes from parallel arrays in `print_task_config`.
|
|
`filament_exist` defines the number of slots; shorter type, subtype, colour,
|
|
vendor, or NFC arrays use safe fallback values. The agent first tries a visible
|
|
vendor, type, and colour preset, then a visible type match, and finally a
|
|
generic identifier when no preset bundle is available. An empty reported type
|
|
is changed to `PLA`, so an unknown occupied spool can look like confirmed PLA.
|
|
An unrecognized type can also reach the visible-preset fallback and be paired
|
|
with an unrelated visible preset. Treat the resulting preset as a suggestion,
|
|
not printer-ground truth.
|
|
|
|
Snapmaker U1 camera support starts the printer's monitor RPC, then serves the
|
|
still JPEG through a small local HTML page that reloads it after each load or
|
|
error. The wrapper is required because a direct still-image URL looks frozen.
|
|
The RPC is sent from a detached thread so the UI timer does not block on socket
|
|
I/O. That thread captures `this` directly, so agent destruction can race with
|
|
the camera command. Do not widen this pattern. Route future asynchronous work
|
|
through owned lifetime-managed work where possible.
|
|
|
|
## Source locations
|
|
|
|
- `src/slic3r/Utils/MoonrakerPrinterAgent.cpp`
|
|
- `src/slic3r/Utils/QidiPrinterAgent.cpp`
|
|
- `src/slic3r/Utils/SnapmakerPrinterAgent.cpp`
|