Compare commits

..
185 changed files with 2480 additions and 6456 deletions
-4
View File
@@ -58,9 +58,6 @@ Paths below are relative to this skill. Commands run from the repository root.
9. **Run the full profile checks before reporting completion.** A vendor-scoped pass is only a
development loop. Review also covers version bumps, assets, non-default processes and hardware
tuning that CI cannot establish.
10. **One all-printer preset per product; color is a runtime property, never a preset.** Never ship
presets that differ only by color — CI accepts them, so this is a review call. See
[color is a runtime property](references/filament-profiles.md#color-is-a-runtime-property).
## Creating or modifying a profile
@@ -110,7 +107,6 @@ Paths below are relative to this skill. Commands run from the repository root.
| A setting has no effect | Key spelling/type, `handle_legacy`, or a config key placed on a `machine_model` |
| A preset exists but is not selectable | Index registration, `instantiation`, installation and compatibility |
| A filament is missing, duplicated, or matches the wrong spool | [Compatibility and alias shadowing](references/filament-profiles.md#compatible_printers); [ids](references/ids.md) |
| Presets differ only by color, or an all-printer library preset lacks `@System` | [Color is a runtime property](references/filament-profiles.md#color-is-a-runtime-property) |
| A bed temperature is ignored | [Plate-specific temperature keys](references/filament-profiles.md#bed-temperature-is-twelve-keys-not-one) |
| A change is absent from the running app | Version bump and [installed profile location](references/validation.md#testing-in-the-app) |
| A check fails | [Error → remedy](references/validation.md#error--remedy) |
@@ -52,15 +52,6 @@ a brand means adding a folder here; the folder name is a directory label only
collection, so there is no duplicate-name error.
- You may inherit from an instantiated preset as well as from a base; it is common.
## Color is a runtime property
`filament_id` identifies a product, not a color; filament sync/AMS reads the color from the spool at
runtime. A product ships one all-printer preset and the color is chosen at runtime — never a sibling
preset that differs only by color. A material family (PLA vs PLA Matte vs PLA Silk) is a new product; a
color is not. A printer tune keeps the product alias and does not multiply per color either.
CI does not catch this — per-color presets pass `check` — so it is a review call.
## The two most common contributions
**A printer vendor tuning a generic.** Keep the `Generic X` base name so the alias shadows the library
@@ -46,17 +46,12 @@ to the first `@`; the target half is a label except for reserved forms:
- `@base` — a non-instantiated product root. `@base` is convention; a base is really identified by
`instantiation: "false"` and no `setting_id` ([the three-part shape](filament-profiles.md#the-three-part-shape)).
- `@System` — the OrcaFilamentLibrary selectable shim, and the convention for an all-printer product
(`<Product> @System`, empty `compatible_printers`); not enforced, so a deviation is worth a review
comment. The literal `Generic <mat> @System` is load-bearing for 3MF/project recovery, beyond the
alias rule ([alias shadowing](filament-profiles.md#alias-shadowing)).
- `@System` — the OrcaFilamentLibrary selectable shim. The literal `Generic <mat> @System` is
load-bearing for 3MF/project recovery, beyond the alias rule ([alias shadowing](filament-profiles.md#alias-shadowing)).
- `@<Vendor>`, `@<Vendor> <Model>`, `@<Vendor> <Model> <nozzle> nozzle` — printer tunes, BBL's shape.
Other vendors differ (a bare model, a printer serial, Creality's `@<Model>-all`). Specificity is judged
from `compatible_printers`, not the name
([one variant, one profile](filament-profiles.md#overlapping-coverage-one-variant-one-profile-per-product)).
- Color is not part of the product name: `<Product> <Color>` presets are not authored; the color is
chosen at runtime
([color is a runtime property](filament-profiles.md#color-is-a-runtime-property)).
## Not the same as the filename
@@ -15,7 +15,6 @@ The table highlights gaps that need human review. What CI *does* run:
| Whether the intended default survived compatibility selection | The sweep can select a different compatible preset |
| A dangling `compatible_printers` inside an `instantiation: "false"` base | A base never becomes a `Preset`, so the reference check never sees it (a bad `inherits` in a base *is* caught) |
| A `renamed_from` whose old name is still a live preset | The redirect is inert while a live preset carries that name |
| A preset differentiated only by color, or an all-printer library preset without `@System` | Per-color presets split one product across ids and the selector fills with near-duplicates; CI stays green |
| Per-extruder vector length on a multi-nozzle printer | Silently padded (with the **first** value) or truncated |
## 1. Was the vendor `version` bumped?
+2 -42
View File
@@ -826,37 +826,6 @@ find_package(OpenSSL REQUIRED)
find_package(CURL REQUIRED)
find_package(Freetype REQUIRED)
if (SLIC3R_GUI)
# LibDataChannel's installed export references its bundled dependencies,
# but does not install their CMake targets. Recreate those targets from
# the same dependency prefix before loading the LibDataChannel config.
if (NOT TARGET Usrsctp::usrsctp)
find_library(_ORCA_USRSCTP_LIBRARY NAMES usrsctp
PATHS "${CMAKE_PREFIX_PATH}/lib" NO_DEFAULT_PATH)
if (_ORCA_USRSCTP_LIBRARY)
add_library(Usrsctp::usrsctp UNKNOWN IMPORTED GLOBAL)
set_target_properties(Usrsctp::usrsctp PROPERTIES
IMPORTED_LOCATION "${_ORCA_USRSCTP_LIBRARY}"
IMPORTED_LINK_INTERFACE_LANGUAGES C
INTERFACE_LINK_LIBRARIES "Threads::Threads")
endif()
endif()
if (NOT TARGET LibJuice::LibJuice)
find_library(_ORCA_LIBJUICE_LIBRARY NAMES juice
PATHS "${CMAKE_PREFIX_PATH}/lib" NO_DEFAULT_PATH)
if (_ORCA_LIBJUICE_LIBRARY)
add_library(LibJuice::LibJuice UNKNOWN IMPORTED GLOBAL)
set_target_properties(LibJuice::LibJuice PROPERTIES
IMPORTED_LOCATION "${_ORCA_LIBJUICE_LIBRARY}"
IMPORTED_LINK_INTERFACE_LANGUAGES C
INTERFACE_LINK_LIBRARIES "Threads::Threads")
endif()
endif()
find_package(LibDataChannel CONFIG REQUIRED)
endif()
add_library(libcurl INTERFACE)
target_link_libraries(libcurl INTERFACE CURL::libcurl)
@@ -1137,7 +1106,6 @@ function(orcaslicer_copy_dlls target config postfix output_dlls)
endif ()
file(COPY ${_occt_dlls}
${CMAKE_PREFIX_PATH}/bin/freetype.dll
${CMAKE_PREFIX_PATH}/bin/avformat-61.dll
${CMAKE_PREFIX_PATH}/bin/avcodec-61.dll
${CMAKE_PREFIX_PATH}/bin/swresample-5.dll
${CMAKE_PREFIX_PATH}/bin/swscale-8.dll
@@ -1150,11 +1118,11 @@ function(orcaslicer_copy_dlls target config postfix output_dlls)
${_out_dir}/WebView2Loader.dll
${_out_dir}/freetype.dll
${_out_dir}/avformat-61.dll
${_out_dir}/avcodec-61.dll
${_out_dir}/swresample-5.dll
${_out_dir}/swscale-8.dll
${_out_dir}/avutil-59.dll
PARENT_SCOPE
)
list(APPEND _dll_list ${_occt_staged})
set(${output_dlls} ${_dll_list} PARENT_SCOPE)
@@ -1174,10 +1142,7 @@ function(orcaslicer_copy_sos target config postfix output_sos)
set(_out_dir "${CMAKE_CURRENT_BINARY_DIR}")
endif ()
file(COPY ${CMAKE_PREFIX_PATH}/lib/libavformat.so
${CMAKE_PREFIX_PATH}/lib/libavformat.so.61
${CMAKE_PREFIX_PATH}/lib/libavformat.so.61.1.100
${CMAKE_PREFIX_PATH}/lib/libavcodec.so
file(COPY ${CMAKE_PREFIX_PATH}/lib/libavcodec.so
${CMAKE_PREFIX_PATH}/lib/libavcodec.so.61
${CMAKE_PREFIX_PATH}/lib/libavcodec.so.61.3.100
${CMAKE_PREFIX_PATH}/lib/libavutil.so
@@ -1192,9 +1157,6 @@ function(orcaslicer_copy_sos target config postfix output_sos)
DESTINATION ${_out_dir})
set(${output_sos}
${_out_dir}/libavformat.so
${_out_dir}/libavformat.so.61
${_out_dir}/libavformat.so.61.1.100
${_out_dir}/libavcodec.so
${_out_dir}/libavcodec.so.61
${_out_dir}/libavcodec.so.61.3.100
@@ -1324,8 +1286,6 @@ endif ()
if (CMAKE_SYSTEM_NAME STREQUAL "Linux")
set(LIBRARY_FILES
${LIBDIR_BIN}/libavformat.so.61
${LIBDIR_BIN}/libavformat.so.61.1.100
${LIBDIR_BIN}/libavcodec.so.61
${LIBDIR_BIN}/libavcodec.so.61.3.100
${LIBDIR_BIN}/libavutil.so.59
+11 -23
View File
@@ -156,7 +156,7 @@ if (NOT _is_multi AND NOT CMAKE_BUILD_TYPE)
endif ()
function(orcaslicer_add_cmake_project projectname)
cmake_parse_arguments(P_ARGS "FORWARD_CONFIG" "INSTALL_DIR;BUILD_COMMAND;INSTALL_COMMAND;SOURCE_DIR" "CMAKE_ARGS" ${ARGN})
cmake_parse_arguments(P_ARGS "FORWARD_CONFIG" "INSTALL_DIR;BUILD_COMMAND;INSTALL_COMMAND" "CMAKE_ARGS" ${ARGN})
# MSVC is true for clang-cl as well, so the sub-build toolchain has to key on the
# generator. A non-Visual-Studio superbuild passes its own generator down, and with
@@ -202,18 +202,12 @@ function(orcaslicer_add_cmake_project projectname)
set(_build_j "-j${NPROC}")
endif ()
set(_source_dir_arg "")
if (P_ARGS_SOURCE_DIR)
set(_source_dir_arg SOURCE_DIR ${P_ARGS_SOURCE_DIR})
endif ()
if (NOT IS_CROSS_COMPILE OR NOT APPLE)
ExternalProject_Add(
dep_${projectname}
EXCLUDE_FROM_ALL ON
INSTALL_DIR ${DESTDIR}
DOWNLOAD_DIR ${DEP_DOWNLOAD_DIR}/${projectname}
${_source_dir_arg}
${_gen}
CMAKE_ARGS
-DCMAKE_POLICY_VERSION_MINIMUM=3.5
@@ -246,14 +240,12 @@ if (NOT IS_CROSS_COMPILE OR NOT APPLE)
# note for future devs: shared libs may actually create a size reduction
# but orcaslicer_deps tends to get really funny regarding linking after that (notably boost)
# so, as much as I would like to use that, it's not happening
if (NOT P_ARGS_SOURCE_DIR)
ExternalProject_Add_Step(dep_${projectname} free_download_space
DEPENDEES download # do after download
COMMENT "Freeing Space: Removing source archive"
WORKING_DIRECTORY ${DEP_DOWNLOAD_DIR}
COMMAND ${CMAKE_COMMAND} -E rm -rf ${projectname}
)
endif ()
ExternalProject_Add_Step(dep_${projectname} free_download_space
DEPENDEES download # do after download
COMMENT "Freeing Space: Removing source archive"
WORKING_DIRECTORY ${DEP_DOWNLOAD_DIR}
COMMAND ${CMAKE_COMMAND} -E rm -r ${projectname}
)
ExternalProject_Add_Step(dep_${projectname} free_build_space
DEPENDEES install # do after install
COMMENT "Freeing Space: Removing source and build files"
@@ -267,7 +259,6 @@ else()
EXCLUDE_FROM_ALL ON
INSTALL_DIR ${DESTDIR}
DOWNLOAD_DIR ${DEP_DOWNLOAD_DIR}/${projectname}
${_source_dir_arg}
${_gen}
CMAKE_ARGS
-DCMAKE_POLICY_VERSION_MINIMUM=3.5
@@ -395,6 +386,10 @@ include(libnoise/libnoise.cmake)
include(Draco/Draco.cmake)
include(FFMPEG/FFMPEG.cmake)
include(Assimp/Assimp.cmake)
# I *think* 1.1 is used for *just* md5 hashing?
# 3.1 has everything in the right place, but the md5 funcs used are deprecated
# a grep across the repo shows it is used for other things
@@ -405,12 +400,6 @@ if(NOT OPENSSL_FOUND)
set(OPENSSL_PKG dep_OpenSSL)
endif()
include(FFMPEG/FFMPEG.cmake)
include(Assimp/Assimp.cmake)
include(DataChannel/DataChannel.cmake)
set(DATACHANNEL_PKG dep_DataChannel)
# we don't want to load a "wrong" openssl when loading curl
# so, just don't even bother
# ...i think this is how it works? change if wrong
@@ -484,7 +473,6 @@ set(_dep_list
dep_wxInspector
dep_FFMPEG
dep_Assimp
${DATACHANNEL_PKG}
)
if (MSVC)
-37
View File
@@ -1,37 +0,0 @@
# libdatachannel is the native ICE/DTLS/SCTP implementation used by the
# GUI WebRTC camera controller. Keep the source revision fixed: the signaling
# protocol is evolving independently of this transport dependency.
#
# It vendors plog, usrsctp and libjuice as git submodules, which a plain
# GitHub tag tarball does not include. The flatpak sandbox has no network
# access during the build, so there the manifest itself clones the repo
# (submodules and all) into the dependency download directory before the
# sandbox closes. ExternalProject_Add is pointed at that existing checkout
# instead of being given its own network-dependent download method.
if (FLATPAK)
set(_datachannel_source
SOURCE_DIR ${DEP_DOWNLOAD_DIR}/DataChannel
)
else()
set(_datachannel_source
GIT_REPOSITORY https://github.com/paullouisageneau/libdatachannel.git
GIT_TAG v0.24.5
GIT_SHALLOW ON
GIT_SUBMODULES_RECURSE ON
)
endif()
orcaslicer_add_cmake_project(DataChannel
DEPENDS ${OPENSSL_PKG}
CMAKE_ARGS
-DNO_EXAMPLES=ON
-DNO_TESTS=ON
-DNO_WEBSOCKET=ON
-DNO_MEDIA=ON
-DUSE_NICE=OFF
-DUSE_SYSTEM_JUICE=OFF
-DUSE_SYSTEM_USRSCTP=OFF
-DOPENSSL_ROOT_DIR:PATH=${DESTDIR}
-DOPENSSL_USE_STATIC_LIBS=ON
${_datachannel_source}
)
+7 -23
View File
@@ -1,26 +1,14 @@
set(_conf_cmd ./configure)
set(_ffmpeg_depends)
set(_ffmpeg_configure_command ${_conf_cmd})
if (TARGET dep_OpenSSL)
set(_ffmpeg_depends DEPENDS dep_OpenSSL)
set(_ffmpeg_configure_command
${CMAKE_COMMAND} -E env
"PKG_CONFIG_PATH=${DESTDIR}/lib/pkgconfig:$ENV{PKG_CONFIG_PATH}"
${_conf_cmd}
)
endif()
if (MSVC)
set(_source_dir "${CMAKE_BINARY_DIR}/dep_FFMPEG-prefix/src/dep_FFMPEG")
set(PREBUILD_URL_arm64 "https://github.com/Noisyfox/FFmpeg-Builds-Orca/releases/download/autobuild-2026-09-18-16-50/ffmpeg-n7.0.3-33-g887d4b4919-winarm64-orca-shared-7.0.zip")
set(PREBUILD_HASH_arm64 "da480cbb39680056de824c57ec4dc3bd577b479ebbc310ff1f9dc55cf014b4c1")
set(PREBUILD_URL_x64 "https://github.com/Noisyfox/FFmpeg-Builds-Orca/releases/download/autobuild-2026-09-18-16-50/ffmpeg-n7.0.3-33-g887d4b4919-win64-orca-shared-7.0.zip")
set(PREBUILD_HASH_x64 "85da19daf198f5548259d8aabb349db84997a3f6e6886d8d7764114add9c6dae")
set(PREBUILD_URL_arm64 "https://github.com/Noisyfox/FFmpeg-Builds-Orca/releases/download/autobuild-2026-07-17-14-28/ffmpeg-n7.0.3-31-g9b6ffd74b5-winarm64-orca-shared-7.0.zip")
set(PREBUILD_HASH_arm64 "12f4140279f2f8469885e1b5b2e8be9d788882914c21523cacd56989f3548054")
set(PREBUILD_URL_x64 "https://github.com/Noisyfox/FFmpeg-Builds-Orca/releases/download/autobuild-2026-07-17-14-28/ffmpeg-n7.0.3-31-g9b6ffd74b5-win64-orca-shared-7.0.zip")
set(PREBUILD_HASH_x64 "e65916020ddb9ef84b2666dfbcbfc9b1d67f69d15b4a66db53754637bf2d498c")
ExternalProject_Add(dep_FFMPEG
${_ffmpeg_depends}
URL ${PREBUILD_URL_${DEPS_ARCH}}
URL_HASH SHA256=${PREBUILD_HASH_${DEPS_ARCH}}
DOWNLOAD_DIR ${DEP_DOWNLOAD_DIR}/FFMPEG
@@ -33,8 +21,6 @@ if (MSVC)
)
else ()
set(_openssl_cmd --enable-openssl)
if (APPLE)
set(_minos_cmd
"--extra-cflags=-mmacosx-version-min=${DEP_OSX_TARGET}"
@@ -66,11 +52,10 @@ else ()
endif()
ExternalProject_Add(dep_FFMPEG
${_ffmpeg_depends}
URL https://github.com/FFmpeg/FFmpeg/archive/refs/tags/n7.0.3.tar.gz
URL_HASH SHA256=DEEDCABE339165214A3637DF4C86A507AEF0D793CF8774FF68735F4737E8DDBC
DOWNLOAD_DIR ${DEP_DOWNLOAD_DIR}/FFMPEG
CONFIGURE_COMMAND ${_ffmpeg_configure_command}
CONFIGURE_COMMAND ${_conf_cmd}
${_cross_cmd}
${_pic_cmd}
${_arch_cmd}
@@ -78,21 +63,20 @@ else ()
"--prefix=${DESTDIR}"
${_link_cmd}
${_minos_cmd}
${_openssl_cmd}
--disable-doc
--enable-small
--disable-outdevs
--disable-filters
--enable-filter=*null*,afade,*fifo,*format,*resample,aeval,allrgb,allyuv,atempo,pan,*bars,color,*key,crop,draw*,eq*,framerate,*_qsv,*_vaapi,*v4l2*,hw*,scale,volume,test*
--disable-protocols
--enable-protocol=file,fd,pipe,http,https,rtp,tcp,udp
--enable-protocol=file,fd,pipe,rtp,udp
--disable-muxers
--enable-muxer=rtp
--disable-encoders
--disable-decoders
--enable-decoder=*aac*,h264*,mp3*,mjpeg,rv*
--disable-demuxers
--enable-demuxer=h264,mp3,mov,mpjpeg,rtsp,sdp
--enable-demuxer=h264,mp3,mov
--disable-zlib
--disable-avdevice
BUILD_IN_SOURCE ON
-240
View File
@@ -1,240 +0,0 @@
# Printer agents
Printer agents isolate printer-specific communication from the rest of OrcaSlicer. The GUI and
`DeviceManager` operate on a shared set of printer operations and device state; a selected printer
agent implements those operations for a particular printer ecosystem. The agent boundary allows
Bambu, Moonraker-based printers, built-in integrations, and Python-provided integrations to use the
same application workflow without making the GUI understand every printer protocol.
The current boundary is an adapter boundary around the existing application contract. In particular,
some request fields and message payloads still use the Bambu-shaped representation that existing
`MachineObject` and `DeviceManager` code consumes. The printer agent is responsible for translating
that representation into the protocol spoken by its printer. This is an intentional compatibility
constraint of the current design; the interface is not yet a neutral printer protocol.
The v1 dialect migration path is deliberately narrow. `DeviceManager` currently speaks the Bambu JSON
dialect because that is the payload shape already used throughout the command and state workflow. The
v1 `OrcaPrinterAgent` also accepts that Bambu dialect. Its transport path places the small translation
needed for the target printer at `deliver_to_sink`, keeping the compatibility code at the edge rather
than spreading it through `DeviceManager` or the agent interface.
The eventual direction is for `DeviceManager` to produce an Orca JSON dialect. The Bambu agent will then
own the translation from Orca JSON to Bambu's protocol, while `OrcaPrinterAgent` can forward the Orca
payload directly to its sink. The v1 translation at `deliver_to_sink` can then be removed without
changing `DeviceManager`, the command callers, or the rest of the agent workflow.
## Components
The system has four relevant layers:
```text
GUI / DeviceManager / MachineObject
|
NetworkAgent
/ \
IPrinterAgent ICloudServiceAgent
| |
printer protocol authentication and cloud services
```
### `DeviceManager` and `MachineObject`
`DeviceManager` owns the application-facing printer workflow. It maintains `MachineObject` instances,
updates their state, filters devices for the active printer agent, and initiates operations such as
homing, temperature changes, printing, subscriptions, and camera playback.
`MachineObject` remains the shared state model used by the GUI. It does not contain the implementation
of a printer protocol. When a device is discovered or returned by a cloud query, the device is tagged
with the active `printer_agent_id`. Device lists and selected-machine operations use that tag to avoid
sending an operation through an agent that does not own the device.
### `NetworkAgent`
`NetworkAgent` is the façade used by the GUI and `DeviceManager`. It owns:
- the currently selected `IPrinterAgent`;
- the registered cloud-service instances, indexed by provider;
- callbacks shared by the active printer agent and the application;
- the forwarding methods for printer commands and cloud operations.
There is one active printer agent for the currently selected printer preset. Switching the preset
increments the machine-list generation, disconnects the old printer agent, removes its callbacks, and
installs the newly selected agent. The façade then forwards printer operations to that agent.
Cloud operations are selected separately using a provider key. `NetworkAgent` forwards a cloud request
to the matching `ICloudServiceAgent`, and forwards cloud camera operations with a device ID. The
printer agent receives a cloud-agent pointer through `set_cloud_agent()` when it is created, allowing
printer communication to obtain cloud tokens without depending on a concrete cloud implementation.
### `IPrinterAgent`
`IPrinterAgent` is the printer-facing contract. It covers:
- cloud-relay and direct-LAN message delivery;
- LAN connection, discovery, binding, and certificates;
- printer subscriptions and callbacks;
- print operations;
- filament synchronization;
- camera capability and local camera URL reporting;
- printer command methods.
Concrete built-in implementations include the Bambu wrapper, the native Orca/Moonraker path, and
other printer-agent implementations registered by the application. A printer agent may use either
the cloud agent, a direct LAN connection, or both.
### `ICloudServiceAgent`
`ICloudServiceAgent` owns authentication and services provided by a cloud backend. It covers login
state, tokens, user and printer lists, settings synchronization, model services, cloud messages, and
cloud camera operations.
Cloud camera operations are device-scoped:
- `get_camera_url(dev_id, callback)` obtains a stream URL for one device;
- `create_camera_signaling_channel(dev_id)` creates signaling for one device where the provider
supports it.
This is separate from the local camera URL exposed by `IPrinterAgent`, which is currently scoped to
the active printer agent because a normal LAN agent represents one physical printer connection.
## Agent registration and selection
`NetworkAgentFactory` maintains the printer-agent registry. Each registry entry contains an agent ID,
a display name, and a factory function. Built-in agents register during application initialization.
Python printer-agent capabilities register dynamically and contribute an agent ID and factory entry.
The selected printer preset contains the printer-agent choice. If no explicit choice is stored, the
application preserves the existing default behavior: Bambu presets select the Bambu agent and other
presets select the native Orca agent. When a preset is changed, `GUI_App` resolves the effective agent
ID, obtains the corresponding cloud agent, creates the printer agent through the registry, and installs
it in `NetworkAgent`.
The registry rejects conflicting agent IDs. This matters for Python plugins because an agent ID is the
stable identity used by presets and device ownership; two enabled plugin capabilities must not claim
the same ID.
## Message and command flow
There are two low-level message paths:
- `send_message()` publishes a command through the printer's cloud relay;
- `send_message_to_printer()` sends a command directly to the printer over the LAN path.
Both paths accept a JSON string, quality-of-service and flag values, and return the existing network
status code domain. The agent owns the conversion from that JSON contract to its native transport.
The typed `command_*` methods are the application-facing convenience layer. The five generic defaults
currently implemented by `IPrinterAgent` construct the existing JSON dialect and route through the
same message path:
| Method | Default operation |
| --- | --- |
| `command_xyz_abs()` | Send `G90` for absolute positioning |
| `command_auto_leveling()` | Send `G29` for bed leveling |
| `command_go_home()` | Use the supported homing operation or send `G28` |
| `command_set_bed()` | Use the supported bed control or send `M140` |
| `command_set_nozzle()` | Send `M104` for nozzle temperature |
These are compatibility defaults for common printer workflows, not a guarantee that every firmware
implements every command identically. An agent can override a method when its protocol needs another
operation. For example, a Klipper configuration may use `BED_MESH_CALIBRATE` instead of `G29`.
The remaining common command methods default to `ORCA_NETWORK_ERR_CMD_NOT_SUPPORTED` because their
existing behavior is vendor-specific or has no portable implementation:
- AMS RFID refresh;
- AMS calibration;
- AMS tray selection;
- camera start;
- axis control.
The methods remain on the common interface so an agent that supports them can override them explicitly.
`sequence_id` remains part of the command contract because `DeviceManager` creates and tracks it as
the command ID.
## Device ownership and stale responses
Printer-agent ownership is represented by `printer_agent_id` on device records and `MachineObject`
instances. The active agent ID is attached when a device is discovered, returned by a cloud list, or
reused after a preset switch. Local-machine configuration also persists the agent ID so a saved LAN
device is not silently reused by an unrelated agent.
Cloud printer-list responses carry three pieces of request context added by `NetworkAgent`:
```text
provider cloud provider used for the request
agent_id active printer agent when the request was made
generation machine-list generation when the request was made
```
`DeviceManager` accepts the response only when those values still match the current provider, active
agent, and generation. This prevents a slow response from the previous preset or provider from
repopulating the current device list.
The provider mapping is currently selected by `GUI_App`: the Bambu agent maps to the Bambu cloud
provider and other agents map to the Orca cloud provider. The generation check protects that existing
selection from races; it does not make cloud-provider ownership intrinsic to an agent. Cloud-printer
ownership and the broader Orca cloud services are therefore still separate architectural concerns.
## Python printer agents
`PrinterAgentPluginCapability` implements `IPrinterAgent` directly. The live capability object is
registered with `NetworkAgentFactory` and handed out as the printer agent when its agent ID is selected.
The plugin receives the selected `ICloudServiceAgent` through `set_cloud_agent()` just like a built-in
printer agent.
Python plugins must implement the core communication and lifecycle methods required by the interface,
including agent metadata, printer connection, discovery callbacks, and the two message-send methods.
Methods that are meaningful only to a particular printer are optional overrides where the C++ base
class provides a default.
All ten `command_*` methods are available in the Python binding and in the trampoline. Their override
status is intentionally optional:
- the five generic commands use the C++ default when Python does not override them;
- the five vendor-specific commands return `NOT_SUPPORTED` unless Python supplies an implementation;
- a Python implementation can replace either behavior for its own protocol.
The Python camera binding exposes HTTP, HTTPS, RTSP, and HTTP-snapshot modes. WebRTC remains a
built-in C++ camera mode, but is not exposed as a Python mode because the current Python capability
does not provide the corresponding cloud signaling-channel contract.
## Camera playback boundary
The camera stream mode describes how a stream is obtained; it does not by itself define ownership of
the wxWidgets view that renders it. `MediaPlayCtrl` selects and tears down the active backend, while
the wx parent owns the child window or renderer. This is important because a web view, native media
control, and frame-based/WebRTC renderer have different wx window-lifetime requirements.
Cloud URL and signaling requests are routed through `NetworkAgent` to the cloud provider selected for
the device. Local URL requests are routed to the active printer agent. The distinction keeps cloud
account services device-scoped while preserving the current one-LAN-agent/one-printer model.
## Compatibility constraints
The printer-agent boundary intentionally preserves several existing application contracts:
- Bambu-shaped JSON is still the shared command representation;
- existing network status codes are reused, with Orca-specific unsupported/capability errors added
in the Orca-reserved range;
- `MachineObject` remains the shared device-state model;
- preset and local-machine data retain compatibility with the existing agent-selection behavior;
- Python plugins use the existing capability and pybind11 registration system.
The agent abstraction is therefore responsible for containing vendor differences, not for pretending
that all vendor protocols are identical. The planned Orca JSON dialect is the protocol-neutral command
model for the `DeviceManager`/agent boundary. Once it is introduced, Bambu-specific translation remains
inside the Bambu agent and the Orca agent's v1 sink adapter can be removed as a self-contained cleanup.
## Main implementation locations
- [`IPrinterAgent`](../../src/slic3r/Utils/IPrinterAgent.hpp) — printer-agent contract and generic command defaults
- [`ICloudServiceAgent`](../../src/slic3r/Utils/ICloudServiceAgent.hpp) — cloud service and per-device
cloud camera contract
- [`NetworkAgent`](../../src/slic3r/Utils/NetworkAgent.hpp) — façade and dispatch between active agents
- [`NetworkAgentFactory`](../../src/slic3r/Utils/NetworkAgentFactory.hpp) — built-in and Python agent registry
- [`DeviceManager`](../../src/slic3r/GUI/DeviceCore/DevManager.cpp) — device ownership, filtering, and
stale-response checks
- [`PrinterAgentPluginCapability`](../../src/slic3r/plugin/pluginTypes/printerAgent/PrinterAgentPluginCapability.cpp)
— Python bindings
- [`MediaPlayCtrl`](../../src/slic3r/GUI/MediaPlayCtrl.cpp) — camera backend selection and playback lifecycle
+4 -4
View File
@@ -8,7 +8,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2026-09-22 17:02+0800\n"
"POT-Creation-Date: 2026-09-22 12:39+0800\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
@@ -6161,6 +6161,9 @@ msgstr ""
msgid "Preferences"
msgstr ""
msgid "Open speed dial"
msgstr ""
msgctxt "Menu"
msgid "Edit"
msgstr ""
@@ -6168,9 +6171,6 @@ msgstr ""
msgid "View"
msgstr ""
msgid "Open Speed Dial"
msgstr ""
msgid "Preset Bundle"
msgstr ""
+4 -4
View File
@@ -8,7 +8,7 @@ msgid ""
msgstr ""
"Project-Id-Version: \n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2026-09-22 17:02+0800\n"
"POT-Creation-Date: 2026-09-22 12:39+0800\n"
"PO-Revision-Date: 2025-03-15 10:55+0100\n"
"Last-Translator: \n"
"Language-Team: \n"
@@ -6637,6 +6637,9 @@ msgstr "Mostrar el contorn al voltant de l'objecte seleccionat a l'escena 3D"
msgid "Preferences"
msgstr "Preferències"
msgid "Open speed dial"
msgstr ""
# AI Translated
msgctxt "Menu"
msgid "Edit"
@@ -6645,9 +6648,6 @@ msgstr "Edita"
msgid "View"
msgstr "Vista"
msgid "Open Speed Dial"
msgstr ""
# AI Translated
msgid "Preset Bundle"
msgstr "Paquet de perfils"
+4 -4
View File
@@ -3,7 +3,7 @@ msgid ""
msgstr ""
"Project-Id-Version: Orca Slicer\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2026-09-22 17:02+0800\n"
"POT-Creation-Date: 2026-09-22 12:39+0800\n"
"PO-Revision-Date: \n"
"Last-Translator: Jakub Hencl\n"
"Language-Team: \n"
@@ -6602,6 +6602,9 @@ msgstr "Zobrazit obrys kolem vybraného objektu ve 3D scéně."
msgid "Preferences"
msgstr "Předvolby"
msgid "Open speed dial"
msgstr ""
# AI Translated
msgctxt "Menu"
msgid "Edit"
@@ -6610,9 +6613,6 @@ msgstr "Upravit"
msgid "View"
msgstr "Zobrazit"
msgid "Open Speed Dial"
msgstr ""
msgid "Preset Bundle"
msgstr "Balík předvoleb"
+4 -4
View File
@@ -3,7 +3,7 @@ msgid ""
msgstr ""
"Project-Id-Version: Orca Slicer\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2026-09-22 17:02+0800\n"
"POT-Creation-Date: 2026-09-22 12:39+0800\n"
"PO-Revision-Date: \n"
"Last-Translator: Heiko Liebscher <hliebschergmail.com>\n"
"Language-Team: \n"
@@ -6490,6 +6490,9 @@ msgstr "Kontur um das ausgewählte Objekt in der 3D-Szene anzeigen."
msgid "Preferences"
msgstr "Einstellungen"
msgid "Open speed dial"
msgstr ""
# AI Translated
msgctxt "Menu"
msgid "Edit"
@@ -6498,9 +6501,6 @@ msgstr "Bearbeiten"
msgid "View"
msgstr "Ansicht"
msgid "Open Speed Dial"
msgstr ""
msgid "Preset Bundle"
msgstr "Vorlagen-Bundle"
+4 -4
View File
@@ -3,7 +3,7 @@ msgid ""
msgstr ""
"Project-Id-Version: Orca Slicer\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2026-09-22 17:02+0800\n"
"POT-Creation-Date: 2026-09-22 12:39+0800\n"
"PO-Revision-Date: 2026-06-17 15:44-0300\n"
"Last-Translator: Alexandre Folle de Menezes\n"
"Language-Team: \n"
@@ -6157,6 +6157,9 @@ msgstr ""
msgid "Preferences"
msgstr ""
msgid "Open speed dial"
msgstr ""
msgctxt "Menu"
msgid "Edit"
msgstr ""
@@ -6164,9 +6167,6 @@ msgstr ""
msgid "View"
msgstr ""
msgid "Open Speed Dial"
msgstr ""
msgid "Preset Bundle"
msgstr ""
+4 -4
View File
@@ -3,7 +3,7 @@ msgid ""
msgstr ""
"Project-Id-Version: Orca Slicer\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2026-09-22 17:02+0800\n"
"POT-Creation-Date: 2026-09-22 12:39+0800\n"
"PO-Revision-Date: \n"
"Last-Translator: Ian A. Bassi <>\n"
"Language-Team: \n"
@@ -6346,6 +6346,9 @@ msgstr "Mostrar el contorno alrededor del objeto seleccionado en la escena 3D."
msgid "Preferences"
msgstr "Preferencias"
msgid "Open speed dial"
msgstr ""
msgctxt "Menu"
msgid "Edit"
msgstr "Edición"
@@ -6353,9 +6356,6 @@ msgstr "Edición"
msgid "View"
msgstr "Vista"
msgid "Open Speed Dial"
msgstr ""
msgid "Preset Bundle"
msgstr "Paquete de perfiles"
+4 -4
View File
@@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: \n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2026-09-22 17:02+0800\n"
"POT-Creation-Date: 2026-09-22 12:39+0800\n"
"PO-Revision-Date: 2026-07-20 13:33+0200\n"
"Last-Translator: Manu Goiogana <mgoiogana@gmail.com>\n"
"Language-Team: \n"
@@ -6392,6 +6392,9 @@ msgstr "Erakutsi hautatutako objektuaren inguruko ingerada 3D eszenan."
msgid "Preferences"
msgstr "Hobespenak"
msgid "Open speed dial"
msgstr ""
# AI Translated
msgctxt "Menu"
msgid "Edit"
@@ -6400,9 +6403,6 @@ msgstr "Editatu"
msgid "View"
msgstr "Bista"
msgid "Open Speed Dial"
msgstr ""
msgid "Preset Bundle"
msgstr "Aurrezarpen-paketea"
+4 -4
View File
@@ -6,7 +6,7 @@ msgid ""
msgstr ""
"Project-Id-Version: Orca Slicer\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2026-09-22 17:02+0800\n"
"POT-Creation-Date: 2026-09-22 12:39+0800\n"
"PO-Revision-Date: \n"
"Last-Translator: \n"
"Language-Team: Guislain Cyril, Thomas Lété\n"
@@ -6439,6 +6439,9 @@ msgstr "Afficher le tracé contour de lobjet sélectionné dans la scène 3D.
msgid "Preferences"
msgstr "Préférences"
msgid "Open speed dial"
msgstr ""
# AI Translated
msgctxt "Menu"
msgid "Edit"
@@ -6447,9 +6450,6 @@ msgstr "Édition"
msgid "View"
msgstr "Affichage"
msgid "Open Speed Dial"
msgstr ""
msgid "Preset Bundle"
msgstr "Paquet de préréglages"
+4 -4
View File
@@ -3,7 +3,7 @@ msgid ""
msgstr ""
"Project-Id-Version: Orca Slicer\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2026-09-22 17:02+0800\n"
"POT-Creation-Date: 2026-09-22 12:39+0800\n"
"Language: hu\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
@@ -6541,6 +6541,9 @@ msgstr "Körvonal megjelenítése a kijelölt objektum körül a 3D nézetben."
msgid "Preferences"
msgstr "Beállítások"
msgid "Open speed dial"
msgstr ""
# AI Translated
msgctxt "Menu"
msgid "Edit"
@@ -6549,9 +6552,6 @@ msgstr "Szerkesztés"
msgid "View"
msgstr "Nézet"
msgid "Open Speed Dial"
msgstr ""
msgid "Preset Bundle"
msgstr "Beállításcsomag"
+4 -4
View File
@@ -3,7 +3,7 @@ msgid ""
msgstr ""
"Project-Id-Version: Orca Slicer\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2026-09-22 17:02+0800\n"
"POT-Creation-Date: 2026-09-22 12:39+0800\n"
"PO-Revision-Date: \n"
"Last-Translator: \n"
"Language-Team: \n"
@@ -6543,6 +6543,9 @@ msgstr "Mostra il contorno attorno all'oggetto selezionato nella scena 3D."
msgid "Preferences"
msgstr "Preferenze"
msgid "Open speed dial"
msgstr ""
# AI Translated
msgctxt "Menu"
msgid "Edit"
@@ -6551,9 +6554,6 @@ msgstr "Modifica"
msgid "View"
msgstr "Vista"
msgid "Open Speed Dial"
msgstr ""
msgid "Preset Bundle"
msgstr "Pacchetto profili"
+4 -4
View File
@@ -3,7 +3,7 @@ msgid ""
msgstr ""
"Project-Id-Version: Orca Slicer\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2026-09-22 17:02+0800\n"
"POT-Creation-Date: 2026-09-22 12:39+0800\n"
"PO-Revision-Date: \n"
"Last-Translator: \n"
"Language-Team: \n"
@@ -6553,6 +6553,9 @@ msgstr "3Dシーンで選択したオブジェクトの周りにアウトライ
msgid "Preferences"
msgstr "設定"
msgid "Open speed dial"
msgstr ""
# AI Translated
msgctxt "Menu"
msgid "Edit"
@@ -6561,9 +6564,6 @@ msgstr "編集"
msgid "View"
msgstr "表示"
msgid "Open Speed Dial"
msgstr ""
# AI Translated
msgid "Preset Bundle"
msgstr "プリセットバンドル"
+4 -4
View File
@@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: Orca Slicer\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2026-09-22 17:02+0800\n"
"POT-Creation-Date: 2026-09-22 12:39+0800\n"
"PO-Revision-Date: 2025-06-02 17:12+0900\n"
"Last-Translator: crwusiz <crwusiz@gmail.com>\n"
"Language-Team: \n"
@@ -6566,6 +6566,9 @@ msgstr "3D 장면에서 선택한 객체 주변에 윤곽선 표시"
msgid "Preferences"
msgstr "기본 설정"
msgid "Open speed dial"
msgstr ""
# AI Translated
msgctxt "Menu"
msgid "Edit"
@@ -6574,9 +6577,6 @@ msgstr "편집"
msgid "View"
msgstr "시점"
msgid "Open Speed Dial"
msgstr ""
# AI Translated
msgid "Preset Bundle"
msgstr "사전 설정 번들"
+4 -4
View File
@@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: \n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2026-09-22 17:02+0800\n"
"POT-Creation-Date: 2026-09-22 12:39+0800\n"
"PO-Revision-Date: 2026-07-02 14:13+0300\n"
"Last-Translator: Gintaras Kučinskas <sharanchius@gmail.com>\n"
"Language-Team: \n"
@@ -6528,6 +6528,9 @@ msgstr "Rodyti kontūrą aplink pasirinktą objektą 3D scenoje."
msgid "Preferences"
msgstr "Parinktys"
msgid "Open speed dial"
msgstr ""
# AI Translated
msgctxt "Menu"
msgid "Edit"
@@ -6536,9 +6539,6 @@ msgstr "Redaguoti"
msgid "View"
msgstr "Vaizdas"
msgid "Open Speed Dial"
msgstr ""
msgid "Preset Bundle"
msgstr "Profilių rinkinys"
+4 -4
View File
@@ -3,7 +3,7 @@ msgid ""
msgstr ""
"Project-Id-Version: Orca Slicer\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2026-09-22 17:02+0800\n"
"POT-Creation-Date: 2026-09-22 12:39+0800\n"
"PO-Revision-Date: \n"
"Last-Translator: \n"
"Language-Team: \n"
@@ -7122,6 +7122,9 @@ msgstr "Toon een omtrek rond het geselecteerde object in de 3D-scène."
msgid "Preferences"
msgstr "Voorkeuren"
msgid "Open speed dial"
msgstr ""
# AI Translated
msgctxt "Menu"
msgid "Edit"
@@ -7130,9 +7133,6 @@ msgstr "Bewerken"
msgid "View"
msgstr "Weergave"
msgid "Open Speed Dial"
msgstr ""
# AI Translated
msgid "Preset Bundle"
msgstr "Voorinstellingenbundel"
+4 -4
View File
@@ -3,7 +3,7 @@ msgid ""
msgstr ""
"Project-Id-Version: OrcaSlicer 2.3.0-rc\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2026-09-22 17:02+0800\n"
"POT-Creation-Date: 2026-09-22 12:39+0800\n"
"PO-Revision-Date: \n"
"Last-Translator: Krzysztof Morga <<tlumaczeniebs@gmail.com>>\n"
"Language-Team: \n"
@@ -6689,6 +6689,9 @@ msgstr "Przełącza wyświetlanie konturu wokół zaznaczonego obiektu w scenie
msgid "Preferences"
msgstr "Preferencje"
msgid "Open speed dial"
msgstr ""
# AI Translated
msgctxt "Menu"
msgid "Edit"
@@ -6697,9 +6700,6 @@ msgstr "Edycja"
msgid "View"
msgstr "Widok"
msgid "Open Speed Dial"
msgstr ""
# AI Translated
msgid "Preset Bundle"
msgstr "Pakiet profili"
+4 -4
View File
@@ -3,7 +3,7 @@ msgid ""
msgstr ""
"Project-Id-Version: Orca Slicer\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2026-09-22 17:02+0800\n"
"POT-Creation-Date: 2026-09-22 12:39+0800\n"
"PO-Revision-Date: 2026-07-26 11:14-0300\n"
"Last-Translator: Alexandre Folle de Menezes\n"
"Language-Team: Portuguese, Brazilian\n"
@@ -6363,6 +6363,9 @@ msgstr "Mostrar contorno ao redor do objeto selecionado na cena 3D."
msgid "Preferences"
msgstr "Preferências"
msgid "Open speed dial"
msgstr ""
msgctxt "Menu"
msgid "Edit"
msgstr "Editar"
@@ -6370,9 +6373,6 @@ msgstr "Editar"
msgid "View"
msgstr "Visualizar"
msgid "Open Speed Dial"
msgstr ""
msgid "Preset Bundle"
msgstr "Pacote de Predefinições"
+4 -4
View File
@@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: OrcaSlicer V2.5.0\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2026-09-22 17:02+0800\n"
"POT-Creation-Date: 2026-09-22 12:39+0800\n"
"PO-Revision-Date: 2026-02-25 13:38+0300\n"
"Last-Translator: Felix14_v2\n"
"Language-Team: Felix14_v2 (ДС/ТГ: @felix14_v2, почта: aleks111001@list.ru), Andylg <andylg@yandex.ru>\n"
@@ -6597,6 +6597,9 @@ msgstr "Отображение контура вокруг выбранных м
msgid "Preferences"
msgstr "Настройки"
msgid "Open speed dial"
msgstr ""
msgctxt "Menu"
msgid "Edit"
msgstr "Правка"
@@ -6604,9 +6607,6 @@ msgstr "Правка"
msgid "View"
msgstr "Вид"
msgid "Open Speed Dial"
msgstr ""
msgid "Preset Bundle"
msgstr "Пакет профилей"
+4 -4
View File
@@ -3,7 +3,7 @@ msgid ""
msgstr ""
"Project-Id-Version: Orca Slicer\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2026-09-22 17:02+0800\n"
"POT-Creation-Date: 2026-09-22 12:39+0800\n"
"Language: sv\n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
@@ -7205,6 +7205,9 @@ msgstr "Visa en kontur runt det markerade objektet i 3D-scenen."
msgid "Preferences"
msgstr "Inställningar"
msgid "Open speed dial"
msgstr ""
# AI Translated
msgctxt "Menu"
msgid "Edit"
@@ -7213,9 +7216,6 @@ msgstr "Redigera"
msgid "View"
msgstr "Vy"
msgid "Open Speed Dial"
msgstr ""
# AI Translated
msgid "Preset Bundle"
msgstr "Förinställningspaket"
+4 -4
View File
@@ -7,7 +7,7 @@ msgid ""
msgstr ""
"Project-Id-Version: Orca Slicer\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2026-09-22 17:02+0800\n"
"POT-Creation-Date: 2026-09-22 12:39+0800\n"
"PO-Revision-Date: 2026-06-19 13:40+0700\n"
"Last-Translator: Icezaza\n"
"Language-Team: Thai\n"
@@ -6520,6 +6520,9 @@ msgstr "แสดงเค้าร่างรอบๆ วัตถุที
msgid "Preferences"
msgstr "การตั้งค่า"
msgid "Open speed dial"
msgstr ""
# AI Translated
msgctxt "Menu"
msgid "Edit"
@@ -6528,9 +6531,6 @@ msgstr "แก้ไข"
msgid "View"
msgstr "มุมมอง"
msgid "Open Speed Dial"
msgstr ""
msgid "Preset Bundle"
msgstr "ชุดที่ตั้งไว้ล่วงหน้า"
+4 -4
View File
@@ -3,7 +3,7 @@ msgid ""
msgstr ""
"Project-Id-Version: Orca Slicer\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2026-09-22 17:02+0800\n"
"POT-Creation-Date: 2026-09-22 12:39+0800\n"
"PO-Revision-Date: 2026-08-21 23:18+0300\n"
"Last-Translator: GlauTech\n"
"Language-Team: \n"
@@ -6588,6 +6588,9 @@ msgstr "3D sahnede seçilen nesnenin etrafındaki ana hatları göster."
msgid "Preferences"
msgstr "Tercihler"
msgid "Open speed dial"
msgstr ""
# AI Translated
msgctxt "Menu"
msgid "Edit"
@@ -6596,9 +6599,6 @@ msgstr "Düzen"
msgid "View"
msgstr "Görünüm"
msgid "Open Speed Dial"
msgstr ""
msgid "Preset Bundle"
msgstr "Ön ayar paketi"
+4 -4
View File
@@ -3,7 +3,7 @@ msgid ""
msgstr ""
"Project-Id-Version: orcaslicerua\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2026-09-22 17:02+0800\n"
"POT-Creation-Date: 2026-09-22 12:39+0800\n"
"PO-Revision-Date: 2026-07-17 16:25+0300\n"
"Last-Translator: Andrij Mizyk <andm1zyk@proton.me>\n"
"Language-Team: Ukrainian\n"
@@ -6557,6 +6557,9 @@ msgstr "Показувати контур навколо виділеного о
msgid "Preferences"
msgstr "Налаштування"
msgid "Open speed dial"
msgstr ""
# AI Translated
msgctxt "Menu"
msgid "Edit"
@@ -6565,9 +6568,6 @@ msgstr "Редагування"
msgid "View"
msgstr "Вигляд"
msgid "Open Speed Dial"
msgstr ""
msgid "Preset Bundle"
msgstr "Набір пресетів"
+4 -4
View File
@@ -3,7 +3,7 @@ msgid ""
msgstr ""
"Project-Id-Version: Orca Slicer\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2026-09-22 17:02+0800\n"
"POT-Creation-Date: 2026-09-22 12:39+0800\n"
"PO-Revision-Date: 2025-10-02 17:43+0700\n"
"Last-Translator: \n"
"Language-Team: hainguyen.ts13@gmail.com\n"
@@ -6907,6 +6907,9 @@ msgstr "Hiện đường viền xung quanh vật thể đã chọn trong cảnh
msgid "Preferences"
msgstr "Tùy chọn"
msgid "Open speed dial"
msgstr ""
# AI Translated
msgctxt "Menu"
msgid "Edit"
@@ -6915,9 +6918,6 @@ msgstr "Chỉnh sửa"
msgid "View"
msgstr "Xem"
msgid "Open Speed Dial"
msgstr ""
# AI Translated
msgid "Preset Bundle"
msgstr "Gói cài đặt sẵn"
+4 -4
View File
@@ -6,7 +6,7 @@ msgid ""
msgstr ""
"Project-Id-Version: Slic3rPE\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2026-09-22 17:02+0800\n"
"POT-Creation-Date: 2026-09-22 12:39+0800\n"
"PO-Revision-Date: 2026-06-11 12:37-0300\n"
"Last-Translator: Handle <mail@bysb.net>\n"
"Language-Team: \n"
@@ -6375,6 +6375,9 @@ msgstr "在3D场景中显示选中对象的轮廓"
msgid "Preferences"
msgstr "偏好设置"
msgid "Open speed dial"
msgstr ""
# AI Translated
msgctxt "Menu"
msgid "Edit"
@@ -6383,9 +6386,6 @@ msgstr "编辑"
msgid "View"
msgstr "视图"
msgid "Open Speed Dial"
msgstr ""
msgid "Preset Bundle"
msgstr "预设包"
+4 -4
View File
@@ -9,7 +9,7 @@ msgid ""
msgstr ""
"Project-Id-Version: Orca Slicer\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2026-09-22 17:02+0800\n"
"POT-Creation-Date: 2026-09-22 12:39+0800\n"
"PO-Revision-Date: 2025-11-28 13:48-0600\n"
"Last-Translator: tntchn <15895303+tntchn@users.noreply.github.com>\n"
"Language-Team: \n"
@@ -6505,6 +6505,9 @@ msgstr "在 3D 場景中顯示選定物件的輪廓"
msgid "Preferences"
msgstr "偏好設定"
msgid "Open speed dial"
msgstr ""
# AI Translated
msgctxt "Menu"
msgid "Edit"
@@ -6513,9 +6516,6 @@ msgstr "編輯"
msgid "View"
msgstr "視角"
msgid "Open Speed Dial"
msgstr ""
msgid "Preset Bundle"
msgstr "設定檔組"
+1 -53
View File
@@ -1,6 +1,6 @@
{
"name": "Eryone",
"version": "02.04.00.06",
"version": "02.04.00.05",
"force_update": "0",
"description": "Eryone configurations",
"machine_model_list": [
@@ -432,18 +432,10 @@
"name": "Eryone ABS",
"sub_path": "filament/Eryone ABS.json"
},
{
"name": "Eryone ABS+-HS @Thinker X400 0.4 nozzle",
"sub_path": "filament/Eryone ABS+-HS @Thinker X400 0.4 nozzle.json"
},
{
"name": "Eryone ABS-CF",
"sub_path": "filament/Eryone ABS-CF.json"
},
{
"name": "Eryone ABS-GF @Thinker X400 0.4 nozzle",
"sub_path": "filament/Eryone ABS-GF @Thinker X400 0.4 nozzle.json"
},
{
"name": "Eryone ASA",
"sub_path": "filament/Eryone ASA.json"
@@ -452,10 +444,6 @@
"name": "Eryone ASA-CF",
"sub_path": "filament/Eryone ASA-CF.json"
},
{
"name": "Eryone ASA-HS @Thinker X400 0.4 nozzle",
"sub_path": "filament/Eryone ASA-HS @Thinker X400 0.4 nozzle.json"
},
{
"name": "Eryone PA",
"sub_path": "filament/Eryone PA.json"
@@ -472,50 +460,18 @@
"name": "Eryone PETG",
"sub_path": "filament/Eryone PETG.json"
},
{
"name": "Eryone PETG HS @Thinker X400 0.4 nozzle",
"sub_path": "filament/Eryone PETG HS @Thinker X400 0.4 nozzle.json"
},
{
"name": "Eryone PETG-CF",
"sub_path": "filament/Eryone PETG-CF.json"
},
{
"name": "Eryone PETG-GF @Thinker X400 0.4 nozzle",
"sub_path": "filament/Eryone PETG-GF @Thinker X400 0.4 nozzle.json"
},
{
"name": "Eryone PETG-Lite @Thinker X400 0.4 nozzle",
"sub_path": "filament/Eryone PETG-Lite @Thinker X400 0.4 nozzle.json"
},
{
"name": "Eryone PETG-Matte @Thinker X400 0.4 nozzle",
"sub_path": "filament/Eryone PETG-Matte @Thinker X400 0.4 nozzle.json"
},
{
"name": "Eryone PETG-Translucent @Thinker X400 0.4 nozzle",
"sub_path": "filament/Eryone PETG-Translucent @Thinker X400 0.4 nozzle.json"
},
{
"name": "Eryone PLA",
"sub_path": "filament/Eryone PLA.json"
},
{
"name": "Eryone PLA+HS @Thinker X400 0.4 nozzle",
"sub_path": "filament/Eryone PLA+HS @Thinker X400 0.4 nozzle.json"
},
{
"name": "Eryone PLA-CF",
"sub_path": "filament/Eryone PLA-CF.json"
},
{
"name": "Eryone PLA-Matte @Thinker X400 0.4 nozzle",
"sub_path": "filament/Eryone PLA-Matte @Thinker X400 0.4 nozzle.json"
},
{
"name": "Eryone PLA-Matte HS @Thinker X400 0.4 nozzle",
"sub_path": "filament/Eryone PLA-Matte HS @Thinker X400 0.4 nozzle.json"
},
{
"name": "Eryone PP",
"sub_path": "filament/Eryone PP.json"
@@ -532,14 +488,6 @@
"name": "Eryone TPU",
"sub_path": "filament/Eryone TPU.json"
},
{
"name": "Eryone petg-Galaxy @Thinker X400 0.4 nozzle",
"sub_path": "filament/Eryone petg-Galaxy @Thinker X400 0.4 nozzle.json"
},
{
"name": "Eryone pla-Ultra Silk @Thinker X400 0.4 nozzle",
"sub_path": "filament/Eryone pla-Ultra Silk @Thinker X400 0.4 nozzle.json"
},
{
"name": "fdm_filament_pla",
"sub_path": "filament/fdm_filament_pla.json"
@@ -1,54 +0,0 @@
{
"type": "filament",
"name": "Eryone ABS+-HS @Thinker X400 0.4 nozzle",
"inherits": "Eryone Standard PLA",
"from": "system",
"setting_id": "aUH1I90BFXa0C84r",
"filament_id": "OFRgjTRp",
"instantiation": "true",
"compatible_printers": [
"Thinker X400 0.4 nozzle"
],
"filament_settings_id": [
"Eryone ABS+-HS @Thinker X400 0.4 nozzle"
],
"fan_max_speed": [
"20"
],
"fan_min_speed": [
"0"
],
"filament_end_gcode": [
"; filament end gcode \nSET_FAN_SPEED FAN=filter_fan SPEED=0"
],
"filament_flow_ratio": [
"0.96"
],
"filament_max_volumetric_speed": [
"25"
],
"filament_start_gcode": [
"; filament start gcode\nSET_FAN_SPEED FAN=filter_fan SPEED=1"
],
"filament_type": [
"ABS"
],
"hot_plate_temp": [
"90"
],
"hot_plate_temp_initial_layer": [
"90"
],
"nozzle_temperature": [
"255"
],
"nozzle_temperature_initial_layer": [
"255"
],
"slow_down_layer_time": [
"4"
],
"slow_down_min_speed": [
"15"
]
}
@@ -1,54 +0,0 @@
{
"type": "filament",
"name": "Eryone ABS-GF @Thinker X400 0.4 nozzle",
"inherits": "Eryone Standard PLA",
"from": "system",
"setting_id": "WnsZC7HYi5Z1MOcn",
"filament_id": "OFenMm2Y",
"instantiation": "true",
"compatible_printers": [
"Thinker X400 0.4 nozzle"
],
"filament_settings_id": [
"Eryone ABS-GF @Thinker X400 0.4 nozzle"
],
"fan_max_speed": [
"20"
],
"fan_min_speed": [
"0"
],
"filament_end_gcode": [
"; filament end gcode \nSET_FAN_SPEED FAN=filter_fan SPEED=0"
],
"filament_flow_ratio": [
"0.96"
],
"filament_max_volumetric_speed": [
"18"
],
"filament_start_gcode": [
"; filament start gcode\nSET_FAN_SPEED FAN=filter_fan SPEED=1"
],
"filament_type": [
"ABS"
],
"hot_plate_temp": [
"90"
],
"hot_plate_temp_initial_layer": [
"90"
],
"nozzle_temperature": [
"265"
],
"nozzle_temperature_initial_layer": [
"265"
],
"slow_down_layer_time": [
"4"
],
"slow_down_min_speed": [
"15"
]
}
@@ -1,54 +0,0 @@
{
"type": "filament",
"name": "Eryone ASA-HS @Thinker X400 0.4 nozzle",
"inherits": "Eryone Standard PLA",
"from": "system",
"setting_id": "xw8zqUsFb2WVaSo1",
"filament_id": "OFIzY5sP",
"instantiation": "true",
"compatible_printers": [
"Thinker X400 0.4 nozzle"
],
"filament_settings_id": [
"Eryone ASA-HS @Thinker X400 0.4 nozzle"
],
"fan_max_speed": [
"20"
],
"fan_min_speed": [
"0"
],
"filament_end_gcode": [
"; filament end gcode \nSET_FAN_SPEED FAN=filter_fan SPEED=0"
],
"filament_flow_ratio": [
"1"
],
"filament_max_volumetric_speed": [
"20"
],
"filament_start_gcode": [
"; filament start gcode\nSET_FAN_SPEED FAN=filter_fan SPEED=1"
],
"filament_type": [
"ASA"
],
"hot_plate_temp": [
"90"
],
"hot_plate_temp_initial_layer": [
"90"
],
"nozzle_temperature": [
"260"
],
"nozzle_temperature_initial_layer": [
"260"
],
"slow_down_layer_time": [
"4"
],
"slow_down_min_speed": [
"15"
]
}
@@ -1,60 +0,0 @@
{
"type": "filament",
"name": "Eryone PETG HS @Thinker X400 0.4 nozzle",
"inherits": "Eryone Standard PLA",
"from": "system",
"setting_id": "GtPiiNgHAnxrR9Tj",
"filament_id": "OFtqfvcE",
"instantiation": "true",
"compatible_printers": [
"Thinker X400 0.4 nozzle"
],
"filament_settings_id": [
"Eryone PETG HS @Thinker X400 0.4 nozzle"
],
"close_fan_the_first_x_layers": [
"3"
],
"fan_max_speed": [
"50"
],
"fan_min_speed": [
"30"
],
"filament_end_gcode": [
"; filament end gcode \nSET_FAN_SPEED FAN=filter_fan SPEED=0"
],
"filament_flow_ratio": [
"0.97"
],
"filament_max_volumetric_speed": [
"15"
],
"filament_start_gcode": [
"; filament start gcode\nSET_FAN_SPEED FAN=filter_fan SPEED=1"
],
"filament_type": [
"PETG"
],
"full_fan_speed_layer": [
"2"
],
"hot_plate_temp": [
"70"
],
"hot_plate_temp_initial_layer": [
"70"
],
"nozzle_temperature": [
"240"
],
"nozzle_temperature_initial_layer": [
"240"
],
"slow_down_layer_time": [
"4"
],
"slow_down_min_speed": [
"15"
]
}
@@ -40,7 +40,7 @@
"14"
],
"filament_settings_id": [
"Eryone PETG-CF"
"Eryone PETG"
],
"filament_type": [
"PETG-CF"
@@ -1,60 +0,0 @@
{
"type": "filament",
"name": "Eryone PETG-GF @Thinker X400 0.4 nozzle",
"inherits": "Eryone Standard PLA",
"from": "system",
"setting_id": "S1FBfw93PzYJMhCa",
"filament_id": "OFbzOkXX",
"instantiation": "true",
"compatible_printers": [
"Thinker X400 0.4 nozzle"
],
"filament_settings_id": [
"Eryone PETG-GF @Thinker X400 0.4 nozzle"
],
"close_fan_the_first_x_layers": [
"3"
],
"fan_max_speed": [
"50"
],
"fan_min_speed": [
"30"
],
"filament_end_gcode": [
"; filament end gcode \nSET_FAN_SPEED FAN=filter_fan SPEED=0"
],
"filament_flow_ratio": [
"0.96"
],
"filament_max_volumetric_speed": [
"13"
],
"filament_start_gcode": [
"; filament start gcode\nSET_FAN_SPEED FAN=filter_fan SPEED=1"
],
"filament_type": [
"PETG"
],
"full_fan_speed_layer": [
"2"
],
"hot_plate_temp": [
"70"
],
"hot_plate_temp_initial_layer": [
"70"
],
"nozzle_temperature": [
"250"
],
"nozzle_temperature_initial_layer": [
"250"
],
"slow_down_layer_time": [
"4"
],
"slow_down_min_speed": [
"15"
]
}
@@ -1,60 +0,0 @@
{
"type": "filament",
"name": "Eryone PETG-Lite @Thinker X400 0.4 nozzle",
"inherits": "Eryone Standard PLA",
"from": "system",
"setting_id": "CWHhUswq3j9XHAuC",
"filament_id": "OFyWb8AA",
"instantiation": "true",
"compatible_printers": [
"Thinker X400 0.4 nozzle"
],
"filament_settings_id": [
"Eryone PETG-Lite @Thinker X400 0.4 nozzle"
],
"close_fan_the_first_x_layers": [
"3"
],
"fan_max_speed": [
"50"
],
"fan_min_speed": [
"30"
],
"filament_end_gcode": [
"; filament end gcode \nSET_FAN_SPEED FAN=filter_fan SPEED=0"
],
"filament_flow_ratio": [
"0.9796"
],
"filament_max_volumetric_speed": [
"13"
],
"filament_start_gcode": [
"; filament start gcode\nSET_FAN_SPEED FAN=filter_fan SPEED=1"
],
"filament_type": [
"PETG"
],
"full_fan_speed_layer": [
"2"
],
"hot_plate_temp": [
"70"
],
"hot_plate_temp_initial_layer": [
"70"
],
"nozzle_temperature": [
"245"
],
"nozzle_temperature_initial_layer": [
"245"
],
"slow_down_layer_time": [
"4"
],
"slow_down_min_speed": [
"15"
]
}
@@ -1,60 +0,0 @@
{
"type": "filament",
"name": "Eryone PETG-Matte @Thinker X400 0.4 nozzle",
"inherits": "Eryone Standard PLA",
"from": "system",
"setting_id": "pzceBG5k2u3X9TLO",
"filament_id": "OFSvr7Aj",
"instantiation": "true",
"compatible_printers": [
"Thinker X400 0.4 nozzle"
],
"filament_settings_id": [
"Eryone PETG-Matte @Thinker X400 0.4 nozzle"
],
"close_fan_the_first_x_layers": [
"3"
],
"fan_max_speed": [
"50"
],
"fan_min_speed": [
"30"
],
"filament_end_gcode": [
"; filament end gcode \nSET_FAN_SPEED FAN=filter_fan SPEED=0"
],
"filament_flow_ratio": [
"1"
],
"filament_max_volumetric_speed": [
"22"
],
"filament_start_gcode": [
"; filament start gcode\nSET_FAN_SPEED FAN=filter_fan SPEED=1"
],
"filament_type": [
"PETG"
],
"full_fan_speed_layer": [
"2"
],
"hot_plate_temp": [
"80"
],
"hot_plate_temp_initial_layer": [
"80"
],
"nozzle_temperature": [
"250"
],
"nozzle_temperature_initial_layer": [
"250"
],
"slow_down_layer_time": [
"4"
],
"slow_down_min_speed": [
"15"
]
}
@@ -1,60 +0,0 @@
{
"type": "filament",
"name": "Eryone PETG-Translucent @Thinker X400 0.4 nozzle",
"inherits": "Eryone Standard PLA",
"from": "system",
"setting_id": "jN64L5SUnQB5dG1x",
"filament_id": "OFY2E8cd",
"instantiation": "true",
"compatible_printers": [
"Thinker X400 0.4 nozzle"
],
"filament_settings_id": [
"Eryone PETG-Translucent @Thinker X400 0.4 nozzle"
],
"close_fan_the_first_x_layers": [
"3"
],
"fan_max_speed": [
"50"
],
"fan_min_speed": [
"30"
],
"filament_end_gcode": [
"; filament end gcode \nSET_FAN_SPEED FAN=filter_fan SPEED=0"
],
"filament_flow_ratio": [
"0.99"
],
"filament_max_volumetric_speed": [
"24"
],
"filament_start_gcode": [
"; filament start gcode\nSET_FAN_SPEED FAN=filter_fan SPEED=1"
],
"filament_type": [
"PETG"
],
"full_fan_speed_layer": [
"2"
],
"hot_plate_temp": [
"70"
],
"hot_plate_temp_initial_layer": [
"70"
],
"nozzle_temperature": [
"215"
],
"nozzle_temperature_initial_layer": [
"215"
],
"slow_down_layer_time": [
"4"
],
"slow_down_min_speed": [
"15"
]
}
@@ -1,48 +0,0 @@
{
"type": "filament",
"name": "Eryone PLA+HS @Thinker X400 0.4 nozzle",
"inherits": "Eryone Standard PLA",
"from": "system",
"setting_id": "AfUEns6VhCXsj9Ft",
"filament_id": "OFeRoHy3",
"instantiation": "true",
"compatible_printers": [
"Thinker X400 0.4 nozzle"
],
"filament_settings_id": [
"Eryone PLA+HS @Thinker X400 0.4 nozzle"
],
"fan_min_speed": [
"80"
],
"filament_end_gcode": [
"; filament end gcode \nSET_FAN_SPEED FAN=filter_fan SPEED=0"
],
"filament_flow_ratio": [
"0.99"
],
"filament_max_volumetric_speed": [
"18"
],
"filament_start_gcode": [
"; filament start gcode\nSET_FAN_SPEED FAN=filter_fan SPEED=1"
],
"hot_plate_temp": [
"55"
],
"hot_plate_temp_initial_layer": [
"55"
],
"nozzle_temperature": [
"210"
],
"nozzle_temperature_initial_layer": [
"210"
],
"slow_down_layer_time": [
"4"
],
"slow_down_min_speed": [
"15"
]
}
@@ -1,48 +0,0 @@
{
"type": "filament",
"name": "Eryone PLA-Matte @Thinker X400 0.4 nozzle",
"inherits": "Eryone Standard PLA",
"from": "system",
"setting_id": "Ho1nQYsweqTTte0g",
"filament_id": "OFFMWFWL",
"instantiation": "true",
"compatible_printers": [
"Thinker X400 0.4 nozzle"
],
"filament_settings_id": [
"Eryone PLA-Matte @Thinker X400 0.4 nozzle"
],
"fan_min_speed": [
"80"
],
"filament_end_gcode": [
"; filament end gcode \nSET_FAN_SPEED FAN=filter_fan SPEED=0"
],
"filament_flow_ratio": [
"0.99"
],
"filament_max_volumetric_speed": [
"21"
],
"filament_start_gcode": [
"; filament start gcode\nSET_FAN_SPEED FAN=filter_fan SPEED=1"
],
"hot_plate_temp": [
"55"
],
"hot_plate_temp_initial_layer": [
"55"
],
"nozzle_temperature": [
"215"
],
"nozzle_temperature_initial_layer": [
"215"
],
"slow_down_layer_time": [
"4"
],
"slow_down_min_speed": [
"15"
]
}
@@ -1,48 +0,0 @@
{
"type": "filament",
"name": "Eryone PLA-Matte HS @Thinker X400 0.4 nozzle",
"inherits": "Eryone Standard PLA",
"from": "system",
"setting_id": "8W0yhuRu7OBJkSOS",
"filament_id": "OF9mNFSM",
"instantiation": "true",
"compatible_printers": [
"Thinker X400 0.4 nozzle"
],
"filament_settings_id": [
"Eryone PLA-Matte HS @Thinker X400 0.4 nozzle"
],
"fan_min_speed": [
"80"
],
"filament_end_gcode": [
"; filament end gcode \nSET_FAN_SPEED FAN=filter_fan SPEED=0"
],
"filament_flow_ratio": [
"1.1"
],
"filament_max_volumetric_speed": [
"28"
],
"filament_start_gcode": [
"; filament start gcode\nSET_FAN_SPEED FAN=filter_fan SPEED=1"
],
"hot_plate_temp": [
"55"
],
"hot_plate_temp_initial_layer": [
"55"
],
"nozzle_temperature": [
"225"
],
"nozzle_temperature_initial_layer": [
"225"
],
"slow_down_layer_time": [
"4"
],
"slow_down_min_speed": [
"15"
]
}
@@ -1,60 +0,0 @@
{
"type": "filament",
"name": "Eryone petg-Galaxy @Thinker X400 0.4 nozzle",
"inherits": "Eryone Standard PLA",
"from": "system",
"setting_id": "R4BPYs8aAc9DFf0n",
"filament_id": "OFWDDu8u",
"instantiation": "true",
"compatible_printers": [
"Thinker X400 0.4 nozzle"
],
"filament_settings_id": [
"Eryone petg-Galaxy @Thinker X400 0.4 nozzle"
],
"close_fan_the_first_x_layers": [
"3"
],
"fan_max_speed": [
"50"
],
"fan_min_speed": [
"30"
],
"filament_end_gcode": [
"; filament end gcode \nSET_FAN_SPEED FAN=filter_fan SPEED=0"
],
"filament_flow_ratio": [
"0.96"
],
"filament_max_volumetric_speed": [
"21"
],
"filament_start_gcode": [
"; filament start gcode\nSET_FAN_SPEED FAN=filter_fan SPEED=1"
],
"filament_type": [
"PETG"
],
"full_fan_speed_layer": [
"2"
],
"hot_plate_temp": [
"70"
],
"hot_plate_temp_initial_layer": [
"70"
],
"nozzle_temperature": [
"245"
],
"nozzle_temperature_initial_layer": [
"245"
],
"slow_down_layer_time": [
"4"
],
"slow_down_min_speed": [
"15"
]
}
@@ -1,48 +0,0 @@
{
"type": "filament",
"name": "Eryone pla-Ultra Silk @Thinker X400 0.4 nozzle",
"inherits": "Eryone Standard PLA",
"from": "system",
"setting_id": "jCbto64oaAKrDaN0",
"filament_id": "OF99Cc22",
"instantiation": "true",
"compatible_printers": [
"Thinker X400 0.4 nozzle"
],
"filament_settings_id": [
"Eryone pla-Ultra Silk @Thinker X400 0.4 nozzle"
],
"fan_min_speed": [
"80"
],
"filament_end_gcode": [
"; filament end gcode \nSET_FAN_SPEED FAN=filter_fan SPEED=0"
],
"filament_flow_ratio": [
"0.97"
],
"filament_max_volumetric_speed": [
"15"
],
"filament_start_gcode": [
"; filament start gcode\nSET_FAN_SPEED FAN=filter_fan SPEED=1"
],
"hot_plate_temp": [
"55"
],
"hot_plate_temp_initial_layer": [
"55"
],
"nozzle_temperature": [
"230"
],
"nozzle_temperature_initial_layer": [
"230"
],
"slow_down_layer_time": [
"4"
],
"slow_down_min_speed": [
"15"
]
}
+7 -11
View File
@@ -1,7 +1,7 @@
{
"name": "WonderMaker",
"url": "",
"version": "02.04.00.04",
"version": "02.04.00.03",
"force_update": "0",
"description": "WonderMaker configurations",
"machine_model_list": [
@@ -432,8 +432,12 @@
"sub_path": "machine/WonderMaker ZR 0.4 nozzle.json"
},
{
"name": "fdm_ultra_common",
"sub_path": "machine/fdm_ultra_common.json"
"name": "WonderMaker ZR Ultra 0.4 nozzle",
"sub_path": "machine/WonderMaker ZR Ultra 0.4 nozzle.json"
},
{
"name": "WonderMaker ZR Ultra S 0.4 nozzle",
"sub_path": "machine/WonderMaker ZR Ultra S 0.4 nozzle.json"
},
{
"name": "WonderMaker ZR 0.2 nozzle",
@@ -451,10 +455,6 @@
"name": "WonderMaker ZR Ultra 0.2 nozzle",
"sub_path": "machine/WonderMaker ZR Ultra 0.2 nozzle.json"
},
{
"name": "WonderMaker ZR Ultra 0.4 nozzle",
"sub_path": "machine/WonderMaker ZR Ultra 0.4 nozzle.json"
},
{
"name": "WonderMaker ZR Ultra 0.6 nozzle",
"sub_path": "machine/WonderMaker ZR Ultra 0.6 nozzle.json"
@@ -467,10 +467,6 @@
"name": "WonderMaker ZR Ultra S 0.2 nozzle",
"sub_path": "machine/WonderMaker ZR Ultra S 0.2 nozzle.json"
},
{
"name": "WonderMaker ZR Ultra S 0.4 nozzle",
"sub_path": "machine/WonderMaker ZR Ultra S 0.4 nozzle.json"
},
{
"name": "WonderMaker ZR Ultra S 0.6 nozzle",
"sub_path": "machine/WonderMaker ZR Ultra S 0.6 nozzle.json"
+7 -8
View File
@@ -1,23 +1,22 @@
{
"type": "machine",
"name": "WonderMaker ZR Ultra 0.2 nozzle",
"inherits": "fdm_ultra_common",
"inherits": "WonderMaker ZR Ultra 0.4 nozzle",
"from": "system",
"setting_id": "AX42zNj8YsJ94ilv",
"instantiation": "true",
"printer_model": "WonderMaker ZR Ultra",
"printer_variant": "0.2",
"default_print_profile": "0.10mm Standard @WonderMaker ZR Ultra 0.2 nozzle",
"default_bed_type": "4",
"max_layer_height": [
"0.14"
],
"nozzle_diameter": [
"0.2",
"0.2",
"0.2",
"0.2"
],
"printer_model": "WonderMaker ZR Ultra",
"printer_variant": "0.2",
"default_print_profile": "0.10mm Standard @WonderMaker ZR Ultra 0.2 nozzle",
"max_layer_height": [
"0.14"
],
"min_layer_height": [
"0.04"
],
+49 -1
View File
@@ -1,17 +1,65 @@
{
"type": "machine",
"name": "WonderMaker ZR Ultra 0.4 nozzle",
"inherits": "fdm_ultra_common",
"inherits": "fdm_klipper_common",
"from": "system",
"setting_id": "xJBdCljSZVDINXnP",
"instantiation": "true",
"printer_model": "WonderMaker ZR Ultra",
"default_print_profile": "0.20mm Standard @WonderMaker ZR Ultra",
"default_bed_type": "4",
"default_filament_profile": [
"WonderMaker PLA Basic"
],
"single_extruder_multi_material": "0",
"nozzle_diameter": [
"0.4",
"0.4",
"0.4",
"0.4"
],
"printable_area": [
"0x0",
"300x0",
"300x270",
"0x270"
],
"bed_exclude_area": [],
"printable_height": "290",
"extruder_clearance_radius": "134",
"bed_mesh_min": [
"10",
"10"
],
"bed_mesh_max": [
"290",
"260"
],
"bed_mesh_probe_distance": [
"50",
"50"
],
"support_air_filtration": "0",
"support_chamber_temp_control": "0",
"machine_tool_change_time": "10",
"machine_load_filament_time": "0",
"machine_unload_filament_time": "0",
"retract_lift_below": [
"279",
"279",
"279",
"279"
],
"enable_long_retraction_when_cut": [
"0",
"0",
"0",
"0"
],
"long_retractions_when_cut": [
"0",
"0",
"0",
"0"
]
}
+7 -8
View File
@@ -1,23 +1,22 @@
{
"type": "machine",
"name": "WonderMaker ZR Ultra 0.6 nozzle",
"inherits": "fdm_ultra_common",
"inherits": "WonderMaker ZR Ultra 0.4 nozzle",
"from": "system",
"setting_id": "6OWxZLFn3RD54QfX",
"instantiation": "true",
"printer_model": "WonderMaker ZR Ultra",
"printer_variant": "0.6",
"default_print_profile": "0.30mm Standard @WonderMaker ZR Ultra 0.6 nozzle",
"default_bed_type": "4",
"max_layer_height": [
"0.42"
],
"nozzle_diameter": [
"0.6",
"0.6",
"0.6",
"0.6"
],
"printer_model": "WonderMaker ZR Ultra",
"printer_variant": "0.6",
"default_print_profile": "0.30mm Standard @WonderMaker ZR Ultra 0.6 nozzle",
"max_layer_height": [
"0.42"
],
"min_layer_height": [
"0.12"
],
+7 -8
View File
@@ -1,23 +1,22 @@
{
"type": "machine",
"name": "WonderMaker ZR Ultra 0.8 nozzle",
"inherits": "fdm_ultra_common",
"inherits": "WonderMaker ZR Ultra 0.4 nozzle",
"from": "system",
"setting_id": "SY9snlsMkurhEYWP",
"instantiation": "true",
"printer_model": "WonderMaker ZR Ultra",
"printer_variant": "0.8",
"default_print_profile": "0.40mm Standard @WonderMaker ZR Ultra 0.8 nozzle",
"default_bed_type": "4",
"max_layer_height": [
"0.56"
],
"nozzle_diameter": [
"0.8",
"0.8",
"0.8",
"0.8"
],
"printer_model": "WonderMaker ZR Ultra",
"printer_variant": "0.8",
"default_print_profile": "0.40mm Standard @WonderMaker ZR Ultra 0.8 nozzle",
"max_layer_height": [
"0.56"
],
"min_layer_height": [
"0.16"
],
+15 -5
View File
@@ -1,19 +1,29 @@
{
"type": "machine",
"name": "WonderMaker ZR Ultra S 0.2 nozzle",
"inherits": "WonderMaker ZR Ultra 0.2 nozzle",
"inherits": "WonderMaker ZR Ultra S 0.4 nozzle",
"from": "system",
"setting_id": "ZNAx3f7tX3DatiK1",
"instantiation": "true",
"printer_model": "WonderMaker ZR Ultra S",
"nozzle_diameter": [
"0.2",
"0.2",
"0.2",
"0.2"
],
"default_print_profile": "0.10mm Standard @WonderMaker ZR Ultra 0.2 nozzle",
"printer_model": "WonderMaker ZR Ultra S",
"printer_variant": "0.2",
"support_air_filtration": "1",
"support_chamber_temp_control": "1"
"default_print_profile": "0.10mm Standard @WonderMaker ZR Ultra 0.2 nozzle",
"max_layer_height": [
"0.14"
],
"min_layer_height": [
"0.04"
],
"retraction_length": [
"0.4",
"0.4",
"0.4",
"0.4"
]
}
+50 -3
View File
@@ -1,18 +1,65 @@
{
"type": "machine",
"name": "WonderMaker ZR Ultra S 0.4 nozzle",
"inherits": "WonderMaker ZR Ultra 0.4 nozzle",
"inherits": "fdm_klipper_common",
"from": "system",
"setting_id": "8xoZ6vYv9ws0J97u",
"instantiation": "true",
"printer_model": "WonderMaker ZR Ultra S",
"default_print_profile": "0.20mm Standard @WonderMaker ZR Ultra",
"default_bed_type": "4",
"default_filament_profile": [
"WonderMaker PLA Basic"
],
"single_extruder_multi_material": "0",
"nozzle_diameter": [
"0.4",
"0.4",
"0.4",
"0.4"
],
"default_print_profile": "0.20mm Standard @WonderMaker ZR Ultra",
"printable_area": [
"0x0",
"300x0",
"300x270",
"0x270"
],
"bed_exclude_area": [],
"printable_height": "290",
"extruder_clearance_radius": "134",
"bed_mesh_min": [
"10",
"10"
],
"bed_mesh_max": [
"290",
"260"
],
"bed_mesh_probe_distance": [
"50",
"50"
],
"support_air_filtration": "1",
"support_chamber_temp_control": "1"
"support_chamber_temp_control": "1",
"machine_tool_change_time": "10",
"machine_load_filament_time": "0",
"machine_unload_filament_time": "0",
"retract_lift_below": [
"279",
"279",
"279",
"279"
],
"enable_long_retraction_when_cut": [
"0",
"0",
"0",
"0"
],
"long_retractions_when_cut": [
"0",
"0",
"0",
"0"
]
}
+22 -9
View File
@@ -1,19 +1,32 @@
{
"type": "machine",
"name": "WonderMaker ZR Ultra S 0.6 nozzle",
"inherits": "WonderMaker ZR Ultra 0.6 nozzle",
"inherits": "WonderMaker ZR Ultra S 0.4 nozzle",
"from": "system",
"setting_id": "xuJnOPTmSW1BMo6p",
"instantiation": "true",
"printer_model": "WonderMaker ZR Ultra S",
"nozzle_diameter": [
"0.6",
"0.6",
"0.6",
"0.6"
],
"default_print_profile": "0.30mm Standard @WonderMaker ZR Ultra 0.6 nozzle",
"printer_variant": "0.6",
"support_air_filtration": "1",
"support_chamber_temp_control": "1"
"nozzle_diameter": [
"0.6"
],
"max_layer_height": [
"0.42"
],
"min_layer_height": [
"0.12"
],
"retraction_length": [
"1.4",
"1.4",
"1.4",
"1.4"
],
"retraction_minimum_travel": [
"3",
"3",
"3",
"3"
]
}
+22 -9
View File
@@ -1,19 +1,32 @@
{
"type": "machine",
"name": "WonderMaker ZR Ultra S 0.8 nozzle",
"inherits": "WonderMaker ZR Ultra 0.8 nozzle",
"inherits": "WonderMaker ZR Ultra S 0.4 nozzle",
"from": "system",
"setting_id": "0BPLKMspArilfkGT",
"instantiation": "true",
"printer_model": "WonderMaker ZR Ultra S",
"nozzle_diameter": [
"0.8",
"0.8",
"0.8",
"0.8"
],
"default_print_profile": "0.40mm Standard @WonderMaker ZR Ultra 0.8 nozzle",
"printer_variant": "0.8",
"support_air_filtration": "1",
"support_chamber_temp_control": "1"
"nozzle_diameter": [
"0.8"
],
"max_layer_height": [
"0.56"
],
"min_layer_height": [
"0.16"
],
"retract_length_toolchange": [
"3",
"3",
"3",
"3"
],
"retraction_length": [
"3",
"3",
"3",
"3"
]
}
@@ -1,64 +0,0 @@
{
"type": "machine",
"name": "fdm_ultra_common",
"inherits": "fdm_klipper_common",
"from": "system",
"instantiation": "false",
"default_bed_type": "4",
"default_filament_profile": [
"WonderMaker PLA Basic"
],
"single_extruder_multi_material": "0",
"enable_filament_mapping": "1",
"filament_swap_gcode": "M117 Swap filament for tool {next_extruder}\nPAUSE",
"nozzle_diameter": [
"0.4",
"0.4",
"0.4",
"0.4"
],
"printable_area": [
"0x0",
"300x0",
"300x270",
"0x270"
],
"bed_exclude_area": [],
"printable_height": "290",
"extruder_clearance_radius": "134",
"bed_mesh_min": [
"10",
"10"
],
"bed_mesh_max": [
"290",
"260"
],
"bed_mesh_probe_distance": [
"50",
"50"
],
"support_air_filtration": "0",
"support_chamber_temp_control": "0",
"machine_tool_change_time": "10",
"machine_load_filament_time": "0",
"machine_unload_filament_time": "0",
"retract_lift_below": [
"279",
"279",
"279",
"279"
],
"enable_long_retraction_when_cut": [
"0",
"0",
"0",
"0"
],
"long_retractions_when_cut": [
"0",
"0",
"0",
"0"
]
}
@@ -1,5 +1,5 @@
<!DOCTYPE html>
<!-- Bootstrap page for WebDialog. The real plugin HTML is loaded via
<!-- Bootstrap page for PluginWebDialog. The real plugin HTML is loaded via
wxWebView::SetPage once this page finishes loading; this file only exists
to bring the webview up. -->
<html>
+1 -3
View File
@@ -575,7 +575,7 @@ function CapabilityCanRun(plugin, capability) {
}
function IsPluginChecked(plugin) {
return plugin.is_loaded;
return GetStatus(plugin) === "Activated";
}
function HasMixedCapabilityState(plugin) {
@@ -1347,8 +1347,6 @@ function StatusDescription(plugin) {
return "This plugin is still loading.";
case "Error":
return "This plugin is blocked until its error is fixed.";
case "RuntimeError":
return "This plugin is loaded but a capability reported an error.";
case "Inactive":
default:
return "This plugin is inactive. Activate it to install or load it.";
@@ -424,11 +424,6 @@ body.pane-resizing {
font-weight: 600;
}
.status-cell.status-runtimeerror {
color: var(--plugin-status-warn);
font-weight: 600;
}
.status-cell.status-loading {
color: var(--plugin-status-warn);
font-weight: 600;
@@ -685,11 +680,6 @@ body.pane-resizing {
color: var(--plugin-status-danger);
}
.detail-status-chip.status-runtimeerror {
background: var(--plugin-status-warn-bg);
color: var(--plugin-status-warn);
}
.detail-status-chip.status-loading {
background: var(--plugin-status-warn-bg);
color: var(--plugin-status-warn);
-146
View File
@@ -1,146 +0,0 @@
# /// script
# requires-python = ">=3.12"
#
# [tool.orcaslicer.plugin]
# name = "Dock Panel Demo"
# description = "Opens a dockable panel beside the 3D view that lists the objects on the plate."
# author = "OrcaSlicer"
# version = "0.0.1"
# ///
"""Dock Panel Demo -- orca.host.ui.create_dock_panel().
Run it from the Plugins dialog. It opens an HTML panel docked on the right of the 3D view, in the
same dock area as the sidebar. Drag its caption to dock it on another side (or float it, where the
platform allows), hide it from the page and run the plugin again to bring it back, or close it with
its close button or from the page.
page --orca.postMessage({command: 'refresh'})--> plugin.on_message()
page --orca.postMessage({command: 'hide'})--> plugin.on_message() -> panel.hide()
page --orca.close()--> panel closes, plugin.on_close()
plugin --panel.post({command: 'objects', ...})--> page (orca.onMessage)
"""
import orca
PAGE = """
<style>
body { margin: 0; padding: 12px; font-size: 13px; }
h3 { margin: 0 0 4px; font-size: 14px; }
.note { margin: 0 0 12px; color: var(--orca-muted); font-size: 12px; }
.actions { display: flex; flex-wrap: wrap; gap: 6px; margin-bottom: 12px; }
.actions button.quiet { background: transparent; color: var(--orca-fg); border-color: var(--orca-border); }
table { width: 100%; border-collapse: collapse; }
td.count { text-align: right; font-variant-numeric: tabular-nums; }
#status { margin-top: 10px; color: var(--orca-muted); font-size: 12px; }
</style>
<h3>Objects on the plate</h3>
<p class="note">Docked beside the 3D view. Drag the caption to move it.</p>
<div class="actions">
<button type="button" id="refresh">Refresh</button>
<button type="button" id="hide" class="quiet">Hide</button>
<button type="button" id="close" class="quiet">Close</button>
</div>
<table>
<thead><tr><th>Name</th><th>Parts</th><th>Copies</th></tr></thead>
<tbody id="rows"></tbody>
</table>
<p id="status">Waiting for the plugin...</p>
<script>
(function () {
function text(value) {
var span = document.createElement("span");
span.textContent = value;
return span.innerHTML;
}
function render(message) {
var rows = document.getElementById("rows");
var status = document.getElementById("status");
if (message.error) {
rows.innerHTML = "";
status.textContent = message.error;
return;
}
rows.innerHTML = message.objects.map(function (object) {
return "<tr><td>" + text(object.name) + "</td><td class=\\"count\\">" + object.volumes +
"</td><td class=\\"count\\">" + object.instances + "</td></tr>";
}).join("");
status.textContent = message.objects.length + " object(s), refreshed " + new Date().toLocaleTimeString();
}
orca.onMessage(function (message) {
if (message && message.command === "objects")
render(message);
});
document.getElementById("refresh").addEventListener("click", function () {
orca.postMessage({ command: "refresh" });
});
document.getElementById("hide").addEventListener("click", function () {
orca.postMessage({ command: "hide" });
});
document.getElementById("close").addEventListener("click", function () {
orca.close();
});
orca.postMessage({ command: "refresh" });
})();
</script>
"""
def plate_objects():
try:
model = orca.host.model()
except RuntimeError as error:
return {"command": "objects", "error": str(error)}
return {
"command": "objects",
"objects": [
{"name": obj.name or "(unnamed)", "volumes": obj.volume_count(), "instances": obj.instance_count()}
for obj in model.objects()
],
}
class DockPanelDemo(orca.script.ScriptPluginCapabilityBase):
panel = None
def get_name(self):
return "Dock Panel Demo"
def execute(self):
# The capability instance lives as long as the plugin, so a second run finds the open panel.
if self.panel is not None and self.panel.is_open():
self.panel.show()
return orca.ExecutionResult.success("Dock Panel Demo is already open.")
self.panel = orca.host.ui.create_dock_panel(
html=PAGE,
title="Dock Panel Demo",
width=320,
height=480,
on_message=self.on_message,
on_close=self.on_close,
dock="right",
)
return orca.ExecutionResult.success("Dock Panel Demo opened.")
# Called on the UI thread when the page posts.
def on_message(self, message):
command = (message or {}).get("command")
if command == "refresh":
self.panel.post(plate_objects())
elif command == "hide":
self.panel.hide()
def on_close(self):
self.panel = None
@orca.plugin
class DockPanelDemoPlugin(orca.base):
def register_capabilities(self):
orca.register_capability(DockPanelDemo)
@@ -331,15 +331,6 @@ modules:
sha256: deedcabe339165214a3637df4c86a507aef0d793cf8774ff68735f4737e8ddbc
dest: external-packages/FFMPEG
# libdatachannel v0.24.5
# The Git checkout includes the submodules that are missing from the
# GitHub source archive. DataChannel.cmake consumes it as SOURCE_DIR.
- type: git
url: https://github.com/paullouisageneau/libdatachannel.git
tag: v0.24.5
commit: 443f6934d9007eb7076ab7825ba330f355fcbead
dest: external-packages/DataChannel
# ---------------------------------------------------------------
# Fallback archives for deps normally provided by the GNOME SDK.
# These are only used if find_package() fails to locate them.
+12 -23
View File
@@ -122,19 +122,17 @@ Vec2d printable_area_center(const DynamicPrintConfig &cfg)
// Put the prime tower where the GUI and CLI would before slicing. The config default (x 15, y 220)
// lies off any bed shallower than the tower, and generation rejects an off-plate tower instead of
// exporting it. Beside the cube at the bed centre, clear of the edge exclusion strips some beds
// carry, with a few mm of clearance so the conflict checker never sees the two touch. The cube and
// the tower's estimated footprint are then pulled inside the printable outline as one rigid pair:
// moving the tower alone would push it back onto the cube on a narrow bed. Returns that move for
// the cube.
Vec2d place_wipe_tower(DynamicPrintConfig &cfg, const Vec2d &center)
// exporting it. Beside the centred cube, clear of the edge exclusion strips some beds carry, then
// pulled inside the printable outline by the tower's own estimated footprint, with a few mm of
// clearance so the conflict checker never sees the two touch.
void place_wipe_tower(DynamicPrintConfig &cfg, const Vec2d &center)
{
const auto *area = cfg.option<ConfigOptionPoints>("printable_area");
if (area == nullptr || area->values.size() < 3)
return Vec2d::Zero();
return;
const WipeTowerFootprint footprint = estimate_wipe_tower_footprint(cfg, resolve_wipe_tower_type(cfg), {0, 1}, cfg.opt_float("layer_height"), 10.);
if (footprint.depth < EPSILON)
return Vec2d::Zero();
return;
const double margin = WIPE_TOWER_MARGIN + footprint.brim_width;
// The position is the tower's own origin; a rotated tower extends from it in another
// direction, so place the rotated box's extents rather than the origin.
@@ -145,22 +143,13 @@ Vec2d place_wipe_tower(DynamicPrintConfig &cfg, const Vec2d &center)
const Vec2d size = unscale(local.max) - lo;
Vec2d pos(center.x() + 5. + margin + 5. - lo.x(), center.y() - size.y() / 2. - lo.y());
box.translate(Point::new_scale(pos.x(), pos.y()));
// A bed too small for the pair keeps the cube at its centre and places the tower alone.
const Polygons bed{Polygon::new_scale(area->values)};
const BoundingBox tower = get_extents(box);
BoundingBox pair = tower;
pair.merge(Point::new_scale(center.x() - 5., center.y() - 5.));
pair.merge(Point::new_scale(center.x() + 5., center.y() + 5.));
const Point room = get_extents(bed).size() - Point::new_scale(2. * margin, 2. * margin);
const bool rigid = pair.size().x() < room.x() && pair.size().y() < room.y();
const Vec2d move = WipeTower::move_box_inside_polygon(rigid ? pair : tower, bed, scaled<coord_t>(margin)).cast<double>();
pos += move;
const Vec2f move = WipeTower::move_box_inside_polygon(get_extents(box), Polygons{Polygon::new_scale(area->values)}, scaled<coord_t>(margin));
pos += move.cast<double>();
cfg.option<ConfigOptionFloats>("wipe_tower_x", true)->values = {pos.x()};
cfg.option<ConfigOptionFloats>("wipe_tower_y", true)->values = {pos.y()};
return rigid ? move : Vec2d::Zero();
}
// Slice one cube that switches from filament 1 to filament 2 partway up, so exactly one
// Slice one centered cube that switches from filament 1 to filament 2 partway up, so exactly one
// filament change fires, then export. The change drives the printer's own change_filament_gcode: on a
// single-nozzle machine it rides the AMS prime tower (append_tcr), on a multi-nozzle machine it routes
// through the nozzle swap (set_extruder / append_tcr2) - the engine picks the path from the printer's
@@ -168,10 +157,10 @@ Vec2d place_wipe_tower(DynamicPrintConfig &cfg, const Vec2d &center)
// Slic3r::PlaceholderParserError from export.
std::string slice_two_color_cube_and_export(DynamicPrintConfig cfg, bool is_bbl)
{
const Vec2d center = printable_area_center(cfg);
const Vec2d cube_min = center - Vec2d(5., 5.) + place_wipe_tower(cfg, center);
const Vec2d center = printable_area_center(cfg);
place_wipe_tower(cfg, center);
TriangleMesh m = make_cube(10, 10, 10);
m.translate(static_cast<float>(cube_min.x()), static_cast<float>(cube_min.y()), 0.f);
m.translate(float(center.x() - 5.), float(center.y() - 5.), 0.f);
Model model;
Print print;
+46 -5
View File
@@ -8,6 +8,7 @@
#include "I18N.hpp"
#include "GCode.hpp"
#include "Exception.hpp"
#include "LifecycleEvents.hpp"
#include "ExtrusionEntity.hpp"
#include "EdgeGrid.hpp"
#include "Geometry/ConvexHull.hpp"
@@ -2476,6 +2477,14 @@ void GCode::do_export(Print* print, const char* path, GCodeProcessorResult* resu
m_writer.set_is_bbl_machine(print->is_BBL_printer());
print->set_started(psGCodeExport);
{
LifecycleEventContext ctx;
ctx.name = std::to_string(print->model().id().id);
ctx.code = LifecycleEvtCode::Ok;
ctx.msg = path;
fire_lifecycle_event(LifecycleEvent::GCodeExportStarted, ctx);
}
// check if any custom gcode contains keywords used by the gcode processor to
// produce time estimation and gcode toolpaths
std::vector<std::pair<std::string, std::string>> validation_res = DoExport::validate_custom_gcode(*print);
@@ -2511,12 +2520,22 @@ void GCode::do_export(Print* print, const char* path, GCodeProcessorResult* resu
m_processor.set_print(print);
GCodeOutputStream file(boost::nowide::fopen(path_tmp.c_str(), "wb"), m_processor);
if (! file.is_open()) {
BOOST_LOG_TRIVIAL(error) << std::string("G-code export to ") + path + " failed.\nCannot open the file for writing.\n" << std::endl;
std::string err_msg = std::string("G-code export to ") + path + " failed.\nCannot open the file for writing.\n";
BOOST_LOG_TRIVIAL(error) << err_msg << std::endl;
if (!fs::exists(folder)) {
//fs::create_directory(folder);
BOOST_LOG_TRIVIAL(error) << "the parent path " + folder.string() +" is not there!!!" << std::endl;
std::string add_err_msg = "the parent path " + folder.string() +" is not there!!!";
BOOST_LOG_TRIVIAL(error) << add_err_msg << std::endl;
err_msg += add_err_msg;
}
throw Slic3r::RuntimeError(std::string("G-code export to ") + path + " failed.\nCannot open the file for writing.\n");
{
LifecycleEventContext ctx;
ctx.name = std::to_string(print->model().id().id);
ctx.code = LifecycleEvtCode::Error;
ctx.msg = std::string(path) + "\n" + err_msg;
fire_lifecycle_event(LifecycleEvent::GCodeExportFinished, ctx);
}
throw Slic3r::RuntimeError(err_msg);
}
try {
@@ -2527,11 +2546,18 @@ void GCode::do_export(Print* print, const char* path, GCodeProcessorResult* resu
boost::nowide::remove(path_tmp.c_str());
throw Slic3r::RuntimeError(std::string("G-code export to ") + path + " failed\nIs the disk full?\n");
}
} catch (std::exception & /* ex */) {
} catch (std::exception &ex) {
// Rethrow on any exception. std::runtime_exception and CanceledException are expected to be thrown.
// Close and remove the file.
file.close();
boost::nowide::remove(path_tmp.c_str());
{
LifecycleEventContext ctx;
ctx.name = std::to_string(print->model().id().id);
ctx.code = LifecycleEvtCode::Error;
ctx.msg = std::string(path) + "\n" + ex.what();
fire_lifecycle_event(LifecycleEvent::GCodeExportFinished, ctx);
}
throw;
}
file.close();
@@ -2637,6 +2663,13 @@ void GCode::do_export(Print* print, const char* path, GCodeProcessorResult* resu
std::error_code ret = rename_file(path_tmp, path);
if (ret) {
{
LifecycleEventContext ctx;
ctx.name = std::to_string(print->model().id().id);
ctx.code = LifecycleEvtCode::Error;
ctx.msg = std::string(path) + "\nFailed to rename the output G-code file: " + ret.message();
fire_lifecycle_event(LifecycleEvent::GCodeExportFinished, ctx);
}
throw Slic3r::RuntimeError(
std::string("Failed to rename the output G-code file from ") + path_tmp + " to " + path + '\n' + "error code " + ret.message() + '\n' +
"Is " + path_tmp + " locked?" + '\n');
@@ -2647,7 +2680,15 @@ void GCode::do_export(Print* print, const char* path, GCodeProcessorResult* resu
BOOST_LOG_TRIVIAL(info) << "Exporting G-code finished" << log_memory_info();
print->set_done(psGCodeExport);
{
LifecycleEventContext ctx;
ctx.name = std::to_string(print->model().id().id);
ctx.code = LifecycleEvtCode::Ok;
ctx.msg = path;
fire_lifecycle_event(LifecycleEvent::GCodeExportFinished, ctx);
}
// Orca: label_object_enabled reflects whether objects are labeled in the g-code (EXCLUDE_OBJECT /
// M486), which is driven by exclude_object for every printer
if(result != nullptr)
+181
View File
@@ -0,0 +1,181 @@
#pragma once
// LifecycleEvents.hpp
// --------------------
// Application lifecycle events (project, slicing, plate editing, preset, printer connection, and
// job activity) that other subsystems -- chiefly the plugin layer above libslic3r -- may want to
// observe. Lives in libslic3r rather than the plugin layer because some events fire from inside
// the slicing engine itself; see fire_lifecycle_event() below.
#include <functional>
#include <string>
#include <utility>
namespace Slic3r
{
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,
};
// Scoped so callers must qualify (LifecycleEvtCode::Error, not ERROR) -- ERROR/OK collide with
// Windows macros (wingdi.h) as unqualified names.
enum class LifecycleEvtCode { Ok, Error, Warn };
struct LifecycleEventContext
{
// The primary subject identifier for the event. This is event-specific (for example, a
// project/output path, preset name, device id, or object name), must not contain status or
// prose, and may be empty when the event has no single subject.
std::string name;
// Outcome of the operation represented by the event. For state-change and start events,
// Ok means that the event occurred; it does not imply that a future operation succeeded.
LifecycleEvtCode code = LifecycleEvtCode::Ok;
// Optional human-readable detail or diagnostic text. It is not a stable parsing contract;
// machine-readable data should be represented by a dedicated field or event instead.
std::string msg;
// Stable subject/object identifier, when the source model provides one.
std::string id;
// Previous value for rename and other before/after events.
std::string previous_name;
// Device identifier for printer and job events.
std::string device_id;
// Job identifier when the originating queue/task provides one.
std::string job_id;
// Source subsystem or operation detail, suitable for filtering but not guaranteed to be
// exhaustive across versions.
std::string source;
// Plate, object, or volume index when the source uses an index rather than a stable id.
int index = -1;
// Aggregate project dirty state for ProjectDirtyChanged.
bool dirty = false;
};
inline std::string lifecycle_event_to_string(LifecycleEvent event)
{
switch (event) {
case LifecycleEvent::NewProject: return "NewProject";
case LifecycleEvent::ProjectOpened: return "ProjectOpened";
case LifecycleEvent::ProjectBeforeSave: return "ProjectBeforeSave";
case LifecycleEvent::ProjectAfterSave: return "ProjectAfterSave";
case LifecycleEvent::ProjectClosed: return "ProjectClosed";
case LifecycleEvent::ProjectDirtyChanged: return "ProjectDirtyChanged";
case LifecycleEvent::SliceStarted: return "SliceStarted";
case LifecycleEvent::SliceGeometryFinished: return "SliceGeometryFinished";
case LifecycleEvent::GCodeExportStarted: return "GCodeExportStarted";
case LifecycleEvent::GCodeExportFinished: return "GCodeExportFinished";
case LifecycleEvent::SlicingJobComplete: return "SlicingJobComplete";
case LifecycleEvent::ObjectAdded: return "ObjectAdded";
case LifecycleEvent::ObjectDeleted: return "ObjectDeleted";
case LifecycleEvent::ObjectTransformed: return "ObjectTransformed";
case LifecycleEvent::ObjectChanged: return "ObjectChanged";
case LifecycleEvent::ObjectRenamed: return "ObjectRenamed";
case LifecycleEvent::PlateCreated: return "PlateCreated";
case LifecycleEvent::PlateDeleted: return "PlateDeleted";
case LifecycleEvent::PlateSelected: return "PlateSelected";
case LifecycleEvent::PlateRenamed: return "PlateRenamed";
case LifecycleEvent::PresetSelected: return "PresetSelected";
case LifecycleEvent::PresetSaved: return "PresetSaved";
case LifecycleEvent::PrintStateChanged: return "PrintStateChanged";
case LifecycleEvent::DeviceOnlineChanged: return "DeviceOnlineChanged";
case LifecycleEvent::DeviceDiscovered: return "DeviceDiscovered";
case LifecycleEvent::DeviceSelected: return "DeviceSelected";
case LifecycleEvent::DeviceConnected: return "DeviceConnected";
case LifecycleEvent::DeviceDisconnected: return "DeviceDisconnected";
case LifecycleEvent::UploadStarted: return "UploadStarted";
case LifecycleEvent::UploadFinished: return "UploadFinished";
case LifecycleEvent::PrintJobStarted: return "PrintJobStarted";
case LifecycleEvent::PrintJobFinished: return "PrintJobFinished";
case LifecycleEvent::SendJobStarted: return "SendJobStarted";
case LifecycleEvent::SendJobFinished: return "SendJobFinished";
default: return "Unknown";
}
}
inline std::string lifecycle_evt_code_to_string(LifecycleEvtCode code)
{
switch (code) {
case LifecycleEvtCode::Ok: return "Ok";
case LifecycleEvtCode::Error: return "Error";
case LifecycleEvtCode::Warn: return "Warn";
default: return "Unknown";
}
}
// Global cross-layer seam (mirrors ConfigBase::set_resolve_capability_fn): any libslic3r code can
// fire a lifecycle event without depending on the plugin layer above it, which installs the
// dispatcher here at startup. Not tied to Print/GCode specifically, since nothing here should
// require callers to hold a Print& just to report an event.
using LifecycleHookFn = std::function<void(LifecycleEvent, const LifecycleEventContext&)>;
inline LifecycleHookFn& lifecycle_hook_fn()
{
static LifecycleHookFn fn;
return fn;
}
inline void set_lifecycle_hook_fn(LifecycleHookFn fn) { lifecycle_hook_fn() = std::move(fn); }
inline void fire_lifecycle_event(LifecycleEvent event, const LifecycleEventContext& ctx)
{
if (const LifecycleHookFn& fn = lifecycle_hook_fn(); fn)
fn(event, ctx);
}
}
+10
View File
@@ -47,6 +47,7 @@
#include <boost/log/trivial.hpp>
#include "libslic3r.h"
#include "LifecycleEvents.hpp"
#include "Utils.hpp"
#include "Time.hpp"
#include "PlaceholderParser.hpp"
@@ -2972,6 +2973,7 @@ void PresetCollection::save_current_preset(const std::string &new_name, bool det
// 1) Find the preset with a new_name or create a new one,
// initialize it with the edited config.
auto it = this->find_preset_internal(new_name);
const bool preset_existed = (it != m_presets.end() && it->name == new_name);
if (it != m_presets.end() && it->name == new_name) {
// Preset with the same name found.
Preset &preset = *it;
@@ -3079,6 +3081,14 @@ void PresetCollection::save_current_preset(const std::string &new_name, bool det
this->get_selected_preset().save(&(parent_preset->config));
else
this->get_selected_preset().save(nullptr);
{
LifecycleEventContext ctx;
ctx.name = new_name;
ctx.msg = preset_existed ? "overwrite" : "new";
ctx.code = LifecycleEvtCode::Ok;
fire_lifecycle_event(LifecycleEvent::PresetSaved, ctx);
}
}
// A detached standalone preset for the Full Publish receiver: create a user preset holding
+1
View File
@@ -3824,6 +3824,7 @@ unsigned int PresetBundle::sync_ams_list(std::vector<std::pair<DynamicPrintConfi
ConfigOptionStrings *filament_color_type = project_config.option<ConfigOptionStrings>("filament_colour_type");
ConfigOptionInts * filament_map = project_config.option<ConfigOptionInts>("filament_map");
ConfigOptionInts * filament_volume_map = project_config.option<ConfigOptionInts>("filament_volume_map");
// Snapshot and temporarily strip mixed filament slots so AMS sync operates on physical
// filaments only. A mixed slot is virtual and has no tray to sync against; leaving it in
// would let AMS mapping overwrite it and would break the physical-first slot ordering the
+39 -1
View File
@@ -14,6 +14,7 @@
#include "Flow.hpp"
#include "Geometry/ConvexHull.hpp"
#include "I18N.hpp"
#include "LifecycleEvents.hpp"
#include "ShortestPath.hpp"
#include "Thread.hpp"
#include "Time.hpp"
@@ -2694,6 +2695,13 @@ void Print::process(long long *time_cost_with_cache, bool use_cache)
if (m_objects.empty())
return;
{
LifecycleEventContext ctx;
ctx.name = std::to_string(m_model.id().id);
ctx.code = LifecycleEvtCode::Ok;
fire_lifecycle_event(LifecycleEvent::SliceStarted, ctx);
}
for (PrintObject *obj : m_objects)
obj->clear_shared_object();
@@ -3312,6 +3320,13 @@ void Print::process(long long *time_cost_with_cache, bool use_cache)
}
BOOST_LOG_TRIVIAL(info) << "Slicing process finished." << log_memory_info();
{
LifecycleEventContext ctx;
ctx.name = std::to_string(m_model.id().id);
ctx.code = LifecycleEvtCode::Ok;
fire_lifecycle_event(LifecycleEvent::SliceGeometryFinished, ctx);
}
}
// G-code export process, running at a background thread.
@@ -4911,6 +4926,14 @@ void Print::set_gcode_file_invalidated()
//BBS: add gcode file preload logic
void Print::export_gcode_from_previous_file(const std::string& file, GCodeProcessorResult* result, ThumbnailsGeneratorCallback thumbnail_cb)
{
{
LifecycleEventContext ctx;
ctx.name = std::to_string(m_model.id().id);
ctx.code = LifecycleEvtCode::Ok;
ctx.msg = file;
fire_lifecycle_event(LifecycleEvent::GCodeExportStarted, ctx);
}
try {
GCodeProcessor processor;
GCodeProcessor::s_IsBBLPrinter = is_BBL_printer();
@@ -4930,13 +4953,28 @@ void Print::export_gcode_from_previous_file(const std::string& file, GCodeProces
*result = std::move(processor.extract_result());
result->filament_change_sequence = filament_seq_loaded;
result->nozzle_change_sequence = nozzle_seq_loaded;
} catch (std::exception & /* ex */) {
} catch (std::exception &ex) {
BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << boost::format(": found errors when process gcode file %1%") %file.c_str();
{
LifecycleEventContext ctx;
ctx.name = std::to_string(m_model.id().id);
ctx.code = LifecycleEvtCode::Error;
ctx.msg = file + "\n" + ex.what();
fire_lifecycle_event(LifecycleEvent::GCodeExportFinished, ctx);
}
throw Slic3r::RuntimeError(
std::string("Failed to process the G-code file ") + file + " from previous 3mf\n");
}
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(": process the G-code file %1% successfully")%file.c_str();
{
LifecycleEventContext ctx;
ctx.name = std::to_string(m_model.id().id);
ctx.code = LifecycleEvtCode::Ok;
ctx.msg = file;
fire_lifecycle_event(LifecycleEvent::GCodeExportFinished, ctx);
}
}
std::tuple<float, float> Print::object_skirt_offset(double margin_height) const
+8 -25
View File
@@ -139,16 +139,8 @@ set(SLIC3R_GUI_SOURCES
GUI/TerminalDialog.hpp
GUI/PluginProgressDialog.cpp
GUI/PluginProgressDialog.hpp
GUI/WebDialog.cpp
GUI/WebDialog.hpp
GUI/DockPanel.cpp
GUI/DockPanel.hpp
GUI/WebPanel.cpp
GUI/WebPanel.hpp
GUI/Widgets/WebHosting.cpp
GUI/Widgets/WebHosting.hpp
GUI/AuiPaneLayout.cpp
GUI/AuiPaneLayout.hpp
GUI/PluginWebDialog.cpp
GUI/PluginWebDialog.hpp
GUI/DragCanvas.cpp
GUI/DragCanvas.hpp
GUI/EditGCodeDialog.cpp
@@ -236,7 +228,6 @@ set(SLIC3R_GUI_SOURCES
GUI/GLToolbar.hpp
GUI/ImageDPIFrame.cpp
GUI/ImageDPIFrame.hpp
GUI/IMediaController.hpp
GUI/GUI_App.cpp
GUI/GUI_App.hpp
GUI/GUI_AuxiliaryList.cpp
@@ -360,8 +351,6 @@ set(SLIC3R_GUI_SOURCES
GUI/MediaFilePanel.h
GUI/MediaPlayCtrl.cpp
GUI/MediaPlayCtrl.h
GUI/WebRtcMediaController.cpp
GUI/WebRtcMediaController.hpp
GUI/MeshUtils.cpp
GUI/MeshUtils.hpp
GUI/ModelMall.cpp
@@ -555,8 +544,6 @@ set(SLIC3R_GUI_SOURCES
GUI/WebUserLoginDialog.hpp
GUI/WebViewDialog.cpp
GUI/WebViewDialog.hpp
GUI/WebMediaController.hpp
GUI/WebMediaController.cpp
GUI/Widgets/AMSControl.cpp
GUI/Widgets/AMSControl.hpp
GUI/Widgets/AMSItem.cpp
@@ -751,7 +738,6 @@ set(SLIC3R_GUI_SOURCES
Utils/NetworkAgentFactory.cpp
Utils/ICloudServiceAgent.hpp
Utils/IPrinterAgent.hpp
Utils/ICameraSignalingChannel.hpp
Utils/OrcaCloudServiceAgent.cpp
Utils/OrcaCloudServiceAgent.hpp
Utils/OrcaPrinterAgent.cpp
@@ -922,7 +908,7 @@ else()
set(_opengl_link_lib OpenGL::GL)
endif()
target_link_libraries(libslic3r_gui libslic3r cereal::cereal imgui imguizmo minilzo libvgcode md4c-html glad ${_opengl_link_lib} hidapi mdns ${wxWidgets_LIBRARIES} glfw libcurl OpenSSL::SSL OpenSSL::Crypto LibDataChannel::LibDataChannel noise::noise pybind11::embed)
target_link_libraries(libslic3r_gui libslic3r cereal::cereal imgui imguizmo minilzo libvgcode md4c-html glad ${_opengl_link_lib} hidapi mdns ${wxWidgets_LIBRARIES} glfw libcurl OpenSSL::SSL OpenSSL::Crypto noise::noise pybind11::embed)
if (CMAKE_SYSTEM_NAME STREQUAL "Linux")
# Linux finds wxWidgets in module mode, whose include dirs and definitions
@@ -979,32 +965,29 @@ endif ()
if (APPLE)
# Static FFmpeg from the deps install: nothing to bundle into the .app,
# no rpath/install_name handling. Order matters: avformat -> avcodec -> swscale -> avutil.
find_library(LIBAVFORMAT_LIBRARY NAMES libavformat.a PATHS ${CMAKE_PREFIX_PATH}/lib NO_DEFAULT_PATH)
# no rpath/install_name handling. Order matters: avcodec -> swscale -> avutil.
find_library(LIBAVCODEC_LIBRARY NAMES libavcodec.a PATHS ${CMAKE_PREFIX_PATH}/lib NO_DEFAULT_PATH)
find_library(LIBSWSCALE_LIBRARY NAMES libswscale.a PATHS ${CMAKE_PREFIX_PATH}/lib NO_DEFAULT_PATH)
find_library(LIBAVUTIL_LIBRARY NAMES libavutil.a PATHS ${CMAKE_PREFIX_PATH}/lib NO_DEFAULT_PATH)
if (NOT LIBAVFORMAT_LIBRARY OR NOT LIBAVCODEC_LIBRARY OR NOT LIBSWSCALE_LIBRARY OR NOT LIBAVUTIL_LIBRARY)
message(FATAL_ERROR "Static FFmpeg (libavformat.a/libavcodec.a/libswscale.a/libavutil.a) not found under ${CMAKE_PREFIX_PATH}/lib. Rebuild the deps — FFMPEG builds static-only on macOS.")
if (NOT LIBAVCODEC_LIBRARY OR NOT LIBSWSCALE_LIBRARY OR NOT LIBAVUTIL_LIBRARY)
message(FATAL_ERROR "Static FFmpeg (libavcodec.a/libswscale.a/libavutil.a) not found under ${CMAKE_PREFIX_PATH}/lib. Rebuild the deps — FFMPEG builds static-only on macOS.")
endif ()
target_link_libraries(libslic3r_gui ${LIBAVFORMAT_LIBRARY} ${LIBAVCODEC_LIBRARY} ${LIBSWSCALE_LIBRARY} ${LIBAVUTIL_LIBRARY})
target_link_libraries(libslic3r_gui ${LIBAVCODEC_LIBRARY} ${LIBSWSCALE_LIBRARY} ${LIBAVUTIL_LIBRARY})
target_include_directories(libslic3r_gui SYSTEM PRIVATE ${CMAKE_PREFIX_PATH}/include)
elseif (WIN32)
# Prebuilt shared FFmpeg from the deps install. Windows has no pkg-config,
# so resolve the import libraries out of the deps prefix directly; the DLLs
# are copied next to the executable by the top level CMakeLists.
find_library(LIBAVFORMAT_LIBRARY NAMES avformat PATHS ${CMAKE_PREFIX_PATH}/lib NO_DEFAULT_PATH)
find_library(LIBAVCODEC_LIBRARY NAMES avcodec PATHS ${CMAKE_PREFIX_PATH}/lib NO_DEFAULT_PATH)
find_library(LIBSWSCALE_LIBRARY NAMES swscale PATHS ${CMAKE_PREFIX_PATH}/lib NO_DEFAULT_PATH)
find_library(LIBAVUTIL_LIBRARY NAMES avutil PATHS ${CMAKE_PREFIX_PATH}/lib NO_DEFAULT_PATH)
if (NOT LIBAVCODEC_LIBRARY OR NOT LIBSWSCALE_LIBRARY OR NOT LIBAVUTIL_LIBRARY)
message(FATAL_ERROR "FFmpeg (avcodec/swscale/avutil) not found under ${CMAKE_PREFIX_PATH}/lib. Rebuild the deps.")
endif ()
target_link_libraries(libslic3r_gui ${LIBAVFORMAT_LIBRARY} ${LIBAVCODEC_LIBRARY} ${LIBSWSCALE_LIBRARY} ${LIBAVUTIL_LIBRARY})
target_link_libraries(libslic3r_gui ${LIBAVCODEC_LIBRARY} ${LIBSWSCALE_LIBRARY} ${LIBAVUTIL_LIBRARY})
target_include_directories(libslic3r_gui SYSTEM PRIVATE ${CMAKE_PREFIX_PATH}/include)
else ()
pkg_check_modules(LIBAV REQUIRED IMPORTED_TARGET
libavformat
libavcodec
libswscale
libavutil
-27
View File
@@ -45,25 +45,6 @@ int AVVideoDecoder::open(Bambu_StreamInfo const &info)
return 0;
}
int AVVideoDecoder::open(AVCodecParameters const &parameters)
{
if (avcodec_parameters_to_context(codec_ctx_, &parameters) < 0)
return -1;
auto codec = avcodec_find_decoder(codec_ctx_->codec_id);
if (codec == nullptr) {
fprintf(stderr, "AVVideoDecoder: unsupported codec!\n");
return -1;
}
if (avcodec_open2(codec_ctx_, codec, nullptr) < 0) {
fprintf(stderr, "AVVideoDecoder: could not open codec\n");
return -1;
}
frame_ = av_frame_alloc();
return frame_ == nullptr ? -1 : 0;
}
int AVVideoDecoder::decode(const Bambu_Sample &sample)
{
int ret = -1;
@@ -90,14 +71,6 @@ int AVVideoDecoder::decode(const Bambu_Sample &sample)
return ret;
}
int AVVideoDecoder::decode(const AVPacket &packet)
{
int ret = avcodec_send_packet(codec_ctx_, &packet);
if (ret == 0)
got_frame_ = avcodec_receive_frame(codec_ctx_, frame_) == 0;
return ret;
}
int AVVideoDecoder::flush()
{
int ret = avcodec_send_packet(codec_ctx_, nullptr);
-10
View File
@@ -23,10 +23,8 @@ public:
public:
int open(Bambu_StreamInfo const &info);
int open(AVCodecParameters const &parameters);
int decode(Bambu_Sample const &sample);
int decode(AVPacket const &packet);
int flush();
@@ -36,14 +34,6 @@ public:
bool toWxBitmap(wxBitmap &bitmap, wxSize const & size);
// Native size of the most recently decoded frame, or an unspecified size if
// nothing has decoded yet. Lets a caller learn the video dimensions when the
// container/probe could not report them up front.
wxSize decoded_frame_size() const
{
return got_frame_ && frame_ ? wxSize{frame_->width, frame_->height} : wxSize{};
}
private:
AVCodecContext *codec_ctx_ = nullptr;
AVFrame * frame_ = nullptr;
-20
View File
@@ -1,20 +0,0 @@
#include "AuiPaneLayout.hpp"
namespace Slic3r { namespace GUI {
std::string aui_pane_layout_entry(const std::string& layout, const std::string& pane_name)
{
// Panes are separated by '|'; SavePerspective() escapes a '|' inside a caption as "\|".
const std::string prefix = "name=" + pane_name + ";";
size_t begin = 0;
for (size_t i = 0; i <= layout.size(); ++i) {
if (i < layout.size() && (layout[i] != '|' || (i > 0 && layout[i - 1] == '\\')))
continue;
if (layout.compare(begin, prefix.size(), prefix) == 0)
return layout.substr(begin, i - begin);
begin = i + 1;
}
return {};
}
}} // namespace Slic3r::GUI
-11
View File
@@ -1,11 +0,0 @@
#pragma once
#include <string>
namespace Slic3r { namespace GUI {
// The part a wxAuiManager layout string (wxAuiManager::SavePerspective) holds for `pane_name`, in the
// form wxAuiManager::LoadPaneInfo() takes, or empty when the layout has no such pane.
std::string aui_pane_layout_entry(const std::string& layout, const std::string& pane_name);
}} // namespace Slic3r::GUI
+1 -1
View File
@@ -328,7 +328,7 @@ void SelectMObjectPopup::update_user_devices()
}
m_bind_machine_list.clear();
m_bind_machine_list = dev->get_my_machine_list(dev->get_current_printer_agent_id());
m_bind_machine_list = dev->get_my_machine_list();
//sort list
std::vector<std::pair<std::string, MachineObject*>> user_machine_list;
+12 -3
View File
@@ -8,7 +8,6 @@
#include "libslic3r/Print.hpp"
#include "DeviceCore/DevConfig.h"
#include "DeviceCore/DevConfigUtil.h"
#include "DeviceCore/DevExtruderSystem.h"
#include "DeviceCore/DevFilaBlackList.h"
#include "DeviceCore/DevFilaSystem.h"
@@ -1648,8 +1647,18 @@ bool CalibrationPresetPage::is_blocking_printing()
if (obj_ == nullptr) return true;
PresetBundle* preset_bundle = wxGetApp().preset_bundle;
const auto source_model = preset_bundle->printers.get_edited_preset().get_printer_type(preset_bundle);
return !DevPrinterConfigUtil::is_printer_model_compatible(source_model, *obj_);
auto source_model = preset_bundle->printers.get_edited_preset().get_printer_type(preset_bundle);
auto target_model = obj_->printer_type;
if (source_model != target_model) {
std::vector<std::string> compatible_machine = obj_->get_compatible_machine();
vector<std::string>::iterator it = find(compatible_machine.begin(), compatible_machine.end(), source_model);
if (it == compatible_machine.end()) {
return true;
}
}
return false;
}
bool CalibrationPresetPage::is_nozzle_info_synced() const
+60
View File
@@ -28,6 +28,7 @@ wxEND_EVENT_TABLE()
wxDEFINE_EVENT(EVT_VCAMERA_SWITCH, wxMouseEvent);
wxDEFINE_EVENT(EVT_SDCARD_ABSENT_HINT, wxCommandEvent);
wxDEFINE_EVENT(EVT_CAM_SOURCE_CHANGE, wxCommandEvent);
#define CAMERAPOPUP_CLICK_INTERVAL 20
@@ -101,6 +102,34 @@ CameraPopup::CameraPopup(wxWindow *parent)
top_sizer->Add(0, 0, wxALL, 0);
}
// Orca: custom IP camera source — lets the user point Live Video at any camera URL (Orca feature; not in the reference)
m_custom_camera_input_confirm = new Button(m_panel, _L("Enable"));
m_custom_camera_input_confirm->SetBackgroundColor(wxColour(38, 166, 154));
m_custom_camera_input_confirm->SetBorderColor(wxColour(38, 166, 154));
m_custom_camera_input_confirm->SetTextColor(wxColour(0xFFFFFE));
m_custom_camera_input_confirm->SetFont(Label::Body_14);
m_custom_camera_input_confirm->SetMinSize(wxSize(FromDIP(90), FromDIP(30)));
m_custom_camera_input_confirm->SetPosition(wxDefaultPosition);
m_custom_camera_input_confirm->SetCornerRadius(FromDIP(12));
m_custom_camera_input = new TextInput(m_panel, wxEmptyString, wxEmptyString, wxEmptyString, wxDefaultPosition, wxDefaultSize);
m_custom_camera_input->GetTextCtrl()->SetHint(_L("Hostname or IP"));
m_custom_camera_input->GetTextCtrl()->SetFont(Label::Body_14);
m_custom_camera_hint = new wxStaticText(m_panel, wxID_ANY, _L("Custom camera source"));
m_custom_camera_hint->Wrap(-1);
m_custom_camera_hint->SetFont(Label::Head_14);
m_custom_camera_hint->SetForegroundColour(TEXT_COL);
m_custom_camera_input_confirm->Bind(wxEVT_BUTTON, &CameraPopup::on_camera_source_changed, this);
if (!wxGetApp().app_config->get("camera", "custom_source").empty()) {
m_custom_camera_input->GetTextCtrl()->SetValue(wxGetApp().app_config->get("camera", "custom_source"));
set_custom_cam_button_state(wxGetApp().app_config->get("camera", "enable_custom_source") == "true");
}
top_sizer->Add(m_custom_camera_hint, 0, wxALIGN_CENTER_VERTICAL | wxALIGN_LEFT | wxALL, FromDIP(5));
top_sizer->Add(0, 0, wxALL, 0);
top_sizer->Add(m_custom_camera_input, 2, wxALIGN_CENTER_VERTICAL | wxEXPAND | wxALL, FromDIP(5));
top_sizer->Add(m_custom_camera_input_confirm, 1, wxALIGN_CENTER_VERTICAL | wxALIGN_RIGHT | wxALL, FromDIP(5));
main_sizer->Add(top_sizer, 0, wxALL, FromDIP(10));
auto url = wxString(L"https://www.orcaslicer.com/wiki/"); // Orca: neutral wiki link (vendor URL removed)
@@ -155,6 +184,37 @@ void CameraPopup::sdcard_absent_hint()
GetEventHandler()->ProcessEvent(evt);
}
void CameraPopup::on_camera_source_changed(wxCommandEvent &event)
{
if (m_obj && !m_custom_camera_input->GetTextCtrl()->IsEmpty()) {
handle_camera_source_change();
}
}
void CameraPopup::handle_camera_source_change()
{
m_custom_camera_enabled = !m_custom_camera_enabled;
set_custom_cam_button_state(m_custom_camera_enabled);
wxGetApp().app_config->set("camera", "custom_source", m_custom_camera_input->GetTextCtrl()->GetValue().ToStdString());
wxGetApp().app_config->set("camera", "enable_custom_source", m_custom_camera_enabled);
wxCommandEvent evt(EVT_CAM_SOURCE_CHANGE);
evt.SetEventObject(this);
GetEventHandler()->ProcessEvent(evt);
}
void CameraPopup::set_custom_cam_button_state(bool state)
{
m_custom_camera_enabled = state;
auto stateColour = state ? wxColour(170, 0, 0) : wxColour(38, 166, 154);
auto stateText = state ? "Disable" : "Enable";
m_custom_camera_input_confirm->SetBackgroundColor(stateColour);
m_custom_camera_input_confirm->SetBorderColor(stateColour);
m_custom_camera_input_confirm->SetLabel(_L(stateText));
}
void CameraPopup::on_switch_recording(wxCommandEvent& event)
{
if (!m_obj) return;
+9 -4
View File
@@ -15,12 +15,14 @@
#include "Widgets/SwitchButton.hpp"
#include "Widgets/RadioBox.hpp"
#include "Widgets/PopupWindow.hpp"
#include "Widgets/TextInput.hpp"
namespace Slic3r {
namespace GUI {
wxDECLARE_EVENT(EVT_VCAMERA_SWITCH, wxMouseEvent);
wxDECLARE_EVENT(EVT_SDCARD_ABSENT_HINT, wxCommandEvent);
wxDECLARE_EVENT(EVT_CAM_SOURCE_CHANGE, wxCommandEvent);
class CameraPopup : public PopupWindow
{
@@ -51,6 +53,9 @@ protected:
void on_switch_recording(wxCommandEvent& event);
void on_set_resolution();
void sdcard_absent_hint();
void on_camera_source_changed(wxCommandEvent& event);
void handle_camera_source_change();
void set_custom_cam_button_state(bool state);
wxWindow * create_item_radiobox(wxString title, wxWindow *parent, wxString tooltip, int padding_left);
void select_curr_radiobox(int btn_idx);
@@ -71,10 +76,10 @@ private:
wxStaticText* m_text_liveview_retry;
SwitchButton* m_switch_liveview_retry;
#endif //BBL_RELEASE_TO_PUBLIC
// wxStaticText* m_custom_camera_hint;
// TextInput* m_custom_camera_input;
// Button* m_custom_camera_input_confirm;
// bool m_custom_camera_enabled{ false };
wxStaticText* m_custom_camera_hint;
TextInput* m_custom_camera_input;
Button* m_custom_camera_input_confirm;
bool m_custom_camera_enabled{ false };
wxStaticText* m_text_resolution;
wxWindow* m_resolution_options[RESOLUTION_OPTIONS_NUM];
wxScrolledWindow *m_panel;
+1 -3
View File
@@ -35,9 +35,7 @@ ConnectPrinterDialog::ConnectPrinterDialog(wxWindow *parent, wxWindowID id, cons
sizer_connect = new wxBoxSizer(wxHORIZONTAL);
m_textCtrl_code = new TextInput(this, wxEmptyString);
// OrcaSonar uses a 12-character base32 access code. Keep this field long
// enough for it while retaining the existing validation for LAN codes.
m_textCtrl_code->GetTextCtrl()->SetMaxLength(12);
m_textCtrl_code->GetTextCtrl()->SetMaxLength(10);
m_textCtrl_code->SetFont(Label::Body_14);
m_textCtrl_code->SetCornerRadius(FromDIP(5));
m_textCtrl_code->SetSize(wxSize(FromDIP(330), FromDIP(40)));
+1 -16
View File
@@ -1,7 +1,5 @@
#include "DevConfigUtil.h"
#include "slic3r/GUI/DeviceManager.hpp"
#include <wx/dir.h>
#include <boost/filesystem/operations.hpp>
#include "../I18N.hpp"
@@ -43,19 +41,6 @@ static void _toolhead_translation_markers()
std::string DevPrinterConfigUtil::m_resource_file_path = "";
bool DevPrinterConfigUtil::is_printer_model_compatible(const std::string& source_model, MachineObject& machine)
{
const std::string& target_model = machine.printer_type;
if (is_optional_printer_model_id(source_model) || is_optional_printer_model_id(target_model))
return true;
if (source_model == target_model)
return true;
const auto compatible_machine = machine.get_compatible_machine();
return std::find(compatible_machine.begin(), compatible_machine.end(), source_model) != compatible_machine.end();
}
std::map<std::string, std::string> DevPrinterConfigUtil::get_all_model_id_with_name()
{
@@ -420,4 +405,4 @@ std::string DevPrinterConfigUtil::get_toolhead_display_name(
return result;
}
};
};
+1 -7
View File
@@ -25,8 +25,6 @@
namespace Slic3r
{
class MachineObject;
/// Toolhead component type (extruder / nozzle / hotend)
enum class ToolHeadComponent {
Extruder,
@@ -61,10 +59,6 @@ public:
/*printer*/
// info
static std::map<std::string, std::string> get_all_model_id_with_name();
// A printer agent may not know the physical model. Keep that case optional so
// model compatibility checks do not turn missing identity into a hard error.
static bool is_printer_model_compatible(const std::string& source_model, MachineObject& machine);
static bool is_optional_printer_model_id(const std::string& model_id) { return model_id.empty(); }
static std::string get_printer_type(const std::string& type_str) { return get_value_from_config<std::string>(type_str, "printer_type"); }
static std::string get_printer_display_name(const std::string& type_str) { return get_value_from_config<std::string>(type_str, "display_name"); }
static std::string get_printer_series_str(std::string type_str) { return get_value_from_config<std::string>(type_str, "printer_series"); }
@@ -233,4 +227,4 @@ static std::string _parse_printer_type(const std::string &type_str)
return type_str;
}
};// namespace Slic3r
};// namespace Slic3r
+4 -4
View File
@@ -762,9 +762,9 @@ void DevFilaSystemParser::ParseV1_0(const json& jj, MachineObject* obj, DevFilaS
{
curr_tray->remain = -1;
}
// The tray objects are reused across status updates. Reset this
// state when a previously empty slot receives a filament again.
curr_tray->is_slot_placeholder = tray_it->contains("tray_slot_placeholder");
if (tray_it->contains("tray_slot_placeholder")) {
curr_tray->is_slot_placeholder = true;
}
int ams_id_int = 0;
int tray_id_int = 0;
try
@@ -999,4 +999,4 @@ void DevFilaSystemParser::ParseAgentFilament(const json& data, MachineObject* ob
}
}
}
}
+49 -91
View File
@@ -3,7 +3,6 @@
#include <exception>
#include "DevManager.h"
#include "CloudProvider.hpp"
#include "DevUtil.h"
// TODO: remove this include
@@ -11,12 +10,11 @@
#include "slic3r/GUI/I18N.hpp"
#include "slic3r/GUI/GUI_App.hpp"
#include "slic3r/GUI/Plater.hpp"
#include "slic3r/plugin/PluginManager.hpp"
#include "slic3r/Utils/NetworkAgentFactory.hpp"
#include "libslic3r/Time.hpp"
#include "IPrinterAgent.hpp"
using namespace nlohmann;
namespace {
@@ -267,10 +265,6 @@ namespace Slic3r
/* update userMachineList info */
auto it = userMachineList.find(dev_id);
if (it != userMachineList.end()) {
// A reused entry may have been created while another printer agent was active.
// The response was obtained through the current agent, so move ownership with
// the entry; otherwise agent-scoped lists hide it after a preset switch.
it->second->printer_agent_id = get_current_printer_agent_id();
if (it->second->get_dev_ip() != dev_ip ||
it->second->bind_state != bind_state ||
it->second->bind_sec_link != sec_link ||
@@ -301,9 +295,6 @@ namespace Slic3r
// update properties
/* ip changed */
obj = it->second;
// A reused LAN entry may have been discovered while another printer agent was
// active. The current discovery message establishes ownership for this agent.
obj->printer_agent_id = get_current_printer_agent_id();
if (obj->get_dev_ip().compare(dev_ip) != 0) {
if ( connection_name.empty() ) {
@@ -365,7 +356,10 @@ namespace Slic3r
obj->bind_state = "free";
obj->last_alive = Slic3r::Utils::get_current_time_utc();
obj->m_is_online = true;
// Route through set_online_state() (rather than writing m_is_online directly) so the
// DeviceOnlineChanged lifecycle event fires consistently; same effective value/behavior
// here since the object was already online in the common case.
obj->set_online_state(true);
obj->set_dev_name(dev_name);
/* if (!obj->dev_ip.empty()) {
Slic3r::GUI::wxGetApp().app_config->set_str("ip_address", obj->dev_id, obj->dev_ip);
@@ -383,6 +377,10 @@ namespace Slic3r
obj->bind_sec_link = sec_link;
obj->dev_connection_name = connection_name;
obj->bind_ssdp_version = ssdp_version;
// Discovery establishes the initial reachability state. Do not report it as an
// online transition; DeviceDiscovered below is the lifecycle event for a new
// device. Subsequent updates route through set_online_state(), so a known device
// still emits DeviceOnlineChanged when its reachability actually changes.
obj->m_is_online = true;
//load access code
@@ -399,6 +397,15 @@ namespace Slic3r
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << " New Machine, dev_id= " << dev_id
<< ", ip = " << dev_ip <<", printer_name = " << dev_name
<< ", con_type= " << connect_type <<", signal= " << printer_signal << ", bind_state= " << bind_state;
// First discovery of a genuinely new device (not a periodic SSDP/heartbeat update to
// an already-known one, which is handled in the branch above).
{
LifecycleEventContext ctx;
ctx.name = dev_id;
ctx.code = LifecycleEvtCode::Ok;
fire_lifecycle_event(LifecycleEvent::DeviceDiscovered, ctx);
}
}
update_local_machine(*obj);
}
@@ -415,9 +422,6 @@ namespace Slic3r
auto it = localMachineList.find(machine.dev_id);
if (it != localMachineList.end()) {
obj = it->second;
// insert_local_device is called by the active agent, so a reused entry must follow
// that agent as well; otherwise the agent-scoped printer list hides it.
obj->printer_agent_id = get_current_printer_agent_id();
} else {
obj = new MachineObject(this, m_agent, machine.dev_name, machine.dev_id, machine.dev_ip);
obj->printer_agent_id = get_current_printer_agent_id();
@@ -510,7 +514,7 @@ namespace Slic3r
MachineObject* DeviceManager::get_my_machine(std::string dev_id)
{
auto list = get_my_machine_list(get_current_printer_agent_id());
auto list = get_my_machine_list();
auto it = list.find(dev_id);
if (it != list.end())
{
@@ -547,15 +551,25 @@ namespace Slic3r
OnSelectedMachineChanged(previous_selected_machine, selected_machine);
}
void DeviceManager::clear_other_devices()
void DeviceManager::clear_other_devices(const std::string& target_agent_id)
{
// Device entries are now scoped by printer_agent_id when they are presented. Keep
// agent-owned discoveries across a switch so agents without automatic discovery (and
// plugins whose devices have not received an access code yet) do not lose their list.
// Entries without an owner are legacy/unscoped and cannot safely be shown.
// why: on agent swap, keep "My Devices" but drop the transient "Other Devices"
// Those belong to the previous agent's network scan; the new agent's start_discovery re-populates its own.
//
// Also drop "My Devices" stamped by a different agent than the one we're swapping to
// (target_agent_id, passed by the caller since the live agent hasn't been repointed yet
// at this point): otherwise a device first discovered under agent A survives every swap
// with a stale printer_agent_id, stays hidden from every agent's filtered list, and only
// gets re-tagged if something happens to delete and re-create it (e.g. account logout).
// Dropping it here instead lets the new agent's start_discovery re-insert and re-stamp it
// like any other fresh device.
const auto my = get_my_machine_list();
for (auto it = localMachineList.begin(); it != localMachineList.end();)
{
if (!it->second || it->second->printer_agent_id.empty())
const bool is_my_device = my.find(it->first) != my.end();
const bool agent_mismatch = !target_agent_id.empty() && it->second &&
it->second->printer_agent_id != target_agent_id;
if (!is_my_device || agent_mismatch)
{
delete it->second;
it = localMachineList.erase(it);
@@ -571,22 +585,8 @@ namespace Slic3r
{
BOOST_LOG_TRIVIAL(info) << "set_selected_machine=" << dev_id
<< " cur_selected=" << selected_machine;
auto my_machine_list = get_my_machine_list(get_current_printer_agent_id());
auto my_machine_list = get_my_machine_list();
auto it = my_machine_list.find(dev_id);
BOOST_LOG_TRIVIAL(info) << "Orca diagnostic: set_selected_machine lookup dev_id=" << dev_id
<< " found=" << (it != my_machine_list.end())
<< " my_machine_count=" << my_machine_list.size()
<< " current_agent=" << get_current_printer_agent_id()
<< " provider=" << GUI::wxGetApp().get_printer_cloud_provider();
if (it != my_machine_list.end() && it->second) {
BOOST_LOG_TRIVIAL(info) << "Orca diagnostic: target machine dev_id=" << it->second->get_dev_id()
<< " printer_agent_id=" << it->second->printer_agent_id
<< " connection_type=" << it->second->connection_type()
<< " dev_connection_type=" << it->second->dev_connection_type;
} else if (!dev_id.empty()) {
BOOST_LOG_TRIVIAL(warning) << "Orca diagnostic: target machine was not found in the current agent's machine list";
return false;
}
// disconnect last if dev_id difference from previous one
auto last_selected = my_machine_list.find(selected_machine);
@@ -597,9 +597,7 @@ namespace Slic3r
m_agent->disconnect_printer();
}
else if (last_selected->second->connection_type() == "cloud") {
const int result = m_agent->set_user_selected_machine("");
BOOST_LOG_TRIVIAL(info) << "Orca diagnostic: cleared previous cloud selection dev_id="
<< selected_machine << " result=" << result;
m_agent->set_user_selected_machine("");
}
}
@@ -651,9 +649,7 @@ namespace Slic3r
{
// diff dev_id, cloud => set_user_selected_machine(new)
BOOST_LOG_TRIVIAL(info) << "set_selected_machine: select new cloud machine, dev_id =" << dev_id;
const int result = m_agent->set_user_selected_machine(dev_id);
BOOST_LOG_TRIVIAL(info) << "Orca diagnostic: set new cloud selection dev_id="
<< dev_id << " result=" << result;
m_agent->set_user_selected_machine(dev_id);
it->second->reset();
}
else
@@ -681,8 +677,6 @@ namespace Slic3r
selected_machine = dev_id;
record_user_last_machine(selected_machine);
BOOST_LOG_TRIVIAL(info) << "Orca diagnostic: DeviceManager selection complete selected_machine="
<< selected_machine;
return true;
}
@@ -713,9 +707,7 @@ namespace Slic3r
dev_list.push_back(it->first);
BOOST_LOG_TRIVIAL(trace) << "add_user_subscribe: " << it->first;
}
const int result = m_agent->add_subscribe(dev_list);
BOOST_LOG_TRIVIAL(info) << "Orca diagnostic: add_user_subscribe count=" << dev_list.size()
<< " result=" << result;
m_agent->add_subscribe(dev_list);
}
@@ -728,9 +720,7 @@ namespace Slic3r
dev_list.push_back(it->first);
BOOST_LOG_TRIVIAL(trace) << "del_user_subscribe: " << it->first;
}
const int result = m_agent->del_subscribe(dev_list);
BOOST_LOG_TRIVIAL(info) << "Orca diagnostic: del_user_subscribe count=" << dev_list.size()
<< " result=" << result;
m_agent->del_subscribe(dev_list);
}
void DeviceManager::subscribe_device_list(std::vector<std::string> dev_list)
@@ -849,23 +839,7 @@ namespace Slic3r
try
{
json j = json::parse(body);
const bool has_request_context = j.contains("provider") && j.contains("agent_id") && j.contains("generation");
const std::string provider = j.contains("provider") ? j["provider"].get<std::string>()
: GUI::wxGetApp().get_printer_cloud_provider();
const std::string agent_id = j.contains("agent_id") ? j["agent_id"].get<std::string>()
: get_current_printer_agent_id();
const std::uint64_t generation = j.value("generation", std::uint64_t(0));
if (has_request_context &&
(provider != GUI::wxGetApp().get_printer_cloud_provider() ||
agent_id != get_current_printer_agent_id() ||
generation != (m_agent ? m_agent->get_user_machine_list_generation() : 0))) {
BOOST_LOG_TRIVIAL(debug) << __FUNCTION__ << ": ignoring stale response provider="
<< provider << " agent_id=" << agent_id
<< " generation=" << generation;
return;
}
const std::string provider = GUI::wxGetApp().get_printer_cloud_provider();
#if !BBL_RELEASE_TO_PUBLIC
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << ": " << j;
@@ -888,14 +862,11 @@ namespace Slic3r
/* update field */
obj = iter->second;
obj->set_dev_id(dev_id);
// A device can be rediscovered by a different agent after a preset
// switch while retaining the same MachineObject instance.
obj->printer_agent_id = agent_id;
}
else
{
obj = new MachineObject(this, m_agent, "", "", "");
obj->printer_agent_id = agent_id;
obj->printer_agent_id = get_current_printer_agent_id();
if (m_agent)
{
obj->set_bind_status(m_agent->get_user_name(provider));
@@ -910,12 +881,6 @@ namespace Slic3r
if (!obj) continue;
// Orca cloud printers are only ever delivered through this REST
// account list; tag them so DeviceManager's cloud/lan branches
// (subscribe + deselect in set_selected_machine) treat them right.
if (provider == ORCA_CLOUD_PROVIDER)
obj->dev_connection_type = "cloud";
if (!elem["dev_id"].is_null())
obj->set_dev_id(elem["dev_id"].get<std::string>());
if (!elem["dev_name"].is_null())
@@ -947,12 +912,6 @@ namespace Slic3r
acc_code.erase(std::remove(acc_code.begin(), acc_code.end(), '\n'), acc_code.end());
obj->set_access_code(acc_code);
}
BOOST_LOG_TRIVIAL(info) << "Orca diagnostic: parsed cloud machine dev_id=" << dev_id
<< " name=" << obj->get_dev_name()
<< " agent_id=" << obj->printer_agent_id
<< " connection_type=" << obj->connection_type()
<< " online=" << obj->m_is_online;
}
//remove MachineObject from userMachineList
@@ -968,9 +927,6 @@ namespace Slic3r
iterat++;
}
}
BOOST_LOG_TRIVIAL(info) << "Orca diagnostic: parse_user_print_info complete provider=" << provider
<< " parsed_count=" << new_list.size()
<< " stored_count=" << userMachineList.size();
}
}
catch (std::exception& e)
@@ -987,14 +943,10 @@ namespace Slic3r
unsigned int http_code;
std::string body;
int result = m_agent->get_user_print_info(&http_code, &body, provider);
BOOST_LOG_TRIVIAL(info) << "Orca diagnostic: get_user_print_info provider=" << provider
<< " result=" << result << " http_code=" << http_code
<< " body_bytes=" << body.size();
if (result == 0)
{
// parse_user_print_info and on_machine_alive (SSDP for discovery) both mutate the same userMachineList map.
// on_machine_alive mutates the map on the UI thread, do the same for parse_user_print_info.
BOOST_LOG_TRIVIAL(info) << "Orca diagnostic: queueing parse_user_print_info on UI thread";
Slic3r::GUI::wxGetApp().CallAfter([this, body]() { parse_user_print_info(body); });
}
}
@@ -1049,8 +1001,14 @@ namespace Slic3r
}
void DeviceManager::OnSelectedMachineChanged(const std::string& /*pre_dev_id*/,
const std::string& /*new_dev_id*/)
const std::string& new_dev_id)
{
{
LifecycleEventContext ctx;
ctx.name = new_dev_id; // empty string is a valid deselection
ctx.code = LifecycleEvtCode::Ok;
fire_lifecycle_event(LifecycleEvent::DeviceSelected, ctx);
}
if (MachineObject* obj_ = get_selected_machine()) {
GUI::wxGetApp().sidebar().update_sync_status(obj_);
if(m_agent->get_filament_sync_mode() == FilamentSyncMode::subscription)
+5 -4
View File
@@ -74,9 +74,10 @@ public:
void erase_user_machine(std::string dev_id) { userMachineList.erase(dev_id); }
void clean_user_info(bool keep_local_selection = false);
// Retain agent-owned LAN discoveries across a switch; the active-agent list filter keeps
// entries from other agents hidden while allowing them to reappear when switched back.
void clear_other_devices();
// target_agent_id: id of the agent being swapped to (empty = no agent-mismatch check,
// just the original "drop Other Devices" behavior). Pass the incoming agent's id, not the
// live one - this runs before the live agent is repointed.
void clear_other_devices(const std::string& target_agent_id = "");
void load_last_machine();
void update_user_machine_list_info(const std::string& provider);
@@ -153,4 +154,4 @@ public:
protected:
virtual void on_timer(wxTimerEvent& event);
};
};
};
+111 -200
View File
@@ -4,8 +4,8 @@
#include "I18N.hpp"
#include "libslic3r/Time.hpp"
#include "libslic3r/Thread.hpp"
#include "slic3r/Utils/Http.hpp"
#include "slic3r/Utils/NetworkAgent.hpp"
#include "slic3r/plugin/PluginManager.hpp"
#include "slic3r/Utils/NetworkAgentFactory.hpp"
#include "GuiColor.hpp"
@@ -54,7 +54,6 @@
#include "DeviceCore/DevStatus.h"
#include "DeviceCore/DevUpgrade.h"
#include "IPrinterAgent.hpp"
#define CALI_DEBUG
#define MINUTE_30 1800000 //ms
@@ -375,22 +374,8 @@ NozzleVolumeType convert_to_nozzle_type(const std::string &str)
wxString MachineObject::get_printer_type_display_str() const
{
std::string display_name = DevPrinterConfigUtil::get_printer_display_name(printer_type);
// Bambu printers use m_resource_file_path + "/printers/" + type_str + ".json", which is a semantic that only works for their profiles.
// For any other profile, we can simply consult preset bundle if the model_id exists.
if (display_name.empty()) {
for (const auto& [vendor_id, vendor] : GUI::wxGetApp().preset_bundle->vendors) {
for (const auto& model : vendor.models) {
if (printer_type == model.model_id)
display_name = model.name;
}
}
}
if (!display_name.empty())
return display_name;
else if (printer_type == "orcasonar")
return "OrcaSonar Printer";
else
return _L("Unknown");
}
@@ -1357,6 +1342,7 @@ int MachineObject::command_get_access_code() {
return this->publish_json(j);
}
int MachineObject::command_request_push_all(bool request_now)
{
auto curr_time = std::chrono::system_clock::now();
@@ -1488,20 +1474,26 @@ int MachineObject::command_upgrade_module(std::string url, std::string module_ty
int MachineObject::command_xyz_abs()
{
if (!m_agent) return -1;
return command_with_dialog(m_agent->command_xyz_abs(get_dev_id(), MachineObject::m_sequence_id++, is_lan_mode_printer()));
return this->publish_gcode("G90 \n");
}
int MachineObject::command_auto_leveling()
{
if (!m_agent) return -1;
return command_with_dialog(m_agent->command_auto_leveling(get_dev_id(), MachineObject::m_sequence_id++, is_lan_mode_printer()));
return this->publish_gcode("G29 \n");
}
int MachineObject::command_go_home()
{
if (!m_agent) return -1;
return command_with_dialog(m_agent->command_go_home(get_dev_id(), this->is_in_printing(), m_support_mqtt_homing, MachineObject::m_sequence_id++, is_lan_mode_printer()));
if (m_support_mqtt_homing)
{
json j;
j["print"]["command"] = "back_to_center";
j["print"]["sequence_id"] = std::to_string(MachineObject::m_sequence_id++);
return this->publish_json(j);
}
// gcode command
return this->is_in_printing() ? this->publish_gcode("G28 X\n") : this->publish_gcode("G28 \n");
}
int MachineObject::command_task_partskip(std::vector<int> part_ids)
@@ -1623,14 +1615,23 @@ int MachineObject::command_stop_buzzer()
int MachineObject::command_set_bed(int temp)
{
if (!m_agent) return -1;
return command_with_dialog(m_agent->command_set_bed(get_dev_id(), temp, m_support_mqtt_bet_ctrl, MachineObject::m_sequence_id++, is_lan_mode_printer()));
if (m_support_mqtt_bet_ctrl)
{
json j;
j["print"]["command"] = "set_bed_temp";
j["print"]["temp"] = temp;
j["print"]["sequence_id"] = std::to_string(MachineObject::m_sequence_id++);
return this->publish_json(j);
}
std::string gcode_str = (boost::format("M140 S%1%\n") % temp).str();
return this->publish_gcode(gcode_str);
}
int MachineObject::command_set_nozzle(int temp)
{
if (!m_agent) return -1;
return command_with_dialog(m_agent->command_set_nozzle(get_dev_id(), temp, MachineObject::m_sequence_id++, is_lan_mode_printer()));
std::string gcode_str = (boost::format("M104 S%1%\n") % temp).str();
return this->publish_gcode(gcode_str);
}
int MachineObject::command_set_nozzle_new(int nozzle_id, int temp)
@@ -1735,8 +1736,9 @@ int MachineObject::command_ams_user_settings(bool start_read_opt, bool tray_read
int MachineObject::command_ams_calibrate(int ams_id)
{
if (!m_agent) return -1;
return command_with_dialog(m_agent->command_ams_calibrate(get_dev_id(), ams_id, MachineObject::m_sequence_id++, is_lan_mode_printer()));
std::string gcode_cmd = (boost::format("M620 C%1% \n") % ams_id).str();
BOOST_LOG_TRIVIAL(trace) << "ams_debug: gcode_cmd" << gcode_cmd;
return this->publish_gcode(gcode_cmd);
}
int MachineObject::command_ams_filament_settings(int ams_id, int slot_id, std::string filament_id, std::string setting_id, std::string tray_color, std::string tray_type, int nozzle_temp_min, int nozzle_temp_max)
@@ -1774,8 +1776,9 @@ int MachineObject::command_ams_filament_settings(int ams_id, int slot_id, std::s
int MachineObject::command_ams_refresh_rfid(std::string tray_id)
{
if (!m_agent) return -1;
return command_with_dialog(m_agent->command_ams_refresh_rfid(get_dev_id(), tray_id, MachineObject::m_sequence_id++, is_lan_mode_printer()));
std::string gcode_cmd = (boost::format("M620 R%1% \n") % tray_id).str();
BOOST_LOG_TRIVIAL(trace) << "ams_debug: gcode_cmd" << gcode_cmd;
return this->publish_gcode(gcode_cmd);
}
int MachineObject::command_ams_refresh_rfid2(int ams_id, int slot_id)
@@ -1788,17 +1791,12 @@ int MachineObject::command_ams_refresh_rfid2(int ams_id, int slot_id)
return this->publish_json(j);
}
int MachineObject::command_start_camera()
{
if (!m_agent) return -1;
return m_agent->command_start_camera(get_dev_id());
}
int MachineObject::command_ams_select_tray(std::string tray_id)
{
if (!m_agent) return -1;
return command_with_dialog(m_agent->command_ams_select_tray(get_dev_id(), tray_id, MachineObject::m_sequence_id++, is_lan_mode_printer()));
std::string gcode_cmd = (boost::format("M620 P%1% \n") % tray_id).str();
BOOST_LOG_TRIVIAL(trace) << "ams_debug: gcode_cmd" << gcode_cmd;
return this->publish_gcode(gcode_cmd);
}
int MachineObject::command_ams_control(std::string action)
@@ -1957,10 +1955,47 @@ int MachineObject::command_ams_air_print_detect(bool air_print_detect)
int MachineObject::command_axis_control(std::string axis, double unit, double input_val, int speed)
{
if (!m_agent) return -1;
return command_with_dialog(m_agent->command_axis_control(get_dev_id(), axis, unit, input_val, speed, is_core_xy(),
m_support_mqtt_axis_control, MachineObject::m_sequence_id++,
is_lan_mode_printer()));
if (m_support_mqtt_axis_control)
{
int dir = input_val > 0 ? 1 : -1;
// i3-arch printers move the bed for Y/Z, so the on-screen direction is
// reversed — same negation the g-code fallback below applies.
if (!is_core_xy() && (axis.compare("Y") == 0 || axis.compare("Z") == 0)) {
dir = -dir;
}
json j;
j["print"]["command"] = "xyz_ctrl";
j["print"]["axis"] = axis;
j["print"]["dir"] = dir;
j["print"]["mode"] = (std::abs(input_val) >= 10) ? 1 : 0;
j["print"]["sequence_id"] = std::to_string(MachineObject::m_sequence_id++);
return this->publish_json(j);
}
double value = input_val;
if (!is_core_xy()) {
if ( axis.compare("Y") == 0
|| axis.compare("Z") == 0) {
value = -1.0 * input_val;
}
}
char cmd[256];
if (axis.compare("X") == 0
|| axis.compare("Y") == 0
|| axis.compare("Z") == 0) {
sprintf(cmd, "M211 S \nM211 X1 Y1 Z1\nM1002 push_ref_mode\nG91 \nG1 %s%0.1f F%d\nM1002 pop_ref_mode\nM211 R\n", axis.c_str(), value * unit, speed);
}
else if (axis.compare("E") == 0) {
sprintf(cmd, "M83 \nG0 %s%0.1f F%d\n", axis.c_str(), value * unit, speed);
}
else {
return -1;
}
return this->publish_gcode(cmd);
}
int MachineObject::command_extruder_control(int nozzle_id, double val)
@@ -2585,67 +2620,33 @@ void MachineObject::reset()
vt_slot.erase(vt_slot.begin() + 1);
}
}
// why: reset reuses MachineObject, so release its lazy subtask
// before dropping the pointer to prevent reconnect leaks.
if (subtask_) {
delete subtask_;
subtask_ = nullptr;
}
subtask_ = nullptr;
has_extra_flow_type = false;
m_partskip_ids.clear();
}
void MachineObject::set_print_state(std::string status)
{
const bool changed = (print_status != status);
print_status = status;
}
// why: printer agents can report progress without BBL cloud task identity.
void MachineObject::update_print_progress(const json& value)
{
if (value.is_string())
mc_print_percent = stoi(value.get<std::string>());
else if (value.is_number_integer())
mc_print_percent = value.get<int>();
else
return;
if (BBLSubTask* curr_task = get_subtask())
curr_task->task_progress = mc_print_percent;
if (changed) {
LifecycleEventContext ctx;
ctx.name = dev_id;
ctx.code = LifecycleEvtCode::Ok;
ctx.msg = print_status;
fire_lifecycle_event(LifecycleEvent::PrintStateChanged, ctx);
}
}
int MachineObject::connect(bool use_openssl)
{
if (get_dev_ip().empty()) return -1;
std::string username = m_agent ? m_agent->default_lan_username() : std::string();
std::string username = "bblp";
std::string password = get_access_code();
std::string port;
std::string host = Http::get_host_from_url(get_dev_ip(), &port);
std::string ca_file;
if (GUI::wxGetApp().preset_bundle) {
const auto& config = GUI::wxGetApp().preset_bundle->printers.get_edited_preset().config;
if (port.empty())
port = config.opt_string("printhost_port");
ca_file = config.opt_string("printhost_cafile");
}
if (host.empty())
host = get_dev_ip();
if (m_agent) {
try {
PrinterConnectionParams params{
get_dev_id(),
host,
port,
username,
password,
use_openssl,
ca_file
};
return m_agent->connect_printer(params);
return m_agent->connect_printer(get_dev_id(), get_dev_ip(), username, password, use_openssl);
} catch (...) {
;
}
@@ -2656,6 +2657,10 @@ int MachineObject::connect(bool use_openssl)
int MachineObject::disconnect()
{
if (m_agent) {
LifecycleEventContext ctx;
ctx.name = dev_id;
ctx.code = LifecycleEvtCode::Ok;
fire_lifecycle_event(LifecycleEvent::DeviceDisconnected, ctx);
return m_agent->disconnect_printer();
}
return -1;
@@ -2686,8 +2691,16 @@ bool MachineObject::is_connecting()
void MachineObject::set_online_state(bool on_off)
{
const bool changed = (m_is_online != on_off);
m_is_online = on_off;
if (!on_off) m_active_state = NotActive;
if (changed) {
LifecycleEventContext ctx;
ctx.name = dev_id;
ctx.code = LifecycleEvtCode::Ok;
ctx.msg = on_off ? "online" : "offline";
fire_lifecycle_event(LifecycleEvent::DeviceOnlineChanged, ctx);
}
}
bool MachineObject::is_info_ready(bool check_version) const
@@ -2750,14 +2763,6 @@ int MachineObject::publish_json(const json& json_item, int qos, int flag)
BOOST_LOG_TRIVIAL(error) << "publish_json: " << json_item.dump() << " code: " << rtn;
}
// why: the agent is the only thing that knows what it can translate, so it reports
// not-supported in its return value and this - the single funnel every command_* builder
// passes through - is the one place that turns it into something the user sees. No list of
// unsupported commands is needed anywhere: an agent that has no case for a command says so.
if (rtn == ORCA_NETWORK_ERR_CMD_NOT_SUPPORTED || rtn == ORCA_NETWORK_ERR_CAP_NOT_AVAILABLE) {
show_unsupported_dlg(rtn);
}
return rtn;
}
@@ -3062,13 +3067,6 @@ int MachineObject::parse_json(std::string tunnel, std::string payload, bool key_
}
} catch (...) {}
try {
if (j.contains("info"))
parse_new_info2(j["info"]);
} catch (...) {
BOOST_LOG_TRIVIAL(error) << "parse_json: failed to parse OrcaSonar capability info";
}
try {
if (auto ptr = m_fila_system->GetAmsFirmwareSwitch().lock()) {
ptr->ParseFirmwareSwitch(j);
@@ -3321,7 +3319,10 @@ int MachineObject::parse_json(std::string tunnel, std::string payload, bool key_
print_type = jj["print_type"].get<std::string>();
}
if (jj.contains("mc_percent")) {
update_print_progress(jj["mc_percent"]);
if (jj["mc_percent"].is_string())
mc_print_percent = stoi(j["print"]["mc_percent"].get<std::string>());
else if (jj["mc_percent"].is_number_integer())
mc_print_percent = j["print"]["mc_percent"].get<int>();
}
if (jj.contains("mc_print_sub_stage")) {
if (jj["mc_print_sub_stage"].is_number_integer())
@@ -3491,9 +3492,6 @@ int MachineObject::parse_json(std::string tunnel, std::string payload, bool key_
this->task_id_ = jj["task_id"].get<std::string>();
}
if (jj.contains("thumbnail_url") && jj["thumbnail_url"].is_string())
m_agent_thumbnail_url = jj["thumbnail_url"].get<std::string>();
if (jj.contains("job_attr")) {
int jobAttr = jj["job_attr"].get<int>();
jobState_ = get_flag_bits(jobAttr, 4, 4);
@@ -3539,6 +3537,7 @@ int MachineObject::parse_json(std::string tunnel, std::string payload, bool key_
update_slice_info(jj["project_id"].get<std::string>(), jj["profile_id"].get<std::string>(), jj["subtask_id"].get<std::string>(), plate_index);
BBLSubTask* curr_task = get_subtask();
if (curr_task) {
curr_task->task_progress = mc_print_percent;
curr_task->printing_status = print_status;
curr_task->task_id = jj["subtask_id"].get<std::string>();
}
@@ -3841,7 +3840,6 @@ int MachineObject::parse_json(std::string tunnel, std::string payload, bool key_
has_ipcam = true;
} else {
has_ipcam = false;
webcam_stream_url.clear();
}
}
if (ipcam.contains("resolution")) {
@@ -3876,9 +3874,6 @@ int MachineObject::parse_json(std::string tunnel, std::string payload, bool key_
liveview_local = local_rtsp_url.empty() ? LVL_None : local_rtsp_url == "disable"
? LVL_Disable : boost::algorithm::starts_with(local_rtsp_url, "rtsps") ? LVL_Rtsps : LVL_Rtsp;
}
if (ipcam.contains("stream_url") && ipcam["stream_url"].is_string()) {
webcam_stream_url = ipcam["stream_url"].get<std::string>();
}
if (ipcam.contains("tutk_server")) {
tutk_state = ipcam["tutk_server"].get<std::string>();
}
@@ -4627,8 +4622,13 @@ int MachineObject::parse_json(std::string tunnel, std::string payload, bool key_
try {
if (j.contains("event")) {
if (j["event"].contains("event")) {
if (j["event"]["event"].get<std::string>() == "client.disconnected")
if (j["event"]["event"].get<std::string>() == "client.disconnected") {
set_online_state(false);
LifecycleEventContext ctx;
ctx.name = dev_id;
ctx.code = LifecycleEvtCode::Ok;
fire_lifecycle_event(LifecycleEvent::DeviceDisconnected, ctx);
}
else if (j["event"]["event"].get<std::string>() == "client.connected")
set_online_state(true);
}
@@ -5458,86 +5458,6 @@ void MachineObject::parse_new_info(json print)
}
}
void MachineObject::parse_new_info2(const json& info)
{
if (!info.is_object() || info.value("command", "") != "get_capabilities")
return;
const auto capabilities_it = info.find("capabilities");
if (capabilities_it == info.end() || !capabilities_it->is_object())
return;
const auto flags_it = capabilities_it->find("flags");
if (flags_it == capabilities_it->end() || !flags_it->is_object())
return;
const json& flags = *flags_it;
BOOST_LOG_TRIVIAL(info) << "parse_new_info2: OrcaSonar capability flags=" << flags.dump();
auto parse_bool = [&flags](const char* name, bool& target) {
const auto it = flags.find(name);
if (it != flags.end() && it->is_boolean())
target = it->get<bool>();
};
parse_bool("support_send_to_sd", is_support_send_to_sdcard);
parse_bool("support_filament_backup", is_support_filament_backup);
parse_bool("support_update_remain", is_support_update_remain);
parse_bool("support_auto_recovery_step_loss", is_support_auto_recovery_step_loss);
parse_bool("support_ams_humidity", is_support_ams_humidity);
parse_bool("support_prompt_sound", is_support_prompt_sound);
parse_bool("support_filament_tangle_detect", is_support_filament_tangle_detect);
parse_bool("support_1080dpi", is_support_1080dpi);
parse_bool("support_cloud_print_only", is_support_cloud_print_only);
parse_bool("support_command_ams_switch", is_support_command_ams_switch);
parse_bool("support_mqtt_alive", is_support_mqtt_alive);
parse_bool("support_motor_noise_cali", is_support_motor_noise_cali);
parse_bool("support_timelapse", is_support_timelapse);
parse_bool("support_user_preset", is_support_user_preset);
parse_bool("support_refresh_nozzle", is_support_refresh_nozzle);
parse_bool("support_flow_calibration", is_support_flow_calibration);
parse_bool("support_build_plate_marker_detect", is_support_build_plate_marker_detect);
parse_bool("support_nozzle_blob_detect", is_support_nozzle_blob_detection);
if (!m_manager->IsMultiMachineEnabled() && !is_support_agora)
parse_bool("support_tunnel_mqtt", is_support_tunnel_mqtt);
const auto bed_leveling_it = flags.find("support_bed_leveling");
if (bed_leveling_it != flags.end() && bed_leveling_it->is_number_integer())
is_support_bed_leveling = bed_leveling_it->get<int>();
auto copy_bool = [&flags](json& target, const char* name) {
const auto it = flags.find(name);
if (it != flags.end() && it->is_boolean())
target[name] = *it;
};
// The capability manifest uses an object for this range, while the legacy
// DeviceCore parser consumes a boolean plus a two-element range array.
json device_config;
copy_bool(device_config, "support_chamber");
copy_bool(device_config, "support_first_layer_inspect");
copy_bool(device_config, "support_ai_monitoring");
copy_bool(device_config, "support_lidar_calibration");
const auto chamber_edit_it = flags.find("support_chamber_temp_edit");
if (chamber_edit_it != flags.end() && chamber_edit_it->is_boolean()) {
device_config["support_chamber_temp_edit"] = *chamber_edit_it;
} else if (chamber_edit_it != flags.end() && chamber_edit_it->is_object()) {
const auto min_it = chamber_edit_it->find("min");
const auto max_it = chamber_edit_it->find("max");
if (min_it != chamber_edit_it->end() && max_it != chamber_edit_it->end() && min_it->is_number() && max_it->is_number()) {
device_config["support_chamber_temp_edit"] = true;
device_config["support_chamber_temp_edit_range"] = {*min_it, *max_it};
}
}
json fan_config;
copy_bool(fan_config, "support_aux_fan");
copy_bool(fan_config, "support_chamber_fan");
m_config->ParseConfig(device_config);
m_fan->ParseV2_0(fan_config);
}
static bool is_hex_digit(char c) {
return std::isxdigit(static_cast<unsigned char>(c)) != 0;
}
@@ -6043,15 +5963,6 @@ bool MachineObject::HasAms() const
return m_fila_system->HasAms();
}
int MachineObject::command_with_dialog(int cmd_result)
{
if (!m_agent)
return -1;
if (cmd_result == ORCA_NETWORK_ERR_CMD_NOT_SUPPORTED || cmd_result == ORCA_NETWORK_ERR_CAP_NOT_AVAILABLE)
show_unsupported_dlg(cmd_result);
return cmd_result;
}
void change_the_opacity(wxColour& colour)
{
if (colour.Alpha() == 255) {
+26 -13
View File
@@ -18,7 +18,6 @@
#include "boost/bimap/bimap.hpp"
#include "libslic3r/calib.hpp"
#include "libslic3r/Utils.hpp"
#include "slic3r/Utils/PrinterNetworkTypes.hpp"
#include "DeviceCore/DevDefs.h"
#include "DeviceCore/DevConfigUtil.h"
@@ -101,6 +100,7 @@ struct DevPrintTaskRatingInfo;
// given nozzle diameter (mm), bucketed per nozzle size to mirror the printer firmware.
bool is_stringing_prone_filament(const std::string& filament_id, float nozzle_diameter);
class MachineObject
{
private:
@@ -307,7 +307,7 @@ public:
bool ams_support_virtual_tray { true };
time_t ams_user_setting_start = 0;
time_t ams_switch_filament_start = 0;
AmsStatusMain ams_status_main = AmsStatusMain::AMS_STATUS_MAIN_IDLE;
AmsStatusMain ams_status_main;
int ams_status_sub;
int ams_version = 0;
@@ -547,12 +547,30 @@ public:
bool xcam_first_layer_inspector { false };
time_t xcam_first_layer_hold_start = 0;
std::string local_rtsp_url;
std::string webcam_stream_url;
std::string tutk_state;
LiveviewLocal liveview_local{ LiveviewLocal::LVL_None };
LiveviewRemote liveview_remote{ LiveviewRemote::LVR_None};
FileLocal file_local{ FileLocal::FL_None };
FileRemote file_remote{ FileRemote::FR_None };
enum LiveviewLocal {
LVL_None,
LVL_Disable,
LVL_Local,
LVL_Rtsps,
LVL_Rtsp
} liveview_local{ LVL_None };
enum LiveviewRemote {
LVR_None,
LVR_Tutk,
LVR_Agora,
LVR_TutkAgora
} liveview_remote{ LVR_None };
enum FileLocal {
FL_None,
FL_Local
} file_local{ FL_None };
enum FileRemote {
FR_None,
FR_Tutk,
FR_Agora,
FR_TutkAgora
} file_remote{ FR_None };
enum PlateMakerDectect : int
{
@@ -690,8 +708,6 @@ public:
std::string subtask_id_;
std::string job_id_;
std::string last_subtask_id_;
// note: printer-agent-supplied thumbnail url, empty when the agent supplies none.
std::string m_agent_thumbnail_url;
BBLSliceInfo* slice_info {nullptr};
boost::thread* get_slice_info_thread { nullptr };
boost::thread* get_model_task_thread { nullptr };
@@ -749,7 +765,6 @@ public:
int command_set_printer_nozzle(std::string nozzle_type, float diameter);
int command_set_printer_nozzle2(int id, std::string nozzle_type, float diameter);
int command_get_access_code();
int command_start_camera();
int command_ack_proceed(json& proceed);
int command_purification_disable();
int command_dont_remind_next_time(json& mqtt_guard_json);
@@ -879,7 +894,6 @@ public:
static bool is_in_printing_status(std::string status);
void set_print_state(std::string status);
void update_print_progress(const json& value);
bool is_connected();
bool is_connecting();
@@ -947,7 +961,6 @@ public:
/*for parse new info*/
bool check_enable_np(const json& print) const;
void parse_new_info(json print);
void parse_new_info2(const json& info);
int get_flag_bits(std::string str, int start, int count = 1) const;
uint32_t get_flag_bits_no_border(std::string str, int start_idx, int count = 1) const;
int get_flag_bits(int num, int start, int count = 1, int base = 10) const;
@@ -976,7 +989,7 @@ public:
void command_set_save_remote_print_file_to_storage(bool save);
private:
int command_with_dialog(int cmd_result);
/* xcam door open check*/
bool is_support_door_open_check = false;
DoorOpenCheckState xcam_door_open_check = DoorOpenCheckState::DOOR_OPEN_CHECK_DISABLE;
-103
View File
@@ -1,103 +0,0 @@
#include "DockPanel.hpp"
#include "GUI_App.hpp"
#include "Plater.hpp"
#include "Widgets/WebHosting.hpp"
#include <wx/weakref.h>
#include <algorithm>
#include <utility>
namespace Slic3r { namespace GUI {
std::string plugin_pane_name(const std::string& plugin_key, const std::string& title)
{
std::string name = "plugin:" + plugin_key + ":" + title;
std::replace_if(name.begin(), name.end(), [](char c) { return c == '|' || c == ';' || c == '=' || c == '\\'; }, '_');
return name;
}
DockPanel::DockPanel(wxWindow* parent,
const std::string& html,
MessageHandler on_message,
CloseHandler on_close,
CloseHandler on_destroyed)
: WebPanel(parent, web_hosting::orca_bridge_script())
, m_html(html)
, m_on_message(std::move(on_message))
, m_on_close(std::move(on_close))
, m_on_destroyed(std::move(on_destroyed))
{
// A link asking for a new window has nowhere to open from a docked panel.
browser()->Bind(wxEVT_WEBVIEW_NEWWINDOW, [](wxWebViewEvent& event) { event.Veto(); });
}
DockPanel::~DockPanel()
{
if (m_on_destroyed)
m_on_destroyed();
}
bool DockPanel::on_page_message(const std::string& kind, const nlohmann::json& data)
{
if (kind == "message") {
if (m_on_message)
m_on_message(data);
return true;
}
if (kind == "close") {
request_close();
return true;
}
return false;
}
void DockPanel::push_message(const nlohmann::json& data)
{
if (!m_closing)
post_to_page(data.dump(-1, ' ', false, nlohmann::json::error_handler_t::replace));
}
void DockPanel::fire_close()
{
if (m_closing)
return;
m_closing = true;
if (m_on_close) {
CloseHandler on_close = std::move(m_on_close);
m_on_close = nullptr;
on_close();
}
}
void DockPanel::request_close()
{
if (m_closing)
return;
fire_close();
// A page-requested close arrives inside the web view's script callback, so destroy later; another
// close path may have destroyed the panel by then.
wxWeakRef<DockPanel> self(this);
CallAfter([self]() {
if (self)
self->remove_pane();
});
}
void DockPanel::destroy_silently()
{
m_closing = true;
m_on_close = nullptr;
remove_pane();
}
void DockPanel::remove_pane()
{
if (Plater* plater = wxGetApp().plater())
plater->remove_dock_pane(this);
else
Destroy();
}
}} // namespace Slic3r::GUI
-54
View File
@@ -1,54 +0,0 @@
#pragma once
#include "WebPanel.hpp"
#include <functional>
#include <string>
namespace Slic3r { namespace GUI {
// Stable across sessions so the saved layout finds the pane; free of wxAuiManager layout delimiters.
std::string plugin_pane_name(const std::string& plugin_key, const std::string& title);
// A WebPanel docked in the Plater, on the plugin-window bridge minus submit. It can be destroyed
// without the GIL, so its hooks must not capture pybind11 objects.
class DockPanel : public WebPanel
{
public:
using MessageHandler = std::function<void(const nlohmann::json& data)>;
using CloseHandler = std::function<void()>;
// on_close fires once, on a user or page close. on_destroyed runs on every destruction and must
// touch host-side state only.
DockPanel(wxWindow* parent,
const std::string& html,
MessageHandler on_message,
CloseHandler on_close,
CloseHandler on_destroyed);
~DockPanel() override;
// Main thread only.
void push_message(const nlohmann::json& data);
// Fires on_close, then removes the pane.
void request_close();
// Removes the pane without on_close, for plugin unload. Destroys at once: unload always comes from
// the host, never from this panel's own callbacks.
void destroy_silently();
// Fires on_close at most once; also run by the pane's own close button.
void fire_close();
protected:
std::optional<std::string> page_html() override { return m_html; }
bool on_page_message(const std::string& kind, const nlohmann::json& data) override;
private:
void remove_pane();
std::string m_html;
bool m_closing{false};
MessageHandler m_on_message;
CloseHandler m_on_close;
CloseHandler m_on_destroyed;
};
}} // namespace Slic3r::GUI
+28 -3
View File
@@ -43,6 +43,7 @@
#include "slic3r/GUI/Gizmos/GLGizmoPainterBase.hpp"
#include "slic3r/Utils/UndoRedo.hpp"
#include "slic3r/Utils/MacDarkMode.hpp"
#include "slic3r/plugin/PluginManager.hpp"
#include <slic3r/GUI/GUI_Utils.hpp>
@@ -5026,8 +5027,16 @@ void GLCanvas3D::do_move(const std::string& snapshot_type)
//BBS: nofity object list to update
wxGetApp().plater()->sidebar().obj_list()->update_plate_values_for_items();
if (object_moved)
if (object_moved) {
Slic3r::LifecycleEventContext ctx;
ctx.code = Slic3r::LifecycleEvtCode::Ok;
ctx.msg = "moved";
if (done.size() == 1)
ctx.name = m_model->objects[done.begin()->first]->name;
Slic3r::fire_lifecycle_event(Slic3r::LifecycleEvent::ObjectTransformed, ctx);
post_event(SimpleEvent(EVT_GLCANVAS_INSTANCE_MOVED));
}
// BBS: support wipe-tower for multi-plates
for (int plate_id = 0; plate_id < wipe_tower_origins.size(); plate_id++) {
@@ -5148,8 +5157,16 @@ void GLCanvas3D::do_rotate(const std::string& snapshot_type)
//BBS: nofity object list to update
wxGetApp().plater()->sidebar().obj_list()->update_plate_values_for_items();
if (!done.empty())
if (!done.empty()) {
Slic3r::LifecycleEventContext ctx;
ctx.code = Slic3r::LifecycleEvtCode::Ok;
ctx.msg = "rotated";
if (done.size() == 1)
ctx.name = m_model->objects[done.begin()->first]->name;
Slic3r::fire_lifecycle_event(Slic3r::LifecycleEvent::ObjectTransformed, ctx);
post_event(SimpleEvent(EVT_GLCANVAS_INSTANCE_ROTATED));
}
m_dirty = true;
}
@@ -5240,8 +5257,16 @@ void GLCanvas3D::do_scale(const std::string& snapshot_type)
//BBS: notify object info update
wxGetApp().plater()->show_object_info();
if (!done.empty())
if (!done.empty()) {
Slic3r::LifecycleEventContext ctx;
ctx.code = Slic3r::LifecycleEvtCode::Ok;
ctx.msg = "scaled";
if (done.size() == 1)
ctx.name = m_model->objects[done.begin()->first]->name;
Slic3r::fire_lifecycle_event(Slic3r::LifecycleEvent::ObjectTransformed, ctx);
post_event(SimpleEvent(EVT_GLCANVAS_INSTANCE_SCALED));
}
m_dirty = true;
}
+57 -20
View File
@@ -4,7 +4,6 @@
#include "libslic3r/Platform.hpp"
#include "GUI_App.hpp"
#include "Shortcuts.hpp"
#include "DeviceCore/DevConfigUtil.h"
#include "BindDialog.hpp"
#include "DeviceManager.hpp"
#include "HMS.hpp"
@@ -25,6 +24,7 @@
#include <boost/locale/encoding_utf.hpp>
#include <boost/log/detail/native_typeof.hpp>
#include <libslic3r/Config.hpp>
#include <mutex>
#include <slic3r/plugin/PythonPluginInterface.hpp>
#include <wx/event.h>
@@ -41,11 +41,9 @@
#include <iterator>
#include <exception>
#include <cstdlib>
#include <mutex>
#include <regex>
#include <thread>
#include <string_view>
#include <boost/algorithm/string/predicate.hpp>
#include <boost/algorithm/string.hpp>
#include <boost/format.hpp>
@@ -90,6 +88,7 @@
#include "libslic3r/Thread.hpp"
#include "libslic3r/miniz_extension.hpp"
#include "libslic3r/Utils.hpp"
#include "libslic3r/Color.hpp"
#include "slic3r/plugin/PluginManager.hpp"
#include "slic3r/plugin/host/PluginHostUi.hpp"
#include "slic3r/plugin/PythonInterpreter.hpp"
@@ -109,18 +108,19 @@
#include "../Utils/PrintHost.hpp"
#include "../Utils/Process.hpp"
#include "../Utils/wxInspectorPlugins/Registration.hpp"
#include "../Utils/MacDarkMode.hpp"
#include "../Utils/Http.hpp"
#include "../Utils/InstanceID.hpp"
#include "../Utils/UndoRedo.hpp"
#include "slic3r/Config/Snapshot.hpp"
#include "Preferences.hpp"
#include "Tab.hpp"
#include "SysInfoDialog.hpp"
#include "UpdateDialogs.hpp"
#include "Mouse3DController.hpp"
#include "RemovableDriveManager.hpp"
#include "InstanceCheck.hpp"
#ifdef __APPLE__
#include "../Utils/MacDarkMode.hpp"
#include "DeepLinkHandlerMac.h"
#endif
#include "NotificationManager.hpp"
@@ -129,6 +129,8 @@
#include "PrintHostDialogs.hpp"
#include "NetworkPluginDialog.hpp"
#include "DesktopIntegrationDialog.hpp"
#include "SendSystemInfoDialog.hpp"
#include "ParamsDialog.hpp"
#include "KBShortcutsDialog.hpp"
#include "DownloadProgressDialog.hpp"
#include "TroubleshootDialog.hpp"
@@ -139,6 +141,7 @@
#include "Widgets/ProgressDialog.hpp"
//BBS: DailyTip and UserGuide Dialog
#include "WebDownPluginDlg.hpp"
#include "WebGuideDialog.hpp"
#include "ReleaseNote.hpp"
#include "PrivacyUpdateDialog.hpp"
@@ -2179,6 +2182,11 @@ void GUI_App::init_networking_callbacks()
obj->command_get_access_code();
if (m_agent)
m_agent->install_device_cert(obj->get_dev_id(), obj->is_lan_mode_printer());
LifecycleEventContext ctx;
ctx.name = obj->get_dev_id();
ctx.code = LifecycleEvtCode::Ok;
fire_lifecycle_event(LifecycleEvent::DeviceConnected, ctx);
}
});
});
@@ -2217,6 +2225,11 @@ void GUI_App::init_networking_callbacks()
obj->command_get_version();
event.SetInt(0);
event.SetString(obj->get_dev_id());
LifecycleEventContext ctx;
ctx.name = obj->get_dev_id();
ctx.code = LifecycleEvtCode::Ok;
fire_lifecycle_event(LifecycleEvent::DeviceConnected, ctx);
} else if (state == ConnectStatus::ConnectStatusFailed) {
// Orca: only update status if same device id
if (m_device_manager->selected_machine != dev_id) return;
@@ -2232,10 +2245,22 @@ void GUI_App::init_networking_callbacks()
wxGetApp().show_dialog(text);
}
event.SetInt(-1);
{
LifecycleEventContext ctx;
ctx.name = dev_id;
ctx.code = LifecycleEvtCode::Ok;
fire_lifecycle_event(LifecycleEvent::DeviceDisconnected, ctx);
}
} else if (state == ConnectStatus::ConnectStatusLost) {
m_device_manager->set_selected_machine("");
event.SetInt(-1);
BOOST_LOG_TRIVIAL(info) << "set_on_local_connect_fn: state = lost";
LifecycleEventContext ctx;
ctx.name = dev_id;
ctx.code = LifecycleEvtCode::Ok;
fire_lifecycle_event(LifecycleEvent::DeviceDisconnected, ctx);
} else {
event.SetInt(-1);
BOOST_LOG_TRIVIAL(info) << "set_on_local_connect_fn: state = " << state;
@@ -2385,8 +2410,14 @@ bool GUI_App::is_blocking_printing(MachineObject *obj_)
{
DeviceManager *dev = Slic3r::GUI::wxGetApp().getDeviceManager();
if (!dev) return true;
std::string target_model;
if (obj_ == nullptr) {
obj_ = dev->get_selected_machine();
if (obj_) {
target_model = obj_->printer_type;
}
} else {
target_model = obj_->printer_type;
}
if (!obj_)
@@ -2397,7 +2428,14 @@ bool GUI_App::is_blocking_printing(MachineObject *obj_)
PresetBundle *preset_bundle = wxGetApp().preset_bundle;
std::string source_model = preset_bundle->printers.get_edited_preset().get_printer_type(preset_bundle);
return !DevPrinterConfigUtil::is_printer_model_compatible(source_model, *obj_);
if (source_model != target_model) {
std::vector<std::string> compatible_machine = obj_->get_compatible_machine();
vector<std::string>::iterator it = find(compatible_machine.begin(), compatible_machine.end(), source_model);
if (it == compatible_machine.end()) {
return true;
}
}
return false;
}
// If formatted for github, plaintext with OpenGL extensions enclosed into <details>.
@@ -3952,16 +3990,20 @@ void GUI_App::set_live_printer_agent(std::shared_ptr<IPrinterAgent> agent)
m_agent->set_user_selected_machine("");
// note: belt-and-suspenders (precedent: DeviceManagerRefresher::on_timer)
dev->OnSelectedMachineLost(); // why: clear stale sidebar sync-status / AMS
// why: retain agent-owned LAN discoveries so agents without automatic discovery (for
// example the Moonraker-based Qidi/Snapmaker agents) can reuse them after a switch.
dev->clear_other_devices();
// why: drop stale LAN discoveries; keep My Devices, but only those belonging to the
// agent we're about to swap to, so a device stamped by the outgoing agent doesn't
// linger hidden - the new agent's start_discovery re-inserts and re-stamps it fresh.
// agent is null when clearing the live agent entirely (e.g. plugin unload); there's no
// target to filter against then, so fall back to the original "keep all My Devices"
// behavior rather than guessing.
dev->clear_other_devices(agent ? agent->get_agent_info().id : std::string());
}
m_agent->set_printer_agent(agent);
sidebar().update_all_preset_comboboxes();
}
std::string GUI_App::resolve_printer_agent_id(const std::string& stored_id) const
std::string GUI_App::resolve_printer_agent_id(const std::string& stored_id)
{
if (!stored_id.empty())
return stored_id;
@@ -3997,7 +4039,6 @@ void GUI_App::switch_printer_agent()
std::string log_dir = data_dir();
std::string cloud_agent_id = agent_info.id == BBL_PRINTER_AGENT_ID ? BBL_CLOUD_PROVIDER : ORCA_CLOUD_PROVIDER;
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << " " << agent_info.id;
std::shared_ptr<ICloudServiceAgent> cloud_agent = m_agent->get_cloud_agent(cloud_agent_id);
// Create new printer agent via registry
@@ -4011,10 +4052,8 @@ void GUI_App::switch_printer_agent()
return;
}
// Compare the registered IDs, not only the implementation pointer. Different registry IDs
// may intentionally be backed by the same implementation object (especially for plugins).
const auto current_printer_agent = m_agent->get_printer_agent();
if (current_printer_agent && current_printer_agent->get_agent_info().id == effective_agent_id) {
// The factory caches agents per ID, so an identical pointer means the agent type is unchanged.
if (m_agent->get_printer_agent() == new_printer_agent) {
// Orca: the agent type is unchanged (e.g. switching between two Moonraker/Klipper
// printer presets), so the selected machine and the agent's cached device_info still
// point at the previously active printer preset. Re-select the machine when the new
@@ -4995,16 +5034,14 @@ bool GUI_App::is_user_login(const std::string& provider/* = ORCA_CLOUD_PROVIDER*
return false;
}
std::string GUI_App::get_printer_cloud_provider() const
const std::string& GUI_App::get_printer_cloud_provider() const
{
const std::string agent_id = resolve_printer_agent_id(
preset_bundle ? preset_bundle->printers.get_edited_preset().config.opt_string("printer_agent")
: std::string());
return agent_id == BBL_PRINTER_AGENT_ID ? BBL_CLOUD_PROVIDER : ORCA_CLOUD_PROVIDER;
// Orca todo: this need to be revisted. currently it is mainly used for device manager and related clausses and only bambu machines use them.
//
return BBL_CLOUD_PROVIDER;
}
bool GUI_App::check_login(const std::string& provider/* = ORCA_CLOUD_PROVIDER*/)
{
bool result = false;
+5 -2
View File
@@ -23,6 +23,9 @@
#include <wx/snglinst.h>
#include <wx/msgdlg.h>
#include <mutex>
#include <stack>
//#define BBL_HAS_FIRST_PAGE 1
#define STUDIO_INACTIVE_TIMEOUT 15*60*1000
#define LOG_FILES_MAX_NUM 30
@@ -375,7 +378,7 @@ public:
// Reconcile the live printer agent with the stored preset selection.
void switch_printer_agent();
std::string resolve_printer_agent_id(const std::string& stored_id) const;
std::string resolve_printer_agent_id(const std::string& stored_id);
// ORCA TODO: in the future, bbl presets should specify "bbl" printer agent id
// then, all resolve and canonical would just be ORCA<->""
std::string canonical_printer_agent_id(const std::string& picked_id);
@@ -501,7 +504,7 @@ public:
bool check_login(const std::string& provider = ORCA_CLOUD_PROVIDER);
void get_login_info(const std::string& provider = ORCA_CLOUD_PROVIDER);
bool is_user_login(const std::string& provider = ORCA_CLOUD_PROVIDER);
std::string get_printer_cloud_provider() const;
const std::string& get_printer_cloud_provider() const;
void request_user_login(int online_login = 0, const std::string& provider = ORCA_CLOUD_PROVIDER);
void request_user_handle(int online_login = 0, const std::string& provider = ORCA_CLOUD_PROVIDER);
+39 -1
View File
@@ -1,5 +1,6 @@
#include "libslic3r/libslic3r.h"
#include "libslic3r/PresetBundle.hpp"
#include "libslic3r/LifecycleEvents.hpp"
#include "GUI_ObjectList.hpp"
#include "GUI_Factories.hpp"
//#include "GUI_ObjectLayers.hpp"
@@ -10,6 +11,7 @@
#include "BitmapComboBox.hpp"
#include "MainFrame.hpp"
#include "slic3r/Utils/UndoRedo.hpp"
#include "slic3r/plugin/PluginManager.hpp"
#include "OptionsGroup.hpp"
#include "Tab.hpp"
@@ -1159,17 +1161,39 @@ void ObjectList::update_name_in_model(const wxDataViewItem& item) const
if (m_objects_model->GetItemType(item) & itObject) {
std::string name = m_objects_model->GetName(item).ToUTF8().data();
if (obj->name != name) {
const std::string previous_name = obj->name;
obj->name = name;
// if object has just one volume, rename this volume too
if (obj->volumes.size() == 1)
obj->volumes[0]->name = obj->name;
Slic3r::save_object_mesh(*obj);
LifecycleEventContext ctx;
ctx.name = name;
ctx.previous_name = previous_name;
ctx.id = std::to_string(obj->id().id);
ctx.index = obj_idx;
ctx.source = "object";
fire_lifecycle_event(LifecycleEvent::ObjectRenamed, ctx);
}
return;
}
if (volume_id < 0) return;
obj->volumes[volume_id]->name = m_objects_model->GetName(item).ToUTF8().data();
std::string name = m_objects_model->GetName(item).ToUTF8().data();
if (obj->volumes[volume_id]->name == name)
return;
const std::string previous_name = obj->volumes[volume_id]->name;
obj->volumes[volume_id]->name = name;
LifecycleEventContext ctx;
ctx.name = name;
ctx.previous_name = previous_name;
ctx.id = std::to_string(obj->volumes[volume_id]->id().id);
ctx.index = volume_id;
ctx.source = "volume";
fire_lifecycle_event(LifecycleEvent::ObjectRenamed, ctx);
}
void ObjectList::update_name_in_list(int obj_idx, int vol_idx) const
@@ -3519,7 +3543,14 @@ void ObjectList::delete_all_connectors_for_object(int obj_idx)
obj->delete_connectors();
if (obj->volumes.empty() || !obj->has_solid_mesh()) {
const std::string deleted_obj_name = obj->name;
model.delete_object(idx);
{
Slic3r::LifecycleEventContext ctx;
ctx.name = deleted_obj_name;
ctx.code = Slic3r::LifecycleEvtCode::Ok;
Slic3r::fire_lifecycle_event(Slic3r::LifecycleEvent::ObjectDeleted, ctx);
}
m_objects_model->Delete(m_objects_model->GetItemById(idx));
continue;
}
@@ -4088,6 +4119,13 @@ void ObjectList::add_object_to_list(size_t obj_idx, bool call_selection_changed,
const auto item = m_objects_model->AddObject(model_object, warning_bitmap, model_object->is_cut());
Expand(m_objects_model->GetParent(item));
{
Slic3r::LifecycleEventContext ctx;
ctx.name = model_object->name;
ctx.code = Slic3r::LifecycleEvtCode::Ok;
Slic3r::fire_lifecycle_event(Slic3r::LifecycleEvent::ObjectAdded, ctx);
}
if (!do_info_update)
return;
-26
View File
@@ -1,26 +0,0 @@
#pragma once
#include <wx/mediactrl.h>
#include <wx/uri.h>
namespace Slic3r { namespace GUI {
class IMediaController
{
public:
virtual ~IMediaController() = default;
virtual void Load(wxURI url) = 0;
virtual void Play() = 0;
virtual void Stop() = 0;
virtual wxMediaState GetState() { return wxMediaState{}; }
virtual int GetLastError() const { return {}; };
virtual wxSize GetVideoSize() const { return {}; };
};
}} // namespace Slic3r::GUI
+8
View File
@@ -13,6 +13,7 @@
#include "slic3r/GUI/NotificationManager.hpp"
#include "slic3r/GUI/format.hpp"
#include "slic3r/GUI/GUI_ObjectList.hpp"
#include "slic3r/plugin/PluginManager.hpp"
#include "libnest2d/common.hpp"
@@ -698,6 +699,13 @@ void ArrangeJob::finalize(bool canceled, std::exception_ptr &eptr) {
BOOST_LOG_TRIVIAL(debug) << __FUNCTION__ << boost::format(":arrange m_unprintable: name: %4%, bed_id %1%, trans {%2%,%3%}") % ap.bed_idx % unscale<double>(ap.translation(X)) % unscale<double>(ap.translation(Y)) % ap.name;
}
{
Slic3r::LifecycleEventContext ctx;
ctx.code = Slic3r::LifecycleEvtCode::Ok;
ctx.msg = "arranged";
Slic3r::fire_lifecycle_event(Slic3r::LifecycleEvent::ObjectTransformed, ctx);
}
m_plater->update();
// BBS
//wxGetApp().obj_manipul()->set_dirty();
+8
View File
@@ -6,6 +6,7 @@
#include "slic3r/GUI/Plater.hpp"
#include "slic3r/GUI/GLCanvas3D.hpp"
#include "slic3r/GUI/GUI_ObjectList.hpp"
#include "slic3r/plugin/PluginManager.hpp"
#include "libnest2d/common.hpp"
#include <numeric>
@@ -347,6 +348,13 @@ void FillBedJob::finalize(bool canceled, std::exception_ptr &eptr)
m_plater->arrange();
}
m_plater->update();
{
Slic3r::LifecycleEventContext ctx;
ctx.code = Slic3r::LifecycleEvtCode::Ok;
ctx.msg = "arranged";
Slic3r::fire_lifecycle_event(Slic3r::LifecycleEvent::ObjectTransformed, ctx);
}
}
m_plater->mark_plate_toolbar_image_dirty();
+7
View File
@@ -5,6 +5,7 @@
#include "slic3r/GUI/GUI.hpp"
#include "slic3r/GUI/GUI_App.hpp"
#include "slic3r/GUI/NotificationManager.hpp"
#include "slic3r/plugin/PluginManager.hpp"
#include "libslic3r/PresetBundle.hpp"
@@ -254,6 +255,12 @@ void OrientJob::finalize(bool canceled, std::exception_ptr &eptr)
mesh.apply();
}
if (!m_selected.empty()) {
Slic3r::LifecycleEventContext ctx;
ctx.code = Slic3r::LifecycleEvtCode::Ok;
ctx.msg = "auto_oriented";
Slic3r::fire_lifecycle_event(Slic3r::LifecycleEvent::ObjectTransformed, ctx);
}
m_plater->update();
+23 -2
View File
@@ -1,4 +1,5 @@
#include "PrintJob.hpp"
#include "libslic3r/LifecycleEvents.hpp"
#include "libslic3r/MTUtils.hpp"
#include "libslic3r/Model.hpp"
#include "libslic3r/PresetBundle.hpp"
@@ -14,7 +15,6 @@
#include "slic3r/Utils/FileTransferUtils.hpp"
#include "slic3r/Utils/BBLNetworkPlugin.hpp"
#include "NetworkAgent.hpp"
namespace Slic3r {
namespace GUI {
@@ -134,6 +134,13 @@ wxString PrintJob::get_http_error_msg(unsigned int status, std::string body)
void PrintJob::process(Ctl &ctl)
{
LifecycleEventContext start_ctx;
start_ctx.name = m_project_name;
start_ctx.device_id = m_dev_id;
start_ctx.source = "print_job";
fire_lifecycle_event(LifecycleEvent::PrintJobStarted, start_ctx);
m_lifecycle_started = true;
/* display info */
std::string msg;
int curr_percent = 10;
@@ -204,7 +211,7 @@ void PrintJob::process(Ctl &ctl)
params.dev_ip = m_dev_ip;
params.use_ssl_for_ftp = m_local_use_ssl_for_ftp;
params.use_ssl_for_mqtt = m_local_use_ssl;
params.username = m_agent->default_lan_username();
params.username = "bblp";
params.password = m_access_code;
// check access code and ip address
@@ -687,6 +694,7 @@ void PrintJob::process(Ctl &ctl)
}
wxQueueEvent(m_plater, evt);
m_job_finished = true;
m_lifecycle_success = true;
}
}
@@ -699,6 +707,19 @@ void PrintJob::finalize(bool canceled, std::exception_ptr &eptr) {
eptr = std::current_exception();
}
if (m_lifecycle_started && !m_lifecycle_finished) {
LifecycleEventContext finish_ctx;
finish_ctx.name = m_project_name;
finish_ctx.device_id = m_dev_id;
finish_ctx.source = "print_job";
finish_ctx.code = canceled ? LifecycleEvtCode::Warn :
(eptr || !m_lifecycle_success ? LifecycleEvtCode::Error : LifecycleEvtCode::Ok);
finish_ctx.msg = canceled ? "cancelled" : (eptr ? "exception" :
(m_lifecycle_success ? "" : "failed"));
fire_lifecycle_event(LifecycleEvent::PrintJobFinished, finish_ctx);
m_lifecycle_finished = true;
}
if (canceled || eptr)
return;
}
+3
View File
@@ -43,6 +43,9 @@ class PrintJob : public Job
std::function<void()> m_success_fun{nullptr};
std::string m_dev_id;
bool m_job_finished{ false };
bool m_lifecycle_started{ false };
bool m_lifecycle_finished{ false };
bool m_lifecycle_success{ false };
int m_print_job_completed_id = 0;
wxString m_completed_evt_data;
std::function<void()> m_enter_ip_address_fun_fail{ nullptr };
+23 -2
View File
@@ -1,4 +1,5 @@
#include "SendJob.hpp"
#include "libslic3r/LifecycleEvents.hpp"
#include "slic3r/GUI/I18N.hpp"
#include "slic3r/Utils/NetworkAgent.hpp"
#include "libslic3r/MTUtils.hpp"
@@ -126,7 +127,7 @@ void SendJob::process(Ctl &ctl)
if (m_is_check_mode) {
PrintParams verify_params;
verify_params.dev_ip = m_dev_ip;
verify_params.username = agent->default_lan_username();
verify_params.username = "bblp";
verify_params.password = m_access_code;
verify_params.use_ssl_for_ftp = m_local_use_ssl_for_ftp;
verify_params.use_ssl_for_mqtt = m_local_use_ssl;
@@ -148,6 +149,13 @@ void SendJob::process(Ctl &ctl)
}
}
LifecycleEventContext start_ctx;
start_ctx.name = m_project_name;
start_ctx.device_id = m_dev_id;
start_ctx.source = "send_job";
fire_lifecycle_event(LifecycleEvent::SendJobStarted, start_ctx);
m_lifecycle_started = true;
int total_plate_num = m_plater->get_partplate_list().get_plate_count();
PartPlate* plate = m_plater->get_partplate_list().get_plate(job_data.plate_idx);
@@ -211,7 +219,7 @@ void SendJob::process(Ctl &ctl)
// local print access
params.dev_ip = m_dev_ip;
params.username = agent->default_lan_username();
params.username = "bblp";
params.password = m_access_code;
params.use_ssl_for_ftp = m_local_use_ssl_for_ftp;
params.use_ssl_for_mqtt = m_local_use_ssl;
@@ -424,6 +432,19 @@ void SendJob::finalize(bool canceled, std::exception_ptr &eptr)
eptr = std::current_exception();
}
if (m_lifecycle_started && !m_lifecycle_finished) {
LifecycleEventContext finish_ctx;
finish_ctx.name = m_project_name;
finish_ctx.device_id = m_dev_id;
finish_ctx.source = "send_job";
finish_ctx.code = canceled ? LifecycleEvtCode::Warn :
(eptr ? LifecycleEvtCode::Error : (m_job_finished ? LifecycleEvtCode::Ok : LifecycleEvtCode::Error));
finish_ctx.msg = canceled ? "cancelled" : (eptr ? "exception" :
(m_job_finished ? "" : "failed"));
fire_lifecycle_event(LifecycleEvent::SendJobFinished, finish_ctx);
m_lifecycle_finished = true;
}
if (canceled || eptr)
return;
}
+2
View File
@@ -22,6 +22,8 @@ class SendJob : public Job
PrintPrepareData job_data;
std::string m_dev_id;
bool m_job_finished{ false };
bool m_lifecycle_started{ false };
bool m_lifecycle_finished{ false };
int m_print_job_completed_id = 0;
bool m_is_check_mode{false};
bool m_check_and_continue{false};

Some files were not shown because too many files have changed in this diff Show More