Commit Graph
19018 Commits
Author SHA1 Message Date
Lam Wei Lun 37b7e70490 Merge impl branch 2026-09-24 14:46:30 +08:00
Lam Wei Lun aa934409a7 feat(orca-agent): integrate OrcaSonar AMS synchronization
- Translate slicer AMS commands to OrcaSonar’s canonical write format
- Gate AMS operations using printer capabilities
- Refresh capabilities when filament topology becomes available
- Apply virtual-slot removals only from full status snapshots
- Add synchronization tests and document the integration
2026-09-24 12:34:51 +08:00
peachismomo 6da938b660 fix: compilation after merge 2026-09-24 01:15:55 +08:00
peachismomo 2dcdf49185 Merge branch 'feat/printer-agent-infra' into feat/printer-agent-impl 2026-09-24 01:02:59 +08:00
peachismomo 52e845a821 fix: add include for non BBL_RELEASE_TO_PUBLIC path in BBLPrinterAgent 2026-09-24 00:23:47 +08:00
Ian Chua ffb4813510 fix: preserve moonraker device names 2026-09-23 20:17:06 +08:00
Ian Chua 7436f68e09 fix: make moonraker gcode commands asynchronous 2026-09-23 20:15:52 +08:00
Ian Chua ad956591ce fix: use correct device id for Moonraker connections 2026-09-23 20:14:21 +08:00
Ian Chua 8da8e9bee1 fix: persist input printer host and port 2026-09-23 20:12:18 +08:00
Ian Chua 0acc8608b1 refactor: make printer connection SSL agent-specific 2026-09-23 20:11:47 +08:00
Ian Chua a2a9b2ce94 refactor: collapse command_ams_refresh_rfid and command_ams_refresh_rfid2 2026-09-23 18:30:05 +08:00
Lam Wei Lun 4cc23e6492 fix: send the AMS tray's own coordinates on tray selection
command_ams_select_tray split the BBL tray id into ams_id/slot_id inline and untested. Extract build_ams_change_filament_body so the mapping is a named unit that command_ams_select_tray routes verbatim, and lock it with a test: tray 9 (a 6-slot box's slot 5) sends (2, 1), never a fabricated flat lane.
2026-09-23 17:00:39 +08:00
Ian Chua 07bee6c7a4 Merge branch 'feat/printer-agent-infra' into feat/printer-agent-impl 2026-09-23 15:35:22 +08:00
Lam Wei Lun 6849979d60 Initial implementation of filament mapping on OrcaSonar side 2026-09-23 15:34:02 +08:00
Ian Chua 1615390182 Merge branch 'main' into feat/printer-agent-infra 2026-09-23 14:54:38 +08:00
Ian Chua 309bb3c6e2 Merge branch 'feat/printer-agent-infra' into feat/printer-agent-impl 2026-09-23 14:53:49 +08:00
Ian Chua 79f20c3996 Merge branch 'feat/printer-agent-infra' into refactor/connect-printer-api 2026-09-23 14:48:33 +08:00
Ian Chua 33179c1902 fix: make integration tests headless 2026-09-23 14:29:57 +08:00
Ian Chua 82b860480f feat: initial draft of lifecycle events API for plugins (#15293)
# Description

This PR introduces lifecycle events to the plugin API. 
For all plugin capabilities, you can define a `on_lifecycle_event`
function in the plugin that takes in a event enum and a small payload
for some generic information on the lifecycle event.
The idea is to keep the payload generic and small, and if you want to
get more information, you should invoke other more targeted APIs to get
more information.
For example, lets say you are keeping track of the the `ObjectAdded*`
event hook for model transformation, addition or deletion. The payload
would tell you the name of the model, and you should use a targeted API
such as `orca.host.plater().model()` to get more information on the
model. This is the overall design principle of the API.

Currently the lifecycle events are the following:
```cpp
    enum class LifecycleEvent {
        // Project (3mf)
        NewProject,
        ProjectOpened,
        ProjectBeforeSave,
        ProjectAfterSave,
        ProjectClosed,
        ProjectDirtyChanged,

        // Slicing pipeline
        SliceStarted,
        SliceGeometryFinished,
        GCodeExportStarted,
        GCodeExportFinished,
        SlicingJobComplete,

        // Plate/model editing
        ObjectAdded,
        ObjectDeleted,
        ObjectTransformed,
        ObjectChanged,
        ObjectRenamed,
        PlateCreated,
        PlateDeleted,
        PlateSelected,
        PlateRenamed,

        // Preset
        PresetSelected,
        PresetSaved,

        // Printer/device
        PrintStateChanged,
        DeviceOnlineChanged,
        DeviceDiscovered,
        DeviceSelected,
        DeviceConnected,
        DeviceDisconnected,
        UploadStarted,
        UploadFinished,

        // Print/send jobs
        PrintJobStarted,
        PrintJobFinished,
        SendJobStarted,
        SendJobFinished,
    };
```

This is an initial draft and lifecycle events can be included later on.

[orca_telegram_notifier_plugin_any.py](https://github.com/user-attachments/files/31220973/orca_telegram_notifier_plugin_any.py)

If you're familiar with telegram bots, after you install the telegram
bot, in the config of this plugin, you can enter the Bot ID and the Chat
ID with said bot.

# Screenshots/Recordings/Graphs

<!--
> Please attach relevant screenshots to showcase the UI changes.
> Please attach images that can help explain the changes.
-->

## Tests

<!--
> Please describe the tests that you have conducted to verify the
changes made in this PR.
-->

For this plugin, I am testing it with a telegram bot that sends me a
message on lifecycle event.


<!--
> A guide for users on how to download the artifacts from this PR.
-->

[How to Download Pull Requests Artifacts for
Testing](https://www.orcaslicer.com/wiki/how_to_download_pr_artifacts)
2026-09-23 12:52:08 +08:00
HanifKoh 89dc4e1b99 Add an Align to Y Axis Option to the CLI Arrange (#15836)
The CLI turned "align to Y axis" on for every i3 printer with no way to opt
out. With rotations forbidden the pre-rotation is the result, so every object
ends up turned 90 degrees from how it was loaded. The GUI defaults the
checkbox the same way for i3 printers, but lets the user untick it.

Add --align-to-y-axis. When it is not given the printer-structure rule still
applies, so existing calls are unchanged; the CLI's own options are filled
with defaults after parsing, so the keys the user typed are remembered to
tell the two apart.
2026-09-23 12:43:08 +08:00
HanifKoh 12670a6e40 Answer the Preview's Per-Frame Lookups From Cached Sums and Draw Segments From an Index Buffer (#15833)
* Answer the Preview's Per-Frame Time Query From a Cached Sum

The G-code preview's cost is linear in the number of toolpath vertices, and on a
tall multi-filament print the wipe tower dominates that count: it emits a roughly
constant 160-180 moves on every layer whatever the object is, measured at 57-61%
of all moves on a three-filament print.

Four places scanned or allocated across the whole vertex array. None of them
needed to.

get_estimated_time_at re-accumulated the estimated time from vertex 0 on every
call, and its caller is the tool marker tooltip, which ImGui re-renders every
frame while the properties panel is unfolded. It now starts from a running sum
kept at each layer's first vertex, built at load in vertex order, and adds only
that layer's vertices: the same additions in the same order, so the float result
is unchanged, at a cost of one float per layer and time mode rather than per
vertex. At the 351k vertices of a 636-layer test print the call scanned the whole
print (238us); it now scans one layer.

update_view_full_range walked from vertex 0 to find where the layer range starts,
on every slider tick. It now starts at the first vertex of that layer. The index
is derived from the vertices rather than from Layers::Item::range, because
Layers::update folds a vertex whose layer_id arrives out of order into whichever
bucket is open, which makes that range the wrong answer in general; the index
costs four bytes per layer, not per vertex.

update_colors_texture allocated one float per vertex of the whole print on every
slider tick. It now reuses a buffer.

render_legend fetched the layer Zs and the per-layer times from inside loops over
the custom G-code items, and built whole vectors only to test them for emptiness.
The times are hoisted, the Zs are built lazily so a print with no colour change
does not pay for them at all, and the emptiness tests use the existing counters.

No rendering behaviour changes.

* Draw the Preview's Toolpath Segments From an Index Buffer

The preview's frame cost is dominated by one call: a single instanced draw of
every visible toolpath segment. On a tall multi-filament print the wipe tower
supplies most of those segments, which is why the preview of a large tower is
slow and why shrinking the layer range speeds it up again.

That draw is not fill bound. Shrinking the model to about a fortieth of its
screen area moved the frame from 419 ms to 401 ms, so the cost is per segment,
not per pixel, and it is paid in the vertex shader: five texelFetch calls plus
several cross/normalize per invocation.

Each segment is a box of eight corners, but it was submitted with
glDrawArraysInstanced over a 24 entry array, so every corner was transformed
once per triangle that touches it and the shader ran 24 times per segment. The
same 24 entries are now an element buffer over the eight distinct corners, which
lets the post-transform cache reuse them and drops the shader to 8 runs per
segment. The triangles, their winding and the vertex_id each corner receives are
unchanged.

Measured over 100 frames on the 636-layer, 351k-vertex three-filament fixture,
the segment draw goes from 381 ms to 322 ms per frame. That is a software
rasterizer, where triangle setup dominates and understates the win; the drop in
shader invocations is the transferable part.

Verified by loading the same project in this build and in a build of the parent
commit and comparing the canvas across three states - the default view, a
rotated camera, and a reduced layer range: pixel identical in all three. The
rotated case matters because the shader picks its corner offsets from the camera
direction. The only pixels that differ anywhere on screen are in the G-code text
panel, which prints a per-process object id that varies between any two runs.
2026-09-23 12:06:02 +08:00
Ian Bassi e71497738f Configurable default G-code preview view type (#15769) 2026-09-22 20:50:59 -03:00
Ian BassiandRodrigo Faselli d820303a3f Improve preview colors (#15809)
Co-authored-by: Rodrigo Faselli <162915171+RF47@users.noreply.github.com>
2026-09-22 20:50:18 -03:00
peachismomo ccaafb3003 tests: cover WebMediaController lifecycle with wxWebView stub 2026-09-23 03:50:35 +08:00
Ian Chua 01fcd71aa3 Merge branch 'main' into feat/plugin-lifecycle-evts 2026-09-23 03:16:07 +08:00
peachismomo 9851874742 fix: fire PrintJobStarted only after preflight validation 2026-09-23 03:12:14 +08:00
peachismomo ff96cce7c3 fix: update lifecycle contract for printer connection 2026-09-23 03:09:03 +08:00
peachismomo c492436228 refactor: centralize printer compatibility checks 2026-09-23 03:06:15 +08:00
peachismomo 99a1cd1b74 chore: reduce diagnostic logging level to trace 2026-09-23 02:58:30 +08:00
peachismomo 4b41bf7f4f fix: clear webview document on stop 2026-09-23 02:57:56 +08:00
peachismomo d5962052ca fix: handle malformed printer progress values safely 2026-09-23 02:53:10 +08:00
peachismomo 4a4c649dbb fix: dispatch task lifecycle events on one worktre thread 2026-09-23 02:51:35 +08:00
peachismomo f1410c8cac feat: extend optional printer model warnings to calibration & ams workflows 2026-09-23 02:49:27 +08:00
peachismomo fb825acbd5 fix: use original project name 2026-09-23 02:46:44 +08:00
peachismomo 7283a05040 fix: validate windows FFmpeg avformat library 2026-09-23 02:33:47 +08:00
peachismomo 24ddd81d7d feat: stop lifecycle dispatch after slicing cancellation 2026-09-23 02:31:46 +08:00
peachismomo 0fb8f3d487 fix: synchronize lifecycle hook shutdown with active dispatches 2026-09-23 02:14:56 +08:00
peachismomo 06f7016d6a fix: preserve printer agent defaults in PrinterAgentPluginCapabilityTrampoline 2026-09-23 01:53:56 +08:00
peachismomo be9ae6ffbf fix(tests): make omitted printer agent operations answer like a missing agent 2026-09-23 00:35:33 +08:00
Ian Chua cda25c3715 refactor: connect_printer api and dialog 2026-09-22 21:54:10 +08:00
Ian Chua 2dcdfe2d32 fix: refresh token before configuring printer MQTT 2026-09-22 21:46:09 +08:00
Ian Chua 9e34ca536b fix: port BBL implementations from #15711 2026-09-22 21:11:03 +08:00
Ian Chua 201617a6c0 fix: guard DeviceManager command dispatch when no printer agent is bound 2026-09-22 21:09:58 +08:00
Ian Chua 2dcbee3d76 Merge branch 'main' into feat/printer-agent-infra 2026-09-22 20:55:29 +08:00
Ian Chua 6c353d9596 feat: add ssl compatibility with moonrakerprinteragent 2026-09-22 18:30:30 +08:00
HanifKoh 19e094a1d1 Move the Cube with the Wipe Tower in the Profile Validator (#15799)
The slice check centres its cube on the bed, puts the prime tower beside
it, then pulls the tower alone inside the printable outline. On a bed too
narrow for the estimated footprint that pull drags the tower back over
the cube: Volumic EXO42 IDRE MIRROR MODE (189 mm wide, 87.6 mm estimate)
logged "gcode path conflicts found between WipeTower and cube" in every
run, and three ~105 mm beds were left with 0.15 to 3.3 mm of clearance.

The cube and the tower's footprint are now pulled inside as one rigid
pair, so the clearance between them is fixed by construction. A bed too
small for the pair keeps the old placement, and presets that were never
clamped keep their exact layout.
2026-09-22 18:22:42 +08:00
Lam Wei Lun 29b9076440 Capitalize Open Speed Dial 2026-09-22 17:07:13 +08:00
Ian Chua 323cd3afe1 Keep Python Printer Agent Exceptions Out of the Host (#15819)
# Description
<!--
> Please provide a summary of the changes made in this PR. Include
details such as:
  > * What issue does this PR address or fix?
  > * What new features or enhancements does this PR introduce?
> * Are there any breaking changes or dependencies that need to be
considered?
-->

A Python printer agent plugin could take the host down, and one of its
operations could never report its result. This PR fixes both in
`PrinterAgentPluginCapabilityTrampoline.hpp`.

## Changes

### A faulty printer agent no longer throws into the GUI

`IPrinterAgent` reports failure through return values, and none of its
callers catch. A Python `raise`, a missing override or a wrongly typed
return from a printer agent plugin therefore escaped the trampoline as a
C++ exception.

Every trampoline operation now catches, logs `Printer agent plugin
'<key>': <operation> failed: <error>`, and answers with what
`NetworkAgent` returns when no printer agent is set. `BBLPrinterAgent`
returns the same values when the Bambu plug-in is unavailable:

- `-1` for every `int` status code
- `false` for `start_discovery` and `fetch_filament_info`
- `""` for `get_user_selected_machine`
- an empty `AgentInfo` for `get_agent_info` (registration already
rejects an empty agent ID)
- `FilamentSyncMode::none` for `get_filament_sync_mode`

`ORCA_PY_AGENT_OVERRIDE(ret, name, ...)` derives the fallback from the
return type through `printer_agent_failure<ret>()`, so the call sites
carry no fallback values of their own.

An exception is the safety net for plugin bugs, not an error channel. A
plugin reports an expected failure by returning a code, as the Bambu
plug-in does. A raise is logged as a failure and collapses to the
generic `-1`, so the GUI shows the generic message instead of the
specific one (`-18` cancelled, `-4020` FTP upload failed, …).

### `bind_detect` results now reach the host

`detect` is an out-parameter (`detectResult&`). pybind11 casts a
reference argument to an override with a copy, so a plugin that filled
in `detect` wrote to a throwaway object and the host always saw an empty
`detectResult`. It is now passed so that Python edits the caller's
struct. Plugins see the same `DetectResult` argument as before.

## TODO

- Expose the `BAMBU_NETWORK_*` return codes to Python (the
`orca.printer_agent` binding and the generated stub from
`scripts/generate_orca_python_stubs.py`). Plugins can already return
them, but only as hard-coded numbers.

# Screenshots/Recordings/Graphs

<!--
> Please attach relevant screenshots to showcase the UI changes.
> Please attach images that can help explain the changes.
-->

## Tests

<!--
> Please describe the tests that you have conducted to verify the
changes made in this PR.
-->

- New `tests/slic3rutils/test_plugin_printer_agent.cpp`: an agent whose
operations raise, one that omits them, and one that returns the wrong
type all answer like a missing agent, and the interpreter stays usable.
A working agent's answers reach the host unchanged, including
`request_bind_ticket`'s out-param and the fields a plugin writes into
`bind_detect`'s `detect`.
- The `bind_detect` check failed before the fix (`"" == "192.168.0.2"`)
and passes after.
- `slic3rutils` passes under `ctest` (144/144); full Release build clean
on Linux.
- End to end on Linux with a test plugin whose chosen operations raise
(`start_discovery`, `get_filament_sync_mode`, `disconnect_printer`):
selecting the plugin's agent in the printer preset and switching back
logged each raise as a `Printer agent plugin '…': <operation> failed`
line, and the app kept running and closed cleanly (exit 0). Without the
guard, the first raise (`start_discovery`, on selecting the agent) ended
the app with `Uncaught exception` and SIGABRT (exit 134); that run used
a build whose printer-agent files are identical to `main`.

<!--
> A guide for users on how to download the artifacts from this PR.
-->

[How to Download Pull Requests Artifacts for
Testing](https://www.orcaslicer.com/wiki/how_to_download_pr_artifacts)
2026-09-22 16:57:55 +08:00
Ian Chua ce0c759ded fix: snapmakerprinteragent use moonraker's lifecycle-owned command worker 2026-09-22 16:06:18 +08:00
Lam Wei Lun a9d145cab9 Merge branch 'feat/printer-agent-impl' into feat/orca_printer_agent_ams_sync 2026-09-22 15:54:35 +08:00