mirror of
https://github.com/OrcaSlicer/OrcaSlicer.git
synced 2026-09-22 16:32:40 +00:00
Compare commits
195
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
cda25c3715 | ||
|
|
9e34ca536b | ||
|
|
201617a6c0 | ||
|
|
2dcbee3d76 | ||
|
|
6183007c28 | ||
|
|
b23953d7cf | ||
|
|
cfc6be5001 | ||
|
|
19e094a1d1 | ||
|
|
a2c631753b | ||
|
|
29b9076440 | ||
|
|
323cd3afe1 | ||
|
|
57b3a040e2 | ||
|
|
811b587eb0 | ||
|
|
2876374b45 | ||
|
|
f769a39b7f | ||
|
|
fd9c1218a4 | ||
|
|
adbeefd424 | ||
|
|
423e7b1dbd | ||
|
|
f27b0a1f9d | ||
|
|
cf1c164546 | ||
|
|
bca8dd00dd | ||
|
|
8b7f8cac82 | ||
|
|
aae03257ed | ||
|
|
3fba237c9a | ||
|
|
cfb6349695 | ||
|
|
f53ad6993a | ||
|
|
e168d900f2 | ||
|
|
bc03e3be2f | ||
|
|
f224b8e4a6 | ||
|
|
8b479c9af3 | ||
|
|
d8299c388e | ||
|
|
4d85b6627e | ||
|
|
ee2b4ccf70 | ||
|
|
753c793ea0 | ||
|
|
9c5494b19f | ||
|
|
93a119427a | ||
|
|
d264fcaef4 | ||
|
|
b4ab6dedfd | ||
|
|
ed59f93456 | ||
|
|
cf52c81dcd | ||
|
|
803a2a3239 | ||
|
|
985092bb46 | ||
|
|
cb16a2e547 | ||
|
|
2c2e91b629 | ||
|
|
c3faf903ca | ||
|
|
5f3c957597 | ||
|
|
8557285389 | ||
|
|
a9edab78a8 | ||
|
|
79dede7d59 | ||
|
|
2ac45b2f78 | ||
|
|
b4f33e71e9 | ||
|
|
8c91cab971 | ||
|
|
79077518e9 | ||
|
|
56aa348c89 | ||
|
|
ae1752fbf2 | ||
|
|
cf3f36a1c6 | ||
|
|
cca7d8adb1 | ||
|
|
396d9ff51a | ||
|
|
81540c81e0 | ||
|
|
e7ace6cc99 | ||
|
|
79c20a1fad | ||
|
|
e48f9f47d2 | ||
|
|
2f566e3779 | ||
|
|
37d1edf69a | ||
|
|
f1cf9c69b1 | ||
|
|
1df362888e | ||
|
|
4b2466b179 | ||
|
|
3adcb3e953 | ||
|
|
104dbb2140 | ||
|
|
e482cbbdd7 | ||
|
|
2a5ddd9edb | ||
|
|
c10f83cf61 | ||
|
|
8eeec6935e | ||
|
|
131a2abf99 | ||
|
|
9daecc59b4 | ||
|
|
58e000d842 | ||
|
|
af6be5858e | ||
|
|
3fc7fd99d6 | ||
|
|
3fe043651b | ||
|
|
b0b78c296c | ||
|
|
62791fabfa | ||
|
|
b0ada2dee5 | ||
|
|
8f4df1aa8c | ||
|
|
5fec9b8d19 | ||
|
|
75ed5afbac | ||
|
|
01c553bad9 | ||
|
|
a4376c77c3 | ||
|
|
c459994290 | ||
|
|
3a0fda7d18 | ||
|
|
eb8733f0f4 | ||
|
|
55acb940a8 | ||
|
|
0d691403de | ||
|
|
9b2b75a51d | ||
|
|
25da273ea0 | ||
|
|
dbcb82075f | ||
|
|
2f82cfe40f | ||
|
|
972031cf06 | ||
|
|
2a4792e762 | ||
|
|
dd15ac6146 | ||
|
|
b0469254bc | ||
|
|
2228589e16 | ||
|
|
4d8480e1a1 | ||
|
|
4320cc78d9 | ||
|
|
d9d9678a7e | ||
|
|
d55a0bfec6 | ||
|
|
3b1017df51 | ||
|
|
f5b81d2ffc | ||
|
|
eb9cfe0ecb | ||
|
|
1e8805d43d | ||
|
|
e8f089dfa4 | ||
|
|
84e929ea24 | ||
|
|
abec603e73 | ||
|
|
0ed19d4826 | ||
|
|
b747c13ef4 | ||
|
|
6a12aca495 | ||
|
|
d486db6459 | ||
|
|
fe777b8801 | ||
|
|
1b3e206f34 | ||
|
|
4df1607ec4 | ||
|
|
a0ec8bec8a | ||
|
|
36c362cbd2 | ||
|
|
84fde32616 | ||
|
|
34e349e323 | ||
|
|
44941e571f | ||
|
|
876d6e2499 | ||
|
|
858b3024ff | ||
|
|
6595557c34 | ||
|
|
c729849843 | ||
|
|
c0563be36e | ||
|
|
462f8dce30 | ||
|
|
b2a0485139 | ||
|
|
f16071f083 | ||
|
|
a1505e9bed | ||
|
|
8e48312b1f | ||
|
|
ba7fdb8ceb | ||
|
|
8be9800567 | ||
|
|
c4eabc3c52 | ||
|
|
0d0d281d0c | ||
|
|
c4bbcd322b | ||
|
|
7aea1b1235 | ||
|
|
41c16436bf | ||
|
|
2bbb229c91 | ||
|
|
c2a43157d2 | ||
|
|
738b30a743 | ||
|
|
6e6ffe13f8 | ||
|
|
eb96d127b0 | ||
|
|
60421c33f4 | ||
|
|
300c4b8afb | ||
|
|
96092ef2d3 | ||
|
|
d9002ad87d | ||
|
|
4c1ea0a602 | ||
|
|
9415812d85 | ||
|
|
d3c728557e | ||
|
|
6558c52849 | ||
|
|
a34d056de2 | ||
|
|
7ba5718be6 | ||
|
|
c1163ce7e5 | ||
|
|
da187eaaf9 | ||
|
|
de0268ce86 | ||
|
|
108923bdaa | ||
|
|
44793f7a21 | ||
|
|
7cb1805272 | ||
|
|
d5c528b7c7 | ||
|
|
742cb712d8 | ||
|
|
fd1c5d826c | ||
|
|
58be7f4861 | ||
|
|
4031b00915 | ||
|
|
07c62112b8 | ||
|
|
f23e4963bf | ||
|
|
95279f7084 | ||
|
|
1014558c91 | ||
|
|
6515062a3a | ||
|
|
8153e26b6d | ||
|
|
79fd33d49a | ||
|
|
cdb662d4b2 | ||
|
|
a27fa7b5df | ||
|
|
cdeed107c5 | ||
|
|
be92f7e78c | ||
|
|
76f9de4cfb | ||
|
|
cd6d2cbf5e | ||
|
|
deaca0189f | ||
|
|
7d54ee286d | ||
|
|
dddfc7a8ad | ||
|
|
673bfb0dee | ||
|
|
b9e8c5a38c | ||
|
|
a2a4ca20e4 | ||
|
|
12075459d2 | ||
|
|
f3770f3106 | ||
|
|
90fbf1770f | ||
|
|
bde94ab37f | ||
|
|
6d25e1777e | ||
|
|
7ce26ca8e5 | ||
|
|
367fcce634 | ||
|
|
9b3a44b339 | ||
|
|
6384191102 |
@@ -58,6 +58,9 @@ 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
|
||||
|
||||
@@ -107,6 +110,7 @@ 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,6 +52,15 @@ 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,12 +46,17 @@ 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. 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, 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)).
|
||||
- `@<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,6 +15,7 @@ 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?
|
||||
|
||||
+42
-2
@@ -826,6 +826,37 @@ 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)
|
||||
@@ -1106,6 +1137,7 @@ 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
|
||||
@@ -1118,11 +1150,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)
|
||||
@@ -1142,7 +1174,10 @@ function(orcaslicer_copy_sos target config postfix output_sos)
|
||||
set(_out_dir "${CMAKE_CURRENT_BINARY_DIR}")
|
||||
endif ()
|
||||
|
||||
file(COPY ${CMAKE_PREFIX_PATH}/lib/libavcodec.so
|
||||
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
|
||||
${CMAKE_PREFIX_PATH}/lib/libavcodec.so.61
|
||||
${CMAKE_PREFIX_PATH}/lib/libavcodec.so.61.3.100
|
||||
${CMAKE_PREFIX_PATH}/lib/libavutil.so
|
||||
@@ -1157,6 +1192,9 @@ 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
|
||||
@@ -1286,6 +1324,8 @@ 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
|
||||
|
||||
Vendored
+23
-11
@@ -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" "CMAKE_ARGS" ${ARGN})
|
||||
cmake_parse_arguments(P_ARGS "FORWARD_CONFIG" "INSTALL_DIR;BUILD_COMMAND;INSTALL_COMMAND;SOURCE_DIR" "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,12 +202,18 @@ 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
|
||||
@@ -240,12 +246,14 @@ 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
|
||||
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}
|
||||
)
|
||||
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_build_space
|
||||
DEPENDEES install # do after install
|
||||
COMMENT "Freeing Space: Removing source and build files"
|
||||
@@ -259,6 +267,7 @@ 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
|
||||
@@ -386,10 +395,6 @@ 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
|
||||
@@ -400,6 +405,12 @@ 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
|
||||
@@ -473,6 +484,7 @@ set(_dep_list
|
||||
dep_wxInspector
|
||||
dep_FFMPEG
|
||||
dep_Assimp
|
||||
${DATACHANNEL_PKG}
|
||||
)
|
||||
|
||||
if (MSVC)
|
||||
|
||||
Vendored
+37
@@ -0,0 +1,37 @@
|
||||
# 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}
|
||||
)
|
||||
Vendored
+23
-7
@@ -1,14 +1,26 @@
|
||||
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-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")
|
||||
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")
|
||||
|
||||
ExternalProject_Add(dep_FFMPEG
|
||||
${_ffmpeg_depends}
|
||||
URL ${PREBUILD_URL_${DEPS_ARCH}}
|
||||
URL_HASH SHA256=${PREBUILD_HASH_${DEPS_ARCH}}
|
||||
DOWNLOAD_DIR ${DEP_DOWNLOAD_DIR}/FFMPEG
|
||||
@@ -21,6 +33,8 @@ if (MSVC)
|
||||
)
|
||||
|
||||
else ()
|
||||
set(_openssl_cmd --enable-openssl)
|
||||
|
||||
if (APPLE)
|
||||
set(_minos_cmd
|
||||
"--extra-cflags=-mmacosx-version-min=${DEP_OSX_TARGET}"
|
||||
@@ -52,10 +66,11 @@ 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 ${_conf_cmd}
|
||||
CONFIGURE_COMMAND ${_ffmpeg_configure_command}
|
||||
${_cross_cmd}
|
||||
${_pic_cmd}
|
||||
${_arch_cmd}
|
||||
@@ -63,20 +78,21 @@ 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,rtp,udp
|
||||
--enable-protocol=file,fd,pipe,http,https,rtp,tcp,udp
|
||||
--disable-muxers
|
||||
--enable-muxer=rtp
|
||||
--disable-encoders
|
||||
--disable-decoders
|
||||
--enable-decoder=*aac*,h264*,mp3*,mjpeg,rv*
|
||||
--disable-demuxers
|
||||
--enable-demuxer=h264,mp3,mov
|
||||
--enable-demuxer=h264,mp3,mov,mpjpeg,rtsp,sdp
|
||||
--disable-zlib
|
||||
--disable-avdevice
|
||||
BUILD_IN_SOURCE ON
|
||||
|
||||
@@ -0,0 +1,240 @@
|
||||
# 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
|
||||
@@ -8,7 +8,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: PACKAGE VERSION\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2026-09-22 12:39+0800\n"
|
||||
"POT-Creation-Date: 2026-09-22 17:02+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,9 +6161,6 @@ msgstr ""
|
||||
msgid "Preferences"
|
||||
msgstr ""
|
||||
|
||||
msgid "Open speed dial"
|
||||
msgstr ""
|
||||
|
||||
msgctxt "Menu"
|
||||
msgid "Edit"
|
||||
msgstr ""
|
||||
@@ -6171,6 +6168,9 @@ msgstr ""
|
||||
msgid "View"
|
||||
msgstr ""
|
||||
|
||||
msgid "Open Speed Dial"
|
||||
msgstr ""
|
||||
|
||||
msgid "Preset Bundle"
|
||||
msgstr ""
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: \n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2026-09-22 12:39+0800\n"
|
||||
"POT-Creation-Date: 2026-09-22 17:02+0800\n"
|
||||
"PO-Revision-Date: 2025-03-15 10:55+0100\n"
|
||||
"Last-Translator: \n"
|
||||
"Language-Team: \n"
|
||||
@@ -6637,9 +6637,6 @@ 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"
|
||||
@@ -6648,6 +6645,9 @@ msgstr "Edita"
|
||||
msgid "View"
|
||||
msgstr "Vista"
|
||||
|
||||
msgid "Open Speed Dial"
|
||||
msgstr ""
|
||||
|
||||
# AI Translated
|
||||
msgid "Preset Bundle"
|
||||
msgstr "Paquet de perfils"
|
||||
|
||||
@@ -3,7 +3,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: Orca Slicer\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2026-09-22 12:39+0800\n"
|
||||
"POT-Creation-Date: 2026-09-22 17:02+0800\n"
|
||||
"PO-Revision-Date: \n"
|
||||
"Last-Translator: Jakub Hencl\n"
|
||||
"Language-Team: \n"
|
||||
@@ -6602,9 +6602,6 @@ 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"
|
||||
@@ -6613,6 +6610,9 @@ msgstr "Upravit"
|
||||
msgid "View"
|
||||
msgstr "Zobrazit"
|
||||
|
||||
msgid "Open Speed Dial"
|
||||
msgstr ""
|
||||
|
||||
msgid "Preset Bundle"
|
||||
msgstr "Balík předvoleb"
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: Orca Slicer\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2026-09-22 12:39+0800\n"
|
||||
"POT-Creation-Date: 2026-09-22 17:02+0800\n"
|
||||
"PO-Revision-Date: \n"
|
||||
"Last-Translator: Heiko Liebscher <hliebschergmail.com>\n"
|
||||
"Language-Team: \n"
|
||||
@@ -6490,9 +6490,6 @@ 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"
|
||||
@@ -6501,6 +6498,9 @@ msgstr "Bearbeiten"
|
||||
msgid "View"
|
||||
msgstr "Ansicht"
|
||||
|
||||
msgid "Open Speed Dial"
|
||||
msgstr ""
|
||||
|
||||
msgid "Preset Bundle"
|
||||
msgstr "Vorlagen-Bundle"
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: Orca Slicer\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2026-09-22 12:39+0800\n"
|
||||
"POT-Creation-Date: 2026-09-22 17:02+0800\n"
|
||||
"PO-Revision-Date: 2026-06-17 15:44-0300\n"
|
||||
"Last-Translator: Alexandre Folle de Menezes\n"
|
||||
"Language-Team: \n"
|
||||
@@ -6157,9 +6157,6 @@ msgstr ""
|
||||
msgid "Preferences"
|
||||
msgstr ""
|
||||
|
||||
msgid "Open speed dial"
|
||||
msgstr ""
|
||||
|
||||
msgctxt "Menu"
|
||||
msgid "Edit"
|
||||
msgstr ""
|
||||
@@ -6167,6 +6164,9 @@ msgstr ""
|
||||
msgid "View"
|
||||
msgstr ""
|
||||
|
||||
msgid "Open Speed Dial"
|
||||
msgstr ""
|
||||
|
||||
msgid "Preset Bundle"
|
||||
msgstr ""
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: Orca Slicer\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2026-09-22 12:39+0800\n"
|
||||
"POT-Creation-Date: 2026-09-22 17:02+0800\n"
|
||||
"PO-Revision-Date: \n"
|
||||
"Last-Translator: Ian A. Bassi <>\n"
|
||||
"Language-Team: \n"
|
||||
@@ -6346,9 +6346,6 @@ 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"
|
||||
@@ -6356,6 +6353,9 @@ msgstr "Edición"
|
||||
msgid "View"
|
||||
msgstr "Vista"
|
||||
|
||||
msgid "Open Speed Dial"
|
||||
msgstr ""
|
||||
|
||||
msgid "Preset Bundle"
|
||||
msgstr "Paquete de perfiles"
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: \n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2026-09-22 12:39+0800\n"
|
||||
"POT-Creation-Date: 2026-09-22 17:02+0800\n"
|
||||
"PO-Revision-Date: 2026-07-20 13:33+0200\n"
|
||||
"Last-Translator: Manu Goiogana <mgoiogana@gmail.com>\n"
|
||||
"Language-Team: \n"
|
||||
@@ -6392,9 +6392,6 @@ msgstr "Erakutsi hautatutako objektuaren inguruko ingerada 3D eszenan."
|
||||
msgid "Preferences"
|
||||
msgstr "Hobespenak"
|
||||
|
||||
msgid "Open speed dial"
|
||||
msgstr ""
|
||||
|
||||
# AI Translated
|
||||
msgctxt "Menu"
|
||||
msgid "Edit"
|
||||
@@ -6403,6 +6400,9 @@ msgstr "Editatu"
|
||||
msgid "View"
|
||||
msgstr "Bista"
|
||||
|
||||
msgid "Open Speed Dial"
|
||||
msgstr ""
|
||||
|
||||
msgid "Preset Bundle"
|
||||
msgstr "Aurrezarpen-paketea"
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: Orca Slicer\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2026-09-22 12:39+0800\n"
|
||||
"POT-Creation-Date: 2026-09-22 17:02+0800\n"
|
||||
"PO-Revision-Date: \n"
|
||||
"Last-Translator: \n"
|
||||
"Language-Team: Guislain Cyril, Thomas Lété\n"
|
||||
@@ -6439,9 +6439,6 @@ msgstr "Afficher le tracé contour de l’objet sélectionné dans la scène 3D.
|
||||
msgid "Preferences"
|
||||
msgstr "Préférences"
|
||||
|
||||
msgid "Open speed dial"
|
||||
msgstr ""
|
||||
|
||||
# AI Translated
|
||||
msgctxt "Menu"
|
||||
msgid "Edit"
|
||||
@@ -6450,6 +6447,9 @@ msgstr "Édition"
|
||||
msgid "View"
|
||||
msgstr "Affichage"
|
||||
|
||||
msgid "Open Speed Dial"
|
||||
msgstr ""
|
||||
|
||||
msgid "Preset Bundle"
|
||||
msgstr "Paquet de préréglages"
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: Orca Slicer\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2026-09-22 12:39+0800\n"
|
||||
"POT-Creation-Date: 2026-09-22 17:02+0800\n"
|
||||
"Language: hu\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
@@ -6541,9 +6541,6 @@ 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"
|
||||
@@ -6552,6 +6549,9 @@ msgstr "Szerkesztés"
|
||||
msgid "View"
|
||||
msgstr "Nézet"
|
||||
|
||||
msgid "Open Speed Dial"
|
||||
msgstr ""
|
||||
|
||||
msgid "Preset Bundle"
|
||||
msgstr "Beállításcsomag"
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: Orca Slicer\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2026-09-22 12:39+0800\n"
|
||||
"POT-Creation-Date: 2026-09-22 17:02+0800\n"
|
||||
"PO-Revision-Date: \n"
|
||||
"Last-Translator: \n"
|
||||
"Language-Team: \n"
|
||||
@@ -6543,9 +6543,6 @@ 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"
|
||||
@@ -6554,6 +6551,9 @@ msgstr "Modifica"
|
||||
msgid "View"
|
||||
msgstr "Vista"
|
||||
|
||||
msgid "Open Speed Dial"
|
||||
msgstr ""
|
||||
|
||||
msgid "Preset Bundle"
|
||||
msgstr "Pacchetto profili"
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: Orca Slicer\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2026-09-22 12:39+0800\n"
|
||||
"POT-Creation-Date: 2026-09-22 17:02+0800\n"
|
||||
"PO-Revision-Date: \n"
|
||||
"Last-Translator: \n"
|
||||
"Language-Team: \n"
|
||||
@@ -6553,9 +6553,6 @@ msgstr "3Dシーンで選択したオブジェクトの周りにアウトライ
|
||||
msgid "Preferences"
|
||||
msgstr "設定"
|
||||
|
||||
msgid "Open speed dial"
|
||||
msgstr ""
|
||||
|
||||
# AI Translated
|
||||
msgctxt "Menu"
|
||||
msgid "Edit"
|
||||
@@ -6564,6 +6561,9 @@ msgstr "編集"
|
||||
msgid "View"
|
||||
msgstr "表示"
|
||||
|
||||
msgid "Open Speed Dial"
|
||||
msgstr ""
|
||||
|
||||
# AI Translated
|
||||
msgid "Preset Bundle"
|
||||
msgstr "プリセットバンドル"
|
||||
|
||||
@@ -7,7 +7,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: Orca Slicer\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2026-09-22 12:39+0800\n"
|
||||
"POT-Creation-Date: 2026-09-22 17:02+0800\n"
|
||||
"PO-Revision-Date: 2025-06-02 17:12+0900\n"
|
||||
"Last-Translator: crwusiz <crwusiz@gmail.com>\n"
|
||||
"Language-Team: \n"
|
||||
@@ -6566,9 +6566,6 @@ msgstr "3D 장면에서 선택한 객체 주변에 윤곽선 표시"
|
||||
msgid "Preferences"
|
||||
msgstr "기본 설정"
|
||||
|
||||
msgid "Open speed dial"
|
||||
msgstr ""
|
||||
|
||||
# AI Translated
|
||||
msgctxt "Menu"
|
||||
msgid "Edit"
|
||||
@@ -6577,6 +6574,9 @@ msgstr "편집"
|
||||
msgid "View"
|
||||
msgstr "시점"
|
||||
|
||||
msgid "Open Speed Dial"
|
||||
msgstr ""
|
||||
|
||||
# AI Translated
|
||||
msgid "Preset Bundle"
|
||||
msgstr "사전 설정 번들"
|
||||
|
||||
@@ -7,7 +7,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: \n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2026-09-22 12:39+0800\n"
|
||||
"POT-Creation-Date: 2026-09-22 17:02+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,9 +6528,6 @@ msgstr "Rodyti kontūrą aplink pasirinktą objektą 3D scenoje."
|
||||
msgid "Preferences"
|
||||
msgstr "Parinktys"
|
||||
|
||||
msgid "Open speed dial"
|
||||
msgstr ""
|
||||
|
||||
# AI Translated
|
||||
msgctxt "Menu"
|
||||
msgid "Edit"
|
||||
@@ -6539,6 +6536,9 @@ msgstr "Redaguoti"
|
||||
msgid "View"
|
||||
msgstr "Vaizdas"
|
||||
|
||||
msgid "Open Speed Dial"
|
||||
msgstr ""
|
||||
|
||||
msgid "Preset Bundle"
|
||||
msgstr "Profilių rinkinys"
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: Orca Slicer\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2026-09-22 12:39+0800\n"
|
||||
"POT-Creation-Date: 2026-09-22 17:02+0800\n"
|
||||
"PO-Revision-Date: \n"
|
||||
"Last-Translator: \n"
|
||||
"Language-Team: \n"
|
||||
@@ -7122,9 +7122,6 @@ 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"
|
||||
@@ -7133,6 +7130,9 @@ msgstr "Bewerken"
|
||||
msgid "View"
|
||||
msgstr "Weergave"
|
||||
|
||||
msgid "Open Speed Dial"
|
||||
msgstr ""
|
||||
|
||||
# AI Translated
|
||||
msgid "Preset Bundle"
|
||||
msgstr "Voorinstellingenbundel"
|
||||
|
||||
@@ -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 12:39+0800\n"
|
||||
"POT-Creation-Date: 2026-09-22 17:02+0800\n"
|
||||
"PO-Revision-Date: \n"
|
||||
"Last-Translator: Krzysztof Morga <<tlumaczeniebs@gmail.com>>\n"
|
||||
"Language-Team: \n"
|
||||
@@ -6689,9 +6689,6 @@ 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"
|
||||
@@ -6700,6 +6697,9 @@ msgstr "Edycja"
|
||||
msgid "View"
|
||||
msgstr "Widok"
|
||||
|
||||
msgid "Open Speed Dial"
|
||||
msgstr ""
|
||||
|
||||
# AI Translated
|
||||
msgid "Preset Bundle"
|
||||
msgstr "Pakiet profili"
|
||||
|
||||
@@ -3,7 +3,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: Orca Slicer\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2026-09-22 12:39+0800\n"
|
||||
"POT-Creation-Date: 2026-09-22 17:02+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,9 +6363,6 @@ 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"
|
||||
@@ -6373,6 +6370,9 @@ msgstr "Editar"
|
||||
msgid "View"
|
||||
msgstr "Visualizar"
|
||||
|
||||
msgid "Open Speed Dial"
|
||||
msgstr ""
|
||||
|
||||
msgid "Preset Bundle"
|
||||
msgstr "Pacote de Predefinições"
|
||||
|
||||
|
||||
@@ -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 12:39+0800\n"
|
||||
"POT-Creation-Date: 2026-09-22 17:02+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,9 +6597,6 @@ msgstr "Отображение контура вокруг выбранных м
|
||||
msgid "Preferences"
|
||||
msgstr "Настройки"
|
||||
|
||||
msgid "Open speed dial"
|
||||
msgstr ""
|
||||
|
||||
msgctxt "Menu"
|
||||
msgid "Edit"
|
||||
msgstr "Правка"
|
||||
@@ -6607,6 +6604,9 @@ msgstr "Правка"
|
||||
msgid "View"
|
||||
msgstr "Вид"
|
||||
|
||||
msgid "Open Speed Dial"
|
||||
msgstr ""
|
||||
|
||||
msgid "Preset Bundle"
|
||||
msgstr "Пакет профилей"
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: Orca Slicer\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2026-09-22 12:39+0800\n"
|
||||
"POT-Creation-Date: 2026-09-22 17:02+0800\n"
|
||||
"Language: sv\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
"Content-Type: text/plain; charset=UTF-8\n"
|
||||
@@ -7205,9 +7205,6 @@ 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"
|
||||
@@ -7216,6 +7213,9 @@ msgstr "Redigera"
|
||||
msgid "View"
|
||||
msgstr "Vy"
|
||||
|
||||
msgid "Open Speed Dial"
|
||||
msgstr ""
|
||||
|
||||
# AI Translated
|
||||
msgid "Preset Bundle"
|
||||
msgstr "Förinställningspaket"
|
||||
|
||||
@@ -7,7 +7,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: Orca Slicer\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2026-09-22 12:39+0800\n"
|
||||
"POT-Creation-Date: 2026-09-22 17:02+0800\n"
|
||||
"PO-Revision-Date: 2026-06-19 13:40+0700\n"
|
||||
"Last-Translator: Icezaza\n"
|
||||
"Language-Team: Thai\n"
|
||||
@@ -6520,9 +6520,6 @@ msgstr "แสดงเค้าร่างรอบๆ วัตถุที
|
||||
msgid "Preferences"
|
||||
msgstr "การตั้งค่า"
|
||||
|
||||
msgid "Open speed dial"
|
||||
msgstr ""
|
||||
|
||||
# AI Translated
|
||||
msgctxt "Menu"
|
||||
msgid "Edit"
|
||||
@@ -6531,6 +6528,9 @@ msgstr "แก้ไข"
|
||||
msgid "View"
|
||||
msgstr "มุมมอง"
|
||||
|
||||
msgid "Open Speed Dial"
|
||||
msgstr ""
|
||||
|
||||
msgid "Preset Bundle"
|
||||
msgstr "ชุดที่ตั้งไว้ล่วงหน้า"
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: Orca Slicer\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2026-09-22 12:39+0800\n"
|
||||
"POT-Creation-Date: 2026-09-22 17:02+0800\n"
|
||||
"PO-Revision-Date: 2026-08-21 23:18+0300\n"
|
||||
"Last-Translator: GlauTech\n"
|
||||
"Language-Team: \n"
|
||||
@@ -6588,9 +6588,6 @@ 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"
|
||||
@@ -6599,6 +6596,9 @@ msgstr "Düzen"
|
||||
msgid "View"
|
||||
msgstr "Görünüm"
|
||||
|
||||
msgid "Open Speed Dial"
|
||||
msgstr ""
|
||||
|
||||
msgid "Preset Bundle"
|
||||
msgstr "Ön ayar paketi"
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: orcaslicerua\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2026-09-22 12:39+0800\n"
|
||||
"POT-Creation-Date: 2026-09-22 17:02+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,9 +6557,6 @@ msgstr "Показувати контур навколо виділеного о
|
||||
msgid "Preferences"
|
||||
msgstr "Налаштування"
|
||||
|
||||
msgid "Open speed dial"
|
||||
msgstr ""
|
||||
|
||||
# AI Translated
|
||||
msgctxt "Menu"
|
||||
msgid "Edit"
|
||||
@@ -6568,6 +6565,9 @@ msgstr "Редагування"
|
||||
msgid "View"
|
||||
msgstr "Вигляд"
|
||||
|
||||
msgid "Open Speed Dial"
|
||||
msgstr ""
|
||||
|
||||
msgid "Preset Bundle"
|
||||
msgstr "Набір пресетів"
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: Orca Slicer\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2026-09-22 12:39+0800\n"
|
||||
"POT-Creation-Date: 2026-09-22 17:02+0800\n"
|
||||
"PO-Revision-Date: 2025-10-02 17:43+0700\n"
|
||||
"Last-Translator: \n"
|
||||
"Language-Team: hainguyen.ts13@gmail.com\n"
|
||||
@@ -6907,9 +6907,6 @@ 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"
|
||||
@@ -6918,6 +6915,9 @@ 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"
|
||||
|
||||
@@ -6,7 +6,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: Slic3rPE\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2026-09-22 12:39+0800\n"
|
||||
"POT-Creation-Date: 2026-09-22 17:02+0800\n"
|
||||
"PO-Revision-Date: 2026-06-11 12:37-0300\n"
|
||||
"Last-Translator: Handle <mail@bysb.net>\n"
|
||||
"Language-Team: \n"
|
||||
@@ -6375,9 +6375,6 @@ msgstr "在3D场景中显示选中对象的轮廓"
|
||||
msgid "Preferences"
|
||||
msgstr "偏好设置"
|
||||
|
||||
msgid "Open speed dial"
|
||||
msgstr ""
|
||||
|
||||
# AI Translated
|
||||
msgctxt "Menu"
|
||||
msgid "Edit"
|
||||
@@ -6386,6 +6383,9 @@ msgstr "编辑"
|
||||
msgid "View"
|
||||
msgstr "视图"
|
||||
|
||||
msgid "Open Speed Dial"
|
||||
msgstr ""
|
||||
|
||||
msgid "Preset Bundle"
|
||||
msgstr "预设包"
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@ msgid ""
|
||||
msgstr ""
|
||||
"Project-Id-Version: Orca Slicer\n"
|
||||
"Report-Msgid-Bugs-To: \n"
|
||||
"POT-Creation-Date: 2026-09-22 12:39+0800\n"
|
||||
"POT-Creation-Date: 2026-09-22 17:02+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,9 +6505,6 @@ msgstr "在 3D 場景中顯示選定物件的輪廓"
|
||||
msgid "Preferences"
|
||||
msgstr "偏好設定"
|
||||
|
||||
msgid "Open speed dial"
|
||||
msgstr ""
|
||||
|
||||
# AI Translated
|
||||
msgctxt "Menu"
|
||||
msgid "Edit"
|
||||
@@ -6516,6 +6513,9 @@ msgstr "編輯"
|
||||
msgid "View"
|
||||
msgstr "視角"
|
||||
|
||||
msgid "Open Speed Dial"
|
||||
msgstr ""
|
||||
|
||||
msgid "Preset Bundle"
|
||||
msgstr "設定檔組"
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "Eryone",
|
||||
"version": "02.04.00.05",
|
||||
"version": "02.04.00.06",
|
||||
"force_update": "0",
|
||||
"description": "Eryone configurations",
|
||||
"machine_model_list": [
|
||||
@@ -432,10 +432,18 @@
|
||||
"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"
|
||||
@@ -444,6 +452,10 @@
|
||||
"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"
|
||||
@@ -460,18 +472,50 @@
|
||||
"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"
|
||||
@@ -488,6 +532,14 @@
|
||||
"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"
|
||||
|
||||
@@ -0,0 +1,54 @@
|
||||
{
|
||||
"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"
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
{
|
||||
"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"
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
{
|
||||
"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"
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
{
|
||||
"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"
|
||||
"Eryone PETG-CF"
|
||||
],
|
||||
"filament_type": [
|
||||
"PETG-CF"
|
||||
|
||||
@@ -0,0 +1,60 @@
|
||||
{
|
||||
"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"
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
{
|
||||
"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"
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
{
|
||||
"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"
|
||||
]
|
||||
}
|
||||
+60
@@ -0,0 +1,60 @@
|
||||
{
|
||||
"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"
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
{
|
||||
"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"
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
{
|
||||
"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"
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
{
|
||||
"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"
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
{
|
||||
"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"
|
||||
]
|
||||
}
|
||||
+48
@@ -0,0 +1,48 @@
|
||||
{
|
||||
"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"
|
||||
]
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "WonderMaker",
|
||||
"url": "",
|
||||
"version": "02.04.00.03",
|
||||
"version": "02.04.00.04",
|
||||
"force_update": "0",
|
||||
"description": "WonderMaker configurations",
|
||||
"machine_model_list": [
|
||||
@@ -432,12 +432,8 @@
|
||||
"sub_path": "machine/WonderMaker ZR 0.4 nozzle.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": "fdm_ultra_common",
|
||||
"sub_path": "machine/fdm_ultra_common.json"
|
||||
},
|
||||
{
|
||||
"name": "WonderMaker ZR 0.2 nozzle",
|
||||
@@ -455,6 +451,10 @@
|
||||
"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,6 +467,10 @@
|
||||
"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"
|
||||
|
||||
Executable → Regular
+8
-7
@@ -1,22 +1,23 @@
|
||||
{
|
||||
"type": "machine",
|
||||
"name": "WonderMaker ZR Ultra 0.2 nozzle",
|
||||
"inherits": "WonderMaker ZR Ultra 0.4 nozzle",
|
||||
"inherits": "fdm_ultra_common",
|
||||
"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"
|
||||
],
|
||||
|
||||
Executable → Regular
+1
-49
@@ -1,65 +1,17 @@
|
||||
{
|
||||
"type": "machine",
|
||||
"name": "WonderMaker ZR Ultra 0.4 nozzle",
|
||||
"inherits": "fdm_klipper_common",
|
||||
"inherits": "fdm_ultra_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"
|
||||
]
|
||||
}
|
||||
|
||||
Executable → Regular
+8
-7
@@ -1,22 +1,23 @@
|
||||
{
|
||||
"type": "machine",
|
||||
"name": "WonderMaker ZR Ultra 0.6 nozzle",
|
||||
"inherits": "WonderMaker ZR Ultra 0.4 nozzle",
|
||||
"inherits": "fdm_ultra_common",
|
||||
"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"
|
||||
],
|
||||
|
||||
Executable → Regular
+8
-7
@@ -1,22 +1,23 @@
|
||||
{
|
||||
"type": "machine",
|
||||
"name": "WonderMaker ZR Ultra 0.8 nozzle",
|
||||
"inherits": "WonderMaker ZR Ultra 0.4 nozzle",
|
||||
"inherits": "fdm_ultra_common",
|
||||
"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"
|
||||
],
|
||||
|
||||
Executable → Regular
+5
-15
@@ -1,29 +1,19 @@
|
||||
{
|
||||
"type": "machine",
|
||||
"name": "WonderMaker ZR Ultra S 0.2 nozzle",
|
||||
"inherits": "WonderMaker ZR Ultra S 0.4 nozzle",
|
||||
"inherits": "WonderMaker ZR Ultra 0.2 nozzle",
|
||||
"from": "system",
|
||||
"setting_id": "ZNAx3f7tX3DatiK1",
|
||||
"instantiation": "true",
|
||||
"printer_model": "WonderMaker ZR Ultra S",
|
||||
"nozzle_diameter": [
|
||||
"0.2",
|
||||
"0.2",
|
||||
"0.2",
|
||||
"0.2"
|
||||
],
|
||||
"printer_model": "WonderMaker ZR Ultra S",
|
||||
"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"
|
||||
],
|
||||
"retraction_length": [
|
||||
"0.4",
|
||||
"0.4",
|
||||
"0.4",
|
||||
"0.4"
|
||||
]
|
||||
"printer_variant": "0.2",
|
||||
"support_air_filtration": "1",
|
||||
"support_chamber_temp_control": "1"
|
||||
}
|
||||
|
||||
Executable → Regular
+3
-50
@@ -1,65 +1,18 @@
|
||||
{
|
||||
"type": "machine",
|
||||
"name": "WonderMaker ZR Ultra S 0.4 nozzle",
|
||||
"inherits": "fdm_klipper_common",
|
||||
"inherits": "WonderMaker ZR Ultra 0.4 nozzle",
|
||||
"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"
|
||||
],
|
||||
"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"
|
||||
],
|
||||
"default_print_profile": "0.20mm Standard @WonderMaker ZR Ultra",
|
||||
"support_air_filtration": "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"
|
||||
]
|
||||
"support_chamber_temp_control": "1"
|
||||
}
|
||||
|
||||
Executable → Regular
+8
-21
@@ -1,32 +1,19 @@
|
||||
{
|
||||
"type": "machine",
|
||||
"name": "WonderMaker ZR Ultra S 0.6 nozzle",
|
||||
"inherits": "WonderMaker ZR Ultra S 0.4 nozzle",
|
||||
"inherits": "WonderMaker ZR Ultra 0.6 nozzle",
|
||||
"from": "system",
|
||||
"setting_id": "xuJnOPTmSW1BMo6p",
|
||||
"instantiation": "true",
|
||||
"printer_model": "WonderMaker ZR Ultra S",
|
||||
"default_print_profile": "0.30mm Standard @WonderMaker ZR Ultra 0.6 nozzle",
|
||||
"printer_variant": "0.6",
|
||||
"nozzle_diameter": [
|
||||
"0.6",
|
||||
"0.6",
|
||||
"0.6",
|
||||
"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"
|
||||
]
|
||||
"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"
|
||||
}
|
||||
|
||||
Executable → Regular
+8
-21
@@ -1,32 +1,19 @@
|
||||
{
|
||||
"type": "machine",
|
||||
"name": "WonderMaker ZR Ultra S 0.8 nozzle",
|
||||
"inherits": "WonderMaker ZR Ultra S 0.4 nozzle",
|
||||
"inherits": "WonderMaker ZR Ultra 0.8 nozzle",
|
||||
"from": "system",
|
||||
"setting_id": "0BPLKMspArilfkGT",
|
||||
"instantiation": "true",
|
||||
"printer_model": "WonderMaker ZR Ultra S",
|
||||
"default_print_profile": "0.40mm Standard @WonderMaker ZR Ultra 0.8 nozzle",
|
||||
"printer_variant": "0.8",
|
||||
"nozzle_diameter": [
|
||||
"0.8",
|
||||
"0.8",
|
||||
"0.8",
|
||||
"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"
|
||||
]
|
||||
"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"
|
||||
}
|
||||
|
||||
@@ -0,0 +1,64 @@
|
||||
{
|
||||
"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"
|
||||
]
|
||||
}
|
||||
@@ -575,7 +575,7 @@ function CapabilityCanRun(plugin, capability) {
|
||||
}
|
||||
|
||||
function IsPluginChecked(plugin) {
|
||||
return GetStatus(plugin) === "Activated";
|
||||
return plugin.is_loaded;
|
||||
}
|
||||
|
||||
function HasMixedCapabilityState(plugin) {
|
||||
@@ -1347,6 +1347,8 @@ 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,6 +424,11 @@ 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;
|
||||
@@ -680,6 +685,11 @@ 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);
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
<!DOCTYPE html>
|
||||
<!-- Bootstrap page for PluginWebDialog. The real plugin HTML is loaded via
|
||||
<!-- Bootstrap page for WebDialog. The real plugin HTML is loaded via
|
||||
wxWebView::SetPage once this page finishes loading; this file only exists
|
||||
to bring the webview up. -->
|
||||
<html>
|
||||
@@ -0,0 +1,146 @@
|
||||
# /// 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,6 +331,15 @@ 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.
|
||||
|
||||
@@ -122,17 +122,19 @@ 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 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 ¢er)
|
||||
// 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 ¢er)
|
||||
{
|
||||
const auto *area = cfg.option<ConfigOptionPoints>("printable_area");
|
||||
if (area == nullptr || area->values.size() < 3)
|
||||
return;
|
||||
return Vec2d::Zero();
|
||||
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;
|
||||
return Vec2d::Zero();
|
||||
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.
|
||||
@@ -143,13 +145,22 @@ void place_wipe_tower(DynamicPrintConfig &cfg, const Vec2d ¢er)
|
||||
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()));
|
||||
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>();
|
||||
// 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;
|
||||
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 centered cube that switches from filament 1 to filament 2 partway up, so exactly one
|
||||
// Slice one 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
|
||||
@@ -157,10 +168,10 @@ void place_wipe_tower(DynamicPrintConfig &cfg, const Vec2d ¢er)
|
||||
// Slic3r::PlaceholderParserError from export.
|
||||
std::string slice_two_color_cube_and_export(DynamicPrintConfig cfg, bool is_bbl)
|
||||
{
|
||||
const Vec2d center = printable_area_center(cfg);
|
||||
place_wipe_tower(cfg, center);
|
||||
const Vec2d center = printable_area_center(cfg);
|
||||
const Vec2d cube_min = center - Vec2d(5., 5.) + place_wipe_tower(cfg, center);
|
||||
TriangleMesh m = make_cube(10, 10, 10);
|
||||
m.translate(float(center.x() - 5.), float(center.y() - 5.), 0.f);
|
||||
m.translate(static_cast<float>(cube_min.x()), static_cast<float>(cube_min.y()), 0.f);
|
||||
|
||||
Model model;
|
||||
Print print;
|
||||
|
||||
@@ -3824,7 +3824,6 @@ 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
|
||||
|
||||
@@ -139,8 +139,16 @@ set(SLIC3R_GUI_SOURCES
|
||||
GUI/TerminalDialog.hpp
|
||||
GUI/PluginProgressDialog.cpp
|
||||
GUI/PluginProgressDialog.hpp
|
||||
GUI/PluginWebDialog.cpp
|
||||
GUI/PluginWebDialog.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/DragCanvas.cpp
|
||||
GUI/DragCanvas.hpp
|
||||
GUI/EditGCodeDialog.cpp
|
||||
@@ -228,6 +236,7 @@ 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
|
||||
@@ -351,6 +360,8 @@ 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
|
||||
@@ -544,6 +555,8 @@ 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
|
||||
@@ -738,6 +751,7 @@ 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
|
||||
@@ -908,7 +922,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 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 LibDataChannel::LibDataChannel noise::noise pybind11::embed)
|
||||
|
||||
if (CMAKE_SYSTEM_NAME STREQUAL "Linux")
|
||||
# Linux finds wxWidgets in module mode, whose include dirs and definitions
|
||||
@@ -965,29 +979,32 @@ endif ()
|
||||
|
||||
if (APPLE)
|
||||
# Static FFmpeg from the deps install: nothing to bundle into the .app,
|
||||
# no rpath/install_name handling. Order matters: avcodec -> swscale -> avutil.
|
||||
# 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)
|
||||
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 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.")
|
||||
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.")
|
||||
endif ()
|
||||
target_link_libraries(libslic3r_gui ${LIBAVCODEC_LIBRARY} ${LIBSWSCALE_LIBRARY} ${LIBAVUTIL_LIBRARY})
|
||||
target_link_libraries(libslic3r_gui ${LIBAVFORMAT_LIBRARY} ${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 ${LIBAVCODEC_LIBRARY} ${LIBSWSCALE_LIBRARY} ${LIBAVUTIL_LIBRARY})
|
||||
target_link_libraries(libslic3r_gui ${LIBAVFORMAT_LIBRARY} ${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
|
||||
|
||||
@@ -45,6 +45,25 @@ int AVVideoDecoder::open(Bambu_StreamInfo const &info)
|
||||
return 0;
|
||||
}
|
||||
|
||||
int AVVideoDecoder::open(AVCodecParameters const ¶meters)
|
||||
{
|
||||
if (avcodec_parameters_to_context(codec_ctx_, ¶meters) < 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;
|
||||
@@ -71,6 +90,14 @@ 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);
|
||||
|
||||
@@ -23,8 +23,10 @@ public:
|
||||
|
||||
public:
|
||||
int open(Bambu_StreamInfo const &info);
|
||||
int open(AVCodecParameters const ¶meters);
|
||||
|
||||
int decode(Bambu_Sample const &sample);
|
||||
int decode(AVPacket const &packet);
|
||||
|
||||
int flush();
|
||||
|
||||
@@ -34,6 +36,14 @@ 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;
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
#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
|
||||
@@ -0,0 +1,11 @@
|
||||
#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
|
||||
@@ -328,7 +328,7 @@ void SelectMObjectPopup::update_user_devices()
|
||||
}
|
||||
|
||||
m_bind_machine_list.clear();
|
||||
m_bind_machine_list = dev->get_my_machine_list();
|
||||
m_bind_machine_list = dev->get_my_machine_list(dev->get_current_printer_agent_id());
|
||||
|
||||
//sort list
|
||||
std::vector<std::pair<std::string, MachineObject*>> user_machine_list;
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
#include "libslic3r/Print.hpp"
|
||||
|
||||
#include "DeviceCore/DevConfig.h"
|
||||
#include "DeviceCore/DevConfigUtil.h"
|
||||
#include "DeviceCore/DevExtruderSystem.h"
|
||||
#include "DeviceCore/DevFilaBlackList.h"
|
||||
#include "DeviceCore/DevFilaSystem.h"
|
||||
@@ -1647,18 +1648,8 @@ bool CalibrationPresetPage::is_blocking_printing()
|
||||
if (obj_ == nullptr) return true;
|
||||
|
||||
PresetBundle* preset_bundle = wxGetApp().preset_bundle;
|
||||
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;
|
||||
const auto source_model = preset_bundle->printers.get_edited_preset().get_printer_type(preset_bundle);
|
||||
return !DevPrinterConfigUtil::is_printer_model_compatible(source_model, *obj_);
|
||||
}
|
||||
|
||||
bool CalibrationPresetPage::is_nozzle_info_synced() const
|
||||
|
||||
@@ -28,7 +28,6 @@ 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
|
||||
|
||||
@@ -102,34 +101,6 @@ 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)
|
||||
@@ -184,37 +155,6 @@ 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;
|
||||
|
||||
@@ -15,14 +15,12 @@
|
||||
#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
|
||||
{
|
||||
@@ -53,9 +51,6 @@ 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);
|
||||
@@ -76,10 +71,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;
|
||||
|
||||
@@ -35,7 +35,9 @@ ConnectPrinterDialog::ConnectPrinterDialog(wxWindow *parent, wxWindowID id, cons
|
||||
sizer_connect = new wxBoxSizer(wxHORIZONTAL);
|
||||
|
||||
m_textCtrl_code = new TextInput(this, wxEmptyString);
|
||||
m_textCtrl_code->GetTextCtrl()->SetMaxLength(10);
|
||||
// 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->SetFont(Label::Body_14);
|
||||
m_textCtrl_code->SetCornerRadius(FromDIP(5));
|
||||
m_textCtrl_code->SetSize(wxSize(FromDIP(330), FromDIP(40)));
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
#include "DevConfigUtil.h"
|
||||
|
||||
#include "slic3r/GUI/DeviceManager.hpp"
|
||||
|
||||
#include <wx/dir.h>
|
||||
#include <boost/filesystem/operations.hpp>
|
||||
#include "../I18N.hpp"
|
||||
@@ -41,6 +43,19 @@ 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()
|
||||
{
|
||||
@@ -405,4 +420,4 @@ std::string DevPrinterConfigUtil::get_toolhead_display_name(
|
||||
return result;
|
||||
}
|
||||
|
||||
};
|
||||
};
|
||||
|
||||
@@ -25,6 +25,8 @@
|
||||
namespace Slic3r
|
||||
{
|
||||
|
||||
class MachineObject;
|
||||
|
||||
/// Toolhead component type (extruder / nozzle / hotend)
|
||||
enum class ToolHeadComponent {
|
||||
Extruder,
|
||||
@@ -59,6 +61,10 @@ 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"); }
|
||||
@@ -227,4 +233,4 @@ static std::string _parse_printer_type(const std::string &type_str)
|
||||
return type_str;
|
||||
}
|
||||
|
||||
};// namespace Slic3r
|
||||
};// namespace Slic3r
|
||||
|
||||
@@ -762,9 +762,9 @@ void DevFilaSystemParser::ParseV1_0(const json& jj, MachineObject* obj, DevFilaS
|
||||
{
|
||||
curr_tray->remain = -1;
|
||||
}
|
||||
if (tray_it->contains("tray_slot_placeholder")) {
|
||||
curr_tray->is_slot_placeholder = true;
|
||||
}
|
||||
// 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");
|
||||
int ams_id_int = 0;
|
||||
int tray_id_int = 0;
|
||||
try
|
||||
@@ -999,4 +999,4 @@ void DevFilaSystemParser::ParseAgentFilament(const json& data, MachineObject* ob
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
#include <exception>
|
||||
|
||||
#include "DevManager.h"
|
||||
#include "CloudProvider.hpp"
|
||||
#include "DevUtil.h"
|
||||
|
||||
// TODO: remove this include
|
||||
@@ -14,6 +15,8 @@
|
||||
|
||||
#include "libslic3r/Time.hpp"
|
||||
|
||||
#include "IPrinterAgent.hpp"
|
||||
|
||||
using namespace nlohmann;
|
||||
|
||||
namespace {
|
||||
@@ -264,6 +267,10 @@ 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 ||
|
||||
@@ -294,6 +301,9 @@ 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() ) {
|
||||
@@ -405,6 +415,9 @@ 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();
|
||||
@@ -497,7 +510,7 @@ namespace Slic3r
|
||||
|
||||
MachineObject* DeviceManager::get_my_machine(std::string dev_id)
|
||||
{
|
||||
auto list = get_my_machine_list();
|
||||
auto list = get_my_machine_list(get_current_printer_agent_id());
|
||||
auto it = list.find(dev_id);
|
||||
if (it != list.end())
|
||||
{
|
||||
@@ -534,25 +547,15 @@ namespace Slic3r
|
||||
OnSelectedMachineChanged(previous_selected_machine, selected_machine);
|
||||
}
|
||||
|
||||
void DeviceManager::clear_other_devices(const std::string& target_agent_id)
|
||||
void DeviceManager::clear_other_devices()
|
||||
{
|
||||
// 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();
|
||||
// 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.
|
||||
for (auto it = localMachineList.begin(); it != localMachineList.end();)
|
||||
{
|
||||
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)
|
||||
if (!it->second || it->second->printer_agent_id.empty())
|
||||
{
|
||||
delete it->second;
|
||||
it = localMachineList.erase(it);
|
||||
@@ -568,8 +571,22 @@ namespace Slic3r
|
||||
{
|
||||
BOOST_LOG_TRIVIAL(info) << "set_selected_machine=" << dev_id
|
||||
<< " cur_selected=" << selected_machine;
|
||||
auto my_machine_list = get_my_machine_list();
|
||||
auto my_machine_list = get_my_machine_list(get_current_printer_agent_id());
|
||||
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);
|
||||
@@ -580,7 +597,9 @@ namespace Slic3r
|
||||
m_agent->disconnect_printer();
|
||||
}
|
||||
else if (last_selected->second->connection_type() == "cloud") {
|
||||
m_agent->set_user_selected_machine("");
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -632,7 +651,9 @@ 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;
|
||||
m_agent->set_user_selected_machine(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;
|
||||
it->second->reset();
|
||||
}
|
||||
else
|
||||
@@ -660,6 +681,8 @@ 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;
|
||||
}
|
||||
|
||||
@@ -690,7 +713,9 @@ namespace Slic3r
|
||||
dev_list.push_back(it->first);
|
||||
BOOST_LOG_TRIVIAL(trace) << "add_user_subscribe: " << it->first;
|
||||
}
|
||||
m_agent->add_subscribe(dev_list);
|
||||
const int result = m_agent->add_subscribe(dev_list);
|
||||
BOOST_LOG_TRIVIAL(info) << "Orca diagnostic: add_user_subscribe count=" << dev_list.size()
|
||||
<< " result=" << result;
|
||||
}
|
||||
|
||||
|
||||
@@ -703,7 +728,9 @@ namespace Slic3r
|
||||
dev_list.push_back(it->first);
|
||||
BOOST_LOG_TRIVIAL(trace) << "del_user_subscribe: " << it->first;
|
||||
}
|
||||
m_agent->del_subscribe(dev_list);
|
||||
const int result = m_agent->del_subscribe(dev_list);
|
||||
BOOST_LOG_TRIVIAL(info) << "Orca diagnostic: del_user_subscribe count=" << dev_list.size()
|
||||
<< " result=" << result;
|
||||
}
|
||||
|
||||
void DeviceManager::subscribe_device_list(std::vector<std::string> dev_list)
|
||||
@@ -822,7 +849,23 @@ namespace Slic3r
|
||||
try
|
||||
{
|
||||
json j = json::parse(body);
|
||||
const std::string provider = GUI::wxGetApp().get_printer_cloud_provider();
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
#if !BBL_RELEASE_TO_PUBLIC
|
||||
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << ": " << j;
|
||||
@@ -845,11 +888,14 @@ 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 = get_current_printer_agent_id();
|
||||
obj->printer_agent_id = agent_id;
|
||||
if (m_agent)
|
||||
{
|
||||
obj->set_bind_status(m_agent->get_user_name(provider));
|
||||
@@ -864,6 +910,12 @@ 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())
|
||||
@@ -895,6 +947,12 @@ 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
|
||||
@@ -910,6 +968,9 @@ 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)
|
||||
@@ -926,10 +987,14 @@ 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); });
|
||||
}
|
||||
}
|
||||
|
||||
@@ -74,10 +74,9 @@ public:
|
||||
void erase_user_machine(std::string dev_id) { userMachineList.erase(dev_id); }
|
||||
void clean_user_info(bool keep_local_selection = false);
|
||||
|
||||
// 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 = "");
|
||||
// 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();
|
||||
|
||||
void load_last_machine();
|
||||
void update_user_machine_list_info(const std::string& provider);
|
||||
@@ -154,4 +153,4 @@ public:
|
||||
protected:
|
||||
virtual void on_timer(wxTimerEvent& event);
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
#include "I18N.hpp"
|
||||
#include "libslic3r/Time.hpp"
|
||||
#include "libslic3r/Thread.hpp"
|
||||
#include "slic3r/Utils/Http.hpp"
|
||||
#include "slic3r/Utils/NetworkAgent.hpp"
|
||||
#include "slic3r/Utils/NetworkAgentFactory.hpp"
|
||||
#include "GuiColor.hpp"
|
||||
@@ -53,6 +54,7 @@
|
||||
#include "DeviceCore/DevStatus.h"
|
||||
#include "DeviceCore/DevUpgrade.h"
|
||||
|
||||
#include "IPrinterAgent.hpp"
|
||||
|
||||
#define CALI_DEBUG
|
||||
#define MINUTE_30 1800000 //ms
|
||||
@@ -373,8 +375,22 @@ 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");
|
||||
}
|
||||
@@ -1341,7 +1357,6 @@ 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();
|
||||
@@ -1473,26 +1488,20 @@ int MachineObject::command_upgrade_module(std::string url, std::string module_ty
|
||||
|
||||
int MachineObject::command_xyz_abs()
|
||||
{
|
||||
return this->publish_gcode("G90 \n");
|
||||
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()));
|
||||
}
|
||||
|
||||
int MachineObject::command_auto_leveling()
|
||||
{
|
||||
return this->publish_gcode("G29 \n");
|
||||
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()));
|
||||
}
|
||||
|
||||
int MachineObject::command_go_home()
|
||||
{
|
||||
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");
|
||||
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()));
|
||||
}
|
||||
|
||||
int MachineObject::command_task_partskip(std::vector<int> part_ids)
|
||||
@@ -1614,23 +1623,14 @@ int MachineObject::command_stop_buzzer()
|
||||
|
||||
int MachineObject::command_set_bed(int temp)
|
||||
{
|
||||
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);
|
||||
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()));
|
||||
}
|
||||
|
||||
int MachineObject::command_set_nozzle(int temp)
|
||||
{
|
||||
std::string gcode_str = (boost::format("M104 S%1%\n") % temp).str();
|
||||
return this->publish_gcode(gcode_str);
|
||||
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()));
|
||||
}
|
||||
|
||||
int MachineObject::command_set_nozzle_new(int nozzle_id, int temp)
|
||||
@@ -1735,9 +1735,8 @@ int MachineObject::command_ams_user_settings(bool start_read_opt, bool tray_read
|
||||
|
||||
int MachineObject::command_ams_calibrate(int ams_id)
|
||||
{
|
||||
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);
|
||||
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()));
|
||||
}
|
||||
|
||||
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)
|
||||
@@ -1775,9 +1774,8 @@ int MachineObject::command_ams_filament_settings(int ams_id, int slot_id, std::s
|
||||
|
||||
int MachineObject::command_ams_refresh_rfid(std::string tray_id)
|
||||
{
|
||||
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);
|
||||
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()));
|
||||
}
|
||||
|
||||
int MachineObject::command_ams_refresh_rfid2(int ams_id, int slot_id)
|
||||
@@ -1790,12 +1788,17 @@ 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)
|
||||
{
|
||||
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);
|
||||
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()));
|
||||
}
|
||||
|
||||
int MachineObject::command_ams_control(std::string action)
|
||||
@@ -1954,47 +1957,10 @@ 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_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);
|
||||
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()));
|
||||
}
|
||||
|
||||
int MachineObject::command_extruder_control(int nozzle_id, double val)
|
||||
@@ -2619,7 +2585,12 @@ void MachineObject::reset()
|
||||
vt_slot.erase(vt_slot.begin() + 1);
|
||||
}
|
||||
}
|
||||
subtask_ = nullptr;
|
||||
// why: reset reuses MachineObject, so release its lazy subtask
|
||||
// before dropping the pointer to prevent reconnect leaks.
|
||||
if (subtask_) {
|
||||
delete subtask_;
|
||||
subtask_ = nullptr;
|
||||
}
|
||||
has_extra_flow_type = false;
|
||||
m_partskip_ids.clear();
|
||||
}
|
||||
@@ -2629,15 +2600,52 @@ void MachineObject::set_print_state(std::string 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;
|
||||
}
|
||||
|
||||
int MachineObject::connect(bool use_openssl)
|
||||
{
|
||||
if (get_dev_ip().empty()) return -1;
|
||||
std::string username = "bblp";
|
||||
std::string username = m_agent ? m_agent->default_lan_username() : std::string();
|
||||
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 {
|
||||
return m_agent->connect_printer(get_dev_id(), get_dev_ip(), username, password, use_openssl);
|
||||
PrinterConnectionParams params{
|
||||
get_dev_id(),
|
||||
host,
|
||||
port,
|
||||
username,
|
||||
password,
|
||||
use_openssl,
|
||||
ca_file
|
||||
};
|
||||
return m_agent->connect_printer(params);
|
||||
} catch (...) {
|
||||
;
|
||||
}
|
||||
@@ -2742,6 +2750,14 @@ 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;
|
||||
}
|
||||
|
||||
@@ -3046,6 +3062,13 @@ 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);
|
||||
@@ -3298,10 +3321,7 @@ 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")) {
|
||||
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>();
|
||||
update_print_progress(jj["mc_percent"]);
|
||||
}
|
||||
if (jj.contains("mc_print_sub_stage")) {
|
||||
if (jj["mc_print_sub_stage"].is_number_integer())
|
||||
@@ -3471,6 +3491,9 @@ 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);
|
||||
@@ -3516,7 +3539,6 @@ 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>();
|
||||
}
|
||||
@@ -3819,6 +3841,7 @@ 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")) {
|
||||
@@ -3853,6 +3876,9 @@ 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>();
|
||||
}
|
||||
@@ -5432,6 +5458,86 @@ 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;
|
||||
}
|
||||
@@ -5937,6 +6043,15 @@ 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) {
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
#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"
|
||||
@@ -100,7 +101,6 @@ 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 = AmsStatusMain::AMS_STATUS_MAIN_IDLE;
|
||||
int ams_status_sub;
|
||||
int ams_version = 0;
|
||||
|
||||
@@ -547,30 +547,12 @@ 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;
|
||||
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 };
|
||||
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 PlateMakerDectect : int
|
||||
{
|
||||
@@ -708,6 +690,8 @@ 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 };
|
||||
@@ -765,6 +749,7 @@ 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);
|
||||
@@ -894,6 +879,7 @@ 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();
|
||||
@@ -961,6 +947,7 @@ 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;
|
||||
@@ -989,7 +976,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;
|
||||
|
||||
@@ -0,0 +1,103 @@
|
||||
#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
|
||||
@@ -0,0 +1,54 @@
|
||||
#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
|
||||
+20
-35
@@ -4,6 +4,7 @@
|
||||
#include "libslic3r/Platform.hpp"
|
||||
#include "GUI_App.hpp"
|
||||
#include "Shortcuts.hpp"
|
||||
#include "DeviceCore/DevConfigUtil.h"
|
||||
#include "BindDialog.hpp"
|
||||
#include "DeviceManager.hpp"
|
||||
#include "HMS.hpp"
|
||||
@@ -24,7 +25,6 @@
|
||||
#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,9 +41,11 @@
|
||||
#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>
|
||||
@@ -88,7 +90,6 @@
|
||||
#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"
|
||||
@@ -108,19 +109,18 @@
|
||||
#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,8 +129,6 @@
|
||||
#include "PrintHostDialogs.hpp"
|
||||
#include "NetworkPluginDialog.hpp"
|
||||
#include "DesktopIntegrationDialog.hpp"
|
||||
#include "SendSystemInfoDialog.hpp"
|
||||
#include "ParamsDialog.hpp"
|
||||
#include "KBShortcutsDialog.hpp"
|
||||
#include "DownloadProgressDialog.hpp"
|
||||
#include "TroubleshootDialog.hpp"
|
||||
@@ -141,7 +139,6 @@
|
||||
#include "Widgets/ProgressDialog.hpp"
|
||||
|
||||
//BBS: DailyTip and UserGuide Dialog
|
||||
#include "WebDownPluginDlg.hpp"
|
||||
#include "WebGuideDialog.hpp"
|
||||
#include "ReleaseNote.hpp"
|
||||
#include "PrivacyUpdateDialog.hpp"
|
||||
@@ -2388,14 +2385,8 @@ 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_)
|
||||
@@ -2406,14 +2397,7 @@ 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);
|
||||
|
||||
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;
|
||||
return !DevPrinterConfigUtil::is_printer_model_compatible(source_model, *obj_);
|
||||
}
|
||||
|
||||
// If formatted for github, plaintext with OpenGL extensions enclosed into <details>.
|
||||
@@ -3968,20 +3952,16 @@ 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: 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());
|
||||
// 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();
|
||||
}
|
||||
|
||||
m_agent->set_printer_agent(agent);
|
||||
sidebar().update_all_preset_comboboxes();
|
||||
}
|
||||
|
||||
std::string GUI_App::resolve_printer_agent_id(const std::string& stored_id)
|
||||
std::string GUI_App::resolve_printer_agent_id(const std::string& stored_id) const
|
||||
{
|
||||
if (!stored_id.empty())
|
||||
return stored_id;
|
||||
@@ -4017,6 +3997,7 @@ 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
|
||||
@@ -4030,8 +4011,10 @@ void GUI_App::switch_printer_agent()
|
||||
return;
|
||||
}
|
||||
|
||||
// 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) {
|
||||
// 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) {
|
||||
// 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
|
||||
@@ -5012,14 +4995,16 @@ bool GUI_App::is_user_login(const std::string& provider/* = ORCA_CLOUD_PROVIDER*
|
||||
return false;
|
||||
}
|
||||
|
||||
const std::string& GUI_App::get_printer_cloud_provider() const
|
||||
std::string GUI_App::get_printer_cloud_provider() const
|
||||
{
|
||||
// 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;
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
|
||||
bool GUI_App::check_login(const std::string& provider/* = ORCA_CLOUD_PROVIDER*/)
|
||||
{
|
||||
bool result = false;
|
||||
|
||||
@@ -23,9 +23,6 @@
|
||||
#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
|
||||
@@ -378,7 +375,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);
|
||||
std::string resolve_printer_agent_id(const std::string& stored_id) const;
|
||||
// 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);
|
||||
@@ -504,7 +501,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);
|
||||
const std::string& get_printer_cloud_provider() 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);
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
#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
|
||||
@@ -14,6 +14,7 @@
|
||||
|
||||
#include "slic3r/Utils/FileTransferUtils.hpp"
|
||||
#include "slic3r/Utils/BBLNetworkPlugin.hpp"
|
||||
#include "NetworkAgent.hpp"
|
||||
|
||||
namespace Slic3r {
|
||||
namespace GUI {
|
||||
@@ -203,7 +204,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 = "bblp";
|
||||
params.username = m_agent->default_lan_username();
|
||||
params.password = m_access_code;
|
||||
|
||||
// check access code and ip address
|
||||
|
||||
@@ -126,7 +126,7 @@ void SendJob::process(Ctl &ctl)
|
||||
if (m_is_check_mode) {
|
||||
PrintParams verify_params;
|
||||
verify_params.dev_ip = m_dev_ip;
|
||||
verify_params.username = "bblp";
|
||||
verify_params.username = agent->default_lan_username();
|
||||
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;
|
||||
@@ -211,7 +211,7 @@ void SendJob::process(Ctl &ctl)
|
||||
|
||||
// local print access
|
||||
params.dev_ip = m_dev_ip;
|
||||
params.username = "bblp";
|
||||
params.username = agent->default_lan_username();
|
||||
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;
|
||||
|
||||
@@ -1189,6 +1189,8 @@ void MainFrame::shutdown()
|
||||
if (m_project != nullptr)
|
||||
m_project->shutdown();
|
||||
m_plugin_pages.shutdown();
|
||||
if (m_plater != nullptr)
|
||||
m_plater->remove_dock_panes();
|
||||
#ifdef __WXGTK__
|
||||
// Edge panels are child windows — wxWidgets destroys them automatically.
|
||||
m_edge_bottom = nullptr;
|
||||
@@ -3424,11 +3426,6 @@ void MainFrame::init_menubar_as_editor()
|
||||
wxGetApp().open_preferences();
|
||||
},
|
||||
"", nullptr, []() { return true; }, this, 1);
|
||||
parent_menu->AppendSeparator();
|
||||
append_shortcut_item(
|
||||
parent_menu, Shortcut::SpeedDial, false, _L("Open speed dial"), "",
|
||||
[](wxCommandEvent &) { wxGetApp().open_speed_dial(); },
|
||||
"", nullptr, []() { return true; }, this);
|
||||
//parent_menu->Insert(1, preference_item);
|
||||
#endif
|
||||
// Help menu
|
||||
@@ -3454,7 +3451,7 @@ void MainFrame::init_menubar_as_editor()
|
||||
top_menu->AppendSeparator();
|
||||
|
||||
append_shortcut_item(
|
||||
top_menu, Shortcut::SpeedDial, false, _L("Open speed dial"), "",
|
||||
top_menu, Shortcut::SpeedDial, false, _L("Open Speed Dial"), "",
|
||||
[](wxCommandEvent &) { wxGetApp().open_speed_dial(); },
|
||||
"", nullptr, []() { return true; }, this);
|
||||
top_menu->AppendSeparator();
|
||||
@@ -3561,7 +3558,7 @@ void MainFrame::init_menubar_as_editor()
|
||||
// On Mac, the Apple menu ignores non-standard custom items, so add Preset Bundle to the File menu
|
||||
fileMenu->AppendSeparator();
|
||||
append_shortcut_item(
|
||||
fileMenu, Shortcut::SpeedDial, false, _L("Open speed dial"), "",
|
||||
fileMenu, Shortcut::SpeedDial, false, _L("Open Speed Dial"), "",
|
||||
[](wxCommandEvent&) { wxGetApp().open_speed_dial(); },
|
||||
"", nullptr, []() { return true; }, this);
|
||||
append_menu_item(
|
||||
@@ -4388,7 +4385,7 @@ void MainFrame::load_printer_url()
|
||||
if (auto *device_manager = wxGetApp().getDeviceManager()) {
|
||||
auto *machine = device_manager->get_selected_machine();
|
||||
if (!machine) {
|
||||
auto machines = device_manager->get_my_machine_list();
|
||||
auto machines = device_manager->get_my_machine_list(device_manager->get_current_printer_agent_id());
|
||||
if (machines.size() == 1)
|
||||
machine = machines.begin()->second;
|
||||
}
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
#include "MediaPlayCtrl.h"
|
||||
#include "WebMediaController.hpp"
|
||||
#include "IPrinterAgent.hpp"
|
||||
#include "Widgets/Button.hpp"
|
||||
#include "Widgets/CheckBox.hpp"
|
||||
#include "Widgets/Label.hpp"
|
||||
@@ -7,6 +9,7 @@
|
||||
#include "DeviceManager.hpp"
|
||||
#include "DeviceCore/DevConfigUtil.h"
|
||||
#include "slic3r/Utils/NetworkAgent.hpp"
|
||||
#include "slic3r/Utils/NetworkAgentFactory.hpp"
|
||||
#include "libslic3r/Thread.hpp"
|
||||
#include "libslic3r/AppConfig.hpp"
|
||||
#include "I18N.hpp"
|
||||
@@ -15,11 +18,15 @@
|
||||
#include "slic3r/Utils/BBLNetworkPlugin.hpp"
|
||||
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
#include <boost/lexical_cast.hpp>
|
||||
#include <boost/log/trivial.hpp>
|
||||
#include <boost/nowide/cstdio.hpp>
|
||||
#include <boost/nowide/fstream.hpp>
|
||||
#include <boost/nowide/utf8_codecvt.hpp>
|
||||
#include <slic3r/GUI/DeviceManager.hpp>
|
||||
#include <wx/mediactrl.h>
|
||||
#undef pid_t
|
||||
#include <boost/process.hpp>
|
||||
#ifdef __WIN32__
|
||||
@@ -48,6 +55,7 @@ namespace GUI {
|
||||
MediaPlayCtrl::MediaPlayCtrl(wxWindow *parent, wxMediaCtrl3 *media_ctrl, const wxPoint &pos, const wxSize &size)
|
||||
: wxPanel(parent, wxID_ANY, pos, size)
|
||||
, m_media_ctrl(media_ctrl)
|
||||
, m_active_media_controller(media_ctrl)
|
||||
{
|
||||
SetLabel("MediaPlayCtrl");
|
||||
SetBackgroundColour(*wxWHITE);
|
||||
@@ -94,7 +102,10 @@ MediaPlayCtrl::MediaPlayCtrl(wxWindow *parent, wxMediaCtrl3 *media_ctrl, const w
|
||||
});
|
||||
|
||||
m_button_play->Bind(wxEVT_COMMAND_BUTTON_CLICKED, [this](auto &e) { TogglePlay(); });
|
||||
m_button_play->Bind(wxEVT_RIGHT_UP, [this](auto & e) { m_media_ctrl->Play(); });
|
||||
m_button_play->Bind(wxEVT_RIGHT_UP, [this](auto & e) {
|
||||
if (m_active_media_controller)
|
||||
m_active_media_controller->Play();
|
||||
});
|
||||
// Orca: live-view FAQ link binding removed (vendor URL)
|
||||
|
||||
Bind(wxEVT_RIGHT_UP, [this](auto & e) {
|
||||
@@ -139,9 +150,12 @@ MediaPlayCtrl::MediaPlayCtrl(wxWindow *parent, wxMediaCtrl3 *media_ctrl, const w
|
||||
|
||||
MediaPlayCtrl::~MediaPlayCtrl()
|
||||
{
|
||||
if (m_webrtc_ctrl)
|
||||
m_webrtc_ctrl->Stop();
|
||||
m_media_ctrl->EndExternalStream();
|
||||
{
|
||||
boost::unique_lock lock(m_mutex);
|
||||
m_tasks.push_back("<exit>");
|
||||
m_tasks.push_back({"<exit>", nullptr});
|
||||
m_cond.notify_all();
|
||||
}
|
||||
while (!m_thread.try_join_for(boost::chrono::milliseconds(10))) {
|
||||
@@ -151,59 +165,144 @@ MediaPlayCtrl::~MediaPlayCtrl()
|
||||
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << ": " << this;
|
||||
}
|
||||
|
||||
void MediaPlayCtrl::SetWebMediaController(IMediaController *ctrl)
|
||||
{
|
||||
m_web_ctrl = ctrl;
|
||||
set_active_media_controller(current_mode());
|
||||
}
|
||||
|
||||
void MediaPlayCtrl::set_active_media_controller(CameraStreamMode mode)
|
||||
{
|
||||
switch (mode) {
|
||||
case CameraStreamMode::http_snapshot:
|
||||
if (auto *web_ctrl = dynamic_cast<WebMediaController *>(m_web_ctrl))
|
||||
web_ctrl->set_mode(mode);
|
||||
m_active_media_controller = m_web_ctrl;
|
||||
break;
|
||||
case CameraStreamMode::webrtc:
|
||||
if (!m_webrtc_ctrl) {
|
||||
m_webrtc_ctrl = std::make_unique<WebRtcMediaController>(
|
||||
[this](const wxImage& image, wxSize size) { m_media_ctrl->SetExternalFrame(image, size); },
|
||||
[this, token = std::weak_ptr<int>(m_token)](WebRtcMediaController::Status status) {
|
||||
if (token.expired())
|
||||
return;
|
||||
CallAfter([this, status] { on_webrtc_status(status); });
|
||||
});
|
||||
}
|
||||
m_active_media_controller = m_webrtc_ctrl.get();
|
||||
break;
|
||||
default:
|
||||
m_active_media_controller = m_media_ctrl;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
CameraStreamMode MediaPlayCtrl::current_mode() const
|
||||
{
|
||||
auto agent = wxGetApp().getAgent();
|
||||
return agent ? agent->get_camera_stream_mode() : CameraStreamMode::none;
|
||||
}
|
||||
|
||||
void MediaPlayCtrl::SetMachineObject(MachineObject* obj)
|
||||
{
|
||||
std::string machine = obj ? obj->get_dev_id() : "";
|
||||
if (obj) {
|
||||
m_camera_exists = obj->has_ipcam;
|
||||
m_dev_ver = obj->get_ota_version();
|
||||
m_lan_mode = obj->is_lan_mode_printer();
|
||||
m_lan_proto = obj->liveview_local;
|
||||
m_remote_proto = obj->get_liveview_remote();
|
||||
m_lan_ip = obj->get_dev_ip();
|
||||
m_lan_passwd = obj->get_access_code();
|
||||
m_device_busy = obj->is_camera_busy_off();
|
||||
m_tutk_state = obj->tutk_state;
|
||||
|
||||
if (DevPrinterConfigUtil::get_printer_series_str(obj->printer_type) == "series_o" && BBLNetworkPlugin::instance().use_legacy_network()) {
|
||||
// Legacy plugin cannot support remote play for H2D, force using local mode
|
||||
m_remote_proto = MachineObject::LVR_None;
|
||||
const CameraStreamMode mode = current_mode();
|
||||
if (mode != m_last_mode) {
|
||||
if (m_last_state != MEDIASTATE_IDLE) {
|
||||
m_failed_code = 0; // a mode switch is not a stream failure - don't arm back-off
|
||||
Stop(" ");
|
||||
}
|
||||
} else {
|
||||
m_camera_exists = false;
|
||||
m_lan_mode = false;
|
||||
m_lan_proto = MachineObject::LVL_None;
|
||||
m_lan_ip.clear();
|
||||
m_lan_passwd.clear();
|
||||
m_dev_ver.clear();
|
||||
m_tutk_state.clear();
|
||||
m_remote_proto = 0;
|
||||
m_device_busy = false;
|
||||
m_last_mode = mode;
|
||||
}
|
||||
Enable(obj && obj->is_info_ready() && obj->m_push_count > 0);
|
||||
if (machine == m_machine) {
|
||||
if (m_last_state == MEDIASTATE_IDLE && IsEnabled())
|
||||
Play();
|
||||
set_active_media_controller(mode);
|
||||
|
||||
const bool uses_local_camera_url = mode == CameraStreamMode::http || mode == CameraStreamMode::https ||
|
||||
mode == CameraStreamMode::http_snapshot || mode == CameraStreamMode::rtsp;
|
||||
const bool uses_webrtc = mode == CameraStreamMode::webrtc;
|
||||
const std::string machine = obj ? obj->get_dev_id() : "";
|
||||
bool changed = false;
|
||||
|
||||
if (uses_local_camera_url) {
|
||||
auto agent = wxGetApp().getAgent();
|
||||
std::string url = agent ? agent->get_local_camera_stream_url() : "";
|
||||
m_camera_exists = !url.empty();
|
||||
Enable(obj && m_camera_exists);
|
||||
changed = machine != m_machine || url != m_agent_camera_url;
|
||||
m_agent_camera_url = url;
|
||||
m_url = from_u8(url);
|
||||
} else if (uses_webrtc) {
|
||||
m_camera_exists = obj != nullptr;
|
||||
Enable(obj != nullptr);
|
||||
changed = machine != m_machine;
|
||||
BOOST_LOG_TRIVIAL(info) << "MediaPlayCtrl::SetMachineObject webrtc: changed=" << changed
|
||||
<< " last_state=" << m_last_state << " web_user_stopped=" << m_web_user_stopped;
|
||||
m_url.clear();
|
||||
m_agent_camera_url.clear();
|
||||
} else {
|
||||
if (obj) {
|
||||
m_camera_exists = obj->has_ipcam;
|
||||
m_dev_ver = obj->get_ota_version();
|
||||
m_lan_mode = obj->is_lan_mode_printer();
|
||||
m_lan_proto = obj->liveview_local;
|
||||
m_remote_proto = obj->get_liveview_remote();
|
||||
m_lan_ip = obj->get_dev_ip();
|
||||
m_lan_passwd = obj->get_access_code();
|
||||
m_device_busy = obj->is_camera_busy_off();
|
||||
m_tutk_state = obj->tutk_state;
|
||||
|
||||
if (DevPrinterConfigUtil::get_printer_series_str(obj->printer_type) == "series_o" && BBLNetworkPlugin::instance().use_legacy_network()) {
|
||||
// Legacy plugin cannot support remote play for H2D, force using local mode
|
||||
m_remote_proto = LiveviewRemote::LVR_None;
|
||||
}
|
||||
} else {
|
||||
m_camera_exists = false;
|
||||
m_lan_mode = false;
|
||||
m_lan_proto = LiveviewLocal::LVL_None;
|
||||
m_lan_ip.clear();
|
||||
m_lan_passwd.clear();
|
||||
m_dev_ver.clear();
|
||||
m_tutk_state.clear();
|
||||
m_remote_proto = 0;
|
||||
m_device_busy = false;
|
||||
}
|
||||
Enable(obj && obj->is_info_ready() && obj->m_push_count > 0);
|
||||
if (machine == m_machine)
|
||||
return;
|
||||
|
||||
m_machine = machine;
|
||||
BOOST_LOG_TRIVIAL(info) << "MediaPlayCtrl switch machine: " << m_machine;
|
||||
m_disable_lan = false;
|
||||
m_failed_retry = 0;
|
||||
m_last_failed_codes.clear();
|
||||
m_last_user_play = wxDateTime::Now();
|
||||
std::string stream_url;
|
||||
if (get_stream_url(&stream_url)) {
|
||||
m_streaming = boost::algorithm::contains(stream_url, "device=" + m_machine);
|
||||
} else {
|
||||
m_streaming = false;
|
||||
}
|
||||
if (m_last_state != MEDIASTATE_IDLE)
|
||||
Stop(" ");
|
||||
if (m_next_retry.IsValid()) // Try open 2 seconds later, to avoid state conflict
|
||||
m_next_retry = wxDateTime::Now() + wxTimeSpan::Seconds(2);
|
||||
else
|
||||
SetStatus("", false);
|
||||
return;
|
||||
}
|
||||
|
||||
m_machine = machine;
|
||||
BOOST_LOG_TRIVIAL(info) << "MediaPlayCtrl switch machine: " << m_machine;
|
||||
m_disable_lan = false;
|
||||
m_failed_retry = 0;
|
||||
m_last_failed_codes.clear();
|
||||
m_last_user_play = wxDateTime::Now();
|
||||
std::string stream_url;
|
||||
if (get_stream_url(&stream_url)) {
|
||||
m_streaming = boost::algorithm::contains(stream_url, "device=" + m_machine);
|
||||
} else {
|
||||
m_streaming = false;
|
||||
if (!changed)
|
||||
return;
|
||||
|
||||
// A genuine target switch is not a stream failure and should clear the
|
||||
// manual-stop state before the new target is allowed to play.
|
||||
m_web_user_stopped = false;
|
||||
if (uses_local_camera_url) {
|
||||
m_failed_code = 0;
|
||||
m_failed_retry = 0;
|
||||
m_next_retry = wxDateTime();
|
||||
}
|
||||
if (m_last_state != MEDIASTATE_IDLE)
|
||||
Stop(" ");
|
||||
if (m_next_retry.IsValid()) // Try open 2 seconds later, to avoid state conflict
|
||||
m_next_retry = wxDateTime::Now() + wxTimeSpan::Seconds(2);
|
||||
else
|
||||
SetStatus("", false);
|
||||
}
|
||||
|
||||
wxString hide_id_middle_string(wxString const &str, size_t offset = 0, size_t length = -1)
|
||||
@@ -254,13 +353,71 @@ void refresh_agora_url(char const* device, char const* dev_ver, char const* chan
|
||||
|
||||
void MediaPlayCtrl::Play()
|
||||
{
|
||||
if (!m_next_retry.IsValid() || wxDateTime::Now() < m_next_retry)
|
||||
if ((m_next_retry.IsValid() && wxDateTime::Now() < m_next_retry) || !IsShownOnScreen() || m_last_state != MEDIASTATE_IDLE)
|
||||
return;
|
||||
if (!IsShownOnScreen())
|
||||
return;
|
||||
if (m_last_state != MEDIASTATE_IDLE) {
|
||||
|
||||
const CameraStreamMode mode = current_mode();
|
||||
set_active_media_controller(mode);
|
||||
m_last_mode = mode;
|
||||
auto agent = wxGetApp().getAgent();
|
||||
|
||||
auto printer_agent = agent ? agent->get_printer_agent() : nullptr;
|
||||
const bool is_bbl = (printer_agent ? printer_agent->get_agent_info().id : "") == BBL_PRINTER_AGENT_ID;
|
||||
|
||||
if (!is_bbl) {
|
||||
const bool is_webrtc = mode == CameraStreamMode::webrtc;
|
||||
const bool is_snapshot = mode == CameraStreamMode::http_snapshot;
|
||||
const bool is_http_stream = mode == CameraStreamMode::http || mode == CameraStreamMode::https || mode == CameraStreamMode::rtsp;
|
||||
if (!is_webrtc && !is_snapshot && !is_http_stream)
|
||||
return;
|
||||
|
||||
auto *webrtc_ctrl = is_webrtc ? dynamic_cast<WebRtcMediaController *>(m_active_media_controller) : nullptr;
|
||||
if (is_webrtc && (!webrtc_ctrl || !m_media_ctrl)) {
|
||||
Stop(_L("Please confirm if the printer is connected."));
|
||||
return;
|
||||
}
|
||||
if (webrtc_ctrl && webrtc_ctrl->is_active())
|
||||
return;
|
||||
|
||||
m_failed_code = 0;
|
||||
if (!m_active_media_controller || m_machine.empty() || !IsEnabled() || !m_camera_exists ||
|
||||
(!is_webrtc && m_url.IsEmpty())) {
|
||||
Stop(_L("Please confirm if the printer is connected."));
|
||||
return;
|
||||
}
|
||||
|
||||
if (is_webrtc) {
|
||||
BOOST_LOG_TRIVIAL(info) << "MediaPlayCtrl::Play webrtc: last_state=" << m_last_state << " failed_retry=" << m_failed_retry
|
||||
<< " shown=" << IsShownOnScreen();
|
||||
auto channel = agent ? agent->create_camera_signaling_channel(m_machine, wxGetApp().get_printer_cloud_provider()) : nullptr;
|
||||
if (!channel) {
|
||||
Stop(_L("Sign in to OrcaCloud to view the camera."));
|
||||
return;
|
||||
}
|
||||
webrtc_ctrl->set_signalling_channel(std::move(channel));
|
||||
m_media_ctrl->BeginExternalStream();
|
||||
m_last_state = MEDIASTATE_INITIALIZING;
|
||||
SetStatus(_L("Initializing..."), false);
|
||||
} else if (is_snapshot) {
|
||||
m_last_state = wxMEDIASTATE_PLAYING;
|
||||
SetStatus(_L("Playing..."), false);
|
||||
}
|
||||
|
||||
m_button_play->SetIcon("media_stop");
|
||||
if (m_active_media_controller == m_media_ctrl) {
|
||||
// wxMediaCtrl3 reports when it has a decoded frame; load() waits for
|
||||
// that event before queuing Play so the stream is not marked stopped.
|
||||
load();
|
||||
} else {
|
||||
m_active_media_controller->Load(wxURI(m_url));
|
||||
m_active_media_controller->Play();
|
||||
}
|
||||
if (webrtc_ctrl) {
|
||||
m_webrtc_epoch = webrtc_ctrl->epoch();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
m_failed_code = 0;
|
||||
if (m_machine.empty()) {
|
||||
Stop(_L("Please confirm if the printer is connected."));
|
||||
@@ -281,16 +438,15 @@ void MediaPlayCtrl::Play()
|
||||
}
|
||||
|
||||
BOOST_LOG_TRIVIAL(info) << "MediaPlayCtrl::Play: " << m_lan_proto << m_remote_proto << m_disable_lan;
|
||||
NetworkAgent *agent = wxGetApp().getAgent();
|
||||
std::string agent_version = agent ? agent->get_version() : "";
|
||||
if (m_lan_proto > MachineObject::LVL_Disable && (m_lan_mode || !m_remote_proto) && !m_disable_lan && !m_lan_ip.empty()) {
|
||||
if (m_lan_proto > LiveviewLocal::LVL_Disable && (m_lan_mode || !m_remote_proto) && !m_disable_lan && !m_lan_ip.empty()) {
|
||||
m_disable_lan = m_remote_proto && !m_lan_mode; // try remote next time
|
||||
std::string url;
|
||||
if (m_lan_proto == MachineObject::LVL_Local)
|
||||
if (m_lan_proto == LiveviewLocal::LVL_Local)
|
||||
url = "bambu:///local/" + m_lan_ip + ".?port=6000&user=" + m_lan_user + "&passwd=" + m_lan_passwd;
|
||||
else if (m_lan_proto == MachineObject::LVL_Rtsps)
|
||||
else if (m_lan_proto == LiveviewLocal::LVL_Rtsps)
|
||||
url = "bambu:///rtsps___" + m_lan_user + ":" + m_lan_passwd + "@" + m_lan_ip + "/streaming/live/1?proto=rtsps";
|
||||
else if (m_lan_proto == MachineObject::LVL_Rtsp)
|
||||
else if (m_lan_proto == LiveviewLocal::LVL_Rtsp)
|
||||
url = "bambu:///rtsp___" + m_lan_user + ":" + m_lan_passwd + "@" + m_lan_ip + "/streaming/live/1?proto=rtsp";
|
||||
url += "&device=" + m_machine;
|
||||
url += "&net_ver=" + agent_version;
|
||||
@@ -312,8 +468,8 @@ void MediaPlayCtrl::Play()
|
||||
// !m_lan_mode && !m_remote_proto && m_lan_proto == LVL_Disable (*)
|
||||
// !m_lan_mode && !m_remote_proto && m_lan_proto == LVL_None (x)
|
||||
|
||||
if (m_lan_proto <= MachineObject::LVL_Disable && (m_lan_mode || !m_remote_proto)) {
|
||||
Stop(m_lan_proto == MachineObject::LVL_None
|
||||
if (m_lan_proto <= LiveviewLocal::LVL_Disable && (m_lan_mode || !m_remote_proto)) {
|
||||
Stop(m_lan_proto == LiveviewLocal::LVL_None
|
||||
? _L("A problem occurred. Please update the printer firmware and try again.")
|
||||
: _L("LAN Only Liveview is off. Please turn on the liveview on printer screen."));
|
||||
return;
|
||||
@@ -381,15 +537,78 @@ void MediaPlayCtrl::Play()
|
||||
|
||||
void start_ping_test();
|
||||
|
||||
void MediaPlayCtrl::StopWebStream()
|
||||
{
|
||||
if (m_last_state == MEDIASTATE_IDLE)
|
||||
return;
|
||||
if (m_active_media_controller && m_active_media_controller == m_web_ctrl)
|
||||
m_active_media_controller->Stop();
|
||||
m_button_play->SetIcon("media_play");
|
||||
m_last_state = MEDIASTATE_IDLE;
|
||||
SetStatus(_L("Video Stopped."), false);
|
||||
}
|
||||
|
||||
void MediaPlayCtrl::Stop(wxString const &msg, wxString const &msg2)
|
||||
{
|
||||
const bool webrtc_active = m_last_mode == CameraStreamMode::webrtc &&
|
||||
dynamic_cast<WebRtcMediaController *>(m_active_media_controller) != nullptr;
|
||||
BOOST_LOG_TRIVIAL(info) << "MediaPlayCtrl::Stop: last_state=" << m_last_state
|
||||
<< " webrtc_active=" << webrtc_active << " failed_code=" << m_failed_code
|
||||
<< " msg='" << msg.ToUTF8().data() << "'";
|
||||
if (webrtc_active) {
|
||||
m_active_media_controller->Stop();
|
||||
m_media_ctrl->EndExternalStream();
|
||||
}
|
||||
switch (m_last_mode) {
|
||||
case CameraStreamMode::http:
|
||||
case CameraStreamMode::https:
|
||||
case CameraStreamMode::http_snapshot: {
|
||||
const bool snapshot = m_last_mode == CameraStreamMode::http_snapshot;
|
||||
if (m_last_state != MEDIASTATE_IDLE) {
|
||||
if (snapshot) {
|
||||
if (m_active_media_controller)
|
||||
m_active_media_controller->Stop();
|
||||
} else {
|
||||
// http/https mode plays through the ffmpeg backend (m_media_ctrl), not
|
||||
// the webview - tear its read thread down too, otherwise it keeps
|
||||
// pulling and painting frames after the UI says "Video Stopped".
|
||||
boost::unique_lock lock(m_mutex);
|
||||
m_tasks.push_back({"<stop>", m_active_media_controller});
|
||||
m_cond.notify_all();
|
||||
}
|
||||
m_button_play->SetIcon("media_play");
|
||||
m_last_state = MEDIASTATE_IDLE;
|
||||
if (!msg.IsEmpty())
|
||||
SetStatus(msg);
|
||||
else
|
||||
SetStatus(_L("Video Stopped."), false);
|
||||
// Keep retries bounded for an explicit or retry-driven playback attempt.
|
||||
// m_failed_retry is cleared on success (onStateChanged) and on a deliberate
|
||||
// machine switch (SetMachineObject); manual playback via TogglePlay resets it.
|
||||
if (m_failed_code != 0) {
|
||||
const bool auto_retry = wxGetApp().app_config->get("liveview", "auto_retry") != "false";
|
||||
++m_failed_retry;
|
||||
m_next_retry = auto_retry
|
||||
? wxDateTime::Now() + wxTimeSpan::Seconds(std::min(5 * m_failed_retry, 30))
|
||||
: wxDateTime::Now() + wxTimeSpan::Days(1); // "off": wait for a manual retry
|
||||
}
|
||||
} else if (!msg.IsEmpty()) {
|
||||
SetStatus(msg, false);
|
||||
}
|
||||
return;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
int last_state = m_last_state;
|
||||
|
||||
if (m_last_state != MEDIASTATE_IDLE) {
|
||||
m_media_ctrl->InvalidateBestSize();
|
||||
m_button_play->SetIcon("media_play");
|
||||
boost::unique_lock lock(m_mutex);
|
||||
m_tasks.push_back("<stop>");
|
||||
if (!webrtc_active)
|
||||
m_tasks.push_back({"<stop>", m_active_media_controller});
|
||||
m_cond.notify_all();
|
||||
if (!msg.IsEmpty())
|
||||
SetStatus(msg);
|
||||
@@ -454,15 +673,38 @@ void MediaPlayCtrl::Stop(wxString const &msg, wxString const &msg2)
|
||||
m_next_retry = wxDateTime::Now() + wxTimeSpan::Seconds(5 * m_failed_retry);
|
||||
}
|
||||
|
||||
void MediaPlayCtrl::on_webrtc_status(WebRtcMediaController::Status status)
|
||||
{
|
||||
// Drop CallAfter-queued events from a superseded Play attempt.
|
||||
if (status.epoch != m_webrtc_epoch)
|
||||
return;
|
||||
if (status.kind == WebRtcMediaController::Status::Connecting) {
|
||||
m_last_state = MEDIASTATE_INITIALIZING;
|
||||
SetStatus(_L("Initializing..."), false);
|
||||
} else if (status.kind == WebRtcMediaController::Status::Playing) {
|
||||
m_last_state = wxMEDIASTATE_PLAYING;
|
||||
m_failed_code = 0;
|
||||
m_failed_retry = 0;
|
||||
SetStatus(_L("Playing..."), false);
|
||||
} else if (status.kind == WebRtcMediaController::Status::Failed) {
|
||||
m_failed_code = static_cast<int>(status.code) + 1;
|
||||
Stop();
|
||||
}
|
||||
// Status::Stopped needs no action: a genuine failure arrives as Failed, and
|
||||
// a stop we initiated is already handled by Stop() itself.
|
||||
}
|
||||
|
||||
void MediaPlayCtrl::TogglePlay()
|
||||
{
|
||||
BOOST_LOG_TRIVIAL(info) << "MediaPlayCtrl::TogglePlay";
|
||||
if (m_last_state != MEDIASTATE_IDLE) {
|
||||
m_next_retry = wxDateTime();
|
||||
m_web_user_stopped = true;
|
||||
Stop();
|
||||
} else {
|
||||
m_failed_retry = 0;
|
||||
m_user_triggered = true;
|
||||
m_web_user_stopped = false;
|
||||
if (m_last_user_play + wxTimeSpan::Minutes(5) < wxDateTime::Now()) {
|
||||
m_last_failed_codes.clear();
|
||||
m_last_user_play = wxDateTime::Now();
|
||||
@@ -528,13 +770,13 @@ void MediaPlayCtrl::ToggleStream()
|
||||
wxGetApp().app_config->set("not_show_vcamera_stop_prev", "1");
|
||||
if (res == wxID_CANCEL) return;
|
||||
}
|
||||
if (m_lan_proto > MachineObject::LVL_Disable && (m_lan_mode || !m_remote_proto) && !m_disable_lan && !m_lan_ip.empty()) {
|
||||
if (m_lan_proto > LiveviewLocal::LVL_Disable && (m_lan_mode || !m_remote_proto) && !m_disable_lan && !m_lan_ip.empty()) {
|
||||
std::string url;
|
||||
if (m_lan_proto == MachineObject::LVL_Local)
|
||||
if (m_lan_proto == LiveviewLocal::LVL_Local)
|
||||
url = "bambu:///local/" + m_lan_ip + ".?port=6000&user=" + m_lan_user + "&passwd=" + m_lan_passwd;
|
||||
else if (m_lan_proto == MachineObject::LVL_Rtsps)
|
||||
else if (m_lan_proto == LiveviewLocal::LVL_Rtsps)
|
||||
url = "bambu:///rtsps___" + m_lan_user + ":" + m_lan_passwd + "@" + m_lan_ip + "/streaming/live/1?proto=rtsps";
|
||||
else if (m_lan_proto == MachineObject::LVL_Rtsp)
|
||||
else if (m_lan_proto == LiveviewLocal::LVL_Rtsp)
|
||||
url = "bambu:///rtsp___" + m_lan_user + ":" + m_lan_passwd + "@" + m_lan_ip + "/streaming/live/1?proto=rtsp";
|
||||
url += "&device=" + into_u8(m_machine);
|
||||
url += "&dev_ver=" + m_dev_ver;
|
||||
@@ -594,7 +836,9 @@ void MediaPlayCtrl::jump_to_play()
|
||||
void MediaPlayCtrl::onStateChanged(wxMediaEvent &event)
|
||||
{
|
||||
auto last_state = m_last_state;
|
||||
auto state = m_media_ctrl->GetState();
|
||||
if (m_active_media_controller != m_media_ctrl)
|
||||
return;
|
||||
auto state = m_active_media_controller->GetState();
|
||||
BOOST_LOG_TRIVIAL(info) << "MediaPlayCtrl::onStateChanged: " << state << ", last_state: " << last_state;
|
||||
if ((int) state < 0) return;
|
||||
{
|
||||
@@ -606,14 +850,14 @@ void MediaPlayCtrl::onStateChanged(wxMediaEvent &event)
|
||||
}
|
||||
if ((last_state == MEDIASTATE_IDLE || last_state == MEDIASTATE_INITIALIZING) && state == wxMEDIASTATE_STOPPED) { return; }
|
||||
if ((last_state == wxMEDIASTATE_PAUSED || last_state == wxMEDIASTATE_PLAYING) && state == wxMEDIASTATE_STOPPED) {
|
||||
m_failed_code = m_media_ctrl->GetLastError();
|
||||
m_failed_code = m_active_media_controller->GetLastError();
|
||||
Stop();
|
||||
return;
|
||||
}
|
||||
if (last_state == MEDIASTATE_LOADING && (state == wxMEDIASTATE_STOPPED || state == wxMEDIASTATE_PAUSED)) {
|
||||
wxSize size = m_media_ctrl->GetVideoSize();
|
||||
wxSize size = m_active_media_controller->GetVideoSize();
|
||||
BOOST_LOG_TRIVIAL(info) << "MediaPlayCtrl::onStateChanged: size: " << size.x << "x" << size.y;
|
||||
m_failed_code = m_media_ctrl->GetLastError();
|
||||
m_failed_code = m_active_media_controller->GetLastError();
|
||||
if (size.GetWidth() >= 320) {
|
||||
m_last_state = state;
|
||||
m_failed_code = 0;
|
||||
@@ -623,7 +867,7 @@ void MediaPlayCtrl::onStateChanged(wxMediaEvent &event)
|
||||
m_failed_retry = 0;
|
||||
m_disable_lan = false;
|
||||
boost::unique_lock lock(m_mutex);
|
||||
m_tasks.push_back("<play>");
|
||||
m_tasks.push_back({"<play>", m_active_media_controller});
|
||||
m_cond.notify_all();
|
||||
} else if (event.GetId()) {
|
||||
if (m_failed_code == 0)
|
||||
@@ -666,7 +910,8 @@ void MediaPlayCtrl::load()
|
||||
{
|
||||
m_last_state = MEDIASTATE_LOADING;
|
||||
SetStatus(_L("Loading..."));
|
||||
if (wxGetApp().app_config->get("internal_developer_mode") == "true") {
|
||||
const auto mode = current_mode();
|
||||
if (mode == CameraStreamMode::none && wxGetApp().app_config->get("internal_developer_mode") == "true") {
|
||||
std::string file_h264 = data_dir() + "/video.h264";
|
||||
std::string file_info = data_dir() + "/video.info";
|
||||
BOOST_LOG_TRIVIAL(info) << "MediaPlayCtrl dump video to " << file_h264;
|
||||
@@ -677,7 +922,7 @@ void MediaPlayCtrl::load()
|
||||
m_url = m_url + "&dump_info=" + boost::lexical_cast<std::string>(dump_info_file);
|
||||
}
|
||||
boost::unique_lock lock(m_mutex);
|
||||
m_tasks.push_back(m_url);
|
||||
m_tasks.push_back({m_url, m_active_media_controller});
|
||||
m_cond.notify_all();
|
||||
}
|
||||
|
||||
@@ -686,9 +931,8 @@ void MediaPlayCtrl::on_show_hide(wxShowEvent &evt)
|
||||
evt.Skip();
|
||||
if (m_isBeingDeleted) return;
|
||||
m_failed_retry = 0;
|
||||
if (m_next_retry.IsValid()) // Try open 2 seconds later, to avoid quick play/stop
|
||||
m_next_retry = wxDateTime::Now() + wxTimeSpan::Seconds(2);
|
||||
IsShownOnScreen() ? Play() : Stop();
|
||||
if (!IsShownOnScreen())
|
||||
Stop();
|
||||
}
|
||||
|
||||
void MediaPlayCtrl::media_proc()
|
||||
@@ -698,30 +942,34 @@ void MediaPlayCtrl::media_proc()
|
||||
while (m_tasks.empty()) {
|
||||
m_cond.wait(lock);
|
||||
}
|
||||
wxString url = m_tasks.front();
|
||||
if (m_tasks.size() >= 2 && !url.IsEmpty() && url[0] != '<' && m_tasks[1] == "<stop>") {
|
||||
BOOST_LOG_TRIVIAL(trace) << "MediaPlayCtrl: busy skip url: " << url;
|
||||
MediaTask task = m_tasks.front();
|
||||
if (m_tasks.size() >= 2 && !task.command.IsEmpty() && task.command[0] != '<' &&
|
||||
m_tasks[1].command == "<stop>" && task.controller == m_tasks[1].controller) {
|
||||
BOOST_LOG_TRIVIAL(trace) << "MediaPlayCtrl: busy skip url: " << task.command;
|
||||
m_tasks.pop_front();
|
||||
m_tasks.pop_front();
|
||||
continue;
|
||||
}
|
||||
lock.unlock();
|
||||
if (url == "<stop>") {
|
||||
if (task.command == "<stop>") {
|
||||
BOOST_LOG_TRIVIAL(info) << "MediaPlayCtrl: start stop";
|
||||
m_media_ctrl->Stop();
|
||||
if (task.controller)
|
||||
task.controller->Stop();
|
||||
BOOST_LOG_TRIVIAL(info) << "MediaPlayCtrl: end stop";
|
||||
}
|
||||
else if (url == "<exit>") {
|
||||
else if (task.command == "<exit>") {
|
||||
break;
|
||||
}
|
||||
else if (url == "<play>") {
|
||||
else if (task.command == "<play>") {
|
||||
BOOST_LOG_TRIVIAL(info) << "MediaPlayCtrl: start play";
|
||||
m_media_ctrl->Play();
|
||||
if (task.controller)
|
||||
task.controller->Play();
|
||||
BOOST_LOG_TRIVIAL(info) << "MediaPlayCtrl: end play";
|
||||
}
|
||||
else {
|
||||
BOOST_LOG_TRIVIAL(info) << "MediaPlayCtrl: start load";
|
||||
m_media_ctrl->Load(wxURI(url));
|
||||
if (task.controller)
|
||||
task.controller->Load(wxURI(task.command));
|
||||
BOOST_LOG_TRIVIAL(info) << "MediaPlayCtrl: end load";
|
||||
}
|
||||
lock.lock();
|
||||
|
||||
@@ -9,6 +9,9 @@
|
||||
#define MediaPlayCtrl_h
|
||||
|
||||
#include "wxMediaCtrl3.h"
|
||||
#include "IMediaController.hpp"
|
||||
#include "WebRtcMediaController.hpp"
|
||||
#include "slic3r/Utils/IPrinterAgent.hpp"
|
||||
|
||||
#include <wx/panel.h>
|
||||
|
||||
@@ -36,6 +39,10 @@ public:
|
||||
|
||||
void SetMachineObject(MachineObject * obj);
|
||||
|
||||
void SetWebMediaController(IMediaController *ctrl);
|
||||
|
||||
void StopWebStream();
|
||||
|
||||
bool IsStreaming() const;
|
||||
|
||||
void ToggleStream();
|
||||
@@ -54,6 +61,7 @@ protected:
|
||||
void TogglePlay();
|
||||
|
||||
void SetStatus(wxString const &msg, bool hyperlink = true);
|
||||
void on_webrtc_status(WebRtcMediaController::Status status);
|
||||
|
||||
private:
|
||||
void load();
|
||||
@@ -66,6 +74,9 @@ private:
|
||||
|
||||
static bool get_stream_url(std::string *url = nullptr);
|
||||
|
||||
CameraStreamMode current_mode() const;
|
||||
void set_active_media_controller(CameraStreamMode mode);
|
||||
|
||||
private:
|
||||
static inline const wxMediaState MEDIASTATE_IDLE = static_cast<wxMediaState>(3);
|
||||
static inline const wxMediaState MEDIASTATE_INITIALIZING = static_cast<wxMediaState>(4);
|
||||
@@ -76,6 +87,13 @@ private:
|
||||
std::shared_ptr<int> m_token = std::make_shared<int>(0);
|
||||
|
||||
wxMediaCtrl3 * m_media_ctrl;
|
||||
IMediaController * m_active_media_controller = nullptr;
|
||||
IMediaController * m_web_ctrl = nullptr;
|
||||
std::unique_ptr<WebRtcMediaController> m_webrtc_ctrl;
|
||||
CameraStreamMode m_last_mode = CameraStreamMode::none;
|
||||
std::uint64_t m_webrtc_epoch = 0;
|
||||
std::string m_agent_camera_url;
|
||||
bool m_web_user_stopped = false;
|
||||
wxMediaState m_last_state = MEDIASTATE_IDLE;
|
||||
std::string m_machine;
|
||||
int m_lan_proto = 0;
|
||||
@@ -91,7 +109,11 @@ private:
|
||||
bool m_disable_lan = false;
|
||||
wxString m_url;
|
||||
|
||||
std::deque<wxString> m_tasks;
|
||||
struct MediaTask {
|
||||
wxString command;
|
||||
IMediaController *controller = nullptr;
|
||||
};
|
||||
std::deque<MediaTask> m_tasks;
|
||||
boost::mutex m_mutex;
|
||||
boost::condition_variable m_cond;
|
||||
boost::thread m_thread;
|
||||
|
||||
@@ -34,6 +34,8 @@
|
||||
|
||||
#include "DeviceCore/DevManager.h"
|
||||
|
||||
#include <boost/log/trivial.hpp>
|
||||
|
||||
namespace Slic3r {
|
||||
namespace GUI {
|
||||
|
||||
@@ -259,6 +261,7 @@ void MonitorPanel::msw_rescale()
|
||||
|
||||
void MonitorPanel::select_machine(std::string machine_sn)
|
||||
{
|
||||
BOOST_LOG_TRIVIAL(info) << "Orca diagnostic: MonitorPanel::select_machine queueing machine_sn=" << machine_sn;
|
||||
wxCommandEvent *event = new wxCommandEvent(wxEVT_COMMAND_CHOICE_SELECTED);
|
||||
event->SetString(machine_sn);
|
||||
wxQueueEvent(this, event);
|
||||
@@ -276,13 +279,20 @@ void MonitorPanel::on_timer(wxTimerEvent& event)
|
||||
void MonitorPanel::on_select_printer(wxCommandEvent& event)
|
||||
{
|
||||
Slic3r::DeviceManager* dev = Slic3r::GUI::wxGetApp().getDeviceManager();
|
||||
const std::string requested_dev_id = event.GetString().ToStdString();
|
||||
BOOST_LOG_TRIVIAL(info) << "Orca diagnostic: MonitorPanel::on_select_printer requested_dev_id="
|
||||
<< requested_dev_id << " device_manager=" << (dev ? "set" : "null");
|
||||
if (!dev) return;
|
||||
|
||||
if ( dev->get_selected_machine() && (dev->get_selected_machine()->get_dev_id() != event.GetString().ToStdString()) && m_hms_panel) {
|
||||
m_hms_panel->clear_hms_tag();
|
||||
}
|
||||
|
||||
if (!dev->set_selected_machine(event.GetString().ToStdString()))
|
||||
const bool selected = dev->set_selected_machine(requested_dev_id);
|
||||
BOOST_LOG_TRIVIAL(info) << "Orca diagnostic: MonitorPanel::on_select_printer set_selected_machine result="
|
||||
<< selected << " selected_dev_id="
|
||||
<< (dev->get_selected_machine() ? dev->get_selected_machine()->get_dev_id() : "<null>");
|
||||
if (!selected)
|
||||
return;
|
||||
|
||||
set_default();
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
|
||||
#include "GUI_App.hpp"
|
||||
#include "MainFrame.hpp"
|
||||
#include "DeviceCore/DevConfigUtil.h"
|
||||
|
||||
namespace Slic3r {
|
||||
namespace GUI {
|
||||
@@ -108,21 +109,12 @@ bool DeviceItem::is_blocking_printing(MachineObject* obj_)
|
||||
{
|
||||
DeviceManager* dev = Slic3r::GUI::wxGetApp().getDeviceManager();
|
||||
if (!dev) return true;
|
||||
auto target_model = obj_->printer_type;
|
||||
std::string source_model = "";
|
||||
|
||||
PresetBundle* preset_bundle = wxGetApp().preset_bundle;
|
||||
source_model = preset_bundle->printers.get_edited_preset().get_printer_type(preset_bundle);
|
||||
|
||||
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;
|
||||
return !DevPrinterConfigUtil::is_printer_model_compatible(source_model, *obj_);
|
||||
}
|
||||
|
||||
void DeviceItem::update_item(const DeviceItem* item)
|
||||
|
||||
@@ -159,4 +159,4 @@ private:
|
||||
void OnApplyDialog(wxCommandEvent &event);
|
||||
};
|
||||
|
||||
}} // namespace Slic3r::GUI
|
||||
}} // namespace Slic3r::GUI
|
||||
|
||||
+222
-24
@@ -87,6 +87,7 @@
|
||||
#ifdef __WXGTK__
|
||||
#include "LinuxDisplayBackend.hpp"
|
||||
#endif
|
||||
#include "AuiPaneLayout.hpp"
|
||||
#include "GUI_Utils.hpp"
|
||||
#include "GUI_Factories.hpp"
|
||||
#include "wxExtensions.hpp"
|
||||
@@ -2025,12 +2026,14 @@ bool Sidebar::priv::sync_extruder_list(bool &only_external_material, bool is_man
|
||||
std::string machine_print_name = obj->get_show_printer_type();
|
||||
PresetBundle *preset_bundle = wxGetApp().preset_bundle;
|
||||
std::string target_model_id = preset_bundle->printers.get_selected_preset().get_printer_type(preset_bundle);
|
||||
Preset* machine_preset = get_printer_preset(obj);
|
||||
if (!machine_preset) {
|
||||
const bool optional_printer_model = DevPrinterConfigUtil::is_optional_printer_model_id(obj->printer_type);
|
||||
const bool optional_target_model = DevPrinterConfigUtil::is_optional_printer_model_id(target_model_id);
|
||||
Preset* machine_preset = optional_printer_model ? nullptr : get_printer_preset(obj);
|
||||
if (!optional_printer_model && !optional_target_model && !machine_preset) {
|
||||
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << __LINE__ << "check error: machine_preset empty";
|
||||
return false;
|
||||
}
|
||||
if (machine_print_name != target_model_id) {
|
||||
if (!optional_printer_model && !optional_target_model && machine_print_name != target_model_id) {
|
||||
MessageDialog dlg(this->plater, _L("The currently selected machine preset is inconsistent with the connected printer type.\n"
|
||||
"Are you sure to continue syncing?"), _L("Sync printer information"), wxICON_WARNING | wxYES | wxNO);
|
||||
if (dlg.ShowModal() == wxID_NO) {
|
||||
@@ -2222,6 +2225,11 @@ void Sidebar::priv::update_sync_status(const MachineObject *obj)
|
||||
return;
|
||||
}
|
||||
|
||||
if (DevPrinterConfigUtil::is_optional_printer_model_id(obj->printer_type)) {
|
||||
clear_all_sync_status();
|
||||
return;
|
||||
}
|
||||
|
||||
bool printer_synced = false;
|
||||
// 1. update printer status
|
||||
const Preset &cur_preset = wxGetApp().preset_bundle->printers.get_edited_preset();
|
||||
@@ -5822,11 +5830,30 @@ void Sidebar::load_ams_list(MachineObject* obj)
|
||||
filament_ams_list = build_filament_ams_list(obj);
|
||||
}
|
||||
|
||||
bool device_change = false;
|
||||
const std::string& device = obj ? obj->get_dev_id() : "";
|
||||
if (p->ams_list_device != device) {
|
||||
const bool same_device = p->ams_list_device == device;
|
||||
|
||||
// Keep sync metadata out of the device payload, but preserve it across a
|
||||
// subscription refresh when the physical filament in a slot is unchanged.
|
||||
// Otherwise the refreshed configs differ only by the missing
|
||||
// filament_changed key, causing combo boxes to rebuild and lose their
|
||||
// transient post-sync badges.
|
||||
auto &previous_filament_ams_list = wxGetApp().preset_bundle->filament_ams_list;
|
||||
for (auto &entry : filament_ams_list) {
|
||||
auto previous = previous_filament_ams_list.find(entry.first);
|
||||
const auto *previous_changed = previous == previous_filament_ams_list.end() ? nullptr :
|
||||
dynamic_cast<const ConfigOptionBool *>(previous->second.option("filament_changed"));
|
||||
if (!same_device || previous_changed == nullptr ||
|
||||
previous->second.opt_string("filament_id", 0u) != entry.second.opt_string("filament_id", 0u)) {
|
||||
continue;
|
||||
}
|
||||
entry.second.set_key_value("filament_changed",
|
||||
new ConfigOptionBool{previous_changed->value});
|
||||
}
|
||||
|
||||
bool device_change = !same_device;
|
||||
if (device_change) {
|
||||
p->ams_list_device = device;
|
||||
device_change = true;
|
||||
}
|
||||
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(": %1% items") % filament_ams_list.size();
|
||||
if (wxGetApp().preset_bundle->filament_ams_list == filament_ams_list && !device_change)
|
||||
@@ -5836,9 +5863,27 @@ void Sidebar::load_ams_list(MachineObject* obj)
|
||||
wxGetApp().preset_bundle->filament_ams_list = filament_ams_list;
|
||||
|
||||
for (auto c : p->combos_filament){
|
||||
c->set_sync_badge(false);
|
||||
c->update();
|
||||
if (device_change) {
|
||||
c->ShowBadge(false);//change printer,then clear badge
|
||||
}
|
||||
|
||||
if (!device_change) {
|
||||
size_t combo_index = 0;
|
||||
for (const auto &entry : filament_ams_list) {
|
||||
const auto &tray = entry.second;
|
||||
const bool has_filament = !tray.opt_string("filament_id", 0u).empty();
|
||||
const bool is_placeholder = tray.has("filament_slot_placeholder") &&
|
||||
tray.opt_bool("filament_slot_placeholder", 0u);
|
||||
if (!has_filament && !is_placeholder) {
|
||||
continue;
|
||||
}
|
||||
if (combo_index >= p->combos_filament.size()) {
|
||||
break;
|
||||
}
|
||||
const auto *filament_changed = dynamic_cast<const ConfigOptionBool *>(tray.option("filament_changed"));
|
||||
p->combos_filament[combo_index]->set_sync_badge(
|
||||
has_filament && !is_placeholder && filament_changed != nullptr && filament_changed->value);
|
||||
++combo_index;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6012,18 +6057,32 @@ void Sidebar::sync_ams_list(bool is_from_big_sync_btn)
|
||||
auto tip = sync_color_only ? _L("Only filament color information has been synchronized from printer.") :
|
||||
_L("Filament type and color information have been synchronized, but slot information is not included.");
|
||||
c->SetToolTip(tip);
|
||||
c->ShowBadge(true);
|
||||
c->set_sync_badge(true);
|
||||
};
|
||||
{ // badge ams filament
|
||||
clear_combos_filament_badge();
|
||||
if (sync_result.direct_sync) {
|
||||
// Orca: PresetBundle::sync_ams_list rebuilds combos_filament
|
||||
// 1:1 from the AMS trays that produce a combo (loaded trays + placeholders; non-placeholder
|
||||
// empty trays are skipped), so every resulting combo is AMS-sourced and gets a badge. The
|
||||
// previous per-tray index walked the full filament_ams_list (including the skipped empties),
|
||||
// so an empty slot before a loaded one dropped the badge for the trailing filaments.
|
||||
for (auto &c : p->combos_filament) {
|
||||
badge_combox_filament(c);
|
||||
// A placeholder contributes a preserved project filament to the
|
||||
// overwrite result, but it is not AMS-sourced and must not get a
|
||||
// sync badge. Non-placeholder empty trays are omitted entirely.
|
||||
size_t combo_index = 0;
|
||||
for (const auto &entry : wxGetApp().preset_bundle->filament_ams_list) {
|
||||
const auto &tray = entry.second;
|
||||
const bool has_filament = !tray.opt_string("filament_id", 0u).empty();
|
||||
const bool is_placeholder = tray.has("filament_slot_placeholder") &&
|
||||
tray.opt_bool("filament_slot_placeholder", 0u);
|
||||
if (!has_filament && !is_placeholder) {
|
||||
continue;
|
||||
}
|
||||
if (combo_index >= p->combos_filament.size()) {
|
||||
break;
|
||||
}
|
||||
if (is_placeholder) {
|
||||
p->combos_filament[combo_index]->set_sync_badge(false);
|
||||
} else {
|
||||
badge_combox_filament(p->combos_filament[combo_index]);
|
||||
}
|
||||
++combo_index;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -6291,6 +6350,11 @@ template<typename T> void setup_dialog_position(T& info)
|
||||
|
||||
void Sidebar::pop_sync_nozzle_and_ams_dialog() {
|
||||
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << " begin pop_sync_nozzle_and_ams_dialog";
|
||||
auto agent = wxGetApp().getAgent();
|
||||
if (!agent || agent->get_filament_sync_mode() == FilamentSyncMode::none) {
|
||||
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << " filament synchronization is not supported; skipping dialog";
|
||||
return;
|
||||
}
|
||||
wxTheApp->CallAfter([this]() {
|
||||
SyncNozzleAndAmsDialog::InputInfo temp_na_info;
|
||||
wxPoint big_btn_pt;
|
||||
@@ -6422,17 +6486,14 @@ void Sidebar::clear_combos_filament_badge()
|
||||
{
|
||||
auto &combos_filament = p->combos_filament;
|
||||
for (auto &c : combos_filament) { // clear flag
|
||||
c->ShowBadge(false);
|
||||
c->set_sync_badge(false);
|
||||
}
|
||||
}
|
||||
|
||||
void Sidebar::udpate_combos_filament_badge() {
|
||||
auto &combos_filament = p->combos_filament;
|
||||
for (auto &c : combos_filament) {
|
||||
auto selection = c->GetSelection();
|
||||
auto select_flag = c->GetFlag(selection);
|
||||
auto ok = select_flag == (int) PresetComboBox::FilamentAMSType::FROM_AMS;
|
||||
c->ShowBadge(ok);
|
||||
c->update_badge_according_flag();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -6748,6 +6809,14 @@ struct Plater::priv
|
||||
|
||||
// GUI elements
|
||||
AuiMgr m_aui_mgr;
|
||||
// Live dock panes. `on_close` runs when the user closes one from its close button; `shown` is
|
||||
// what the owner asked for.
|
||||
struct DockPane
|
||||
{
|
||||
std::function<void()> on_close;
|
||||
bool shown{true};
|
||||
};
|
||||
std::map<wxWindow*, DockPane> m_dock_panes;
|
||||
wxString m_default_window_layout;
|
||||
wxPanel* current_panel{ nullptr };
|
||||
std::vector<wxPanel*> panels;
|
||||
@@ -6920,6 +6989,11 @@ struct Plater::priv
|
||||
void update_sidebar(bool force_update = false);
|
||||
void reset_window_layout();
|
||||
Sidebar::DockingState get_sidebar_docking_state();
|
||||
void add_dock_pane(wxWindow* window, const std::string& name, const wxString& caption, const std::string& dock,
|
||||
const wxSize& size, std::function<void()> on_close);
|
||||
void remove_dock_pane(wxWindow* window);
|
||||
void show_dock_pane(wxWindow* window, bool show);
|
||||
bool dock_pane_visible(const DockPane& dock_pane, const wxAuiPaneInfo& pane) const;
|
||||
|
||||
bool is_view3D_layers_editing_enabled() const { return (current_panel == view3D) && view3D->get_canvas3d()->is_layers_editing_enabled(); }
|
||||
|
||||
@@ -7500,6 +7574,18 @@ Plater::priv::priv(Plater *q, MainFrame *main_frame)
|
||||
panel_3d->SetSizer(panel_sizer);
|
||||
m_aui_mgr.AddPane(panel_3d, wxAuiPaneInfo().Name("main").CenterPane().PaneBorder(false));
|
||||
|
||||
q->Bind(wxEVT_AUI_PANE_CLOSE, [this](wxAuiManagerEvent& evt) {
|
||||
const wxAuiPaneInfo* pane = evt.GetPane();
|
||||
auto it = pane != nullptr ? m_dock_panes.find(pane->window) : m_dock_panes.end();
|
||||
if (it != m_dock_panes.end()) {
|
||||
const std::function<void()> on_close = std::move(it->second.on_close);
|
||||
m_dock_panes.erase(it);
|
||||
if (on_close)
|
||||
on_close();
|
||||
}
|
||||
evt.Skip();
|
||||
});
|
||||
|
||||
m_default_window_layout = m_aui_mgr.SavePerspective();
|
||||
|
||||
{
|
||||
@@ -8165,6 +8251,14 @@ void Plater::priv::update_sidebar(bool force_update) {
|
||||
}
|
||||
}
|
||||
|
||||
for (const auto& [window, dock_pane] : m_dock_panes) {
|
||||
wxAuiPaneInfo& pane = m_aui_mgr.GetPane(window);
|
||||
if (pane.IsOk() && pane.IsShown() != dock_pane_visible(dock_pane, pane)) {
|
||||
pane.Show(!pane.IsShown());
|
||||
needs_update = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (needs_update) {
|
||||
notification_manager->set_sidebar_collapsed(sidebar.IsShown());
|
||||
m_aui_mgr.Update();
|
||||
@@ -8174,10 +8268,96 @@ void Plater::priv::update_sidebar(bool force_update) {
|
||||
void Plater::priv::reset_window_layout()
|
||||
{
|
||||
m_aui_mgr.LoadPerspective(m_default_window_layout, false);
|
||||
// Loading a layout docks and hides every pane it does not list, and the default layout lists no
|
||||
// dock panes: a floating dock pane is docked again, like the rest of the window.
|
||||
for (const auto& [window, dock_pane] : m_dock_panes)
|
||||
if (wxAuiPaneInfo& pane = m_aui_mgr.GetPane(window); pane.IsOk())
|
||||
pane.Show(dock_pane_visible(dock_pane, pane));
|
||||
sidebar_layout.is_collapsed = false;
|
||||
update_sidebar(true);
|
||||
}
|
||||
|
||||
bool Plater::priv::dock_pane_visible(const DockPane& dock_pane, const wxAuiPaneInfo& pane) const
|
||||
{
|
||||
// A floating pane is a top-level window, so it does not hide with the Plater on other tabs.
|
||||
return dock_pane.shown && (!pane.IsFloating() || sidebar_layout.show);
|
||||
}
|
||||
|
||||
void Plater::priv::add_dock_pane(wxWindow* window, const std::string& name, const wxString& caption, const std::string& dock,
|
||||
const wxSize& size, std::function<void()> on_close)
|
||||
{
|
||||
const wxString base_name = wxString::FromUTF8(name);
|
||||
wxString unique_name = base_name;
|
||||
for (int i = 2; m_aui_mgr.GetPane(unique_name).IsOk(); ++i)
|
||||
unique_name = base_name + wxString::Format("#%d", i);
|
||||
|
||||
// A restored layout below already holds pixels.
|
||||
const wxSize pixels = q->FromDIP(size);
|
||||
wxAuiPaneInfo info;
|
||||
info.Name(unique_name).Caption(caption).BestSize(pixels).FloatingSize(pixels).DestroyOnClose(true);
|
||||
if (dock == "left")
|
||||
info.Left();
|
||||
else if (dock == "bottom")
|
||||
info.Bottom();
|
||||
else
|
||||
info.Right();
|
||||
if (dock == "float")
|
||||
info.Float();
|
||||
|
||||
// Put the pane back where it was the last time the window layout was saved with it open.
|
||||
const std::string saved = aui_pane_layout_entry(wxGetApp().app_config->get("window_layout"), unique_name.utf8_string());
|
||||
if (!saved.empty()) {
|
||||
m_aui_mgr.LoadPaneInfo(wxString::FromUTF8(saved), info);
|
||||
info.Caption(caption).DestroyOnClose(true).Show();
|
||||
}
|
||||
|
||||
// Floating is disabled on Wayland.
|
||||
if ((m_aui_mgr.GetFlags() & wxAUI_MGR_ALLOW_FLOATING) == 0) {
|
||||
info.Dock().Floatable(false);
|
||||
if (info.dock_direction == wxAUI_DOCK_NONE)
|
||||
info.Right();
|
||||
}
|
||||
|
||||
const DockPane& dock_pane = m_dock_panes[window] = DockPane{std::move(on_close)};
|
||||
info.Show(dock_pane_visible(dock_pane, info));
|
||||
m_aui_mgr.AddPane(window, info);
|
||||
|
||||
// wxAUI does not record a dragged sash in best_size, so track the docked size like the sidebar
|
||||
// does, for the saved layout.
|
||||
window->Bind(wxEVT_IDLE, [this, window](wxIdleEvent& evt) {
|
||||
wxAuiPaneInfo& pane = m_aui_mgr.GetPane(window);
|
||||
if (pane.IsOk() && pane.IsShown() && pane.IsDocked() && pane.rect.GetWidth() > 0 && pane.rect.GetHeight() > 0) {
|
||||
const bool horizontal = pane.dock_direction == wxAUI_DOCK_TOP || pane.dock_direction == wxAUI_DOCK_BOTTOM;
|
||||
pane.BestSize(horizontal ? pane.best_size.GetWidth() : pane.rect.GetWidth(),
|
||||
horizontal ? pane.rect.GetHeight() : pane.best_size.GetHeight());
|
||||
}
|
||||
evt.Skip();
|
||||
});
|
||||
|
||||
m_aui_mgr.Update();
|
||||
}
|
||||
|
||||
void Plater::priv::remove_dock_pane(wxWindow* window)
|
||||
{
|
||||
m_dock_panes.erase(window);
|
||||
if (m_aui_mgr.DetachPane(window))
|
||||
m_aui_mgr.Update();
|
||||
window->Destroy();
|
||||
}
|
||||
|
||||
void Plater::priv::show_dock_pane(wxWindow* window, bool show)
|
||||
{
|
||||
const auto it = m_dock_panes.find(window);
|
||||
wxAuiPaneInfo& pane = m_aui_mgr.GetPane(window);
|
||||
if (it == m_dock_panes.end() || !pane.IsOk())
|
||||
return;
|
||||
it->second.shown = show;
|
||||
if (pane.IsShown() == dock_pane_visible(it->second, pane))
|
||||
return;
|
||||
pane.Show(!pane.IsShown());
|
||||
m_aui_mgr.Update();
|
||||
}
|
||||
|
||||
Sidebar::DockingState Plater::priv::get_sidebar_docking_state() {
|
||||
if (!sidebar_layout.is_enabled) {
|
||||
return Sidebar::None;
|
||||
@@ -12271,7 +12451,7 @@ void Plater::priv::on_select_preset(wxCommandEvent &evt)
|
||||
sidebar->auto_calc_flushing_volumes(idx);
|
||||
}
|
||||
auto select_flag = combo->GetFlag(selection);
|
||||
combo->ShowBadge(select_flag == (int)PresetComboBox::FilamentAMSType::FROM_AMS);
|
||||
combo->set_sync_badge(select_flag == (int)PresetComboBox::FilamentAMSType::FROM_AMS);
|
||||
q->on_filament_change(idx);
|
||||
}
|
||||
bool select_preset = !combo->selection_is_changed_according_to_physical_printers();
|
||||
@@ -17772,6 +17952,19 @@ Sidebar::DockingState Plater::get_sidebar_docking_state() const { return p->get_
|
||||
|
||||
void Plater::reset_window_layout() { p->reset_window_layout(); }
|
||||
|
||||
void Plater::add_dock_pane(wxWindow* window, const std::string& name, const wxString& caption, const std::string& dock,
|
||||
const wxSize& size, std::function<void()> on_close)
|
||||
{
|
||||
p->add_dock_pane(window, name, caption, dock, size, std::move(on_close));
|
||||
}
|
||||
void Plater::remove_dock_pane(wxWindow* window) { p->remove_dock_pane(window); }
|
||||
void Plater::remove_dock_panes()
|
||||
{
|
||||
while (!p->m_dock_panes.empty())
|
||||
p->remove_dock_pane(p->m_dock_panes.begin()->first);
|
||||
}
|
||||
void Plater::show_dock_pane(wxWindow* window, bool show) { p->show_dock_pane(window, show); }
|
||||
|
||||
//BBS
|
||||
void Plater::select_curr_plate_all() { p->select_curr_plate_all(); }
|
||||
void Plater::remove_curr_plate_all() { p->remove_curr_plate_all(); }
|
||||
@@ -20877,9 +21070,14 @@ bool Plater::is_same_printer_for_connected_and_selected(bool popup_warning)
|
||||
}
|
||||
if (!check_printer_initialized(obj, true, popup_warning))
|
||||
return false;
|
||||
Preset * machine_preset = get_printer_preset(obj);
|
||||
if (!machine_preset)
|
||||
const std::string machine_model = obj->printer_type;
|
||||
PresetBundle *preset_bundle = wxGetApp().preset_bundle;
|
||||
const std::string selected_model = preset_bundle ? preset_bundle->printers.get_edited_preset().get_printer_type(preset_bundle) : std::string();
|
||||
if (!DevPrinterConfigUtil::is_optional_printer_model_id(machine_model) &&
|
||||
!DevPrinterConfigUtil::is_optional_printer_model_id(selected_model) &&
|
||||
!get_printer_preset(obj)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (wxGetApp().is_blocking_printing()) {
|
||||
if (popup_warning) {
|
||||
|
||||
@@ -476,6 +476,17 @@ public:
|
||||
|
||||
void reset_window_layout();
|
||||
|
||||
// Dock panes sit alongside the sidebar; `window` must be a child of the Plater. `dock` is
|
||||
// "left", "right", "bottom" or "float", and `size` is in DIPs. A pane closed from its own close
|
||||
// button is destroyed after on_close runs; remove_dock_pane() destroys it without calling on_close.
|
||||
void add_dock_pane(wxWindow* window, const std::string& name, const wxString& caption, const std::string& dock,
|
||||
const wxSize& size, std::function<void()> on_close);
|
||||
void remove_dock_pane(wxWindow* window);
|
||||
void show_dock_pane(wxWindow* window, bool show);
|
||||
// Removes every dock pane without calling on_close, for MainFrame::shutdown() (app exit and a
|
||||
// language switch), while the Plater and any floating frames still exist.
|
||||
void remove_dock_panes();
|
||||
|
||||
// Called after the Preferences dialog is closed and the program settings are saved.
|
||||
// Update the UI based on the current preferences.
|
||||
void update_ui_from_settings();
|
||||
|
||||
@@ -11,6 +11,7 @@ namespace Slic3r
|
||||
// IMPORTANT: ordinal order is the Plugins dialog Status sort priority.
|
||||
Activated,
|
||||
Error,
|
||||
RuntimeError,
|
||||
Inactive,
|
||||
Loading
|
||||
};
|
||||
@@ -21,11 +22,28 @@ namespace Slic3r
|
||||
{
|
||||
case PluginStatus::Activated: return "Activated";
|
||||
case PluginStatus::Error: return "Error";
|
||||
case PluginStatus::RuntimeError: return "RuntimeError";
|
||||
case PluginStatus::Inactive: return "Inactive";
|
||||
case PluginStatus::Loading: return "Loading";
|
||||
}
|
||||
|
||||
return "Inactive";
|
||||
}
|
||||
|
||||
// why: a plugin whose module is live but whose catalog carries an error is a
|
||||
// RUNTIME fault (e.g. a capability rejected at register time) - it stays
|
||||
// loaded/checked and is only flagged, distinct from a load-time Error where
|
||||
// the module never came up. Loading wins over both so an in-flight reload
|
||||
// never flashes an error.
|
||||
inline PluginStatus resolve_plugin_status(bool loading, bool has_error, bool is_loaded)
|
||||
{
|
||||
if (loading)
|
||||
return PluginStatus::Loading;
|
||||
if (has_error)
|
||||
return is_loaded ? PluginStatus::RuntimeError : PluginStatus::Error;
|
||||
if (is_loaded)
|
||||
return PluginStatus::Activated;
|
||||
return PluginStatus::Inactive;
|
||||
}
|
||||
}
|
||||
} // namespace Slic3r::GUI
|
||||
|
||||
@@ -193,6 +193,7 @@ nlohmann::json build_plugin_payload_item(const PluginDialogItem& dialog_item)
|
||||
payload_item["label"] = dialog_item.display_name;
|
||||
payload_item["source"] = to_string(dialog_item.source);
|
||||
payload_item["status"] = to_string(dialog_item.status);
|
||||
payload_item["is_loaded"] = dialog_item.is_loaded;
|
||||
payload_item["error"] = dialog_item.error_text;
|
||||
payload_item["update_status"] = to_string(dialog_item.update_status);
|
||||
payload_item["unauthorized"] = dialog_item.unauthorized;
|
||||
@@ -337,14 +338,7 @@ PluginDialogItem build_plugin_dialog_item(const PluginDescriptor& descriptor)
|
||||
item.sharing_token = descriptor.sharing_token;
|
||||
item.thumbnail_url = descriptor.thumbnail_url;
|
||||
|
||||
if (item.loading)
|
||||
item.status = PluginStatus::Loading;
|
||||
else if (item.has_error)
|
||||
item.status = PluginStatus::Error;
|
||||
else if (item.is_loaded)
|
||||
item.status = PluginStatus::Activated;
|
||||
else
|
||||
item.status = PluginStatus::Inactive;
|
||||
item.status = resolve_plugin_status(item.loading, item.has_error, item.is_loaded);
|
||||
|
||||
item.available_actions = evaluate_action_policy(item);
|
||||
const bool has_enabled_script = std::any_of(item.capabilities.begin(), item.capabilities.end(),
|
||||
@@ -790,6 +784,9 @@ void PluginsDialog::toggle_plugin(const std::string& plugin_key, bool enabled)
|
||||
}
|
||||
|
||||
BOOST_LOG_TRIVIAL(info) << "Plugin unloaded from Plugins dialog: " << plugin_key;
|
||||
// A user-disabled plugin has no meaningful error state.
|
||||
if (!manager.clear_plugin_error(plugin_key))
|
||||
BOOST_LOG_TRIVIAL(warning) << "Failed to clear plugin error for " << plugin_key << " (failed to find)";
|
||||
// A prior activation of this plugin is moot now; drop it so no stale "Activated" arrives later.
|
||||
if (m_activating_plugin_key == plugin_key)
|
||||
m_activating_plugin_key.clear();
|
||||
|
||||
@@ -63,6 +63,7 @@ std::string PrePrintChecker::get_print_status_info(PrintDialogStatus status)
|
||||
case PrintStatusRackReading: return "PrintStatusRackReading";
|
||||
case PrintStatusRackNozzleNumUnmeetWarning: return "PrintStatusRackNozzleNumUnmeetWarning";
|
||||
case PrintStatusHasUnreliableNozzleWarning: return "PrintStatusHasUnreliableNozzleWarning";
|
||||
case PrintStatusOptionalPrinterModel: return "PrintStatusOptionalPrinterModel";
|
||||
case PrintStatusWarningExtFilamentNotMatch: return "PrintStatusWarningExtFilamentNotMatch";
|
||||
case PrintStatusFilamentWarningNozzleHRC: return "PrintStatusFilamentWarningNozzleHRC";
|
||||
case PrintStatusTPUUnsupportCaliOn: return "PrintStatusTPUUnsupportCaliOn";
|
||||
@@ -104,6 +105,7 @@ wxString PrePrintChecker::get_pre_state_msg(PrintDialogStatus status)
|
||||
case PrintStatusNeedConsistencyUpgrading: return _L("Cannot send the print job to a printer whose firmware must be updated.");
|
||||
case PrintStatusBlankPlate: return _L("Cannot send a print job for an empty plate.");
|
||||
case PrintStatusTimelapseNoSdcard: return _L("Storage needs to be inserted to record timelapse.");
|
||||
case PrintStatusOptionalPrinterModel: return _L("The selected printer model could not be identified, so compatibility with the print file configuration cannot be verified. Please verify the printer preset before sending.");
|
||||
case PrintStatusMixAmsAndVtSlotWarning: return _L("You have selected both external and AMS filaments for an extruder. You will need to manually switch the external filament during printing.");
|
||||
case PrintStatusTPUUnsupportAutoCali: return _L("TPU 90A/TPU 85A is too soft and does not support automatic Flow Dynamics calibration.");
|
||||
case PrintStatusWarningKvalueNotUsed: return _L("Set dynamic flow calibration to 'OFF' to enable custom dynamic flow value.");
|
||||
@@ -379,4 +381,3 @@ bool PrinterMsgPanel::UpdateInfos(const std::vector<prePrintInfo>& infos)
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user