mirror of
https://github.com/OrcaSlicer/OrcaSlicer.git
synced 2026-10-10 01:01:57 +00:00
Merge branch 'main' into feature/texture_displacement
This commit is contained in:
@@ -129,6 +129,23 @@ if (MINGW)
|
||||
set_target_properties(OrcaSlicer PROPERTIES PREFIX "")
|
||||
endif (MINGW)
|
||||
|
||||
# The GUI embeds the bundled shared libpython (libslic3r_gui -> pybind11::embed);
|
||||
# the dep recipe stamps it with an @rpath id / plain SONAME, so the executable
|
||||
# needs an rpath entry for each layout in the "Bundled Python/uv layout" map
|
||||
# further down. Dead entries are skipped by the loader.
|
||||
# OrcaSlicer_profile_validator links libslic3r only (no Python).
|
||||
if (APPLE)
|
||||
# A linker flag rather than the BUILD_RPATH property because the release
|
||||
# flow uses the Xcode generator, which does not honor BUILD_RPATH.
|
||||
target_link_options(OrcaSlicer PRIVATE "LINKER:-rpath,@executable_path/python/lib")
|
||||
elseif (UNIX AND NOT WIN32)
|
||||
set_target_properties(OrcaSlicer PROPERTIES
|
||||
# Build tree + AppImage, which packages the build-tree binary.
|
||||
BUILD_RPATH "$ORIGIN/python/lib;$ORIGIN/../lib/python/lib"
|
||||
# Flatpak (cmake --install).
|
||||
INSTALL_RPATH "$ORIGIN/../libpython/lib")
|
||||
endif ()
|
||||
|
||||
if (NOT WIN32 AND NOT APPLE)
|
||||
# Binary name on unix like systems (Linux, Unix)
|
||||
set_target_properties(OrcaSlicer PROPERTIES OUTPUT_NAME "orca-slicer")
|
||||
@@ -197,6 +214,26 @@ endif ()
|
||||
set(output_dlls_Release "")
|
||||
set(output_dlls_Debug "")
|
||||
set(output_dlls_RelWithDebInfo "")
|
||||
# ---- Bundled Python/uv layout (canonical map) ---------------------------
|
||||
# The runtime and uv are staged three ways because packaging differs:
|
||||
# install() rules - Windows installer; on FHS/Flatpak they stage
|
||||
# only uv (Flatpak's runtime comes from the deps
|
||||
# build installing to /app/libpython; plain FHS
|
||||
# installs bundle no python runtime)
|
||||
# POST_BUILD copies (here) - build-tree runs, and the macOS .app/AppImage
|
||||
# flows, which package the build tree and
|
||||
# never run install()
|
||||
# packaging scripts - build_release_macos.sh (copies the .app),
|
||||
# build_linux_image.sh.in (assembles $APPDIR)
|
||||
# Final locations relative to the executable:
|
||||
# Windows: <exe dir>/python <resources>/tools/uv
|
||||
# macOS: Contents/MacOS/python Contents/MacOS/tools/uv
|
||||
# AppImage: $APPDIR/lib/python $APPDIR/resources/tools/uv
|
||||
# Flatpak: /app/libpython (dep prefix) /app/share/OrcaSlicer/tools/uv
|
||||
# Runtime lookup: PythonInterpreter::initialize() / bundled_uv_path().
|
||||
# libpython resolution: rpath on the OrcaSlicer target (above); the dep
|
||||
# recipe gives the bundled interpreter a self-relative rpath.
|
||||
# --------------------------------------------------------------------------
|
||||
if (WIN32)
|
||||
# This has to be a separate target due to the windows command line length limits
|
||||
add_custom_target(COPY_DLLS ALL DEPENDS OrcaSlicer)
|
||||
@@ -231,6 +268,12 @@ if (WIN32)
|
||||
VERBATIM
|
||||
)
|
||||
endif ()
|
||||
# copy libpython to the bin folder for Windows
|
||||
add_custom_command(TARGET OrcaSlicer POST_BUILD
|
||||
COMMAND ${CMAKE_COMMAND} -E rm -rf "$<TARGET_FILE_DIR:OrcaSlicer>/python"
|
||||
COMMAND ${CMAKE_COMMAND} -E copy_directory "${CMAKE_PREFIX_PATH}/libpython" "$<TARGET_FILE_DIR:OrcaSlicer>/python"
|
||||
COMMENT "Copying Python runtime into the build tree"
|
||||
VERBATIM)
|
||||
|
||||
|
||||
else ()
|
||||
@@ -248,25 +291,51 @@ else ()
|
||||
if (XCODE)
|
||||
# Because of Debug/Release/etc. configurations (similar to MSVC) the slic3r binary is located in an extra level
|
||||
set(BIN_RESOURCES_DIR "${CMAKE_CURRENT_BINARY_DIR}/resources")
|
||||
set(BIN_DIR "${CMAKE_CURRENT_BINARY_DIR}")
|
||||
set(BIN_CONF_DIR "Debug")
|
||||
else ()
|
||||
set(BIN_RESOURCES_DIR "${CMAKE_CURRENT_BINARY_DIR}/../resources")
|
||||
set(BIN_DIR "$<TARGET_FILE_DIR:OrcaSlicer>")
|
||||
endif ()
|
||||
if (CMAKE_MACOSX_BUNDLE)
|
||||
if (CMAKE_CONFIGURATION_TYPES)
|
||||
set(BIN_RESOURCES_DIR "${CMAKE_CURRENT_BINARY_DIR}/$<CONFIG>/OrcaSlicer.app/Contents/Resources")
|
||||
set(BIN_DIR "${CMAKE_CURRENT_BINARY_DIR}/$<CONFIG>/OrcaSlicer.app/Contents/MacOS")
|
||||
else()
|
||||
set(BIN_RESOURCES_DIR "${CMAKE_CURRENT_BINARY_DIR}/OrcaSlicer.app/Contents/Resources")
|
||||
set(BIN_DIR "${CMAKE_CURRENT_BINARY_DIR}/OrcaSlicer.app/Contents/MacOS")
|
||||
endif()
|
||||
set(MACOSX_BUNDLE_ICON_FILE Icon.icns)
|
||||
set(MACOSX_BUNDLE_BUNDLE_NAME "OrcaSlicer")
|
||||
set(MACOSX_BUNDLE_SHORT_VERSION_STRING ${SoftFever_VERSION})
|
||||
set(MACOSX_BUNDLE_COPYRIGHT "Copyright(C) 2022-2024 Li Jiang All Rights Reserved")
|
||||
if (XCODE)
|
||||
# Xcode's CodeSign phase fails on the bundled Python runtime's dotted
|
||||
# dirs (python3.12) under Contents/MacOS. Skip it for local dev builds
|
||||
# and let the linker ad-hoc sign; the shipped bundle is signed by the
|
||||
# Ninja/CI packaging path (build_release_macos.sh, build_orca.yml).
|
||||
set_target_properties(OrcaSlicer PROPERTIES XCODE_ATTRIBUTE_CODE_SIGNING_ALLOWED NO)
|
||||
endif()
|
||||
endif()
|
||||
add_custom_command(TARGET OrcaSlicer POST_BUILD
|
||||
COMMAND ln -sfn "${SLIC3R_RESOURCES_DIR}" "${BIN_RESOURCES_DIR}"
|
||||
COMMENT "Symlinking the resources directory into the build tree"
|
||||
VERBATIM)
|
||||
add_custom_command(TARGET OrcaSlicer POST_BUILD
|
||||
COMMAND ${CMAKE_COMMAND} -E rm -rf "${BIN_DIR}/python"
|
||||
COMMAND ${CMAKE_COMMAND} -E copy_directory "${CMAKE_PREFIX_PATH}/libpython" "${BIN_DIR}/python"
|
||||
COMMENT "Copying Python runtime into the build tree"
|
||||
VERBATIM)
|
||||
# Stage uv next to the binary for the build-tree/.app flows, which never
|
||||
# run install() -- see the layout map above; lookup is bundled_uv_path().
|
||||
if(ORCA_BUNDLED_UV_EXECUTABLE AND EXISTS "${ORCA_BUNDLED_UV_EXECUTABLE}")
|
||||
add_custom_command(TARGET OrcaSlicer POST_BUILD
|
||||
COMMAND ${CMAKE_COMMAND} -E make_directory "${BIN_DIR}/tools/uv"
|
||||
COMMAND ${CMAKE_COMMAND} -E copy_if_different "${ORCA_BUNDLED_UV_EXECUTABLE}" "${BIN_DIR}/tools/uv/${ORCA_BUNDLED_UV_FILENAME}"
|
||||
COMMAND chmod +x "${BIN_DIR}/tools/uv/${ORCA_BUNDLED_UV_FILENAME}"
|
||||
COMMENT "Copying bundled uv into the build tree"
|
||||
VERBATIM)
|
||||
endif()
|
||||
endif ()
|
||||
|
||||
# Slic3r binary install target. Default build type is release in case no CMAKE_BUILD_TYPE is provided.
|
||||
@@ -288,6 +357,7 @@ if (WIN32)
|
||||
install(TARGETS OrcaSlicer_app_gui RUNTIME DESTINATION ".")
|
||||
endif ()
|
||||
install(FILES ${output_dlls_${build_type}} DESTINATION ".")
|
||||
install(DIRECTORY "${CMAKE_PREFIX_PATH}/libpython/" DESTINATION "python")
|
||||
else ()
|
||||
install(TARGETS OrcaSlicer RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}" BUNDLE DESTINATION ${CMAKE_INSTALL_BINDIR})
|
||||
endif ()
|
||||
|
||||
@@ -51,7 +51,6 @@ using namespace nlohmann;
|
||||
#include "libslic3r/Config.hpp"
|
||||
#include "libslic3r/Geometry.hpp"
|
||||
#include "libslic3r/GCode.hpp"
|
||||
#include "libslic3r/GCode/PostProcessor.hpp"
|
||||
#include "libslic3r/Model.hpp"
|
||||
#include "libslic3r/ModelArrange.hpp"
|
||||
#include "libslic3r/Platform.hpp"
|
||||
|
||||
@@ -263,6 +263,14 @@ int wmain(int argc, wchar_t **argv)
|
||||
_wsplitpath(path_to_exe, drive, dir, fname, ext);
|
||||
_wmakepath(path_to_exe, drive, dir, nullptr, nullptr);
|
||||
|
||||
wchar_t path_to_python[MAX_PATH + 1] = { 0 };
|
||||
wcscpy(path_to_python, path_to_exe);
|
||||
wcscat(path_to_python, L"python");
|
||||
DWORD python_attrs = GetFileAttributesW(path_to_python);
|
||||
if (python_attrs != INVALID_FILE_ATTRIBUTES && (python_attrs & FILE_ATTRIBUTE_DIRECTORY)) {
|
||||
SetDllDirectoryW(path_to_python);
|
||||
}
|
||||
|
||||
#ifdef SLIC3R_GUI
|
||||
// https://wiki.qt.io/Cross_compiling_Mesa_for_Windows
|
||||
// http://download.qt.io/development_releases/prebuilt/llvmpipe/windows/
|
||||
|
||||
@@ -171,6 +171,33 @@ echo -n "[9/9] Generating Linux app..."
|
||||
fi
|
||||
cp -fl "${ORIGINAL_BINARY_LOCATION}" "$BIN_DIR/@SLIC3R_APP_CMD@"
|
||||
|
||||
# Bundle the embedded Python runtime (interpreter + stdlib) that CMake staged
|
||||
# next to the binary. OrcaSlicer resolves PYTHONHOME at <resources_dir>/../lib/python,
|
||||
# which is $APPDIR/lib/python (resources_dir is $APPDIR/resources). Staging it here,
|
||||
# before the dependency-closure pass below, lets that pass also bundle the extension
|
||||
# modules' shared libraries; libpython itself is found via rpath ($ORIGIN entries set
|
||||
# in src/CMakeLists.txt and by the dep recipe). Without this the plugin system cannot start.
|
||||
PYTHON_RUNTIME_SRC="$(dirname "${ORIGINAL_BINARY_LOCATION}")/python"
|
||||
if [ -d "$PYTHON_RUNTIME_SRC" ]; then
|
||||
echo "Bundling Python runtime from ${PYTHON_RUNTIME_SRC} ..."
|
||||
copy_directory_if_present "$PYTHON_RUNTIME_SRC" "$LIB_DIR/python"
|
||||
else
|
||||
echo "Warning: bundled Python runtime not found at ${PYTHON_RUNTIME_SRC}; plugin support will be disabled in this AppImage."
|
||||
fi
|
||||
|
||||
# Bundle the uv executable (CMake staged it next to the binary) so the plugin
|
||||
# system can install Python package dependencies. bundled_uv_path() looks under
|
||||
# <resources_dir>/tools/uv == $APPDIR/resources/tools/uv -- the same layout the
|
||||
# install()-based packagers (Windows/Flatpak/FHS) use. Unlike the AppImage, those
|
||||
# run `make install`; this build copies artifacts, so stage uv explicitly.
|
||||
UV_RUNTIME_SRC="$(dirname "${ORIGINAL_BINARY_LOCATION}")/tools/uv"
|
||||
if [ -d "$UV_RUNTIME_SRC" ]; then
|
||||
echo "Bundling uv from ${UV_RUNTIME_SRC} ..."
|
||||
copy_directory_if_present "$UV_RUNTIME_SRC" "$APPDIR/resources/tools/uv"
|
||||
else
|
||||
echo "Warning: bundled uv not found at ${UV_RUNTIME_SRC}; Python package installation will be unavailable in this AppImage."
|
||||
fi
|
||||
|
||||
if [ "$BUNDLE_DESKTOP_STACK" = "1" ]; then
|
||||
GSTREAMER_PLUGIN_SOURCE_DIR="$(find_pkg_config_dir pluginsdir gstreamer-1.0 || true)"
|
||||
if [ -z "$GSTREAMER_PLUGIN_SOURCE_DIR" ]; then
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
/**
|
||||
* Loader generated by glad 2.0.8 on Tue Apr 7 00:45:10 2026
|
||||
* Loader generated by glad 2.0.8 on Thu Jul 16 03:42:05 2026
|
||||
*
|
||||
* SPDX-License-Identifier: (WTFPL OR CC0-1.0) AND Apache-2.0
|
||||
*
|
||||
* Generator: C/C++
|
||||
* Specification: gl
|
||||
* Extensions: 5
|
||||
* Extensions: 7
|
||||
*
|
||||
* APIs:
|
||||
* - gl:compatibility=4.6
|
||||
@@ -19,10 +19,10 @@
|
||||
* - ON_DEMAND = False
|
||||
*
|
||||
* Commandline:
|
||||
* --api='gl:compatibility=4.6' --extensions='GL_ARB_compatibility,GL_ARB_framebuffer_object,GL_EXT_framebuffer_object,GL_EXT_texture_compression_s3tc,GL_EXT_texture_filter_anisotropic' c --loader
|
||||
* --api='gl:compatibility=4.6' --extensions='GL_ARB_compatibility,GL_ARB_framebuffer_object,GL_EXT_framebuffer_blit,GL_EXT_framebuffer_multisample,GL_EXT_framebuffer_object,GL_EXT_texture_compression_s3tc,GL_EXT_texture_filter_anisotropic' c --loader
|
||||
*
|
||||
* Online:
|
||||
* http://glad.sh/#api=gl%3Acompatibility%3D4.6&extensions=GL_ARB_compatibility%2CGL_ARB_framebuffer_object%2CGL_EXT_framebuffer_object%2CGL_EXT_texture_compression_s3tc%2CGL_EXT_texture_filter_anisotropic&generator=c&options=LOADER
|
||||
* http://glad.sh/#api=gl%3Acompatibility%3D4.6&extensions=GL_ARB_compatibility%2CGL_ARB_framebuffer_object%2CGL_EXT_framebuffer_blit%2CGL_EXT_framebuffer_multisample%2CGL_EXT_framebuffer_object%2CGL_EXT_texture_compression_s3tc%2CGL_EXT_texture_filter_anisotropic&generator=c&options=LOADER
|
||||
*
|
||||
*/
|
||||
|
||||
@@ -616,6 +616,8 @@ typedef void (*GLADpostcallback)(void *ret, const char *name, GLADapiproc apipro
|
||||
#define GL_DRAW_BUFFER9 0x882E
|
||||
#define GL_DRAW_FRAMEBUFFER 0x8CA9
|
||||
#define GL_DRAW_FRAMEBUFFER_BINDING 0x8CA6
|
||||
#define GL_DRAW_FRAMEBUFFER_BINDING_EXT 0x8CA6
|
||||
#define GL_DRAW_FRAMEBUFFER_EXT 0x8CA9
|
||||
#define GL_DRAW_INDIRECT_BUFFER 0x8F3F
|
||||
#define GL_DRAW_INDIRECT_BUFFER_BINDING 0x8F43
|
||||
#define GL_DRAW_PIXEL_TOKEN 0x0705
|
||||
@@ -746,6 +748,7 @@ typedef void (*GLADpostcallback)(void *ret, const char *name, GLADapiproc apipro
|
||||
#define GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT 0x8CD7
|
||||
#define GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT_EXT 0x8CD7
|
||||
#define GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE 0x8D56
|
||||
#define GL_FRAMEBUFFER_INCOMPLETE_MULTISAMPLE_EXT 0x8D56
|
||||
#define GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER 0x8CDC
|
||||
#define GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER_EXT 0x8CDC
|
||||
#define GL_FRAMEBUFFER_RENDERABLE 0x8289
|
||||
@@ -1106,6 +1109,7 @@ typedef void (*GLADpostcallback)(void *ret, const char *name, GLADapiproc apipro
|
||||
#define GL_MAX_RENDERBUFFER_SIZE 0x84E8
|
||||
#define GL_MAX_RENDERBUFFER_SIZE_EXT 0x84E8
|
||||
#define GL_MAX_SAMPLES 0x8D57
|
||||
#define GL_MAX_SAMPLES_EXT 0x8D57
|
||||
#define GL_MAX_SAMPLE_MASK_WORDS 0x8E59
|
||||
#define GL_MAX_SERVER_WAIT_TIMEOUT 0x9111
|
||||
#define GL_MAX_SHADER_STORAGE_BLOCK_SIZE 0x90DE
|
||||
@@ -1400,6 +1404,8 @@ typedef void (*GLADpostcallback)(void *ret, const char *name, GLADapiproc apipro
|
||||
#define GL_READ_BUFFER 0x0C02
|
||||
#define GL_READ_FRAMEBUFFER 0x8CA8
|
||||
#define GL_READ_FRAMEBUFFER_BINDING 0x8CAA
|
||||
#define GL_READ_FRAMEBUFFER_BINDING_EXT 0x8CAA
|
||||
#define GL_READ_FRAMEBUFFER_EXT 0x8CA8
|
||||
#define GL_READ_ONLY 0x88B8
|
||||
#define GL_READ_PIXELS 0x828C
|
||||
#define GL_READ_PIXELS_FORMAT 0x828D
|
||||
@@ -1437,6 +1443,7 @@ typedef void (*GLADpostcallback)(void *ret, const char *name, GLADapiproc apipro
|
||||
#define GL_RENDERBUFFER_RED_SIZE 0x8D50
|
||||
#define GL_RENDERBUFFER_RED_SIZE_EXT 0x8D50
|
||||
#define GL_RENDERBUFFER_SAMPLES 0x8CAB
|
||||
#define GL_RENDERBUFFER_SAMPLES_EXT 0x8CAB
|
||||
#define GL_RENDERBUFFER_STENCIL_SIZE 0x8D55
|
||||
#define GL_RENDERBUFFER_STENCIL_SIZE_EXT 0x8D55
|
||||
#define GL_RENDERBUFFER_WIDTH 0x8D42
|
||||
@@ -2150,6 +2157,10 @@ GLAD_API_CALL int GLAD_GL_VERSION_4_6;
|
||||
GLAD_API_CALL int GLAD_GL_ARB_compatibility;
|
||||
#define GL_ARB_framebuffer_object 1
|
||||
GLAD_API_CALL int GLAD_GL_ARB_framebuffer_object;
|
||||
#define GL_EXT_framebuffer_blit 1
|
||||
GLAD_API_CALL int GLAD_GL_EXT_framebuffer_blit;
|
||||
#define GL_EXT_framebuffer_multisample 1
|
||||
GLAD_API_CALL int GLAD_GL_EXT_framebuffer_multisample;
|
||||
#define GL_EXT_framebuffer_object 1
|
||||
GLAD_API_CALL int GLAD_GL_EXT_framebuffer_object;
|
||||
#define GL_EXT_texture_compression_s3tc 1
|
||||
@@ -2205,6 +2216,7 @@ typedef void (GLAD_API_PTR *PFNGLBLENDFUNCSEPARATEPROC)(GLenum sfactorRGB, GLenu
|
||||
typedef void (GLAD_API_PTR *PFNGLBLENDFUNCSEPARATEIPROC)(GLuint buf, GLenum srcRGB, GLenum dstRGB, GLenum srcAlpha, GLenum dstAlpha);
|
||||
typedef void (GLAD_API_PTR *PFNGLBLENDFUNCIPROC)(GLuint buf, GLenum src, GLenum dst);
|
||||
typedef void (GLAD_API_PTR *PFNGLBLITFRAMEBUFFERPROC)(GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1, GLbitfield mask, GLenum filter);
|
||||
typedef void (GLAD_API_PTR *PFNGLBLITFRAMEBUFFEREXTPROC)(GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1, GLbitfield mask, GLenum filter);
|
||||
typedef void (GLAD_API_PTR *PFNGLBLITNAMEDFRAMEBUFFERPROC)(GLuint readFramebuffer, GLuint drawFramebuffer, GLint srcX0, GLint srcY0, GLint srcX1, GLint srcY1, GLint dstX0, GLint dstY0, GLint dstX1, GLint dstY1, GLbitfield mask, GLenum filter);
|
||||
typedef void (GLAD_API_PTR *PFNGLBUFFERDATAPROC)(GLenum target, GLsizeiptr size, const void * data, GLenum usage);
|
||||
typedef void (GLAD_API_PTR *PFNGLBUFFERSTORAGEPROC)(GLenum target, GLsizeiptr size, const void * data, GLbitfield flags);
|
||||
@@ -2881,6 +2893,7 @@ typedef GLint (GLAD_API_PTR *PFNGLRENDERMODEPROC)(GLenum mode);
|
||||
typedef void (GLAD_API_PTR *PFNGLRENDERBUFFERSTORAGEPROC)(GLenum target, GLenum internalformat, GLsizei width, GLsizei height);
|
||||
typedef void (GLAD_API_PTR *PFNGLRENDERBUFFERSTORAGEEXTPROC)(GLenum target, GLenum internalformat, GLsizei width, GLsizei height);
|
||||
typedef void (GLAD_API_PTR *PFNGLRENDERBUFFERSTORAGEMULTISAMPLEPROC)(GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height);
|
||||
typedef void (GLAD_API_PTR *PFNGLRENDERBUFFERSTORAGEMULTISAMPLEEXTPROC)(GLenum target, GLsizei samples, GLenum internalformat, GLsizei width, GLsizei height);
|
||||
typedef void (GLAD_API_PTR *PFNGLRESUMETRANSFORMFEEDBACKPROC)(void);
|
||||
typedef void (GLAD_API_PTR *PFNGLROTATEDPROC)(GLdouble angle, GLdouble x, GLdouble y, GLdouble z);
|
||||
typedef void (GLAD_API_PTR *PFNGLROTATEFPROC)(GLfloat angle, GLfloat x, GLfloat y, GLfloat z);
|
||||
@@ -3318,6 +3331,8 @@ GLAD_API_CALL PFNGLBLENDFUNCIPROC glad_glBlendFunci;
|
||||
#define glBlendFunci glad_glBlendFunci
|
||||
GLAD_API_CALL PFNGLBLITFRAMEBUFFERPROC glad_glBlitFramebuffer;
|
||||
#define glBlitFramebuffer glad_glBlitFramebuffer
|
||||
GLAD_API_CALL PFNGLBLITFRAMEBUFFEREXTPROC glad_glBlitFramebufferEXT;
|
||||
#define glBlitFramebufferEXT glad_glBlitFramebufferEXT
|
||||
GLAD_API_CALL PFNGLBLITNAMEDFRAMEBUFFERPROC glad_glBlitNamedFramebuffer;
|
||||
#define glBlitNamedFramebuffer glad_glBlitNamedFramebuffer
|
||||
GLAD_API_CALL PFNGLBUFFERDATAPROC glad_glBufferData;
|
||||
@@ -4670,6 +4685,8 @@ GLAD_API_CALL PFNGLRENDERBUFFERSTORAGEEXTPROC glad_glRenderbufferStorageEXT;
|
||||
#define glRenderbufferStorageEXT glad_glRenderbufferStorageEXT
|
||||
GLAD_API_CALL PFNGLRENDERBUFFERSTORAGEMULTISAMPLEPROC glad_glRenderbufferStorageMultisample;
|
||||
#define glRenderbufferStorageMultisample glad_glRenderbufferStorageMultisample
|
||||
GLAD_API_CALL PFNGLRENDERBUFFERSTORAGEMULTISAMPLEEXTPROC glad_glRenderbufferStorageMultisampleEXT;
|
||||
#define glRenderbufferStorageMultisampleEXT glad_glRenderbufferStorageMultisampleEXT
|
||||
GLAD_API_CALL PFNGLRESUMETRANSFORMFEEDBACKPROC glad_glResumeTransformFeedback;
|
||||
#define glResumeTransformFeedback glad_glResumeTransformFeedback
|
||||
GLAD_API_CALL PFNGLROTATEDPROC glad_glRotated;
|
||||
|
||||
+22
-1
@@ -44,6 +44,8 @@ int GLAD_GL_VERSION_4_5 = 0;
|
||||
int GLAD_GL_VERSION_4_6 = 0;
|
||||
int GLAD_GL_ARB_compatibility = 0;
|
||||
int GLAD_GL_ARB_framebuffer_object = 0;
|
||||
int GLAD_GL_EXT_framebuffer_blit = 0;
|
||||
int GLAD_GL_EXT_framebuffer_multisample = 0;
|
||||
int GLAD_GL_EXT_framebuffer_object = 0;
|
||||
int GLAD_GL_EXT_texture_compression_s3tc = 0;
|
||||
int GLAD_GL_EXT_texture_filter_anisotropic = 0;
|
||||
@@ -97,6 +99,7 @@ PFNGLBLENDFUNCSEPARATEPROC glad_glBlendFuncSeparate = NULL;
|
||||
PFNGLBLENDFUNCSEPARATEIPROC glad_glBlendFuncSeparatei = NULL;
|
||||
PFNGLBLENDFUNCIPROC glad_glBlendFunci = NULL;
|
||||
PFNGLBLITFRAMEBUFFERPROC glad_glBlitFramebuffer = NULL;
|
||||
PFNGLBLITFRAMEBUFFEREXTPROC glad_glBlitFramebufferEXT = NULL;
|
||||
PFNGLBLITNAMEDFRAMEBUFFERPROC glad_glBlitNamedFramebuffer = NULL;
|
||||
PFNGLBUFFERDATAPROC glad_glBufferData = NULL;
|
||||
PFNGLBUFFERSTORAGEPROC glad_glBufferStorage = NULL;
|
||||
@@ -773,6 +776,7 @@ PFNGLRENDERMODEPROC glad_glRenderMode = NULL;
|
||||
PFNGLRENDERBUFFERSTORAGEPROC glad_glRenderbufferStorage = NULL;
|
||||
PFNGLRENDERBUFFERSTORAGEEXTPROC glad_glRenderbufferStorageEXT = NULL;
|
||||
PFNGLRENDERBUFFERSTORAGEMULTISAMPLEPROC glad_glRenderbufferStorageMultisample = NULL;
|
||||
PFNGLRENDERBUFFERSTORAGEMULTISAMPLEEXTPROC glad_glRenderbufferStorageMultisampleEXT = NULL;
|
||||
PFNGLRESUMETRANSFORMFEEDBACKPROC glad_glResumeTransformFeedback = NULL;
|
||||
PFNGLROTATEDPROC glad_glRotated = NULL;
|
||||
PFNGLROTATEFPROC glad_glRotatef = NULL;
|
||||
@@ -2249,6 +2253,14 @@ static void glad_gl_load_GL_ARB_framebuffer_object( GLADuserptrloadfunc load, vo
|
||||
glad_glRenderbufferStorage = (PFNGLRENDERBUFFERSTORAGEPROC) load(userptr, "glRenderbufferStorage");
|
||||
glad_glRenderbufferStorageMultisample = (PFNGLRENDERBUFFERSTORAGEMULTISAMPLEPROC) load(userptr, "glRenderbufferStorageMultisample");
|
||||
}
|
||||
static void glad_gl_load_GL_EXT_framebuffer_blit( GLADuserptrloadfunc load, void* userptr) {
|
||||
if(!GLAD_GL_EXT_framebuffer_blit) return;
|
||||
glad_glBlitFramebufferEXT = (PFNGLBLITFRAMEBUFFEREXTPROC) load(userptr, "glBlitFramebufferEXT");
|
||||
}
|
||||
static void glad_gl_load_GL_EXT_framebuffer_multisample( GLADuserptrloadfunc load, void* userptr) {
|
||||
if(!GLAD_GL_EXT_framebuffer_multisample) return;
|
||||
glad_glRenderbufferStorageMultisampleEXT = (PFNGLRENDERBUFFERSTORAGEMULTISAMPLEEXTPROC) load(userptr, "glRenderbufferStorageMultisampleEXT");
|
||||
}
|
||||
static void glad_gl_load_GL_EXT_framebuffer_object( GLADuserptrloadfunc load, void* userptr) {
|
||||
if(!GLAD_GL_EXT_framebuffer_object) return;
|
||||
glad_glBindFramebufferEXT = (PFNGLBINDFRAMEBUFFEREXTPROC) load(userptr, "glBindFramebufferEXT");
|
||||
@@ -2366,6 +2378,8 @@ static int glad_gl_find_extensions_gl(void) {
|
||||
|
||||
GLAD_GL_ARB_compatibility = glad_gl_has_extension(exts, exts_i, "GL_ARB_compatibility");
|
||||
GLAD_GL_ARB_framebuffer_object = glad_gl_has_extension(exts, exts_i, "GL_ARB_framebuffer_object");
|
||||
GLAD_GL_EXT_framebuffer_blit = glad_gl_has_extension(exts, exts_i, "GL_EXT_framebuffer_blit");
|
||||
GLAD_GL_EXT_framebuffer_multisample = glad_gl_has_extension(exts, exts_i, "GL_EXT_framebuffer_multisample");
|
||||
GLAD_GL_EXT_framebuffer_object = glad_gl_has_extension(exts, exts_i, "GL_EXT_framebuffer_object");
|
||||
GLAD_GL_EXT_texture_compression_s3tc = glad_gl_has_extension(exts, exts_i, "GL_EXT_texture_compression_s3tc");
|
||||
GLAD_GL_EXT_texture_filter_anisotropic = glad_gl_has_extension(exts, exts_i, "GL_EXT_texture_filter_anisotropic");
|
||||
@@ -2451,6 +2465,8 @@ int gladLoadGLUserPtr( GLADuserptrloadfunc load, void *userptr) {
|
||||
|
||||
if (!glad_gl_find_extensions_gl()) return 0;
|
||||
glad_gl_load_GL_ARB_framebuffer_object(load, userptr);
|
||||
glad_gl_load_GL_EXT_framebuffer_blit(load, userptr);
|
||||
glad_gl_load_GL_EXT_framebuffer_multisample(load, userptr);
|
||||
glad_gl_load_GL_EXT_framebuffer_object(load, userptr);
|
||||
|
||||
|
||||
@@ -2574,7 +2590,9 @@ static void* glad_gl_dlopen_handle(void) {
|
||||
"libGL-1.so",
|
||||
#endif
|
||||
"libGL.so.1",
|
||||
"libGL.so"
|
||||
"libGL.so",
|
||||
"libEGL.so.1",
|
||||
"libEGL.so"
|
||||
};
|
||||
#endif
|
||||
|
||||
@@ -2597,6 +2615,9 @@ static struct _glad_gl_userptr glad_gl_build_userptr(void *handle) {
|
||||
#else
|
||||
userptr.gl_get_proc_address_ptr =
|
||||
(GLADglprocaddrfunc) glad_dlsym_handle(handle, "glXGetProcAddressARB");
|
||||
if (!userptr.gl_get_proc_address_ptr)
|
||||
userptr.gl_get_proc_address_ptr =
|
||||
(GLADglprocaddrfunc) glad_dlsym_handle(handle, "eglGetProcAddress");
|
||||
#endif
|
||||
|
||||
return userptr;
|
||||
|
||||
@@ -202,6 +202,10 @@ void AppConfig::set_defaults()
|
||||
if (get("seq_top_layer_only").empty())
|
||||
set("seq_top_layer_only", "1");
|
||||
|
||||
// ORCA: darken layers below the current one while scrubbing the preview (ported from preFlight)
|
||||
if (get("preview_dim_previous_layers").empty())
|
||||
set_bool("preview_dim_previous_layers", false);
|
||||
|
||||
if (get("filaments_area_preferred_count").empty())
|
||||
set("filaments_area_preferred_count", "10");
|
||||
|
||||
|
||||
@@ -125,7 +125,7 @@ public:
|
||||
template<class It, class = IteratorOnly<It> > BoundingBox3Base(It from, It to)
|
||||
{
|
||||
if (from == to)
|
||||
throw Slic3r::InvalidArgument("Empty point set supplied to BoundingBox3Base constructor");
|
||||
throw Slic3r::InvalidArgument("Empty point set supplied to BoundingBox3Base constructor.");
|
||||
|
||||
auto it = from;
|
||||
this->min = it->template cast<typename PointType::Scalar>();
|
||||
|
||||
+43
-129
@@ -34,14 +34,14 @@ static void append_and_translate(ExPolygons &dst, const ExPolygons &src, const P
|
||||
}
|
||||
// BBS: generate brim area by objs
|
||||
static void append_and_translate(ExPolygons& dst, const ExPolygons& src,
|
||||
const PrintInstance& instance, const Print& print, std::map<ObjectID, ExPolygons>& brimAreaMap) {
|
||||
const PrintInstance& instance, size_t instance_idx, std::map<ObjectInstanceID, ExPolygons>& brimAreaMap) {
|
||||
ExPolygons srcShifted = src;
|
||||
Point instance_shift = instance.shift_without_plate_offset();
|
||||
for (size_t src_idx = 0; src_idx < srcShifted.size(); ++src_idx)
|
||||
srcShifted[src_idx].translate(instance_shift);
|
||||
srcShifted = diff_ex(srcShifted, dst);
|
||||
//expolygons_append(dst, temp2);
|
||||
expolygons_append(brimAreaMap[instance.print_object->id()], std::move(srcShifted));
|
||||
expolygons_append(brimAreaMap[{ instance.print_object->id(), instance_idx }], std::move(srcShifted));
|
||||
}
|
||||
|
||||
static void append_and_translate(Polygons &dst, const Polygons &src, const PrintInstance &instance) {
|
||||
@@ -420,8 +420,7 @@ static ExPolygons make_brim_ears(const PrintObject* object, const double& flowWi
|
||||
|
||||
//BBS: create all brims
|
||||
static ExPolygons outer_inner_brim_area(const Print& print,
|
||||
const float no_brim_offset, std::map<ObjectID, ExPolygons>& brimAreaMap,
|
||||
std::map<ObjectID, ExPolygons>& supportBrimAreaMap,
|
||||
const float no_brim_offset, std::map<ObjectInstanceID, ExPolygons>& brimAreaMap,
|
||||
std::vector<std::pair<ObjectID, unsigned int>>& objPrintVec,
|
||||
std::vector<unsigned int>& printExtruders)
|
||||
{
|
||||
@@ -470,7 +469,6 @@ static ExPolygons outer_inner_brim_area(const Print& print,
|
||||
|
||||
ExPolygons brim_area_object;
|
||||
ExPolygons no_brim_area_object;
|
||||
ExPolygons brim_area_support;
|
||||
ExPolygons no_brim_area_support;
|
||||
Polygons holes_object;
|
||||
Polygons holes_support;
|
||||
@@ -571,16 +569,18 @@ static ExPolygons outer_inner_brim_area(const Print& print,
|
||||
append(no_brim_area_object, objectIsland);
|
||||
|
||||
brimToWrite.at(object->id()).obj = false;
|
||||
for (const PrintInstance& instance : object->instances()) {
|
||||
for (size_t instance_idx = 0; instance_idx < object->instances().size(); ++instance_idx) {
|
||||
const PrintInstance& instance = object->instances()[instance_idx];
|
||||
if (!brim_area_object.empty())
|
||||
append_and_translate(brim_area, brim_area_object, instance, print, brimAreaMap);
|
||||
append_and_translate(brim_area, brim_area_object, instance, instance_idx, brimAreaMap);
|
||||
append_and_translate(no_brim_area, no_brim_area_object, instance);
|
||||
append_and_translate(holes, holes_object, instance);
|
||||
append_and_translate(objectIslands, objectIsland, instance);
|
||||
|
||||
}
|
||||
if (brimAreaMap.find(object->id()) != brimAreaMap.end())
|
||||
expolygons_append(brim_area, brimAreaMap[object->id()]);
|
||||
for (const auto& [key, areas] : brimAreaMap)
|
||||
if (key.object_id == object->id())
|
||||
expolygons_append(brim_area, areas);
|
||||
}
|
||||
support_material_extruder = object->config().support_filament;
|
||||
if (support_material_extruder == 0 && object->has_support_material()) {
|
||||
@@ -592,32 +592,12 @@ static ExPolygons outer_inner_brim_area(const Print& print,
|
||||
if (support_material_extruder == extruderNo && brimToWrite.at(object->id()).sup) {
|
||||
if (!object->support_layers().empty() && object->support_layers().front()->support_type==stInnerNormal) {
|
||||
for (const Polygon& support_contour : object->support_layers().front()->support_fills.polygons_covered_by_spacing()) {
|
||||
// Brim will not be generated for supports
|
||||
/*
|
||||
if (has_outer_brim) {
|
||||
append(brim_area_support, diff_ex(offset_ex(support_contour, brim_width + brim_offset, jtRound, SCALED_RESOLUTION), offset_ex(support_contour, brim_offset)));
|
||||
}
|
||||
if (has_inner_brim || has_outer_brim)
|
||||
append(no_brim_area_support, offset_ex(support_contour, 0));
|
||||
*/
|
||||
no_brim_area_support.emplace_back(support_contour);
|
||||
}
|
||||
}
|
||||
// BBS
|
||||
if (!object->support_layers().empty() && object->support_layers().front()->support_type == stInnerTree) {
|
||||
for (const ExPolygon &ex_poly : object->support_layers().front()->lslices) {
|
||||
// BBS: additional brim width will be added if adhesion area is too small without brim
|
||||
float brim_width_mod = ex_poly.area() / ex_poly.contour.length() < scaled_half_min_adh_length
|
||||
&& brim_width < scaled_flow_width ? brim_width + scaled_additional_brim_width : brim_width;
|
||||
brim_width_mod = floor(brim_width_mod / scaled_flow_width / 2) * scaled_flow_width * 2;
|
||||
// Brim will not be generated for supports
|
||||
/*
|
||||
if (has_outer_brim) {
|
||||
append(brim_area_support, diff_ex(offset_ex(ex_poly.contour, brim_width_mod + brim_offset, jtRound, SCALED_RESOLUTION), offset_ex(ex_poly.contour, brim_offset)));
|
||||
}
|
||||
if (has_inner_brim)
|
||||
append(brim_area_support, diff_ex(offset_ex(ex_poly.holes, -brim_offset), offset_ex(ex_poly.holes, -brim_width - brim_offset)));
|
||||
*/
|
||||
if (!has_outer_brim)
|
||||
append(no_brim_area_support, diff_ex(offset(ex_poly.contour, no_brim_offset), ex_poly.holes));
|
||||
if (!has_inner_brim && !has_outer_brim)
|
||||
@@ -630,13 +610,9 @@ static ExPolygons outer_inner_brim_area(const Print& print,
|
||||
}
|
||||
brimToWrite.at(object->id()).sup = false;
|
||||
for (const PrintInstance& instance : object->instances()) {
|
||||
if (!brim_area_support.empty())
|
||||
append_and_translate(brim_area, brim_area_support, instance, print, supportBrimAreaMap);
|
||||
append_and_translate(no_brim_area, no_brim_area_support, instance);
|
||||
append_and_translate(holes, holes_support, instance);
|
||||
}
|
||||
if (supportBrimAreaMap.find(object->id()) != supportBrimAreaMap.end())
|
||||
expolygons_append(brim_area, supportBrimAreaMap[object->id()]);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -676,28 +652,27 @@ static ExPolygons outer_inner_brim_area(const Print& print,
|
||||
|
||||
}
|
||||
|
||||
if (brimAreaMap.find(object->id()) != brimAreaMap.end()) {
|
||||
brimAreaMap[object->id()] = diff_ex(brimAreaMap[object->id()], extruder_no_brim_area);
|
||||
}
|
||||
for (auto& [key, areas] : brimAreaMap)
|
||||
if (key.object_id == object->id())
|
||||
areas = diff_ex(areas, extruder_no_brim_area);
|
||||
|
||||
if (supportBrimAreaMap.find(object->id()) != supportBrimAreaMap.end())
|
||||
supportBrimAreaMap[object->id()] = diff_ex(supportBrimAreaMap[object->id()], extruder_no_brim_area);
|
||||
}
|
||||
|
||||
brim_area.clear();
|
||||
for (const PrintObject* object : print.objects()) {
|
||||
// BBS: brim should be contacted to at least one object's island or brim area
|
||||
if (brimAreaMap.find(object->id()) != brimAreaMap.end()) {
|
||||
for (auto map_it = brimAreaMap.begin(); map_it != brimAreaMap.end(); ++map_it) {
|
||||
if (map_it->first.object_id != object->id())
|
||||
continue;
|
||||
|
||||
// find other objects' brim area
|
||||
ExPolygons otherExPolys;
|
||||
for (const PrintObject* otherObject : print.objects()) {
|
||||
if ((otherObject->id() != object->id()) && (brimAreaMap.find(otherObject->id()) != brimAreaMap.end())) {
|
||||
expolygons_append(otherExPolys, brimAreaMap[otherObject->id()]);
|
||||
}
|
||||
}
|
||||
for (const auto& [other_key, other_areas] : brimAreaMap)
|
||||
if (other_key != map_it->first)
|
||||
expolygons_append(otherExPolys, other_areas);
|
||||
|
||||
auto tempArea = brimAreaMap[object->id()];
|
||||
brimAreaMap[object->id()].clear();
|
||||
auto tempArea = map_it->second;
|
||||
map_it->second.clear();
|
||||
|
||||
for (int ia = 0; ia != tempArea.size(); ++ia) {
|
||||
// find this object's other brim area
|
||||
@@ -709,9 +684,9 @@ static ExPolygons outer_inner_brim_area(const Print& print,
|
||||
if (!intersection_ex(offsetedTa, objectIslands).empty() ||
|
||||
!intersection_ex(offsetedTa, otherExPoly).empty() ||
|
||||
!intersection_ex(offsetedTa, otherExPolys).empty())
|
||||
brimAreaMap[object->id()].push_back(tempArea[ia]);
|
||||
map_it->second.push_back(tempArea[ia]);
|
||||
}
|
||||
expolygons_append(brim_area, brimAreaMap[object->id()]);
|
||||
expolygons_append(brim_area, map_it->second);
|
||||
}
|
||||
}
|
||||
return brim_area;
|
||||
@@ -890,19 +865,15 @@ ExtrusionEntityCollection makeBrimInfillFromPlateCoordinates(const ExPolygons& s
|
||||
//BBS: an overload of the orignal brim generator that generates the brim by obj and by extruders
|
||||
void make_brim(const Print& print, PrintTryCancel try_cancel, Polygons& islands_area,
|
||||
std::map<ObjectID, ExtrusionEntityCollection>& brimMap,
|
||||
std::map<ObjectID, ExtrusionEntityCollection>& supportBrimMap,
|
||||
std::map<ObjectInstanceID, ExtrusionEntityCollection>& brimMapByInstance,
|
||||
std::vector<std::pair<ObjectID, unsigned int>> &objPrintVec,
|
||||
std::vector<unsigned int>& printExtruders,
|
||||
std::map<ObjectID, ExPolygons>* objectBrimAreasOut,
|
||||
std::map<ObjectID, ExPolygons>* supportBrimAreasOut)
|
||||
std::map<ObjectInstanceID, ExPolygons>* objectBrimAreasByInstanceOut)
|
||||
{
|
||||
std::map<ObjectID, double> brim_width_map;
|
||||
std::map<ObjectID, ExPolygons> brimAreaMap;
|
||||
std::map<ObjectID, ExPolygons> supportBrimAreaMap;
|
||||
std::map<ObjectInstanceID, ExPolygons> brimAreaMap;
|
||||
Flow flow = print.brim_flow();
|
||||
const auto scaled_resolution = scaled<double>(print.config().resolution.value);
|
||||
ExPolygons islands_area_ex = outer_inner_brim_area(print,
|
||||
float(flow.scaled_spacing()), brimAreaMap, supportBrimAreaMap, objPrintVec, printExtruders);
|
||||
float(flow.scaled_spacing()), brimAreaMap, objPrintVec, printExtruders);
|
||||
|
||||
// BBS: Find boundingbox of the first layer
|
||||
for (const ObjectID printObjID : print.print_object_ids()) {
|
||||
@@ -924,14 +895,10 @@ void make_brim(const Print& print, PrintTryCancel try_cancel, Polygons& islands_
|
||||
ex_poly_translated.translate(instance.shift_without_plate_offset());
|
||||
bbx.merge(get_extents(ex_poly_translated));
|
||||
}
|
||||
if (supportBrimAreaMap.find(printObjID) != supportBrimAreaMap.end()) {
|
||||
for (const ExPolygon& ex_poly : supportBrimAreaMap.at(printObjID))
|
||||
bbx.merge(get_extents(ex_poly.contour));
|
||||
}
|
||||
if (brimAreaMap.find(printObjID) != brimAreaMap.end()) {
|
||||
for (const ExPolygon& ex_poly : brimAreaMap.at(printObjID))
|
||||
bbx.merge(get_extents(ex_poly.contour));
|
||||
}
|
||||
for (const auto& [key, areas] : brimAreaMap)
|
||||
if (key.object_id == printObjID)
|
||||
for (const ExPolygon& ex_poly : areas)
|
||||
bbx.merge(get_extents(ex_poly.contour));
|
||||
object->firstLayerObjectBrimBoundingBox = bbx;
|
||||
}
|
||||
|
||||
@@ -944,77 +911,24 @@ void make_brim(const Print& print, PrintTryCancel try_cancel, Polygons& islands_
|
||||
islands_area[iia].translate(plate_shift);
|
||||
|
||||
// Orca: keep translated brim footprints for skirt grouping.
|
||||
auto translate_area_map = [plate_shift](const std::map<ObjectID, ExPolygons>& src) {
|
||||
std::map<ObjectID, ExPolygons> dst = src;
|
||||
auto translate_area_map = [plate_shift](const auto& src) {
|
||||
auto dst = src;
|
||||
for (auto& [_, areas] : dst)
|
||||
for (ExPolygon& area : areas)
|
||||
area.translate(plate_shift);
|
||||
return dst;
|
||||
};
|
||||
if (objectBrimAreasOut != nullptr)
|
||||
*objectBrimAreasOut = translate_area_map(brimAreaMap);
|
||||
if (supportBrimAreasOut != nullptr)
|
||||
*supportBrimAreasOut = translate_area_map(supportBrimAreaMap);
|
||||
if (objectBrimAreasByInstanceOut != nullptr)
|
||||
*objectBrimAreasByInstanceOut = translate_area_map(brimAreaMap);
|
||||
|
||||
const bool has_per_object_skirt_or_shield = print.config().skirt_type == stPerObject &&
|
||||
(print.has_skirt() || print.has_infinite_skirt());
|
||||
const bool combine_brims = print.config().combine_brims.value &&
|
||||
!has_per_object_skirt_or_shield &&
|
||||
print.config().print_sequence != PrintSequence::ByObject;
|
||||
|
||||
if (!combine_brims) {
|
||||
// Orca: Generate brims separately when brims cannot be combined.
|
||||
for (auto iter = brimAreaMap.begin(); iter != brimAreaMap.end(); ++iter) {
|
||||
if (!iter->second.empty()) {
|
||||
brimMap.insert(std::make_pair(iter->first, makeBrimInfill(iter->second, print, islands_area)));
|
||||
};
|
||||
}
|
||||
for (auto iter = supportBrimAreaMap.begin(); iter != supportBrimAreaMap.end(); ++iter) {
|
||||
if (!iter->second.empty()) {
|
||||
supportBrimMap.insert(std::make_pair(iter->first, makeBrimInfill(iter->second, print, islands_area)));
|
||||
};
|
||||
}
|
||||
} else {
|
||||
// Orca: Unified brim mode.
|
||||
ExPolygons all_brims_merged;
|
||||
std::vector<ObjectID> brim_object_ids;
|
||||
|
||||
// Add all object brims
|
||||
for (auto& [obj_id, brims] : brimAreaMap) {
|
||||
if (!brims.empty()) {
|
||||
expolygons_append(all_brims_merged, brims);
|
||||
brim_object_ids.push_back(obj_id);
|
||||
}
|
||||
}
|
||||
|
||||
if (!all_brims_merged.empty()) {
|
||||
// Merge all brims into a single continuous area
|
||||
all_brims_merged = union_ex(all_brims_merged);
|
||||
|
||||
// Apply a tiny morphological cleanup to reduce boolean-union micro-artifacts.
|
||||
const float brim_cleanup_delta = std::max(float(scaled_resolution), float(SCALED_EPSILON));
|
||||
all_brims_merged = offset2_ex(all_brims_merged, brim_cleanup_delta, -brim_cleanup_delta, jtRound, scaled_resolution);
|
||||
|
||||
// Generate infill once for the merged brim area.
|
||||
ExtrusionEntityCollection merged_brim = makeBrimInfill(all_brims_merged, print, islands_area);
|
||||
|
||||
// In unified mode, assign the merged brim to a deterministic carrier object.
|
||||
// Pick the first object in print order that actually contributed brim area.
|
||||
ObjectID carrier_id;
|
||||
bool carrier_found = false;
|
||||
for (const auto& [obj_id, _extruder] : objPrintVec) {
|
||||
if (std::find(brim_object_ids.begin(), brim_object_ids.end(), obj_id) != brim_object_ids.end()) {
|
||||
carrier_id = obj_id;
|
||||
carrier_found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!carrier_found)
|
||||
carrier_id = brim_object_ids.front();
|
||||
|
||||
brimMap[carrier_id] = std::move(merged_brim);
|
||||
}
|
||||
// Orca: Generate brims per object instance. If Combine brims is enabled,
|
||||
// Print::_make_skirt() will join the touching ones.
|
||||
for (auto iter = brimAreaMap.begin(); iter != brimAreaMap.end(); ++iter) {
|
||||
if (!iter->second.empty()) {
|
||||
ExtrusionEntityCollection brim = makeBrimInfill(iter->second, print, islands_area);
|
||||
brimMap[iter->first.object_id].append(brim.entities);
|
||||
brimMapByInstance.emplace(iter->first, std::move(brim));
|
||||
};
|
||||
}
|
||||
}
|
||||
} // namespace Slic3r
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
#define slic3r_Brim_hpp_
|
||||
|
||||
#include "ExPolygon.hpp"
|
||||
#include "ObjectID.hpp"
|
||||
#include "Point.hpp"
|
||||
|
||||
#include<map>
|
||||
@@ -12,17 +13,15 @@ namespace Slic3r {
|
||||
class Print;
|
||||
class ExtrusionEntityCollection;
|
||||
class PrintTryCancel;
|
||||
class ObjectID;
|
||||
|
||||
// Produce brim lines around those objects, that have the brim enabled.
|
||||
// Collect islands_area to be merged into the final 1st layer convex hull.
|
||||
void make_brim(const Print& print, PrintTryCancel try_cancel,
|
||||
Polygons& islands_area, std::map<ObjectID, ExtrusionEntityCollection>& brimMap,
|
||||
std::map<ObjectID, ExtrusionEntityCollection>& supportBrimMap,
|
||||
std::map<ObjectInstanceID, ExtrusionEntityCollection>& brimMapByInstance,
|
||||
std::vector<std::pair<ObjectID, unsigned int>>& objPrintVec,
|
||||
std::vector<unsigned int>& printExtruders,
|
||||
std::map<ObjectID, ExPolygons>* objectBrimAreasOut = nullptr,
|
||||
std::map<ObjectID, ExPolygons>* supportBrimAreasOut = nullptr);
|
||||
std::map<ObjectInstanceID, ExPolygons>* objectBrimAreasByInstanceOut = nullptr);
|
||||
|
||||
ExtrusionEntityCollection makeBrimInfill(const ExPolygons& singleBrimArea, const Print& print, const Polygons& islands_area);
|
||||
ExtrusionEntityCollection makeBrimInfillFromPlateCoordinates(const ExPolygons& singleBrimArea, const Print& print, const Polygons& islands_area);
|
||||
|
||||
@@ -225,8 +225,6 @@ set(lisbslic3r_sources
|
||||
GCode.hpp
|
||||
GCode/PchipInterpolatorHelper.cpp
|
||||
GCode/PchipInterpolatorHelper.hpp
|
||||
GCode/PostProcessor.cpp
|
||||
GCode/PostProcessor.hpp
|
||||
GCode/PressureEqualizer.cpp
|
||||
GCode/PressureEqualizer.hpp
|
||||
GCode/PrintExtents.cpp
|
||||
|
||||
+104
-3
@@ -4,6 +4,7 @@
|
||||
#include "LocalesUtils.hpp"
|
||||
#include "Preset.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
#include <assert.h>
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
@@ -36,6 +37,8 @@ using namespace nlohmann;
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
std::function<std::string(std::string, std::string)> ConfigBase::resolve_capability_fn = nullptr;
|
||||
|
||||
//BBS: add json support
|
||||
//static const std::string CONFIG_VERSION_KEY = "version";
|
||||
//static const std::string CONFIG_NAME_KEY = "name";
|
||||
@@ -84,12 +87,12 @@ std::string escape_strings_cstyle(const std::vector<std::string> &strs)
|
||||
// Separate the strings.
|
||||
(*outptr ++) = ';';
|
||||
const std::string &str = strs[j];
|
||||
// Is the string simple or complex? Complex string contains spaces, tabs, new lines and other
|
||||
// escapable characters. Empty string shall be quoted as well, if it is the only string in strs.
|
||||
// Is the string simple or complex? Complex string contains spaces, tabs, semicolons, new lines
|
||||
// and other escapable characters. Empty string shall be quoted as well, if it is the only string in strs.
|
||||
bool should_quote = strs.size() == 1 && str.empty();
|
||||
for (size_t i = 0; i < str.size(); ++ i) {
|
||||
char c = str[i];
|
||||
if (c == ' ' || c == '\t' || c == '\\' || c == '"' || c == '\r' || c == '\n') {
|
||||
if (c == ' ' || c == '\t' || c == ';' || c == '\\' || c == '"' || c == '\r' || c == '\n') {
|
||||
should_quote = true;
|
||||
break;
|
||||
}
|
||||
@@ -1486,6 +1489,29 @@ ConfigSubstitutions ConfigBase::load_from_gcode_file(const std::string &file, Fo
|
||||
return std::move(substitutions_ctxt.substitutions);
|
||||
}
|
||||
|
||||
std::optional<PluginCapabilityRef> parse_capability_ref(const std::string& value)
|
||||
{
|
||||
// Capability references are stored as "<plugin_name>;<cloud_uuid>;<capability_name>".
|
||||
// The cloud UUID is empty for local plugins (two consecutive semicolons).
|
||||
if (value.empty())
|
||||
return std::nullopt;
|
||||
|
||||
const size_t first = value.find(';');
|
||||
if (first == std::string::npos)
|
||||
return std::nullopt;
|
||||
const size_t second = value.find(';', first + 1);
|
||||
if (second == std::string::npos)
|
||||
return std::nullopt;
|
||||
|
||||
std::string name = value.substr(0, first);
|
||||
std::string uuid = value.substr(first + 1, second - first - 1);
|
||||
std::string capability_name = value.substr(second + 1);
|
||||
if (name.empty() || capability_name.empty())
|
||||
return std::nullopt;
|
||||
|
||||
return PluginCapabilityRef{ std::move(name), std::move(capability_name), std::move(uuid) };
|
||||
}
|
||||
|
||||
//BBS: add json support
|
||||
void ConfigBase::save_to_json(const std::string &file, const std::string &name, const std::string &from, const std::string &version) const
|
||||
{
|
||||
@@ -1522,6 +1548,18 @@ void ConfigBase::save_to_json(const std::string &file, const std::string &name,
|
||||
}
|
||||
}
|
||||
|
||||
// Serialize the top-level "plugins" manifest: the individual plugin-backed options keep bare
|
||||
// capability names; the full "name;uuid;capability" references are derived here (same helper as
|
||||
// update_plugin_manifest). Only with a resolver (GUI); without one (CLI/headless) leave whatever
|
||||
// the "plugins" option already serialized above, so a round-trip never drops the manifest.
|
||||
if (resolve_capability_fn) {
|
||||
std::vector<std::string> unique_refs = this->collect_plugin_manifest();
|
||||
if (unique_refs.empty())
|
||||
j.erase("plugins");
|
||||
else
|
||||
j["plugins"] = unique_refs;
|
||||
}
|
||||
|
||||
boost::nowide::ofstream c;
|
||||
c.open(file, std::ios::out | std::ios::trunc);
|
||||
c << j.dump(1, '\t') << std::endl;
|
||||
@@ -1553,6 +1591,69 @@ void ConfigBase::null_nullables()
|
||||
}
|
||||
}
|
||||
|
||||
void ConfigBase::save_plugin_collection(const std::string& opt_key, const ConfigOption* opt, std::vector<std::string>& plugin_refs) const {
|
||||
// Full plugin capability references ("name;uuid;capability") can only be derived through the
|
||||
// resolver registered by the GUI once plugins are loaded. In non-GUI/headless contexts (e.g.
|
||||
// the CLI) it stays null, so skip silently rather than calling an empty std::function.
|
||||
if (!resolve_capability_fn)
|
||||
return;
|
||||
|
||||
// A plugin-backed option declares its capability type via ConfigOptionDef::plugin_type (the same
|
||||
// metadata PluginResolver::find_option_for_capability scans). Deriving off the def rather than a
|
||||
// per-key branch keeps this generic across every plugin-backed option.
|
||||
const ConfigDef* def = this->def();
|
||||
const ConfigOptionDef* opt_def = def ? def->get(opt_key) : nullptr;
|
||||
if (opt_def == nullptr || !opt_def->is_plugin_backed())
|
||||
return;
|
||||
const std::string& type = opt_def->plugin_type;
|
||||
|
||||
// Resolve a single bare capability value into its full reference and append it, skipping unset
|
||||
// values, capabilities that could not be resolved (resolver returns ""), and duplicates already
|
||||
// collected (preserving insertion order).
|
||||
const auto append_ref = [&plugin_refs, &type](const std::string& capability_value) {
|
||||
if (capability_value.empty())
|
||||
return;
|
||||
std::string ref = resolve_capability_fn(capability_value, type);
|
||||
if (!ref.empty() && std::find(plugin_refs.begin(), plugin_refs.end(), ref) == plugin_refs.end())
|
||||
plugin_refs.emplace_back(std::move(ref));
|
||||
};
|
||||
|
||||
// Scalar options carry a single capability name; vector options carry a list. Same scalar/vector
|
||||
// dispatch as PluginResolver::find_option_for_capability.
|
||||
if (const auto* string_option = dynamic_cast<const ConfigOptionString*>(opt))
|
||||
append_ref(string_option->value);
|
||||
else if (const auto* vector_option = dynamic_cast<const ConfigOptionVectorBase*>(opt))
|
||||
for (const std::string& val : vector_option->vserialize())
|
||||
append_ref(val);
|
||||
}
|
||||
|
||||
std::vector<std::string> ConfigBase::collect_plugin_manifest() const
|
||||
{
|
||||
std::vector<std::string> refs;
|
||||
if (!resolve_capability_fn)
|
||||
return refs;
|
||||
|
||||
// Each plugin-backed option (ConfigOptionDef::is_plugin_backed) contributes its resolved
|
||||
// reference(s) via save_plugin_collection, which appends in order and skips duplicates, so no
|
||||
// second de-duplication pass is needed here.
|
||||
for (const std::string& opt_key : this->keys())
|
||||
if (const ConfigOption* opt = this->option(opt_key))
|
||||
this->save_plugin_collection(opt_key, opt, refs);
|
||||
return refs;
|
||||
}
|
||||
|
||||
void ConfigBase::update_plugin_manifest()
|
||||
{
|
||||
// Writes the derived manifest back into this config's "plugins" option (save_to_json writes the
|
||||
// same manifest into a JSON document instead), so an in-memory backend config carries a resolved
|
||||
// manifest even when the source preset was never serialized (picked-but-unsaved). Without a
|
||||
// resolver (CLI/headless) leave whatever manifest was loaded from disk untouched.
|
||||
if (!resolve_capability_fn)
|
||||
return;
|
||||
if (auto* manifest = this->option<ConfigOptionStrings>("plugins", true))
|
||||
manifest->values = this->collect_plugin_manifest();
|
||||
}
|
||||
|
||||
DynamicConfig::DynamicConfig(const ConfigBase& rhs, const t_config_option_keys& keys)
|
||||
{
|
||||
for (const t_config_option_key& opt_key : keys)
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
#include <cstdlib>
|
||||
#include <functional>
|
||||
#include <iostream>
|
||||
#include <optional>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
@@ -2254,6 +2255,9 @@ public:
|
||||
legend,
|
||||
// Vector value, but edited as a single string.
|
||||
one_string,
|
||||
plugin_picker,
|
||||
// Raw JSON string value, edited through a dialog behind a button rather than in the row.
|
||||
plugin_config,
|
||||
};
|
||||
|
||||
// Identifier of this option. It is stored here so that it is accessible through the by_serialization_key_ordinal map.
|
||||
@@ -2470,6 +2474,13 @@ public:
|
||||
// "serialized" - vector valued option is entered in a single edit field. Values are separated by a semicolon.
|
||||
// "show_value" - even if enum_values / enum_labels are set, still display the value, not the enum label.
|
||||
std::string gui_flags;
|
||||
// Capability type of a plugin-backed option, e.g. "slicing-pipeline" / "printer-connection"
|
||||
// (empty for ordinary options). GUIType::plugin_picker filters the plugin list by it, and it
|
||||
// resolves the option's "plugins" manifest reference; see is_plugin_backed().
|
||||
std::string plugin_type;
|
||||
// Whether this option holds plugin capability name(s) that feed the "plugins" manifest -- true
|
||||
// iff it declares a plugin_type. Setting plugin_type is the only step needed to add one.
|
||||
bool is_plugin_backed() const { return !plugin_type.empty(); }
|
||||
// Label of the GUI input field.
|
||||
// In case the GUI input fields are grouped in some views, the label defines a short label of a grouped value,
|
||||
// while full_label contains a label of a stand-alone field.
|
||||
@@ -2783,14 +2794,29 @@ public:
|
||||
//BBS: add json support
|
||||
void save_to_json(const std::string &file, const std::string &name, const std::string &from, const std::string &version) const;
|
||||
|
||||
// Rebuild the in-memory "plugins" manifest (the "name;uuid;capability" references the plugin
|
||||
// dispatchers consume) from the plugin-backed options via the registered resolver. save_to_json()
|
||||
// derives the same manifest, but only when a preset is written to disk; a config assembled in
|
||||
// memory for the backend (PresetBundle::full_config -> Print::apply) must refresh it here or a
|
||||
// picked-but-unsaved plugin never resolves at slice/export time. No-op without a resolver.
|
||||
void update_plugin_manifest();
|
||||
|
||||
// Set all the nullable values to nils.
|
||||
void null_nullables();
|
||||
|
||||
static size_t load_from_gcode_string_legacy(ConfigBase& config, const char* str, ConfigSubstitutionContext& substitutions);
|
||||
|
||||
static void set_resolve_capability_fn(std::function<std::string(std::string, std::string)> fn) { resolve_capability_fn = fn; }
|
||||
private:
|
||||
// Set a configuration value from a string.
|
||||
bool set_deserialize_raw(const t_config_option_key& opt_key_src, const std::string& value, ConfigSubstitutionContext& substitutions, bool append);
|
||||
void save_plugin_collection(const std::string& opt_key, const ConfigOption* opt, std::vector<std::string>& plugin_refs) const;
|
||||
// Collect the de-duplicated "name;uuid;capability" plugin references derived from this config's
|
||||
// plugin-backed options via the resolver. Shared by save_to_json (serializes them into the JSON
|
||||
// manifest) and update_plugin_manifest (writes them back into the "plugins" option). Order is
|
||||
// preserved and empties are dropped; returns empty without a resolver (CLI/headless).
|
||||
std::vector<std::string> collect_plugin_manifest() const;
|
||||
|
||||
static std::function<std::string(std::string, std::string)> resolve_capability_fn;
|
||||
};
|
||||
|
||||
// Configuration store with dynamic number of configuration values.
|
||||
@@ -3027,6 +3053,15 @@ protected:
|
||||
void set_defaults();
|
||||
};
|
||||
|
||||
struct PluginCapabilityRef
|
||||
{
|
||||
std::string name;
|
||||
std::string capability_name;
|
||||
std::string uuid;
|
||||
};
|
||||
|
||||
std::optional<PluginCapabilityRef> parse_capability_ref(const std::string& value);
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -61,7 +61,7 @@ static inline FlowRole opt_key_to_flow_role(const std::string &opt_key)
|
||||
|
||||
static inline void throw_on_missing_variable(const std::string &opt_key, const char *dependent_opt_key)
|
||||
{
|
||||
throw FlowErrorMissingVariable((boost::format(L("Failed to calculate line width of %1%. Cannot get value of \u201c%2%\u201d ")) % opt_key % dependent_opt_key).str());
|
||||
throw FlowErrorMissingVariable((boost::format("Failed to calculate line width of %1%. Cannot get value of \u201c%2%\u201d.") % opt_key % dependent_opt_key).str());
|
||||
}
|
||||
|
||||
// Used to provide hints to the user on default extrusion width values, and to provide reasonable values to the PlaceholderParser.
|
||||
@@ -129,7 +129,7 @@ double Flow::extrusion_width(const std::string& opt_key, const ConfigOptionResol
|
||||
Flow Flow::new_from_config_width(FlowRole role, const ConfigOptionFloatOrPercent &width, float nozzle_diameter, float height)
|
||||
{
|
||||
if (height <= 0)
|
||||
throw Slic3r::InvalidArgument("Invalid flow height supplied to new_from_config_width()");
|
||||
throw Slic3r::InvalidArgument("Invalid flow height supplied to new_from_config_width().");
|
||||
|
||||
float w;
|
||||
if (!width.percent && width.value <= 0.) {
|
||||
@@ -157,7 +157,7 @@ Flow Flow::with_spacing(float new_spacing) const
|
||||
assert(m_width >= m_height);
|
||||
out.m_width += new_spacing - m_spacing;
|
||||
if (out.m_width < out.m_height)
|
||||
throw Slic3r::InvalidArgument(L("Invalid spacing supplied to Flow::with_spacing(), check your layer height and extrusion width"));
|
||||
throw Slic3r::InvalidArgument("Invalid spacing supplied to Flow::with_spacing(), check your layer height and extrusion width.");
|
||||
}
|
||||
out.m_spacing = new_spacing;
|
||||
return out;
|
||||
|
||||
+56
-54
@@ -1845,7 +1845,7 @@ static std::vector<Vec2d> get_path_of_change_filament(const Print& print)
|
||||
std::string WipeTowerIntegration::finalize(GCode &gcodegen)
|
||||
{
|
||||
std::string gcode;
|
||||
if (gcodegen.wipe_tower_type() == WipeTowerType::Type2) {
|
||||
if (gcodegen.wipe_tower_type() == WipeTowerType::Type2 && !m_final_purge.gcode.empty()) {
|
||||
if (std::abs(gcodegen.writer().get_position().z() - m_final_purge.print_z) > EPSILON)
|
||||
gcode += gcodegen.change_layer(m_final_purge.print_z);
|
||||
gcode += append_tcr2(gcodegen, m_final_purge, -1);
|
||||
@@ -3246,7 +3246,8 @@ void GCode::_do_export(Print& print, GCodeOutputStream &file, ThumbnailsGenerato
|
||||
|
||||
BoundingBoxf bbox;
|
||||
auto pts = std::make_unique<ConfigOptionPoints>();
|
||||
if (print.calib_mode() == CalibMode::Calib_PA_Line || print.calib_mode() == CalibMode::Calib_PA_Pattern) {
|
||||
if (print.calib_mode() == CalibMode::Calib_PA_Pattern) {
|
||||
//PA_Pattern can have any size or arrangement - not dependent on 3mf model size
|
||||
bbox = bbox_bed;
|
||||
bbox.offset(-25.0);
|
||||
// add 4 corner points of bbox into pts
|
||||
@@ -3256,6 +3257,22 @@ void GCode::_do_export(Print& print, GCodeOutputStream &file, ThumbnailsGenerato
|
||||
pts->values.emplace_back(bbox.max.x(), bbox.max.y());
|
||||
pts->values.emplace_back(bbox.min.x(), bbox.max.y());
|
||||
|
||||
} else if (print.calib_mode() == CalibMode::Calib_PA_Line) {
|
||||
// Derive X bounds from the actual calibration geometry.
|
||||
CalibPressureAdvanceLine temp_pa_line_forsize(this);
|
||||
BoundingBoxf pattern_extents = temp_pa_line_forsize.print_extents(bbox_bed);
|
||||
|
||||
bbox = bbox_bed;
|
||||
bbox.offset(-25.0);
|
||||
bbox.min.x() = std::max(pattern_extents.min.x(), bbox.min.x());
|
||||
bbox.max.x() = std::min(pattern_extents.max.x(), bbox.max.x());
|
||||
|
||||
pts->values.reserve(4);
|
||||
pts->values.emplace_back(bbox.min.x(), bbox.min.y());
|
||||
pts->values.emplace_back(bbox.max.x(), bbox.min.y());
|
||||
pts->values.emplace_back(bbox.max.x(), bbox.max.y());
|
||||
pts->values.emplace_back(bbox.min.x(), bbox.max.y());
|
||||
|
||||
} else {
|
||||
// Convex hull of the 1st layer extrusions, for bed leveling and placing the initial purge line.
|
||||
// It encompasses the object extrusions, support extrusions, skirt, brim, wipe tower.
|
||||
@@ -3591,19 +3608,19 @@ void GCode::_do_export(Print& print, GCodeOutputStream &file, ThumbnailsGenerato
|
||||
}
|
||||
|
||||
this->m_objsWithBrim.clear();
|
||||
this->m_objSupportsWithBrim.clear();
|
||||
m_brim_done = false;
|
||||
|
||||
// BBS: set that indicates objs with brim
|
||||
for (auto iter = print.m_brimMap.begin(); iter != print.m_brimMap.end(); ++iter) {
|
||||
if (!iter->second.empty())
|
||||
this->m_objsWithBrim.insert(iter->first);
|
||||
// Orca: Track brims by instance. When a combined brim is printed, all of
|
||||
// its instances are marked done together.
|
||||
for (const Print::SkirtBrimGroup& group : print.skirt_brim_groups()) {
|
||||
for (const Print::SkirtBrimGroup::Brim& brim : group.brims) {
|
||||
if (brim.brim.empty())
|
||||
continue;
|
||||
for (const ObjectInstanceID& instance : brim.instances)
|
||||
this->m_objsWithBrim.insert(instance);
|
||||
}
|
||||
}
|
||||
for (auto iter = print.m_supportBrimMap.begin(); iter != print.m_supportBrimMap.end(); ++iter) {
|
||||
if (!iter->second.empty())
|
||||
this->m_objSupportsWithBrim.insert(iter->first);
|
||||
}
|
||||
if (this->m_objsWithBrim.empty() && this->m_objSupportsWithBrim.empty()) m_brim_done = true;
|
||||
if (this->m_objsWithBrim.empty()) m_brim_done = true;
|
||||
|
||||
// SoftFever: calib
|
||||
if (print.calib_params().mode == CalibMode::Calib_PA_Line) {
|
||||
@@ -4900,25 +4917,30 @@ std::string GCode::generate_skirt(const Print &print,
|
||||
return gcode;
|
||||
}
|
||||
|
||||
static size_t find_skirt_brim_group_idx(const Print& print, ObjectID object_id)
|
||||
static size_t find_skirt_brim_group_idx(const Print& print, ObjectID object_id, size_t instance_id)
|
||||
{
|
||||
const std::vector<Print::SkirtBrimGroup>& groups = print.skirt_brim_groups();
|
||||
for (size_t idx = 0; idx < groups.size(); ++idx)
|
||||
if (std::find(groups[idx].object_ids.begin(), groups[idx].object_ids.end(), object_id) != groups[idx].object_ids.end())
|
||||
for (size_t idx = 0; idx < groups.size(); ++idx) {
|
||||
const std::vector<ObjectInstanceID>& instances = groups[idx].instances;
|
||||
if (std::any_of(instances.begin(), instances.end(), [object_id, instance_id](const ObjectInstanceID& instance) {
|
||||
return instance.object_id == object_id && instance.instance_id == instance_id;
|
||||
}))
|
||||
return idx;
|
||||
}
|
||||
return size_t(-1);
|
||||
}
|
||||
|
||||
std::string GCode::generate_object_skirt_group(const Print &print,
|
||||
const PrintObject &object,
|
||||
size_t instance_id,
|
||||
const LayerTools &layer_tools,
|
||||
const Layer& layer,
|
||||
unsigned int extruder_id)
|
||||
{
|
||||
if (print.config().skirt_type != stPerObject || print.skirt_brim_groups().empty())
|
||||
if (print.config().skirt_type != stPerObject || !layer_tools.has_skirt || print.skirt_brim_groups().empty())
|
||||
return {};
|
||||
|
||||
const size_t group_idx = find_skirt_brim_group_idx(print, object.id());
|
||||
const size_t group_idx = find_skirt_brim_group_idx(print, object.id(), instance_id);
|
||||
if (group_idx == size_t(-1) || print.skirt_brim_groups()[group_idx].skirt.empty())
|
||||
return {};
|
||||
|
||||
@@ -4930,15 +4952,15 @@ std::string GCode::generate_object_skirt_group(const Print &print,
|
||||
object_skirt_tools, layer, extruder_id, m_skirt_group_done[group_idx]);
|
||||
}
|
||||
|
||||
std::string GCode::generate_object_brim(const Print &print, const PrintObject &object, bool first_layer)
|
||||
std::string GCode::generate_object_brim(const Print &print, const PrintObject &object, size_t instance_id, bool first_layer)
|
||||
{
|
||||
if (!first_layer)
|
||||
return {};
|
||||
|
||||
auto emit_brim = [this](const ExtrusionEntityCollection& brim, const std::vector<ObjectID>& object_ids) {
|
||||
auto emit_brim = [this](const ExtrusionEntityCollection& brim, const std::vector<ObjectInstanceID>& instances) {
|
||||
std::string gcode;
|
||||
const bool already_emitted = std::none_of(object_ids.begin(), object_ids.end(), [this](ObjectID object_id) {
|
||||
return m_objsWithBrim.find(object_id) != m_objsWithBrim.end();
|
||||
const bool already_emitted = std::none_of(instances.begin(), instances.end(), [this](const ObjectInstanceID& instance) {
|
||||
return m_objsWithBrim.find(instance) != m_objsWithBrim.end();
|
||||
});
|
||||
if (already_emitted || brim.empty())
|
||||
return gcode;
|
||||
@@ -4950,32 +4972,22 @@ std::string GCode::generate_object_brim(const Print &print, const PrintObject &o
|
||||
gcode += this->extrude_entity(*ee, "brim", NOZZLE_CONFIG(support_speed));
|
||||
m_avoid_crossing_perimeters.use_external_mp(false);
|
||||
m_avoid_crossing_perimeters.disable_once();
|
||||
for (ObjectID object_id : object_ids)
|
||||
m_objsWithBrim.erase(object_id);
|
||||
for (const ObjectInstanceID& instance : instances)
|
||||
m_objsWithBrim.erase(instance);
|
||||
return gcode;
|
||||
};
|
||||
|
||||
const bool has_per_object_skirt_or_shield = print.config().skirt_type == stPerObject &&
|
||||
(print.has_skirt() || print.has_infinite_skirt());
|
||||
if (print.config().combine_brims && !has_per_object_skirt_or_shield &&
|
||||
print.config().print_sequence != PrintSequence::ByObject && print.m_brimMap.size() == 1) {
|
||||
const auto brim_it = print.m_brimMap.begin();
|
||||
return emit_brim(brim_it->second, { brim_it->first });
|
||||
}
|
||||
|
||||
const size_t group_idx = find_skirt_brim_group_idx(print, object.id());
|
||||
const ObjectInstanceID object_instance_id{ object.id(), instance_id };
|
||||
const size_t group_idx = find_skirt_brim_group_idx(print, object.id(), instance_id);
|
||||
if (group_idx != size_t(-1)) {
|
||||
std::string gcode;
|
||||
for (const Print::SkirtBrimGroup::Brim& brim : print.skirt_brim_groups()[group_idx].brims)
|
||||
if (std::find(brim.object_ids.begin(), brim.object_ids.end(), object.id()) != brim.object_ids.end())
|
||||
gcode += emit_brim(brim.brim, brim.object_ids);
|
||||
if (std::find(brim.instances.begin(), brim.instances.end(), object_instance_id) != brim.instances.end())
|
||||
gcode += emit_brim(brim.brim, brim.instances);
|
||||
return gcode;
|
||||
}
|
||||
|
||||
const auto brim_it = print.m_brimMap.find(object.id());
|
||||
if (brim_it == print.m_brimMap.end())
|
||||
return {};
|
||||
return emit_brim(brim_it->second, { object.id() });
|
||||
return {};
|
||||
}
|
||||
|
||||
// Bedslinger model. The heavier the bed load, the lower the achievable Y acceleration for a given
|
||||
@@ -6096,8 +6108,8 @@ LayerResult GCode::process_layer(
|
||||
const auto& inst = instance_to_print.print_object.instances()[instance_to_print.instance_id];
|
||||
const LayerToPrint &layer_to_print = layers[instance_to_print.layer_id];
|
||||
if (print_wipe_extrusions == (is_anything_overridden ? 1 : 0)) {
|
||||
gcode += generate_object_skirt_group(print, instance_to_print.print_object, layer_tools, layer, extruder_id);
|
||||
gcode += generate_object_brim(print, instance_to_print.print_object, first_layer);
|
||||
gcode += generate_object_skirt_group(print, instance_to_print.print_object, instance_to_print.instance_id, layer_tools, layer, extruder_id);
|
||||
gcode += generate_object_brim(print, instance_to_print.print_object, instance_to_print.instance_id, first_layer);
|
||||
}
|
||||
|
||||
// To control print speed of the 1st object layer printed over raft interface.
|
||||
@@ -6152,18 +6164,6 @@ LayerResult GCode::process_layer(
|
||||
m_layer = layers[instance_to_print.layer_id].support_layer;
|
||||
m_object_layer_over_raft = false;
|
||||
|
||||
//BBS: print supports' brims first
|
||||
if (this->m_objSupportsWithBrim.find(instance_to_print.print_object.id()) != this->m_objSupportsWithBrim.end() && !print_wipe_extrusions) {
|
||||
this->set_origin(0., 0.);
|
||||
m_avoid_crossing_perimeters.use_external_mp();
|
||||
for (const ExtrusionEntity* ee : print.m_supportBrimMap.at(instance_to_print.print_object.id()).entities) {
|
||||
gcode += this->extrude_entity(*ee, "brim", NOZZLE_CONFIG(support_speed));
|
||||
}
|
||||
m_avoid_crossing_perimeters.use_external_mp(false);
|
||||
// Allow a straight travel move to the first object point.
|
||||
m_avoid_crossing_perimeters.disable_once();
|
||||
this->m_objSupportsWithBrim.erase(instance_to_print.print_object.id());
|
||||
}
|
||||
// When starting a new object, use the external motion planner for the first travel move.
|
||||
const Point& offset = instance_to_print.print_object.instances()[instance_to_print.instance_id].shift;
|
||||
std::pair<const PrintObject*, Point> this_object_copy(&instance_to_print.print_object, offset);
|
||||
@@ -7549,7 +7549,7 @@ std::string GCode::_extrude(const ExtrusionPath &path, std::string description,
|
||||
// If adaptive PA is enabled, by default evaluate PA on all extrusion moves
|
||||
bool is_pa_calib = m_curr_print->calib_mode() == CalibMode::Calib_PA_Line ||
|
||||
m_curr_print->calib_mode() == CalibMode::Calib_PA_Pattern ||
|
||||
m_curr_print->calib_mode() == CalibMode::Calib_PA_Tower;
|
||||
m_curr_print->calib_mode() == CalibMode::Calib_PA_Tower;
|
||||
bool evaluate_adaptive_pa = false;
|
||||
bool role_change = (m_last_extrusion_role != path.role());
|
||||
if (!is_pa_calib && FILAMENT_CONFIG(adaptive_pressure_advance) && FILAMENT_CONFIG(enable_pressure_advance)) {
|
||||
@@ -9104,7 +9104,7 @@ std::string GCode::set_object_info(Print *print) {
|
||||
std::ostringstream gcode;
|
||||
size_t object_id = 0;
|
||||
// Orca: check if we are in pa calib mode
|
||||
if (print->calib_mode() == CalibMode::Calib_PA_Line || print->calib_mode() == CalibMode::Calib_PA_Pattern) {
|
||||
if (print->calib_mode() == CalibMode::Calib_PA_Pattern) {
|
||||
BoundingBoxf bbox_bed(print->config().printable_area.values);
|
||||
bbox_bed.offset(-25.0);
|
||||
Polygon polygon_bed;
|
||||
@@ -9115,6 +9115,8 @@ std::string GCode::set_object_info(Print *print) {
|
||||
gcode << "EXCLUDE_OBJECT_DEFINE NAME="
|
||||
<< "Orca-PA-Calibration-Test"
|
||||
<< " CENTER=" << 0 << "," << 0 << " POLYGON=" << polygon_to_string(polygon_bed, print, true) << "\n";
|
||||
} else if (print->calib_mode() == CalibMode::Calib_PA_Line) {
|
||||
// PA_Line has only one object, no EXCLUDE_OBJECT_DEFINE needed
|
||||
} else {
|
||||
size_t unique_id = 0;
|
||||
for (PrintObject* object : print->objects()) {
|
||||
|
||||
@@ -359,11 +359,13 @@ private:
|
||||
std::vector<coordf_t> &skirt_done);
|
||||
std::string generate_object_skirt_group(const Print &print,
|
||||
const PrintObject &object,
|
||||
size_t instance_id,
|
||||
const LayerTools &layer_tools,
|
||||
const Layer& layer,
|
||||
unsigned int extruder_id);
|
||||
std::string generate_object_brim(const Print &print,
|
||||
const PrintObject &object,
|
||||
size_t instance_id,
|
||||
bool first_layer);
|
||||
|
||||
LayerResult process_layer(
|
||||
@@ -533,8 +535,7 @@ private:
|
||||
// BBS
|
||||
LiftType to_lift_type(ZHopType z_hop_types);
|
||||
|
||||
std::set<ObjectID> m_objsWithBrim; // indicates the objs with brim
|
||||
std::set<ObjectID> m_objSupportsWithBrim; // indicates the objs' supports with brim
|
||||
std::set<ObjectInstanceID> m_objsWithBrim; // indicates the object instances with brim
|
||||
// Cache for custom seam enforcers/blockers for each layer.
|
||||
SeamPlacer m_seam_placer;
|
||||
|
||||
|
||||
@@ -1,379 +0,0 @@
|
||||
#include "PostProcessor.hpp"
|
||||
|
||||
#include "libslic3r/Utils.hpp"
|
||||
#include "libslic3r/format.hpp"
|
||||
#include "libslic3r/I18N.hpp"
|
||||
|
||||
#include <boost/algorithm/string.hpp>
|
||||
#include <boost/log/trivial.hpp>
|
||||
#include <boost/format.hpp>
|
||||
#include <boost/filesystem.hpp>
|
||||
#include <boost/nowide/cstdlib.hpp>
|
||||
#include <boost/nowide/convert.hpp>
|
||||
#include <boost/nowide/fstream.hpp>
|
||||
|
||||
// BBS
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
|
||||
#ifdef WIN32
|
||||
|
||||
// The standard Windows includes.
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#define NOMINMAX
|
||||
#include <Windows.h>
|
||||
#include <shellapi.h>
|
||||
|
||||
// https://blogs.msdn.microsoft.com/twistylittlepassagesallalike/2011/04/23/everyone-quotes-command-line-arguments-the-wrong-way/
|
||||
// This routine appends the given argument to a command line such that CommandLineToArgvW will return the argument string unchanged.
|
||||
// Arguments in a command line should be separated by spaces; this function does not add these spaces.
|
||||
// Argument - Supplies the argument to encode.
|
||||
// CommandLine - Supplies the command line to which we append the encoded argument string.
|
||||
static void quote_argv_winapi(const std::wstring &argument, std::wstring &commmand_line_out)
|
||||
{
|
||||
// Don't quote unless we actually need to do so --- hopefully avoid problems if programs won't parse quotes properly.
|
||||
if (argument.empty() == false && argument.find_first_of(L" \t\n\v\"") == argument.npos)
|
||||
commmand_line_out.append(argument);
|
||||
else {
|
||||
commmand_line_out.push_back(L'"');
|
||||
for (auto it = argument.begin(); ; ++ it) {
|
||||
unsigned number_backslashes = 0;
|
||||
while (it != argument.end() && *it == L'\\') {
|
||||
++ it;
|
||||
++ number_backslashes;
|
||||
}
|
||||
if (it == argument.end()) {
|
||||
// Escape all backslashes, but let the terminating double quotation mark we add below be interpreted as a metacharacter.
|
||||
commmand_line_out.append(number_backslashes * 2, L'\\');
|
||||
break;
|
||||
} else if (*it == L'"') {
|
||||
// Escape all backslashes and the following double quotation mark.
|
||||
commmand_line_out.append(number_backslashes * 2 + 1, L'\\');
|
||||
commmand_line_out.push_back(*it);
|
||||
} else {
|
||||
// Backslashes aren't special here.
|
||||
commmand_line_out.append(number_backslashes, L'\\');
|
||||
commmand_line_out.push_back(*it);
|
||||
}
|
||||
}
|
||||
commmand_line_out.push_back(L'"');
|
||||
}
|
||||
}
|
||||
|
||||
static DWORD execute_process_winapi(const std::wstring &command_line)
|
||||
{
|
||||
// Extract the current environment to be passed to the child process.
|
||||
std::wstring envstr;
|
||||
{
|
||||
wchar_t *env = GetEnvironmentStrings();
|
||||
assert(env != nullptr);
|
||||
const wchar_t* var = env;
|
||||
size_t totallen = 0;
|
||||
size_t len;
|
||||
while ((len = wcslen(var)) > 0) {
|
||||
totallen += len + 1;
|
||||
var += len + 1;
|
||||
}
|
||||
envstr = std::wstring(env, totallen);
|
||||
FreeEnvironmentStrings(env);
|
||||
}
|
||||
|
||||
STARTUPINFOW startup_info;
|
||||
memset(&startup_info, 0, sizeof(startup_info));
|
||||
startup_info.cb = sizeof(STARTUPINFO);
|
||||
#if 0
|
||||
startup_info.dwFlags = STARTF_USESHOWWINDOW;
|
||||
startup_info.wShowWindow = SW_HIDE;
|
||||
#endif
|
||||
PROCESS_INFORMATION process_info;
|
||||
if (! ::CreateProcessW(
|
||||
nullptr /* lpApplicationName */, (LPWSTR)command_line.c_str(), nullptr /* lpProcessAttributes */, nullptr /* lpThreadAttributes */, false /* bInheritHandles */,
|
||||
CREATE_UNICODE_ENVIRONMENT /* | CREATE_NEW_CONSOLE */ /* dwCreationFlags */, (LPVOID)envstr.c_str(), nullptr /* lpCurrentDirectory */, &startup_info, &process_info))
|
||||
throw Slic3r::RuntimeError(std::string("Failed starting the script ") + boost::nowide::narrow(command_line) + ", Win32 error: " + std::to_string(int(::GetLastError())));
|
||||
::WaitForSingleObject(process_info.hProcess, INFINITE);
|
||||
ULONG rc = 0;
|
||||
::GetExitCodeProcess(process_info.hProcess, &rc);
|
||||
::CloseHandle(process_info.hThread);
|
||||
::CloseHandle(process_info.hProcess);
|
||||
return rc;
|
||||
}
|
||||
|
||||
// Run the script. If it is a perl script, run it through the bundled perl interpreter.
|
||||
// If it is a batch file, run it through the cmd.exe.
|
||||
// Otherwise run it directly.
|
||||
static int run_script(const std::string &script, const std::string &gcode, std::string &/*std_err*/)
|
||||
{
|
||||
// Unpack the argument list provided by the user.
|
||||
int nArgs;
|
||||
LPWSTR *szArglist = CommandLineToArgvW(boost::nowide::widen(script).c_str(), &nArgs);
|
||||
if (szArglist == nullptr || nArgs <= 0) {
|
||||
// CommandLineToArgvW failed. Maybe the command line escapment is invalid?
|
||||
throw Slic3r::RuntimeError(std::string("Post processing script ") + script + " on file " + gcode + " failed. CommandLineToArgvW() refused to parse the command line path.");
|
||||
}
|
||||
|
||||
std::wstring command_line;
|
||||
std::wstring command = szArglist[0];
|
||||
if (! boost::filesystem::exists(boost::filesystem::path(command)))
|
||||
throw Slic3r::RuntimeError(std::string("The configured post-processing script does not exist: ") + boost::nowide::narrow(command));
|
||||
if (boost::iends_with(command, L".pl")) {
|
||||
// This is a perl script. Run it through the perl interpreter.
|
||||
// The current process may be slic3r.exe or slic3r-console.exe.
|
||||
// Find the path of the process:
|
||||
wchar_t wpath_exe[_MAX_PATH + 1];
|
||||
::GetModuleFileNameW(nullptr, wpath_exe, _MAX_PATH);
|
||||
boost::filesystem::path path_exe(wpath_exe);
|
||||
boost::filesystem::path path_perl = path_exe.parent_path() / "perl" / "perl.exe";
|
||||
if (! boost::filesystem::exists(path_perl)) {
|
||||
LocalFree(szArglist);
|
||||
throw Slic3r::RuntimeError(std::string("Perl interpreter ") + path_perl.string() + " does not exist.");
|
||||
}
|
||||
// Replace it with the current perl interpreter.
|
||||
quote_argv_winapi(boost::nowide::widen(path_perl.string()), command_line);
|
||||
command_line += L" ";
|
||||
} else if (boost::iends_with(command, ".bat")) {
|
||||
// Run a batch file through the command line interpreter.
|
||||
command_line = L"cmd.exe /C ";
|
||||
}
|
||||
|
||||
for (int i = 0; i < nArgs; ++ i) {
|
||||
quote_argv_winapi(szArglist[i], command_line);
|
||||
command_line += L" ";
|
||||
}
|
||||
LocalFree(szArglist);
|
||||
quote_argv_winapi(boost::nowide::widen(gcode), command_line);
|
||||
return (int)execute_process_winapi(command_line);
|
||||
}
|
||||
|
||||
#else
|
||||
// POSIX
|
||||
|
||||
#include <cstdlib> // getenv()
|
||||
#include <sstream>
|
||||
#include <boost/process.hpp>
|
||||
|
||||
namespace process = boost::process;
|
||||
|
||||
static int run_script(const std::string &script, const std::string &gcode, std::string &std_err)
|
||||
{
|
||||
// Try to obtain user's default shell
|
||||
const char *shell = ::getenv("SHELL");
|
||||
if (shell == nullptr) { shell = "/bin/sh"; }
|
||||
|
||||
// Quote and escape the gcode path argument
|
||||
std::string command { script };
|
||||
command.append(" '");
|
||||
for (char c : gcode) {
|
||||
if (c == '\'') { command.append("'\\''"); }
|
||||
else { command.push_back(c); }
|
||||
}
|
||||
command.push_back('\'');
|
||||
|
||||
BOOST_LOG_TRIVIAL(debug) << boost::format("Executing script, shell: %1%, command: %2%") % shell % command;
|
||||
|
||||
process::ipstream istd_err;
|
||||
process::child child(shell, "-c", command, process::std_err > istd_err);
|
||||
|
||||
std_err.clear();
|
||||
std::string line;
|
||||
|
||||
while (child.running() && std::getline(istd_err, line)) {
|
||||
std_err.append(line);
|
||||
std_err.push_back('\n');
|
||||
}
|
||||
|
||||
child.wait();
|
||||
return child.exit_code();
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
//! macro used to mark string used at localization,
|
||||
//! return same string
|
||||
#define L(s) (s)
|
||||
#define _(s) Slic3r::I18N::translate(s)
|
||||
|
||||
// BBS
|
||||
void gcode_add_line_number(const std::string& path, const DynamicPrintConfig& config)
|
||||
{
|
||||
const ConfigOptionBool* opt = config.opt<ConfigOptionBool>("gcode_add_line_number");
|
||||
if (!opt->getBool())
|
||||
return;
|
||||
|
||||
auto gcode_file = boost::filesystem::path(path);
|
||||
if (!boost::filesystem::exists(gcode_file))
|
||||
return;
|
||||
|
||||
std::fstream fs;
|
||||
std::string new_gcode;
|
||||
fs.open(gcode_file.c_str(), std::fstream::in | std::fstream::out);
|
||||
|
||||
size_t line_number = 1;
|
||||
std::string gcode_line;
|
||||
while (std::getline(fs, gcode_line)) {
|
||||
char num_str[128];
|
||||
memset(num_str, 0, sizeof(num_str));
|
||||
snprintf(num_str, sizeof(num_str), "%zd", line_number);
|
||||
new_gcode += std::string("N") + num_str + " " + gcode_line + "\n";
|
||||
line_number++;
|
||||
}
|
||||
|
||||
fs.clear();
|
||||
fs.seekp(0, std::ios_base::beg);
|
||||
fs.write(new_gcode.c_str(), new_gcode.length());
|
||||
fs.close();
|
||||
}
|
||||
|
||||
// Run post processing script / scripts if defined.
|
||||
// Returns true if a post-processing script was executed.
|
||||
// Returns false if no post-processing script was defined.
|
||||
// Throws an exception on error.
|
||||
// host is one of "File", "PrusaLink", "Repetier", "SL1Host", "OctoPrint", "FlashAir", "Duet", "AstroBox" ...
|
||||
// For a "File" target, a temp file will be created for src_path by adding a ".pp" suffix and src_path will be updated.
|
||||
// In that case the caller is responsible to delete the temp file created.
|
||||
// output_name is the final name of the G-code on SD card or when uploaded to PrusaLink or OctoPrint.
|
||||
// If uploading to PrusaLink or OctoPrint, then the file will be renamed to output_name first on the target host.
|
||||
// The post-processing script may change the output_name.
|
||||
bool run_post_process_scripts(std::string &src_path, bool make_copy, const std::string &host, std::string &output_name, const DynamicPrintConfig &config)
|
||||
{
|
||||
const auto *post_process = config.opt<ConfigOptionStrings>("post_process");
|
||||
if (// likely running in SLA mode
|
||||
post_process == nullptr ||
|
||||
// no post-processing script
|
||||
post_process->values.empty())
|
||||
return false;
|
||||
|
||||
std::string path;
|
||||
if (make_copy) {
|
||||
// Don't run the post-processing script on the input file, it will be memory mapped by the G-code viewer.
|
||||
// Make a copy.
|
||||
path = src_path + ".pp";
|
||||
// First delete an old file if it exists.
|
||||
try {
|
||||
if (boost::filesystem::exists(path))
|
||||
boost::filesystem::remove(path);
|
||||
} catch (const std::exception &err) {
|
||||
BOOST_LOG_TRIVIAL(error) << Slic3r::format("Failed deleting an old temporary file %1% before running a post-processing script: %2%", path, err.what());
|
||||
}
|
||||
// Second make a copy.
|
||||
std::string error_message;
|
||||
if (copy_file(src_path, path, error_message, false) != SUCCESS)
|
||||
throw Slic3r::RuntimeError(Slic3r::format("Failed making a temporary copy of G-code file %1% before running a post-processing script: %2%", src_path, error_message));
|
||||
} else {
|
||||
// Don't make a copy of the G-code before running the post-processing script.
|
||||
path = src_path;
|
||||
}
|
||||
|
||||
auto delete_copy = [&path, &src_path, make_copy]() {
|
||||
if (make_copy)
|
||||
try {
|
||||
if (boost::filesystem::exists(path))
|
||||
boost::filesystem::remove(path);
|
||||
} catch (const std::exception &err) {
|
||||
BOOST_LOG_TRIVIAL(error) << Slic3r::format("Failed deleting a temporary copy %1% of a G-code file %2% : %3%", path, src_path, err.what());
|
||||
}
|
||||
};
|
||||
|
||||
auto gcode_file = boost::filesystem::path(path);
|
||||
if (! boost::filesystem::exists(gcode_file))
|
||||
throw Slic3r::RuntimeError(std::string("Post-processor can't find exported gcode file"));
|
||||
|
||||
// Store print configuration into environment variables.
|
||||
config.setenv_();
|
||||
// Let the post-processing script know the target host ("File", "PrusaLink", "Repetier", "SL1Host", "OctoPrint", "FlashAir", "Duet", "AstroBox" ...)
|
||||
boost::nowide::setenv("SLIC3R_PP_HOST", host.c_str(), 1);
|
||||
// Let the post-processing script know the final file name. For "File" host, it is a full path of the target file name and its location, for example pointing to an SD card.
|
||||
// For "PrusaLink" or "OctoPrint", it is a file name optionally with a directory on the target host.
|
||||
boost::nowide::setenv("SLIC3R_PP_OUTPUT_NAME", output_name.c_str(), 1);
|
||||
|
||||
// Path to an optional file that the post-processing script may create and populate it with a single line containing the output_name replacement.
|
||||
std::string path_output_name = path + ".output_name";
|
||||
auto remove_output_name_file = [&path_output_name, &src_path]() {
|
||||
try {
|
||||
if (boost::filesystem::exists(path_output_name))
|
||||
boost::filesystem::remove(path_output_name);
|
||||
} catch (const std::exception &err) {
|
||||
BOOST_LOG_TRIVIAL(error) << Slic3r::format("Failed deleting a file %1% carrying the final name / path of a G-code file %2%: %3%", path_output_name, src_path, err.what());
|
||||
}
|
||||
};
|
||||
// Remove possible stalled path_output_name of the previous run.
|
||||
remove_output_name_file();
|
||||
|
||||
try {
|
||||
for (const std::string &scripts : post_process->values) {
|
||||
std::vector<std::string> lines;
|
||||
boost::split(lines, scripts, boost::is_any_of("\r\n"));
|
||||
for (std::string script : lines) {
|
||||
// Ignore empty post processing script lines.
|
||||
boost::trim(script);
|
||||
if (script.empty())
|
||||
continue;
|
||||
BOOST_LOG_TRIVIAL(info) << "Executing script " << script << " on file " << path;
|
||||
std::string std_err;
|
||||
const int result = run_script(script, gcode_file.string(), std_err);
|
||||
if (result != 0) {
|
||||
const std::string msg = std_err.empty() ? (boost::format("Post-processing script %1% on file %2% failed.\nError code: %3%") % script % path % result).str()
|
||||
: (boost::format("Post-processing script %1% on file %2% failed.\nError code: %3%\nOutput:\n%4%") % script % path % result % std_err).str();
|
||||
BOOST_LOG_TRIVIAL(error) << msg;
|
||||
delete_copy();
|
||||
throw Slic3r::RuntimeError(msg);
|
||||
}
|
||||
if (! boost::filesystem::exists(gcode_file)) {
|
||||
const std::string msg = (boost::format(_(L(
|
||||
"Post-processing script %1% failed.\n\n"
|
||||
"The post-processing script is expected to change the G-code file %2% in place, but the G-code file was deleted and likely saved under a new name.\n"
|
||||
"Please adjust the post-processing script to change the G-code in place and consult the manual on how to optionally rename the post-processed G-code file.\n")))
|
||||
% script % path).str();
|
||||
BOOST_LOG_TRIVIAL(error) << msg;
|
||||
throw Slic3r::RuntimeError(msg);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (boost::filesystem::exists(path_output_name)) {
|
||||
try {
|
||||
// Read a single line from path_output_name, which should contain the new output name of the post-processed G-code.
|
||||
boost::nowide::fstream f;
|
||||
f.open(path_output_name, std::ios::in);
|
||||
std::string new_output_name;
|
||||
std::getline(f, new_output_name);
|
||||
f.close();
|
||||
|
||||
if (host == "File") {
|
||||
namespace fs = boost::filesystem;
|
||||
fs::path op(new_output_name);
|
||||
if (op.is_relative() && op.has_filename() && op.parent_path().empty()) {
|
||||
// Is this just a filename? Make it an absolute path.
|
||||
auto outpath = fs::path(output_name).parent_path();
|
||||
outpath /= op.string();
|
||||
new_output_name = outpath.string();
|
||||
}
|
||||
else {
|
||||
if (! op.is_absolute() || ! op.has_filename())
|
||||
throw Slic3r::RuntimeError("Unable to parse desired new path from output name file");
|
||||
}
|
||||
if (! fs::exists(fs::path(new_output_name).parent_path()))
|
||||
throw Slic3r::RuntimeError(Slic3r::format("Output directory does not exist: %1%",
|
||||
fs::path(new_output_name).parent_path().string()));
|
||||
}
|
||||
|
||||
BOOST_LOG_TRIVIAL(trace) << "Post-processing script changed the file name from " << output_name << " to " << new_output_name;
|
||||
output_name = new_output_name;
|
||||
} catch (const std::exception &err) {
|
||||
throw Slic3r::RuntimeError(Slic3r::format("run_post_process_scripts: Failed reading a file %1% "
|
||||
"carrying the final name / path of a G-code file: %2%",
|
||||
path_output_name, err.what()));
|
||||
}
|
||||
remove_output_name_file();
|
||||
}
|
||||
} catch (...) {
|
||||
remove_output_name_file();
|
||||
delete_copy();
|
||||
throw;
|
||||
}
|
||||
|
||||
src_path = std::move(path);
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace Slic3r
|
||||
@@ -1,34 +0,0 @@
|
||||
#ifndef slic3r_GCode_PostProcessor_hpp_
|
||||
#define slic3r_GCode_PostProcessor_hpp_
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "../libslic3r.h"
|
||||
#include "../PrintConfig.hpp"
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
// Run post processing script / scripts if defined.
|
||||
// Returns true if a post-processing script was executed.
|
||||
// Returns false if no post-processing script was defined.
|
||||
// Throws an exception on error.
|
||||
// host is one of "File", "PrusaLink", "Repetier", "SL1Host", "OctoPrint", "FlashAir", "Duet", "AstroBox" ...
|
||||
// If make_copy, then a temp file will be created for src_path by adding a ".pp" suffix and src_path will be updated.
|
||||
// In that case the caller is responsible to delete the temp file created.
|
||||
// output_name is the final name of the G-code on SD card or when uploaded to PrusaLink or OctoPrint.
|
||||
// If uploading to PrusaLink or OctoPrint, then the file will be renamed to output_name first on the target host.
|
||||
// The post-processing script may change the output_name.
|
||||
extern bool run_post_process_scripts(std::string &src_path, bool make_copy, const std::string &host, std::string &output_name, const DynamicPrintConfig &config);
|
||||
|
||||
inline bool run_post_process_scripts(std::string &src_path, const DynamicPrintConfig &config)
|
||||
{
|
||||
std::string src_path_name = src_path;
|
||||
return run_post_process_scripts(src_path, false, "File", src_path_name, config);
|
||||
}
|
||||
|
||||
// BBS
|
||||
extern void gcode_add_line_number(const std::string &path, const DynamicPrintConfig &config);
|
||||
|
||||
} // namespace Slic3r
|
||||
|
||||
#endif /* slic3r_GCode_PostProcessor_hpp_ */
|
||||
@@ -3,6 +3,7 @@
|
||||
|
||||
#include <cereal/access.hpp>
|
||||
#include <cereal/types/base_class.hpp>
|
||||
#include <cstddef>
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
@@ -41,6 +42,18 @@ private:
|
||||
template<class Archive> void serialize(Archive &ar) { ar(id); }
|
||||
};
|
||||
|
||||
struct ObjectInstanceID {
|
||||
ObjectID object_id;
|
||||
size_t instance_id { size_t(-1) };
|
||||
|
||||
bool operator==(const ObjectInstanceID& rhs) const { return object_id == rhs.object_id && instance_id == rhs.instance_id; }
|
||||
bool operator!=(const ObjectInstanceID& rhs) const { return !(*this == rhs); }
|
||||
bool operator<(const ObjectInstanceID& rhs) const
|
||||
{
|
||||
return object_id < rhs.object_id || (object_id == rhs.object_id && instance_id < rhs.instance_id);
|
||||
}
|
||||
};
|
||||
|
||||
// Base for Model, ModelObject, ModelVolume, ModelInstance or ModelMaterial to provide a unique ID
|
||||
// to synchronize the front end (UI) with the back end (BackgroundSlicingProcess / Print / PrintObject).
|
||||
// Also base for Print, PrintObject, SLAPrint, SLAPrintObject to provide a unique ID for matching Model / ModelObject
|
||||
|
||||
@@ -105,6 +105,32 @@ PlatformFlavor platform_flavor()
|
||||
return s_platform_flavor;
|
||||
}
|
||||
|
||||
std::string platform_os_type()
|
||||
{
|
||||
#if defined(_WIN32)
|
||||
return "win";
|
||||
#elif defined(__APPLE__)
|
||||
return "macos";
|
||||
#elif defined(__linux__) || defined(__LINUX__)
|
||||
return "linux";
|
||||
#else
|
||||
return "unknown";
|
||||
#endif
|
||||
}
|
||||
|
||||
std::string platform_architecture()
|
||||
{
|
||||
#if defined(__aarch64__) || defined(__arm64__) || defined(_M_ARM64)
|
||||
return "arm64";
|
||||
#elif defined(__x86_64__) || defined(__x86_64) || defined(__amd64__) || defined(__amd64) || defined(_M_X64) || defined(_M_AMD64)
|
||||
return "x86_64";
|
||||
#elif defined(__i386__) || defined(__i386) || defined(i386) || defined(_M_IX86)
|
||||
return "i386";
|
||||
#else
|
||||
return "unknown";
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
|
||||
std::string platform_to_string(Platform platform)
|
||||
|
||||
@@ -36,6 +36,8 @@ void detect_platform();
|
||||
Platform platform();
|
||||
PlatformFlavor platform_flavor();
|
||||
|
||||
std::string platform_os_type();
|
||||
std::string platform_architecture();
|
||||
std::string platform_to_string(Platform platform);
|
||||
std::string platform_flavor_to_string(PlatformFlavor pf);
|
||||
|
||||
|
||||
@@ -1201,6 +1201,9 @@ static std::vector<std::string> s_Preset_print_options{
|
||||
"min_feature_size",
|
||||
"min_bead_width",
|
||||
"post_process",
|
||||
"slicing_pipeline_plugin",
|
||||
"plugins",
|
||||
"plugin_config_overrides",
|
||||
"process_change_extrusion_role_gcode",
|
||||
"min_length_factor",
|
||||
"wall_maximum_resolution",
|
||||
@@ -1375,6 +1378,7 @@ static std::vector<std::string> s_Preset_filament_options {/*"filament_colour",
|
||||
"filament_preheat_temperature_delta", "filament_retract_length_nc",
|
||||
"filament_change_length_nc", "filament_prime_volume_nc",
|
||||
"long_retractions_when_ec", "retraction_distances_when_ec",
|
||||
"plugin_config_overrides",
|
||||
//ams chamber
|
||||
"filament_dev_ams_drying_ams_limitations", "filament_dev_ams_drying_temperature", "filament_dev_ams_drying_time", "filament_dev_ams_drying_heat_distortion_temperature",
|
||||
"filament_dev_chamber_drying_bed_temperature", "filament_dev_chamber_drying_time",
|
||||
@@ -1425,7 +1429,8 @@ static std::vector<std::string> s_Preset_printer_options {
|
||||
"machine_hotend_change_time", "machine_prepare_compensation_time",
|
||||
// Fast-purge printer flag + device/firmware-facing per-variant extruder-change
|
||||
// deretraction speed (unconsumed by the slicer; carried by H2D/A2L/X2D/P2S machine profiles).
|
||||
"support_fast_purge_mode", "deretract_speed_extruder_change"
|
||||
"support_fast_purge_mode", "deretract_speed_extruder_change",
|
||||
"plugin_config_overrides"
|
||||
};
|
||||
|
||||
static std::vector<std::string> s_Preset_sla_print_options {
|
||||
@@ -3453,7 +3458,18 @@ void add_correct_opts_to_diff(const std::string &opt_key, t_config_option_keys&
|
||||
|
||||
for (int i = 0; i < int(opt_cur->values.size()); i++)
|
||||
{
|
||||
int init_id = i <= opt_init_max_id ? i : 0;
|
||||
const bool is_new_index = i > opt_init_max_id;
|
||||
int init_id = is_new_index ? 0 : i;
|
||||
if (is_new_index) {
|
||||
// Orca: intentional divergence from upstream. Any new vector index (at or
|
||||
// beyond the reference vector's length) is flagged dirty unconditionally --
|
||||
// independent of its value and nil-state -- so preset dirty-detection notices
|
||||
// per-extruder/filament entries added by growth (e.g. extruder count). This
|
||||
// applies to every vector option type routed through deep_diff().
|
||||
// Covered by tests/libslic3r/test_preset_diff.cpp.
|
||||
vec.emplace_back(opt_key + "#" + std::to_string(i));
|
||||
continue;
|
||||
}
|
||||
if (opt_cur->values[i] != opt_init->values[init_id]) {
|
||||
if (opt_cur->nullable()) {
|
||||
if (opt_cur->is_nil(i)) {
|
||||
@@ -3488,7 +3504,7 @@ inline t_config_option_keys deep_diff(const ConfigBase &config_this, const Confi
|
||||
if (this_opt != nullptr && other_opt != nullptr && *this_opt != *other_opt)
|
||||
{
|
||||
//BBS: add bed_exclude_area
|
||||
if (opt_key == "printable_area" || opt_key == "bed_exclude_area" || opt_key == "compatible_prints" || opt_key == "compatible_printers" || opt_key == "thumbnails" || opt_key == "wrapping_exclude_area") {
|
||||
if (opt_key == "printable_area" || opt_key == "bed_exclude_area" || opt_key == "compatible_prints" || opt_key == "compatible_printers" || opt_key == "thumbnails" || opt_key == "wrapping_exclude_area" || opt_key == "slicing_pipeline_plugin") {
|
||||
// Scalar variable, or a vector variable, which is independent from number of extruders,
|
||||
// thus the vector is presented to the user as a single input.
|
||||
diff.emplace_back(opt_key);
|
||||
|
||||
+175
-90
@@ -51,6 +51,8 @@ using namespace nlohmann;
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
Print::SlicingPipelineHookFn Print::s_slicing_pipeline_hook_fn = nullptr;
|
||||
|
||||
template class PrintState<PrintStep, psCount>;
|
||||
template class PrintState<PrintObjectStep, posCount>;
|
||||
|
||||
@@ -124,6 +126,9 @@ bool Print::invalidate_state_by_config_options(const ConfigOptionResolver & /* n
|
||||
"printing_by_object_gcode",
|
||||
"filament_end_gcode",
|
||||
"post_process",
|
||||
// "plugins" is the derived manifest backing the plugin-picker options; on its own it only
|
||||
// affects G-code export. The specific option (e.g. slicing_pipeline_plugin) drives any re-slice.
|
||||
"plugins",
|
||||
"extruder_clearance_height_to_rod",
|
||||
"extruder_clearance_height_to_lid",
|
||||
"extruder_clearance_radius",
|
||||
@@ -279,7 +284,8 @@ bool Print::invalidate_state_by_config_options(const ConfigOptionResolver & /* n
|
||||
|| opt_key == "wipe_tower_rotation_angle") {
|
||||
steps.emplace_back(psSkirtBrim);
|
||||
} else if (
|
||||
opt_key == "initial_layer_print_height"
|
||||
opt_key == "slicing_pipeline_plugin"
|
||||
|| opt_key == "initial_layer_print_height"
|
||||
|| opt_key == "nozzle_diameter"
|
||||
|| opt_key == "filament_shrink"
|
||||
|| opt_key == "filament_shrinkage_compensation_z"
|
||||
@@ -2207,6 +2213,11 @@ void Print::process(long long *time_cost_with_cache, bool use_cache)
|
||||
if (time_cost_with_cache)
|
||||
*time_cost_with_cache = 0;
|
||||
|
||||
{
|
||||
const auto* sp = this->config().option<ConfigOptionStrings>("slicing_pipeline_plugin");
|
||||
m_pipeline_plugin_active = s_slicing_pipeline_hook_fn && sp && !sp->values.empty();
|
||||
}
|
||||
|
||||
name_tbb_thread_pool_threads_set_locale();
|
||||
|
||||
//compute the PrintObject with the same geometries
|
||||
@@ -2316,20 +2327,47 @@ void Print::process(long long *time_cost_with_cache, bool use_cache)
|
||||
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(": total object counts %1% in current print, need to slice %2%")%m_objects.size()%need_slicing_objects.size();
|
||||
BOOST_LOG_TRIVIAL(info) << "Starting the slicing process." << log_memory_info();
|
||||
if (!use_cache) {
|
||||
// Fire the SlicingPipeline hook for `obj` iff it just (re)computed `pstep` this pass.
|
||||
auto hook_after = [this](PrintObject* obj, bool was_done, PrintObjectStep pstep, SlicingPipelineStepPlugin sstep) {
|
||||
if (m_pipeline_plugin_active && !was_done && obj->is_step_done(pstep))
|
||||
run_pipeline_hook(sstep, obj);
|
||||
};
|
||||
|
||||
// SlicingPipeline: dedicated slice loop so the Slice boundary is hookable before perimeters.
|
||||
for (PrintObject *obj : m_objects) {
|
||||
if (need_slicing_objects.count(obj) != 0) {
|
||||
obj->make_perimeters();
|
||||
}
|
||||
else {
|
||||
if (obj->set_started(posSlice))
|
||||
obj->set_done(posSlice);
|
||||
if (obj->set_started(posPerimeters))
|
||||
obj->set_done(posPerimeters);
|
||||
const bool was_done = obj->is_step_done(posSlice);
|
||||
obj->slice();
|
||||
hook_after(obj, was_done, posSlice, SlicingPipelineStepPlugin::posSlice);
|
||||
// re-snapshot each layer's raw_slices AFTER the Slice hook ran, so the
|
||||
// plugin's mutation becomes the untyped baseline. Without this, a later
|
||||
// perimeter-only re-run (make_perimeters -> restore_untyped_slices) reverts
|
||||
// slices to the PRE-hook geometry while posSlice stays cached (the hook does
|
||||
// not re-fire), silently un-applying the mutation; raw_slices consumers
|
||||
// (sharp-tail support, ToolOrdering) also read this backup directly. Gated on
|
||||
// an active plugin AND a genuine (re)slice, so the inactive path is untouched
|
||||
// and re-backing-up an unmutated layer is a harmless identical copy.
|
||||
if (m_pipeline_plugin_active && !was_done && obj->is_step_done(posSlice))
|
||||
for (Layer *layer : obj->layers())
|
||||
layer->backup_untyped_slices();
|
||||
} else {
|
||||
if (obj->set_started(posSlice)) obj->set_done(posSlice); // shared/duplicate — no hook
|
||||
}
|
||||
}
|
||||
for (PrintObject *obj : m_objects) {
|
||||
if (need_slicing_objects.count(obj) != 0) {
|
||||
const bool was_done = obj->is_step_done(posPerimeters);
|
||||
obj->make_perimeters(); // slice() inside is a no-op: posSlice already DONE
|
||||
hook_after(obj, was_done, posPerimeters, SlicingPipelineStepPlugin::posPerimeters);
|
||||
} else {
|
||||
if (obj->set_started(posPerimeters)) obj->set_done(posPerimeters);
|
||||
}
|
||||
}
|
||||
for (PrintObject *obj : m_objects) {
|
||||
if (need_slicing_objects.count(obj) != 0) {
|
||||
const bool was_done = obj->is_step_done(posEstimateCurledExtrusions);
|
||||
obj->estimate_curled_extrusions();
|
||||
hook_after(obj, was_done, posEstimateCurledExtrusions, SlicingPipelineStepPlugin::posEstimateCurledExtrusions);
|
||||
}
|
||||
else {
|
||||
if (obj->set_started(posEstimateCurledExtrusions))
|
||||
@@ -2338,7 +2376,17 @@ void Print::process(long long *time_cost_with_cache, bool use_cache)
|
||||
}
|
||||
for (PrintObject *obj : m_objects) {
|
||||
if (need_slicing_objects.count(obj) != 0) {
|
||||
// split prepare_infill (fill-surface prep) from infill (make_fills) so a
|
||||
// plugin can mutate fill surfaces at the PrepareInfill seam and have make_fills
|
||||
// consume them (unlike the Infill seam, which fires after the fills are already
|
||||
// built). infill() re-invokes prepare_infill() as a no-op once posPrepareInfill
|
||||
// is DONE, so this is a mechanical split mirroring the slice/perimeters loop.
|
||||
const bool prepare_was_done = obj->is_step_done(posPrepareInfill);
|
||||
obj->prepare_infill();
|
||||
hook_after(obj, prepare_was_done, posPrepareInfill, SlicingPipelineStepPlugin::posPrepareInfill);
|
||||
const bool was_done = obj->is_step_done(posInfill);
|
||||
obj->infill();
|
||||
hook_after(obj, was_done, posInfill, SlicingPipelineStepPlugin::posInfill);
|
||||
}
|
||||
else {
|
||||
if (obj->set_started(posPrepareInfill))
|
||||
@@ -2349,7 +2397,9 @@ void Print::process(long long *time_cost_with_cache, bool use_cache)
|
||||
}
|
||||
for (PrintObject *obj : m_objects) {
|
||||
if (need_slicing_objects.count(obj) != 0) {
|
||||
const bool was_done = obj->is_step_done(posIroning);
|
||||
obj->ironing();
|
||||
hook_after(obj, was_done, posIroning, SlicingPipelineStepPlugin::posIroning);
|
||||
}
|
||||
else {
|
||||
if (obj->set_started(posIroning))
|
||||
@@ -2361,13 +2411,22 @@ void Print::process(long long *time_cost_with_cache, bool use_cache)
|
||||
for (PrintObject *obj : m_objects) {
|
||||
bool need_contouring = need_slicing_objects.count(obj) != 0 && obj->need_z_contouring();
|
||||
if (need_contouring) {
|
||||
const bool was_done = obj->is_step_done(posContouring);
|
||||
obj->contour_z();
|
||||
hook_after(obj, was_done, posContouring, SlicingPipelineStepPlugin::posContouring);
|
||||
} else {
|
||||
if (obj->set_started(posContouring))
|
||||
obj->set_done(posContouring);
|
||||
}
|
||||
}
|
||||
|
||||
// SlicingPipeline: support runs in the parallel block below; the hook must fire in a
|
||||
// sequential loop afterward. Snapshot per-object done-state just before the parallel_for.
|
||||
std::vector<char> sup_was_done(m_objects.size(), 1);
|
||||
if (m_pipeline_plugin_active)
|
||||
for (size_t i = 0; i < m_objects.size(); ++i)
|
||||
sup_was_done[i] = m_objects[i]->is_step_done(posSupportMaterial) ? 1 : 0;
|
||||
|
||||
tbb::parallel_for(tbb::blocked_range<int>(0, int(m_objects.size())),
|
||||
[this, need_slicing_objects](const tbb::blocked_range<int>& range) {
|
||||
for (int i = range.begin(); i < range.end(); i++) {
|
||||
@@ -2383,9 +2442,17 @@ void Print::process(long long *time_cost_with_cache, bool use_cache)
|
||||
}
|
||||
);
|
||||
|
||||
if (m_pipeline_plugin_active)
|
||||
for (size_t i = 0; i < m_objects.size(); ++i)
|
||||
if (need_slicing_objects.count(m_objects[i]) != 0 && !sup_was_done[i]
|
||||
&& m_objects[i]->is_step_done(posSupportMaterial))
|
||||
run_pipeline_hook(SlicingPipelineStepPlugin::posSupportMaterial, m_objects[i]);
|
||||
|
||||
for (PrintObject* obj : m_objects) {
|
||||
if (need_slicing_objects.count(obj) != 0) {
|
||||
const bool was_done = obj->is_step_done(posDetectOverhangsForLift);
|
||||
obj->detect_overhangs_for_lift();
|
||||
hook_after(obj, was_done, posDetectOverhangsForLift, SlicingPipelineStepPlugin::posDetectOverhangsForLift);
|
||||
}
|
||||
else {
|
||||
if (obj->set_started(posDetectOverhangsForLift))
|
||||
@@ -2462,6 +2529,7 @@ void Print::process(long long *time_cost_with_cache, bool use_cache)
|
||||
|
||||
}
|
||||
this->set_done(psWipeTower);
|
||||
if (m_pipeline_plugin_active) run_pipeline_hook(SlicingPipelineStepPlugin::psWipeTower, nullptr);
|
||||
}
|
||||
|
||||
if (this->has_wipe_tower()) {
|
||||
@@ -2478,8 +2546,7 @@ void Print::process(long long *time_cost_with_cache, bool use_cache)
|
||||
m_skirt_brim_groups.clear();
|
||||
m_has_shared_per_object_skirt = false;
|
||||
m_skirt_convex_hull.clear();
|
||||
m_objectBrimAreas.clear();
|
||||
m_supportBrimAreas.clear();
|
||||
m_objectBrimAreasByInstance.clear();
|
||||
m_first_layer_convex_hull.points.clear();
|
||||
for (PrintObject *object : m_objects) object->m_skirt.clear();
|
||||
|
||||
@@ -2635,14 +2702,16 @@ void Print::process(long long *time_cost_with_cache, bool use_cache)
|
||||
if (!existObject && objectExtruderMap.find(print_object_ID) != objectExtruderMap.end())
|
||||
objPrintVec.push_back(std::make_pair(print_object_ID, objectExtruderMap.at(print_object_ID)));
|
||||
}
|
||||
// BBS: m_brimMap and m_supportBrimMap are used instead of m_brim to generate brim of objs and supports seperately
|
||||
// Orca: Build both the old object-keyed brim map and the per-instance
|
||||
// maps used by skirt/brim groups.
|
||||
m_brimMap.clear();
|
||||
m_supportBrimMap.clear();
|
||||
m_brimMapByInstance.clear();
|
||||
m_first_layer_convex_hull.points.clear();
|
||||
if (this->has_brim()) {
|
||||
Polygons islands_area;
|
||||
make_brim(*this, this->make_try_cancel(), islands_area, m_brimMap,
|
||||
m_supportBrimMap, objPrintVec, printExtruders, &m_objectBrimAreas, &m_supportBrimAreas);
|
||||
m_brimMapByInstance, objPrintVec, printExtruders,
|
||||
&m_objectBrimAreasByInstance);
|
||||
for (Polygon& poly_ex : islands_area)
|
||||
poly_ex.douglas_peucker(SCALED_RESOLUTION);
|
||||
for (Polygon &poly : union_(this->first_layer_islands(), islands_area))
|
||||
@@ -2664,6 +2733,7 @@ void Print::process(long long *time_cost_with_cache, bool use_cache)
|
||||
|
||||
this->finalize_first_layer_convex_hull();
|
||||
this->set_done(psSkirtBrim);
|
||||
if (m_pipeline_plugin_active) run_pipeline_hook(SlicingPipelineStepPlugin::psSkirtBrim, nullptr);
|
||||
|
||||
if (time_cost_with_cache) {
|
||||
end_time = (long long)Slic3r::Utils::get_current_time_utc();
|
||||
@@ -2674,7 +2744,13 @@ void Print::process(long long *time_cost_with_cache, bool use_cache)
|
||||
for (PrintObject *obj : m_objects) {
|
||||
if (((!use_cache)&&(need_slicing_objects.count(obj) != 0))
|
||||
|| (use_cache &&(re_slicing_objects.count(obj) != 0))){
|
||||
const bool was_done = obj->is_step_done(posSimplifyPath);
|
||||
obj->simplify_extrusion_path();
|
||||
// Unlike every other seam (all inside the `if (!use_cache)` block above), this loop is
|
||||
// shared with the use_cache path (re_slicing_objects), so `!use_cache` must be checked
|
||||
// explicitly here to keep hooks from ever firing on cache-loaded (plugin-final) objects.
|
||||
if (!use_cache && m_pipeline_plugin_active && !was_done && obj->is_step_done(posSimplifyPath))
|
||||
run_pipeline_hook(SlicingPipelineStepPlugin::posSimplifyPath, obj);
|
||||
}
|
||||
else {
|
||||
if (obj->set_started(posSimplifyPath))
|
||||
@@ -2784,7 +2860,8 @@ void Print::_make_skirt()
|
||||
Polygon hull;
|
||||
};
|
||||
|
||||
// Orca: build one local occupied hull per object from object and support geometry up to skirt height.
|
||||
// Orca: Build one local occupied hull per object from object/support
|
||||
// geometry up to skirt height. Instances translate this hull later.
|
||||
std::vector<ObjectSkirtHull> object_convex_hulls;
|
||||
for (PrintObject *object : m_objects) {
|
||||
Points object_points;
|
||||
@@ -2894,49 +2971,49 @@ void Print::_make_skirt()
|
||||
object_hull.object->m_skirt.clear();
|
||||
|
||||
if (m_config.skirt_type == stCombined || m_config.skirt_type == stPerObject) {
|
||||
struct SkirtGroupItem {
|
||||
struct SkirtBrimGroupItem {
|
||||
Points occupied_points;
|
||||
ObjectID object_id;
|
||||
size_t instance_id;
|
||||
bool emits_skirt;
|
||||
};
|
||||
|
||||
// Orca: group items represent occupied first-layer areas. Object items emit skirts;
|
||||
// obstacle-only items, such as wipe tower, only force nearby object groups to merge.
|
||||
std::vector<SkirtGroupItem> group_items;
|
||||
// Orca: Each object instance can emit skirt/brim. Wipe tower is only an
|
||||
// obstacle here; it may merge nearby items, but does not emit anything.
|
||||
std::vector<SkirtBrimGroupItem> group_items;
|
||||
const coord_t grouping_offset = scale_(m_config.skirt_distance.value + m_config.skirt_loops.value * spacing);
|
||||
for (const ObjectSkirtHull& object_hull : object_convex_hulls) {
|
||||
PrintObject* object = object_hull.object;
|
||||
Points occupied_points;
|
||||
for (const PrintInstance &instance : object->instances()) {
|
||||
std::vector<size_t> object_item_indices;
|
||||
object_item_indices.reserve(object->instances().size());
|
||||
for (size_t instance_idx = 0; instance_idx < object->instances().size(); ++instance_idx) {
|
||||
const PrintInstance &instance = object->instances()[instance_idx];
|
||||
Points copy_points = object_hull.hull.points;
|
||||
for (Point &pt : copy_points)
|
||||
pt += instance.shift;
|
||||
append(occupied_points, copy_points);
|
||||
if (copy_points.size() < 3)
|
||||
continue;
|
||||
|
||||
object_item_indices.push_back(group_items.size());
|
||||
group_items.push_back({ std::move(copy_points), object->id(), instance_idx, true });
|
||||
}
|
||||
|
||||
auto append_brim_points = [&occupied_points](const ExPolygons& areas) {
|
||||
for (const ExPolygon& area : areas)
|
||||
append(occupied_points, area.contour.points);
|
||||
};
|
||||
if (auto it = m_objectBrimAreas.find(object->id()); it != m_objectBrimAreas.end())
|
||||
append_brim_points(it->second);
|
||||
if (auto it = m_supportBrimAreas.find(object->id()); it != m_supportBrimAreas.end())
|
||||
append_brim_points(it->second);
|
||||
if (occupied_points.size() < 3)
|
||||
continue;
|
||||
|
||||
// Orca: include the object's brim/support-brim footprint before checking skirt collisions.
|
||||
group_items.push_back({ std::move(occupied_points), object->id(), true });
|
||||
for (size_t item_idx : object_item_indices) {
|
||||
const ObjectInstanceID key{ object->id(), group_items[item_idx].instance_id };
|
||||
if (auto instance_brim_it = m_objectBrimAreasByInstance.find(key); instance_brim_it != m_objectBrimAreasByInstance.end())
|
||||
for (const ExPolygon& area : instance_brim_it->second)
|
||||
append(group_items[item_idx].occupied_points, area.contour.points);
|
||||
}
|
||||
}
|
||||
|
||||
// Orca: the wipe tower contributes occupied area, but does not emit a skirt by itself.
|
||||
Points wipe_tower_points = this->first_layer_wipe_tower_corners();
|
||||
if (wipe_tower_points.size() >= 3)
|
||||
group_items.push_back({ std::move(wipe_tower_points), ObjectID(), false });
|
||||
group_items.push_back({ std::move(wipe_tower_points), ObjectID(), size_t(-1), false });
|
||||
|
||||
std::vector<size_t> parent(group_items.size());
|
||||
std::iota(parent.begin(), parent.end(), 0);
|
||||
// Orca: union-find keeps collision merging local without repeatedly rebuilding item lists.
|
||||
// Orca: Use union-find so touching items can be merged while scanning.
|
||||
auto find_parent = [&parent](size_t idx) {
|
||||
while (parent[idx] != idx) {
|
||||
parent[idx] = parent[parent[idx]];
|
||||
@@ -2951,24 +3028,24 @@ void Print::_make_skirt()
|
||||
parent[b] = a;
|
||||
};
|
||||
|
||||
// Orca: combined skirt is the same grouping model with all items forced into one group.
|
||||
// Orca: Combined skirt starts with all items in the same group.
|
||||
if (m_config.skirt_type == stCombined && !group_items.empty())
|
||||
for (size_t i = 1; i < group_items.size(); ++i)
|
||||
unite(0, i);
|
||||
|
||||
auto build_grouped_points = [&]() {
|
||||
struct GroupData {
|
||||
Points points;
|
||||
std::vector<ObjectID> object_ids;
|
||||
bool emits_skirt = false;
|
||||
auto build_skirt_brim_groups = [&]() {
|
||||
struct SkirtBrimGroupData {
|
||||
Points points;
|
||||
std::vector<ObjectInstanceID> instances;
|
||||
bool emits_skirt = false;
|
||||
};
|
||||
|
||||
std::map<size_t, GroupData> grouped;
|
||||
std::map<size_t, SkirtBrimGroupData> grouped;
|
||||
for (size_t i = 0; i < group_items.size(); ++i) {
|
||||
GroupData& group = grouped[find_parent(i)];
|
||||
SkirtBrimGroupData& group = grouped[find_parent(i)];
|
||||
append(group.points, group_items[i].occupied_points);
|
||||
if (group_items[i].object_id.valid())
|
||||
group.object_ids.push_back(group_items[i].object_id);
|
||||
group.instances.push_back({ group_items[i].object_id, group_items[i].instance_id });
|
||||
group.emits_skirt = group.emits_skirt || group_items[i].emits_skirt;
|
||||
}
|
||||
return grouped;
|
||||
@@ -2977,16 +3054,18 @@ void Print::_make_skirt()
|
||||
bool groups_changed = m_config.skirt_type == stPerObject;
|
||||
while (groups_changed) {
|
||||
groups_changed = false;
|
||||
auto grouped_points = build_grouped_points();
|
||||
auto grouped_points = build_skirt_brim_groups();
|
||||
std::vector<std::pair<size_t, Polygon>> group_envelopes;
|
||||
for (const auto& [root, group] : grouped_points) {
|
||||
if (group.points.size() < 3)
|
||||
continue;
|
||||
|
||||
// Orca: emitting groups are expanded to their final skirt reach; obstacle groups are not.
|
||||
// Orca: Only skirt-emitting groups are expanded by skirt distance;
|
||||
// obstacle-only groups stay at their occupied outline.
|
||||
Polygon envelope = Geometry::convex_hull(group.points);
|
||||
if (group.emits_skirt) {
|
||||
// Orca: merge groups when a skirt envelope intersects another group or obstacle.
|
||||
// Orca: If the expanded skirt outline touches another group
|
||||
// or obstacle, merge them and run the pass again.
|
||||
Polygons envelopes = offset(envelope, grouping_offset, ClipperLib::jtRound, float(scale_(0.1)));
|
||||
if (envelopes.empty())
|
||||
continue;
|
||||
@@ -3007,28 +3086,23 @@ void Print::_make_skirt()
|
||||
}
|
||||
}
|
||||
|
||||
auto make_group_brims = [this](const std::vector<ObjectID>& group_object_ids) {
|
||||
auto make_brims_for_skirt_brim_group = [this](const std::vector<ObjectInstanceID>& group_instances) {
|
||||
std::vector<SkirtBrimGroup::Brim> brims;
|
||||
std::vector<ObjectID> brim_object_ids;
|
||||
for (ObjectID object_id : group_object_ids) {
|
||||
const auto brim_it = m_brimMap.find(object_id);
|
||||
if (brim_it != m_brimMap.end() && !brim_it->second.empty())
|
||||
brim_object_ids.push_back(object_id);
|
||||
std::vector<ObjectInstanceID> brim_instances;
|
||||
for (const ObjectInstanceID& instance : group_instances) {
|
||||
const auto brim_it = m_brimMapByInstance.find(instance);
|
||||
if (brim_it != m_brimMapByInstance.end() && !brim_it->second.empty())
|
||||
brim_instances.push_back(instance);
|
||||
}
|
||||
|
||||
const bool global_combined_brim = m_config.combine_brims && m_config.skirt_type != stPerObject && m_brimMap.size() == 1;
|
||||
auto brim_owner_ids = [&group_object_ids, global_combined_brim](const std::vector<ObjectID>& object_ids) {
|
||||
return global_combined_brim ? group_object_ids : object_ids;
|
||||
};
|
||||
|
||||
const bool combine_group_brims = m_config.combine_brims && brim_object_ids.size() > 1;
|
||||
const bool combine_group_brims = m_config.combine_brims && brim_instances.size() > 1;
|
||||
if (!combine_group_brims) {
|
||||
for (ObjectID object_id : brim_object_ids)
|
||||
brims.push_back({ m_brimMap.at(object_id), brim_owner_ids({ object_id }) });
|
||||
for (const ObjectInstanceID& instance : brim_instances)
|
||||
brims.push_back({ m_brimMapByInstance.at(instance), { instance } });
|
||||
return brims;
|
||||
}
|
||||
|
||||
std::vector<size_t> brim_parent(brim_object_ids.size());
|
||||
std::vector<size_t> brim_parent(brim_instances.size());
|
||||
std::iota(brim_parent.begin(), brim_parent.end(), 0);
|
||||
auto find_brim_parent = [&brim_parent](size_t idx) {
|
||||
while (brim_parent[idx] != idx) {
|
||||
@@ -3045,61 +3119,64 @@ void Print::_make_skirt()
|
||||
};
|
||||
|
||||
const coord_t brim_contact_distance = coord_t(brim_flow().scaled_spacing() * 2.);
|
||||
for (size_t i = 0; i < brim_object_ids.size(); ++i) {
|
||||
const auto area_i = m_objectBrimAreas.find(brim_object_ids[i]);
|
||||
if (area_i == m_objectBrimAreas.end())
|
||||
for (size_t i = 0; i < brim_instances.size(); ++i) {
|
||||
const auto area_i = m_objectBrimAreasByInstance.find(brim_instances[i]);
|
||||
if (area_i == m_objectBrimAreasByInstance.end())
|
||||
continue;
|
||||
for (size_t j = i + 1; j < brim_object_ids.size(); ++j) {
|
||||
const auto area_j = m_objectBrimAreas.find(brim_object_ids[j]);
|
||||
if (area_j != m_objectBrimAreas.end() &&
|
||||
for (size_t j = i + 1; j < brim_instances.size(); ++j) {
|
||||
const auto area_j = m_objectBrimAreasByInstance.find(brim_instances[j]);
|
||||
if (area_j != m_objectBrimAreasByInstance.end() &&
|
||||
!intersection_ex(offset_ex(area_i->second, brim_contact_distance, jtRound, SCALED_RESOLUTION), area_j->second).empty())
|
||||
unite_brims(i, j);
|
||||
}
|
||||
}
|
||||
|
||||
std::map<size_t, std::vector<ObjectID>> combined_brim_ids;
|
||||
for (size_t i = 0; i < brim_object_ids.size(); ++i)
|
||||
combined_brim_ids[find_brim_parent(i)].push_back(brim_object_ids[i]);
|
||||
std::map<size_t, std::vector<ObjectInstanceID>> combined_brim_ids;
|
||||
for (size_t i = 0; i < brim_instances.size(); ++i)
|
||||
combined_brim_ids[find_brim_parent(i)].push_back(brim_instances[i]);
|
||||
|
||||
for (const auto& [_, object_ids] : combined_brim_ids) {
|
||||
if (object_ids.size() == 1) {
|
||||
brims.push_back({ m_brimMap.at(object_ids.front()), brim_owner_ids(object_ids) });
|
||||
for (const auto& [_, instances] : combined_brim_ids) {
|
||||
if (instances.size() == 1) {
|
||||
const ObjectInstanceID& instance = instances.front();
|
||||
brims.push_back({ m_brimMapByInstance.at(instance), { instance } });
|
||||
continue;
|
||||
}
|
||||
|
||||
ExPolygons combined_area;
|
||||
for (ObjectID object_id : object_ids)
|
||||
expolygons_append(combined_area, m_objectBrimAreas.at(object_id));
|
||||
for (const ObjectInstanceID& instance : instances)
|
||||
expolygons_append(combined_area, m_objectBrimAreasByInstance.at(instance));
|
||||
combined_area = union_ex(combined_area);
|
||||
const float scaled_resolution = float(scaled(m_config.resolution.value));
|
||||
const float brim_cleanup_delta = std::max(scaled_resolution, float(SCALED_EPSILON));
|
||||
combined_area = offset2_ex(combined_area, brim_cleanup_delta, -brim_cleanup_delta, jtRound, scaled_resolution);
|
||||
|
||||
Polygons islands_area;
|
||||
brims.push_back({ makeBrimInfillFromPlateCoordinates(combined_area, *this, islands_area), object_ids });
|
||||
brims.push_back({ makeBrimInfillFromPlateCoordinates(combined_area, *this, islands_area), instances });
|
||||
}
|
||||
|
||||
return brims;
|
||||
};
|
||||
|
||||
auto grouped_points = build_grouped_points();
|
||||
auto grouped_points = build_skirt_brim_groups();
|
||||
for (auto& [_, group] : grouped_points) {
|
||||
if (!group.emits_skirt || group.points.size() < 3)
|
||||
continue;
|
||||
if (generate_skirt && m_config.skirt_type == stPerObject && group.object_ids.size() > 1)
|
||||
if (generate_skirt && m_config.skirt_type == stPerObject && group.instances.size() > 1)
|
||||
m_has_shared_per_object_skirt = true;
|
||||
// Orca: after merging, use the occupied outline directly; do not add skirt distance twice.
|
||||
// Orca: Group points already include the occupied outline, so don't
|
||||
// add skirt distance here again.
|
||||
ExtrusionEntityCollection group_skirt;
|
||||
if (generate_skirt)
|
||||
append_skirt_loops_for_hull(Geometry::convex_hull(group.points), group_skirt, true);
|
||||
std::vector<SkirtBrimGroup::Brim> group_brims = make_group_brims(group.object_ids);
|
||||
std::vector<SkirtBrimGroup::Brim> group_brims = make_brims_for_skirt_brim_group(group.instances);
|
||||
if (!group_skirt.empty()) {
|
||||
group_skirt.reverse();
|
||||
// Orca: keep m_skirt as a flattened compatibility mirror for preview/extents.
|
||||
// Orca: Keep m_skirt filled for code that still reads a flat
|
||||
// skirt collection.
|
||||
m_skirt.append(group_skirt.entities);
|
||||
}
|
||||
if (!group_skirt.empty() || !group_brims.empty())
|
||||
m_skirt_brim_groups.push_back({ std::move(group_skirt), std::move(group.object_ids), std::move(group_brims) });
|
||||
m_skirt_brim_groups.push_back({ std::move(group_skirt), std::move(group.instances), std::move(group_brims) });
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -4049,6 +4126,7 @@ void Print::_make_wipe_tower()
|
||||
|
||||
// Unload the current filament over the purge tower.
|
||||
coordf_t layer_height = m_objects.front()->config().layer_height.value;
|
||||
bool generate_final_purge = true;
|
||||
if (m_wipe_tower_data.tool_ordering.back().wipe_tower_partitions > 0) {
|
||||
// The wipe tower goes up to the last layer of the print.
|
||||
if (wipe_tower.layer_finished()) {
|
||||
@@ -4060,11 +4138,14 @@ void Print::_make_wipe_tower()
|
||||
// There is yet enough space at this layer of the wipe tower for the final purge.
|
||||
}
|
||||
} else {
|
||||
// The wipe tower does not reach the last print layer, perform the pruge at the last print layer.
|
||||
// The wipe tower does not reach the last print layer.
|
||||
// Skip final purge to avoid generating purge lines in mid-air.
|
||||
assert(m_wipe_tower_data.tool_ordering.back().wipe_tower_partitions == 0);
|
||||
wipe_tower.set_layer(float(m_wipe_tower_data.tool_ordering.back().print_z), float(layer_height), 0, false, true);
|
||||
generate_final_purge = false;
|
||||
}
|
||||
m_wipe_tower_data.final_purge = Slic3r::make_unique<WipeTower::ToolChangeResult>(wipe_tower.tool_change((unsigned int) (-1)));
|
||||
m_wipe_tower_data.final_purge = generate_final_purge
|
||||
? Slic3r::make_unique<WipeTower::ToolChangeResult>(wipe_tower.tool_change((unsigned int)(-1)))
|
||||
: Slic3r::make_unique<WipeTower::ToolChangeResult>();
|
||||
|
||||
m_wipe_tower_data.used_filament = wipe_tower.get_used_filament();
|
||||
m_wipe_tower_data.number_of_toolchanges = wipe_tower.get_number_of_toolchanges();
|
||||
@@ -4160,6 +4241,7 @@ void Print::_make_wipe_tower()
|
||||
|
||||
// Unload the current filament over the purge tower.
|
||||
coordf_t layer_height = m_objects.front()->config().layer_height.value;
|
||||
bool generate_final_purge = true;
|
||||
if (m_wipe_tower_data.tool_ordering.back().wipe_tower_partitions > 0) {
|
||||
// The wipe tower goes up to the last layer of the print.
|
||||
if (wipe_tower.layer_finished()) {
|
||||
@@ -4171,11 +4253,14 @@ void Print::_make_wipe_tower()
|
||||
// There is yet enough space at this layer of the wipe tower for the final purge.
|
||||
}
|
||||
} else {
|
||||
// The wipe tower does not reach the last print layer, perform the pruge at the last print layer.
|
||||
// The wipe tower does not reach the last print layer.
|
||||
// Skip final purge to avoid generating purge lines in mid-air.
|
||||
assert(m_wipe_tower_data.tool_ordering.back().wipe_tower_partitions == 0);
|
||||
wipe_tower.set_layer(float(m_wipe_tower_data.tool_ordering.back().print_z), float(layer_height), 0, false, true);
|
||||
generate_final_purge = false;
|
||||
}
|
||||
m_wipe_tower_data.final_purge = Slic3r::make_unique<WipeTower::ToolChangeResult>(wipe_tower.tool_change((unsigned int) (-1)));
|
||||
m_wipe_tower_data.final_purge = generate_final_purge
|
||||
? Slic3r::make_unique<WipeTower::ToolChangeResult>(wipe_tower.tool_change((unsigned int)(-1)))
|
||||
: Slic3r::make_unique<WipeTower::ToolChangeResult>();
|
||||
|
||||
m_wipe_tower_data.used_filament = wipe_tower.get_used_filament();
|
||||
m_wipe_tower_data.number_of_toolchanges = wipe_tower.get_number_of_toolchanges();
|
||||
|
||||
+29
-8
@@ -17,6 +17,7 @@
|
||||
#include "GCode/ThumbnailData.hpp"
|
||||
#include "GCode/GCodeProcessor.hpp"
|
||||
#include "MultiMaterialSegmentation.hpp"
|
||||
#include "ObjectID.hpp"
|
||||
#include "libslic3r.h"
|
||||
|
||||
#include <Eigen/Geometry>
|
||||
@@ -101,6 +102,14 @@ enum PrintObjectStep {
|
||||
posCount,
|
||||
};
|
||||
|
||||
enum class SlicingPipelineStepPlugin {
|
||||
posSlice, posPerimeters, posEstimateCurledExtrusions, posPrepareInfill, posInfill, posIroning, posContouring,
|
||||
posSupportMaterial, posDetectOverhangsForLift, posSimplifyPath, psWipeTower, psSkirtBrim,
|
||||
// Fires from the GUI G-code export/post-process seam (PostProcessor.cpp), NOT from Print::process().
|
||||
// At this step the plugin edits the exported G-code file in place; see SlicingPipelinePluginCapability for the full contract.
|
||||
psGCodePostProcess
|
||||
};
|
||||
|
||||
// A PrintRegion object represents a group of volumes to print
|
||||
// sharing the same config (including the same assigned extruder(s))
|
||||
class PrintRegion
|
||||
@@ -894,6 +903,11 @@ private: // Prevents erroneous use by other classes.
|
||||
typedef std::pair<PrintObject *, bool> PrintObjectInfo;
|
||||
|
||||
public:
|
||||
using SlicingPipelineHookFn = std::function<void(Print&, const PrintObject*, SlicingPipelineStepPlugin)>;
|
||||
// Cross-layer injection (mirrors ConfigBase::set_resolve_capability_fn): the GUI/plugin
|
||||
// layer registers a dispatcher; libslic3r stays free of any plugin/Python dependency.
|
||||
static void set_slicing_pipeline_hook_fn(SlicingPipelineHookFn fn) { s_slicing_pipeline_hook_fn = std::move(fn); }
|
||||
|
||||
Print() = default;
|
||||
virtual ~Print() { this->clear(); }
|
||||
|
||||
@@ -961,7 +975,7 @@ public:
|
||||
[object_id](const PrintObject *obj) { return obj->id() == object_id; });
|
||||
return (it == m_objects.end()) ? nullptr : *it;
|
||||
}
|
||||
//BBS: Function to get m_brimMap;
|
||||
// Orca: Old callers still expect object-keyed brim paths.
|
||||
std::map<ObjectID, ExtrusionEntityCollection>&
|
||||
get_brimMap() { return m_brimMap; }
|
||||
|
||||
@@ -976,11 +990,11 @@ public:
|
||||
struct SkirtBrimGroup {
|
||||
struct Brim {
|
||||
ExtrusionEntityCollection brim;
|
||||
std::vector<ObjectID> object_ids;
|
||||
std::vector<ObjectInstanceID> instances;
|
||||
};
|
||||
|
||||
ExtrusionEntityCollection skirt;
|
||||
std::vector<ObjectID> object_ids;
|
||||
std::vector<ObjectInstanceID> instances;
|
||||
// Brims stay separate unless Combine brims merges colliding brims inside this group.
|
||||
std::vector<Brim> brims;
|
||||
};
|
||||
@@ -1263,6 +1277,13 @@ private:
|
||||
// Islands of objects and their supports extruded at the 1st layer.
|
||||
Polygons first_layer_islands() const;
|
||||
|
||||
static SlicingPipelineHookFn s_slicing_pipeline_hook_fn;
|
||||
bool m_pipeline_plugin_active { false };
|
||||
void run_pipeline_hook(SlicingPipelineStepPlugin step, const PrintObject* object) {
|
||||
if (m_pipeline_plugin_active && s_slicing_pipeline_hook_fn)
|
||||
s_slicing_pipeline_hook_fn(*this, object, step);
|
||||
}
|
||||
|
||||
PrintConfig m_config;
|
||||
PrintObjectConfig m_default_object_config;
|
||||
PrintRegionConfig m_default_region_config;
|
||||
@@ -1276,12 +1297,12 @@ private:
|
||||
ExtrusionEntityCollection m_skirt;
|
||||
std::vector<SkirtBrimGroup> m_skirt_brim_groups;
|
||||
bool m_has_shared_per_object_skirt { false };
|
||||
// BBS: collecting extrusion paths to build brim by objs
|
||||
// Orca: Object-keyed brim paths kept for existing code.
|
||||
std::map<ObjectID, ExtrusionEntityCollection> m_brimMap;
|
||||
std::map<ObjectID, ExtrusionEntityCollection> m_supportBrimMap;
|
||||
// Orca: cached occupied brim footprints used when grouping per-object skirts.
|
||||
std::map<ObjectID, ExPolygons> m_objectBrimAreas;
|
||||
std::map<ObjectID, ExPolygons> m_supportBrimAreas;
|
||||
// Orca: Actual brim paths keyed by object instance.
|
||||
std::map<ObjectInstanceID, ExtrusionEntityCollection> m_brimMapByInstance;
|
||||
// Orca: Translated brim areas keyed by instance, used to find touching brims.
|
||||
std::map<ObjectInstanceID, ExPolygons> m_objectBrimAreasByInstance;
|
||||
// Convex hull of the 1st layer extrusions.
|
||||
// It encompasses the object extrusions, support extrusions, skirt, brim, wipe tower.
|
||||
// It does NOT encompass user extrusions generated by custom G-code,
|
||||
|
||||
@@ -980,6 +980,9 @@ void PrintConfigDef::init_common_params()
|
||||
def->tooltip = L("Select the network agent implementation for printer communication.");
|
||||
def->mode = comAdvanced;
|
||||
def->cli = ConfigOptionDef::nocli;
|
||||
// Plugin-backed (see ConfigOptionDef::is_plugin_backed), but edited through the Choice widget above
|
||||
// rather than a plugin_picker field.
|
||||
def->plugin_type = "printer-connection";
|
||||
def->set_default_value(new ConfigOptionString(""));
|
||||
|
||||
def = this->add("print_host", coString);
|
||||
@@ -1077,6 +1080,17 @@ void PrintConfigDef::init_common_params()
|
||||
def = this->add("preset_name", coString);
|
||||
def->set_default_value(new ConfigOptionString());
|
||||
}
|
||||
|
||||
def = this->add("plugin_config_overrides", coString);
|
||||
def->label = L("Capabilities");
|
||||
def->tooltip = L("Configuration for the plugin capabilities this preset uses, overriding the global "
|
||||
"Capabilities configuration. Stored as a raw JSON array and edited through the dialog "
|
||||
"behind the button, never typed in directly.");
|
||||
// Never shown as a text field: GUIType::plugin_config renders a button that opens PluginsConfigDialog.
|
||||
def->gui_type = ConfigOptionDef::GUIType::plugin_config;
|
||||
def->mode = comAdvanced;
|
||||
def->cli = ConfigOptionDef::nocli;
|
||||
def->set_default_value(new ConfigOptionString(""));
|
||||
}
|
||||
|
||||
void PrintConfigDef::init_fff_params()
|
||||
@@ -2273,8 +2287,8 @@ void PrintConfigDef::init_fff_params()
|
||||
def->label = L("Top surface expansion");
|
||||
def->category = L("Strength");
|
||||
def->tooltip = L("Expands the top surfaces by this distance to connect distinct top surfaces and fill gaps.\n"
|
||||
"Useful for cases where the top surface is interrupted by a raised feature, such as text on a plane."
|
||||
"Expanding it removes the holes beneath these features and creates a continuous path with a better finish for printing on top."
|
||||
"Useful for cases where the top surface is interrupted by a raised feature, such as text on a plane. "
|
||||
"Expanding it removes the holes beneath these features and creates a continuous path with a better finish for printing on top. "
|
||||
"The expansion is applied to the original top surface, before any other processing such as bridging or overhang detection.");
|
||||
def->sidetext = L("mm");
|
||||
def->min = 0;
|
||||
@@ -2849,7 +2863,7 @@ void PrintConfigDef::init_fff_params()
|
||||
def->nullable = true;
|
||||
def->min = 0;
|
||||
def->max = max_temp;
|
||||
def->sidetext = L(u8"℃" /* °C */); // degrees Celsius, CIS languages need translation
|
||||
def->sidetext = L(u8"\u2103" /* °C */); // degrees Celsius, CIS languages need translation
|
||||
def->set_default_value(new ConfigOptionIntsNullable{0});
|
||||
|
||||
def = this->add("filament_flush_volumetric_speed", coFloats);
|
||||
@@ -5488,7 +5502,23 @@ void PrintConfigDef::init_fff_params()
|
||||
def->height = 5;
|
||||
def->mode = comAdvanced;
|
||||
def->set_default_value(new ConfigOptionString());
|
||||
|
||||
|
||||
def = this->add("plugins", coStrings);
|
||||
def->label = L("Plugins Used");
|
||||
def->tooltip = L("Plugin capabilities referenced by this preset, stored as name;uuid;capability.");
|
||||
def->mode = comDevelop;
|
||||
def->set_default_value(new ConfigOptionStrings());
|
||||
|
||||
def = this->add("slicing_pipeline_plugin", coStrings);
|
||||
def->label = L("Slicing Pipeline Plugin");
|
||||
def->tooltip = L("Python plugin(s) invoked at each slicing pipeline step to read and modify intermediate slicing data, "
|
||||
"including a final G-code post-processing step. Research/experimental.");
|
||||
def->gui_type = ConfigOptionDef::GUIType::plugin_picker;
|
||||
def->plugin_type = "slicing-pipeline";
|
||||
def->full_width = true;
|
||||
def->mode = comAdvanced;
|
||||
def->set_default_value(new ConfigOptionStrings());
|
||||
|
||||
def = this->add("printer_model", coString);
|
||||
def->label = L("Printer type");
|
||||
def->tooltip = L("Type of the printer.");
|
||||
@@ -7930,7 +7960,7 @@ void PrintConfigDef::init_fff_params()
|
||||
def->label = L("Extruder change");
|
||||
def->tooltip = L("To prevent oozing, the nozzle temperature will be cooled during ramming. Therefore, the ramming time must be greater than the cooldown time. 0 means disabled.");
|
||||
def->mode = comAdvanced;
|
||||
def->sidetext = "°C";
|
||||
def->sidetext = L(u8"\u2103" /* °C */); // degrees Celsius, CIS languages need translation
|
||||
def->min = 0;
|
||||
def->nullable = true;
|
||||
def->set_default_value(new ConfigOptionIntsNullable{0});
|
||||
@@ -7960,7 +7990,7 @@ void PrintConfigDef::init_fff_params()
|
||||
def->tooltip = L(
|
||||
"To prevent oozing, the nozzle temperature will be cooled during ramming. Note: only a cooldown command and fan activation are triggered, reaching the target temperature is not guaranteed. 0 means disabled.");
|
||||
def->mode = comAdvanced;
|
||||
def->sidetext = "°C";
|
||||
def->sidetext = L(u8"\u2103" /* °C */); // degrees Celsius, CIS languages need translation
|
||||
def->min = 0;
|
||||
def->nullable = true;
|
||||
def->set_default_value(new ConfigOptionIntsNullable{0});
|
||||
@@ -8011,7 +8041,7 @@ void PrintConfigDef::init_fff_params()
|
||||
def = this->add("filament_preheat_temperature_delta", coFloats);
|
||||
def->label = L("Preheat temperature delta");
|
||||
def->tooltip = L("Temperature delta applied during pre-heating before tool change.");
|
||||
def->sidetext = "°C";
|
||||
def->sidetext = L(u8"\u2103" /* °C */); // degrees Celsius, CIS languages need translation
|
||||
def->mode = comDevelop;
|
||||
def->nullable = true;
|
||||
def->set_default_value(new ConfigOptionFloatsNullable{0});
|
||||
|
||||
@@ -1779,6 +1779,7 @@ PRINT_CONFIG_CLASS_DERIVED_DEFINE(
|
||||
((ConfigOptionBool, ooze_prevention))
|
||||
((ConfigOptionString, filename_format))
|
||||
((ConfigOptionStrings, post_process))
|
||||
((ConfigOptionStrings, slicing_pipeline_plugin))
|
||||
((ConfigOptionString, printer_model))
|
||||
((ConfigOptionFloat, resolution))
|
||||
((ConfigOptionFloats, retraction_minimum_travel))
|
||||
|
||||
@@ -376,10 +376,7 @@ SupportGeneratorLayersPtr generate_raft_base(
|
||||
Polygons trimming;
|
||||
// BBS: if first layer of support is intersected with object island, it must have the same function as brim unless in nobrim mode.
|
||||
// brim_object_gap is changed to 0 by default, it's no longer appropriate to use it to determine the gap of first layer support.
|
||||
//if (object.has_brim())
|
||||
// trimming = offset(object.layers().front()->lslices, (float)scale_(object.config().brim_object_gap.value), SUPPORT_SURFACES_OFFSET_PARAMETERS);
|
||||
//else
|
||||
trimming = offset(object.layers().front()->lslices, (float)scale_(support_params.gap_xy_first_layer), SUPPORT_SURFACES_OFFSET_PARAMETERS);
|
||||
trimming = offset(object.layers().front()->lslices, (float) scale_(support_params.gap_xy_first_layer), SUPPORT_SURFACES_OFFSET_PARAMETERS);
|
||||
if (inflate_factor_1st_layer > SCALED_EPSILON) {
|
||||
// Inflate in multiple steps to avoid leaking of the support 1st layer through object walls.
|
||||
auto nsteps = std::max(5, int(ceil(inflate_factor_1st_layer / support_params.first_layer_flow.scaled_width())));
|
||||
|
||||
+46
-4
@@ -469,6 +469,31 @@ std::string CalibPressureAdvanceLine::generate_test(double start_pa /*= 0*/, dou
|
||||
return print_pa_lines(startx, starty, start_pa, step_pa, count);
|
||||
}
|
||||
|
||||
BoundingBoxf CalibPressureAdvanceLine::print_extents(const BoundingBoxf &bed_ext) const
|
||||
{
|
||||
BoundingBoxf adjusted_bed = bed_ext;
|
||||
if (is_delta()) {
|
||||
CalibPressureAdvanceLine::delta_scale_bed_ext(adjusted_bed);
|
||||
}
|
||||
|
||||
double bed_width = adjusted_bed.size().x();
|
||||
// m_length_long adjusts for narrow beds – exactly as in generate_test()
|
||||
double line_long = 40.0 + std::min(bed_width - 120.0, 0.0);
|
||||
double total_line_len = m_length_short * 2 + line_long;
|
||||
double start_x = adjusted_bed.min.x() + (bed_width - 2 * m_length_short - line_long - 20.0) / 2.0;
|
||||
double box_width = m_draw_numbers ? (number_spacing() * 8) : 0.0; // 3.0 * 8 = 24 mm
|
||||
|
||||
BoundingBoxf extent;
|
||||
extent.min.x() = start_x;
|
||||
extent.max.x() = start_x + total_line_len + m_line_width + box_width;
|
||||
|
||||
// Y bounds are the full bed (the caller will inset them by -25)
|
||||
extent.min.y() = adjusted_bed.min.y();
|
||||
extent.max.y() = adjusted_bed.max.y();
|
||||
|
||||
return extent;
|
||||
}
|
||||
|
||||
bool CalibPressureAdvanceLine::is_delta() const { return mp_gcodegen->config().printable_area.values.size() > 4; }
|
||||
|
||||
std::string CalibPressureAdvanceLine::print_pa_lines(double start_x, double start_y, double start_pa, double step_pa, int num)
|
||||
@@ -491,9 +516,10 @@ std::string CalibPressureAdvanceLine::print_pa_lines(double start_x, double star
|
||||
const double slow = CalibPressureAdvance::speed_adjust(m_slow_speed);
|
||||
std::stringstream gcode;
|
||||
gcode << mp_gcodegen->writer().travel_to_z(m_height_layer + z_offset);
|
||||
gcode << ";" << GCodeProcessor::reserved_tag(GCodeProcessor::ETags::Height) << (m_height_layer + z_offset) << "\n";
|
||||
double y_pos = start_y;
|
||||
|
||||
// prime line
|
||||
// Purge/first perimeter - acts as an anchor to the rest of the model
|
||||
gcode << ";" << GCodeProcessor::reserved_tag(GCodeProcessor::ETags::Role) << "Custom\n";
|
||||
gcode << writer.set_pressure_advance(0.0);
|
||||
auto prime_x = start_x;
|
||||
gcode << move_to(Vec2d(prime_x, y_pos + (num) * m_space_y), writer);
|
||||
@@ -503,10 +529,13 @@ std::string CalibPressureAdvanceLine::print_pa_lines(double start_x, double star
|
||||
for (int i = 0; i < num; ++i) {
|
||||
gcode << writer.set_pressure_advance(start_pa + i * step_pa);
|
||||
gcode << move_to(Vec2d(start_x, y_pos + i * m_space_y), writer);
|
||||
gcode << ";" << GCodeProcessor::reserved_tag(GCodeProcessor::ETags::Role) << "Custom\n";
|
||||
gcode << writer.set_speed(slow);
|
||||
gcode << writer.extrude_to_xy(Vec2d(start_x + m_length_short, y_pos + i * m_space_y), e_per_mm * m_length_short);
|
||||
gcode << ";" << GCodeProcessor::reserved_tag(GCodeProcessor::ETags::Role) << "Outer wall\n";
|
||||
gcode << writer.set_speed(fast);
|
||||
gcode << writer.extrude_to_xy(Vec2d(start_x + m_length_short + m_length_long, y_pos + i * m_space_y), e_per_mm * m_length_long);
|
||||
gcode << ";" << GCodeProcessor::reserved_tag(GCodeProcessor::ETags::Role) << "Custom\n";
|
||||
gcode << writer.set_speed(slow);
|
||||
gcode << writer.extrude_to_xy(Vec2d(start_x + m_length_short + m_length_long + m_length_short, y_pos + i * m_space_y),
|
||||
e_per_mm * m_length_short);
|
||||
@@ -530,9 +559,15 @@ std::string CalibPressureAdvanceLine::print_pa_lines(double start_x, double star
|
||||
|
||||
const auto box_start_x = start_x + m_length_short + m_length_long + m_length_short + m_line_width;
|
||||
DrawBoxOptArgs default_box_opt_args(2, m_height_layer, m_line_width, fast);
|
||||
//Draw box
|
||||
default_box_opt_args.is_filled = true;
|
||||
gcode << ";" << GCodeProcessor::reserved_tag(GCodeProcessor::ETags::Role) << "Bottom surface\n";
|
||||
gcode << draw_box(writer, box_start_x, start_y - m_space_y,
|
||||
number_spacing() * 8, (num + 1) * m_space_y, default_box_opt_args);
|
||||
//Ensure numbers are shown on the next layer in gcode processor, as in reality
|
||||
gcode << ";" << GCodeProcessor::reserved_tag(GCodeProcessor::ETags::Layer_Change) << "\n";
|
||||
gcode << ";" << GCodeProcessor::reserved_tag(GCodeProcessor::ETags::Height) << (m_height_layer*2 + z_offset) << "\n";
|
||||
gcode << ";" << GCodeProcessor::reserved_tag(GCodeProcessor::ETags::Role) << "Top surface\n";
|
||||
gcode << writer.travel_to_z(m_height_layer*2 + z_offset);
|
||||
for (int i = 0; i < num; i += 2) {
|
||||
gcode << draw_number(box_start_x + 3 + m_line_width, y_pos + i * m_space_y + m_space_y / 2, start_pa + i * step_pa, m_draw_digit_mode,
|
||||
@@ -595,12 +630,16 @@ CustomGCode::Info CalibPressureAdvancePattern::generate_custom_gcodes(const Dyna
|
||||
speed_adjust(speed_first_layer()));
|
||||
|
||||
// create anchoring frame
|
||||
//pattern uses outer wall speed/width
|
||||
gcode << ";" << GCodeProcessor::reserved_tag(GCodeProcessor::ETags::Role) << "Outer wall\n";
|
||||
gcode << draw_box(m_writer, m_starting_point.x(), m_starting_point.y(), print_size_x(), frame_size_y(), default_box_opt_args);
|
||||
|
||||
// create tab for numbers
|
||||
DrawBoxOptArgs draw_box_opt_args = default_box_opt_args;
|
||||
draw_box_opt_args.is_filled = true;
|
||||
draw_box_opt_args.num_perimeters = wall_count();
|
||||
//draw box as bottom surface, so numbers are clearly visible on top
|
||||
gcode << ";" << GCodeProcessor::reserved_tag(GCodeProcessor::ETags::Role) << "Bottom surface\n";
|
||||
gcode << draw_box(m_writer, m_starting_point.x(), m_starting_point.y() + frame_size_y() + line_spacing_first_layer(),
|
||||
print_size_x(),
|
||||
max_numbering_height() + line_spacing_first_layer() + m_glyph_padding_vertical * 2, draw_box_opt_args);
|
||||
@@ -611,7 +650,9 @@ CustomGCode::Info CalibPressureAdvancePattern::generate_custom_gcodes(const Dyna
|
||||
const double zhop_config_value = m_config.option<ConfigOptionFloats>("z_hop")->get_at(0);
|
||||
const auto accel = accel_perimeter();
|
||||
|
||||
// draw pressure advance pattern
|
||||
// Draw pressure advance pattern
|
||||
// pattern uses outer wall speed, label it as such
|
||||
gcode << ";" << GCodeProcessor::reserved_tag(GCodeProcessor::ETags::Role) << "Outer wall\n";
|
||||
for (int i = 0; i < m_num_layers; ++i) {
|
||||
const double layer_height = height_first_layer() + height_z_offset() + (i * height_layer());
|
||||
const double zhop_height = layer_height + zhop_config_value;
|
||||
@@ -643,6 +684,7 @@ CustomGCode::Info CalibPressureAdvancePattern::generate_custom_gcodes(const Dyna
|
||||
m_config.option<ConfigOptionFloats>("filament_flow_ratio")->get_at(0));
|
||||
|
||||
// glyph on every other line
|
||||
gcode << ";" << GCodeProcessor::reserved_tag(GCodeProcessor::ETags::Role) << "Outer wall\n";
|
||||
for (int j = 0; j < num_patterns; j += 2) {
|
||||
gcode << draw_number(glyph_start_x(j), m_starting_point.y() + frame_size_y() + m_glyph_padding_vertical + line_width(),
|
||||
m_params.start + (j * m_params.step), m_draw_digit_mode, line_width(), number_e_per_mm,
|
||||
@@ -692,7 +734,7 @@ CustomGCode::Info CalibPressureAdvancePattern::generate_custom_gcodes(const Dyna
|
||||
for (int j = 0; j < num_patterns; ++j) {
|
||||
// increment pressure advance
|
||||
gcode << m_writer.set_pressure_advance(m_params.start + (j * m_params.step));
|
||||
|
||||
gcode << ";" << GCodeProcessor::reserved_tag(GCodeProcessor::ETags::Role) << "Outer wall\n";
|
||||
for (int k = 0; k < wall_count(); ++k) {
|
||||
to_x += std::cos(to_radians(m_corner_angle) / 2) * side_length;
|
||||
to_y += std::sin(to_radians(m_corner_angle) / 2) * side_length;
|
||||
|
||||
@@ -254,6 +254,8 @@ class CalibPressureAdvanceLine : public CalibPressureAdvance
|
||||
public:
|
||||
CalibPressureAdvanceLine(GCode* gcodegen);
|
||||
~CalibPressureAdvanceLine(){};
|
||||
// Return the X‑bounds of the pattern on the given bed.
|
||||
BoundingBoxf print_extents(const BoundingBoxf &bed_ext) const;
|
||||
|
||||
std::string generate_test(double start_pa = 0, double step_pa = 0.002, int count = 50);
|
||||
|
||||
@@ -379,4 +381,4 @@ private:
|
||||
const double m_glyph_padding_vertical{1};
|
||||
};
|
||||
|
||||
} // namespace Slic3r
|
||||
} // namespace Slic3r
|
||||
|
||||
@@ -91,6 +91,13 @@ public:
|
||||
//
|
||||
void toggle_top_layer_only_view_range();
|
||||
//
|
||||
// Dim previous layers (ORCA, ported from preFlight)
|
||||
// Whether the layers below the current top layer are rendered darkened while
|
||||
// scrubbing below the full print, so only the current layer is shown at full brightness.
|
||||
//
|
||||
bool is_dim_previous_layers() const;
|
||||
void set_dim_previous_layers(bool value);
|
||||
//
|
||||
// Returns true if the given option is visible.
|
||||
//
|
||||
bool is_option_visible(EOptionType type) const;
|
||||
|
||||
@@ -19,6 +19,9 @@ struct Settings
|
||||
EViewType view_type{ EViewType::FeatureType };
|
||||
ETimeMode time_mode{ ETimeMode::Normal };
|
||||
bool top_layer_only_view_range{ false };
|
||||
// ORCA: when enabled, all layers below the current top layer are rendered
|
||||
// darkened (keeping their color) while scrubbing below the full print (ported from preFlight)
|
||||
bool dim_previous_layers{ false };
|
||||
bool spiral_vase_mode{ false };
|
||||
//
|
||||
// Required update flags
|
||||
|
||||
@@ -72,6 +72,16 @@ void Viewer::toggle_top_layer_only_view_range()
|
||||
m_impl->toggle_top_layer_only_view_range();
|
||||
}
|
||||
|
||||
bool Viewer::is_dim_previous_layers() const
|
||||
{
|
||||
return m_impl->is_dim_previous_layers();
|
||||
}
|
||||
|
||||
void Viewer::set_dim_previous_layers(bool value)
|
||||
{
|
||||
m_impl->set_dim_previous_layers(value);
|
||||
}
|
||||
|
||||
bool Viewer::is_option_visible(EOptionType type) const
|
||||
{
|
||||
return m_impl->is_option_visible(type);
|
||||
|
||||
@@ -1223,6 +1223,20 @@ static float encode_color(const Color& color) {
|
||||
return static_cast<float>(i_color);
|
||||
}
|
||||
|
||||
// ORCA: how much the layers below the current top layer are darkened when
|
||||
// Settings::dim_previous_layers is enabled (ported from preFlight). 0.0 = no change, 1.0 = black.
|
||||
static constexpr float PREVIOUS_LAYER_DARKEN_FACTOR = 0.60f;
|
||||
|
||||
// ORCA: returns the encoded color scaled towards black by 'factor', preserving its hue
|
||||
static float encode_color_darkened(const Color& color, float factor) {
|
||||
const float keep = 1.0f - factor;
|
||||
const int r = static_cast<int>(color[0] * keep);
|
||||
const int g = static_cast<int>(color[1] * keep);
|
||||
const int b = static_cast<int>(color[2] * keep);
|
||||
const int i_color = r << 16 | g << 8 | b;
|
||||
return static_cast<float>(i_color);
|
||||
}
|
||||
|
||||
|
||||
void ViewerImpl::update_colors_texture()
|
||||
{
|
||||
@@ -1234,14 +1248,30 @@ void ViewerImpl::update_colors_texture()
|
||||
const size_t top_layer_id = m_settings.top_layer_only_view_range ? m_layers.get_view_range()[1] : 0;
|
||||
const bool color_top_layer_only = m_view_range.get_full()[1] != m_view_range.get_visible()[1];
|
||||
|
||||
// ORCA: when dim_previous_layers is enabled, darken every layer below the current top layer
|
||||
// (keeping its color) whenever we are not rendering the whole print, so that only the layer
|
||||
// being scrubbed to is shown at full brightness (ported from preFlight). This shares
|
||||
// top_layer_id with the greying path, so it only applies while in top-layer-only mode - that
|
||||
// way the moves slider still animates normally across all layers when that mode is disabled.
|
||||
const bool dim_previous_layers = m_settings.dim_previous_layers && !m_layers.empty();
|
||||
const bool full_render = (m_layers.get_view_range()[0] == 0) &&
|
||||
(m_layers.get_view_range()[1] >= static_cast<uint32_t>(m_layers.count()) - 1) &&
|
||||
(m_view_range.get_visible()[1] == m_view_range.get_full()[1]);
|
||||
|
||||
// Based on current settings and slider position, we might want to render some
|
||||
// vertices as dark grey. Use either that or the normal color (from the cache).
|
||||
// vertices as dark grey (or darkened, see above). Use either that or the normal color (from the cache).
|
||||
std::vector<float> colors(m_vertices_colors.size());
|
||||
assert(colors.size() == m_vertices.size() && m_vertices_colors.size() == m_vertices.size());
|
||||
for (size_t i=0; i<m_vertices.size(); ++i)
|
||||
colors[i] = (color_top_layer_only && m_vertices[i].layer_id < top_layer_id &&
|
||||
(!m_settings.spiral_vase_mode || i != m_view_range.get_enabled()[0])) ?
|
||||
encode_color(DUMMY_COLOR) : m_vertices_colors[i];
|
||||
for (size_t i=0; i<m_vertices.size(); ++i) {
|
||||
const PathVertex& v = m_vertices[i];
|
||||
const bool keep_spiral_seam = m_settings.spiral_vase_mode && i == m_view_range.get_enabled()[0];
|
||||
if (dim_previous_layers && !full_render && v.layer_id < top_layer_id && !keep_spiral_seam)
|
||||
colors[i] = encode_color_darkened(get_vertex_color(v), PREVIOUS_LAYER_DARKEN_FACTOR);
|
||||
else if (color_top_layer_only && v.layer_id < top_layer_id && !keep_spiral_seam)
|
||||
colors[i] = encode_color(DUMMY_COLOR);
|
||||
else
|
||||
colors[i] = m_vertices_colors[i];
|
||||
}
|
||||
|
||||
#ifdef ENABLE_OPENGL_ES
|
||||
if (!colors.empty())
|
||||
@@ -1349,6 +1379,17 @@ void ViewerImpl::toggle_top_layer_only_view_range()
|
||||
update_colors_texture();
|
||||
}
|
||||
|
||||
// ORCA: enable/disable darkening of the layers below the current top layer (ported from preFlight)
|
||||
void ViewerImpl::set_dim_previous_layers(bool value)
|
||||
{
|
||||
if (m_settings.dim_previous_layers == value)
|
||||
return;
|
||||
m_settings.dim_previous_layers = value;
|
||||
// defer the actual color/texture rebuild to the next render(), when the GL context is current
|
||||
// (this may be toggled from the Preferences dialog, outside the canvas context)
|
||||
m_settings.update_colors = true;
|
||||
}
|
||||
|
||||
std::vector<ETimeMode> ViewerImpl::get_time_modes() const
|
||||
{
|
||||
std::vector<ETimeMode> ret;
|
||||
|
||||
@@ -85,6 +85,10 @@ public:
|
||||
bool is_top_layer_only_view_range() const { return m_settings.top_layer_only_view_range; }
|
||||
void toggle_top_layer_only_view_range();
|
||||
|
||||
// ORCA: darken layers below the current top layer while scrubbing (ported from preFlight)
|
||||
bool is_dim_previous_layers() const { return m_settings.dim_previous_layers; }
|
||||
void set_dim_previous_layers(bool value);
|
||||
|
||||
bool is_spiral_vase_mode() const { return m_settings.spiral_vase_mode; }
|
||||
|
||||
std::vector<ETimeMode> get_time_modes() const;
|
||||
|
||||
+110
-1
@@ -23,6 +23,7 @@ set(SLIC3R_GUI_SOURCES
|
||||
GUI/AboutDialog.cpp
|
||||
GUI/AboutDialog.hpp
|
||||
GUI/AmsMappingPopup.cpp
|
||||
GUI/AmsMappingPopupUpdate.cpp
|
||||
GUI/AmsMappingPopup.hpp
|
||||
GUI/AMSMaterialsSetting.cpp
|
||||
GUI/AMSMaterialsSetting.hpp
|
||||
@@ -40,6 +41,8 @@ set(SLIC3R_GUI_SOURCES
|
||||
GUI/Auxiliary.hpp
|
||||
GUI/BackgroundSlicingProcess.cpp
|
||||
GUI/BackgroundSlicingProcess.hpp
|
||||
GUI/PostProcessor.cpp
|
||||
GUI/PostProcessor.hpp
|
||||
GUI/BBLStatusBarBind.cpp
|
||||
GUI/BBLStatusBarBind.hpp
|
||||
GUI/BBLStatusBar.cpp
|
||||
@@ -109,6 +112,27 @@ set(SLIC3R_GUI_SOURCES
|
||||
GUI/Downloader.hpp
|
||||
GUI/DownloadProgressDialog.cpp
|
||||
GUI/DownloadProgressDialog.hpp
|
||||
GUI/PluginSource.hpp
|
||||
GUI/PluginSort.hpp
|
||||
GUI/PluginStatus.hpp
|
||||
GUI/PluginPickerDialog.cpp
|
||||
GUI/PluginPickerDialog.hpp
|
||||
GUI/PluginsDialog.cpp
|
||||
GUI/PluginsDialog.hpp
|
||||
GUI/SpeedDialDialog.cpp
|
||||
GUI/SpeedDialDialog.hpp
|
||||
GUI/ActionRegistry.cpp
|
||||
GUI/ActionRegistry.hpp
|
||||
GUI/PluginsConfigDialog.cpp
|
||||
GUI/PluginsConfigDialog.hpp
|
||||
GUI/ProcessRunner.cpp
|
||||
GUI/ProcessRunner.hpp
|
||||
GUI/TerminalDialog.cpp
|
||||
GUI/TerminalDialog.hpp
|
||||
GUI/PluginProgressDialog.cpp
|
||||
GUI/PluginProgressDialog.hpp
|
||||
GUI/PluginWebDialog.cpp
|
||||
GUI/PluginWebDialog.hpp
|
||||
GUI/DragCanvas.cpp
|
||||
GUI/DragCanvas.hpp
|
||||
GUI/EditGCodeDialog.cpp
|
||||
@@ -582,11 +606,58 @@ set(SLIC3R_GUI_SOURCES
|
||||
GUI/Widgets/TempInput.hpp
|
||||
GUI/Widgets/TextInput.cpp
|
||||
GUI/Widgets/TextInput.hpp
|
||||
GUI/Widgets/WebViewHostDialog.cpp
|
||||
GUI/Widgets/WebViewHostDialog.hpp
|
||||
GUI/Widgets/WebView.cpp
|
||||
GUI/Widgets/WebView.hpp
|
||||
GUI/WipeTowerDialog.cpp
|
||||
GUI/wxExtensions.cpp
|
||||
GUI/wxExtensions.hpp
|
||||
plugin/PythonInterpreter.cpp
|
||||
plugin/PythonInterpreter.hpp
|
||||
plugin/PythonPluginBridge.cpp
|
||||
plugin/PythonPluginBridge.hpp
|
||||
plugin/PythonPluginInterface.hpp
|
||||
plugin/PyPluginPackage.hpp
|
||||
plugin/PluginBindingUtils.hpp
|
||||
plugin/host/PluginHost.cpp
|
||||
plugin/host/PluginHost.hpp
|
||||
plugin/host/PluginHostBindings.hpp
|
||||
plugin/host/PluginHostApp.cpp
|
||||
plugin/host/PluginHostGeometry.cpp
|
||||
plugin/host/PluginHostMesh.cpp
|
||||
plugin/host/PluginHostMesh.hpp
|
||||
plugin/host/PluginHostModel.cpp
|
||||
plugin/host/PluginHostPresets.cpp
|
||||
plugin/host/PluginHostSlicing.cpp
|
||||
plugin/host/PluginHostUi.cpp
|
||||
plugin/host/PluginHostUi.hpp
|
||||
plugin/CloudPluginService.cpp
|
||||
plugin/CloudPluginService.hpp
|
||||
plugin/PluginFsUtils.cpp
|
||||
plugin/PluginFsUtils.hpp
|
||||
plugin/PluginConfig.cpp
|
||||
plugin/PluginConfig.hpp
|
||||
plugin/PluginLoader.cpp
|
||||
plugin/PluginLoader.hpp
|
||||
plugin/PluginDescriptor.hpp
|
||||
plugin/PluginHooks.cpp
|
||||
plugin/PluginHooks.hpp
|
||||
plugin/PluginManager.cpp
|
||||
plugin/PluginManager.hpp
|
||||
plugin/PluginAuditManager.cpp
|
||||
plugin/PluginAuditManager.hpp
|
||||
plugin/PluginResolver.cpp
|
||||
plugin/PluginResolver.hpp
|
||||
plugin/pluginTypes/printerAgent/PrinterAgentPluginCapability.hpp
|
||||
plugin/pluginTypes/printerAgent/PrinterAgentPluginCapability.cpp
|
||||
plugin/pluginTypes/printerAgent/PrinterAgentPluginCapabilityTrampoline.hpp
|
||||
plugin/pluginTypes/script/ScriptPluginCapability.hpp
|
||||
plugin/pluginTypes/script/ScriptPluginCapability.cpp
|
||||
plugin/pluginTypes/script/ScriptPluginCapabilityTrampoline.hpp
|
||||
plugin/pluginTypes/slicingPipeline/SlicingPipelinePluginCapability.hpp
|
||||
plugin/pluginTypes/slicingPipeline/SlicingPipelinePluginCapability.cpp
|
||||
plugin/pluginTypes/slicingPipeline/SlicingPipelinePluginCapabilityTrampoline.hpp
|
||||
pchheader.cpp
|
||||
pchheader.hpp
|
||||
Utils/ASCIIFolding.cpp
|
||||
@@ -782,8 +853,16 @@ 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)
|
||||
target_link_libraries(libslic3r_gui libslic3r cereal::cereal imgui imguizmo minilzo libvgcode md4c-html glad ${_opengl_link_lib} hidapi mdns ${wxWidgets_LIBRARIES} glfw libcurl OpenSSL::SSL OpenSSL::Crypto noise::noise pybind11::embed)
|
||||
|
||||
if (CMAKE_SYSTEM_NAME STREQUAL "Linux")
|
||||
# Linux finds wxWidgets in module mode, whose include dirs and definitions
|
||||
# only apply at src/ directory scope; export them from the target so wx
|
||||
# headers reach GUI-header consumers outside src/ (tests). The CONFIG-mode
|
||||
# wx::* imported targets used on Windows/macOS already propagate these.
|
||||
target_include_directories(libslic3r_gui SYSTEM PUBLIC ${wxWidgets_INCLUDE_DIRS})
|
||||
target_compile_definitions(libslic3r_gui PUBLIC ${wxWidgets_DEFINITIONS} $<$<CONFIG:Debug>:${wxWidgets_DEFINITIONS_DEBUG}>)
|
||||
endif ()
|
||||
|
||||
if (MSVC)
|
||||
target_link_libraries(libslic3r_gui Setupapi.lib)
|
||||
@@ -861,3 +940,33 @@ endif ()
|
||||
|
||||
# Add a definition so that we can tell we are compiling slic3r.
|
||||
target_compile_definitions(libslic3r_gui PRIVATE SLIC3R_CURRENTLY_COMPILING_GUI_MODULE)
|
||||
|
||||
if(ORCA_BUNDLED_UV_EXECUTABLE_CONFIG)
|
||||
target_compile_definitions(libslic3r_gui PRIVATE "ORCA_BUNDLED_UV_EXECUTABLE=\"${ORCA_BUNDLED_UV_EXECUTABLE_CONFIG}\"")
|
||||
endif()
|
||||
|
||||
if (ORCA_BUILD_PYTHON_STUBGEN_MODULE)
|
||||
add_library(orca_stubgen MODULE
|
||||
plugin/PythonPluginBridge.cpp
|
||||
)
|
||||
|
||||
target_compile_definitions(orca_stubgen PRIVATE
|
||||
ORCA_PYTHON_STUBGEN_MODULE
|
||||
)
|
||||
|
||||
target_include_directories(orca_stubgen PRIVATE
|
||||
${CMAKE_CURRENT_SOURCE_DIR}
|
||||
${CMAKE_CURRENT_BINARY_DIR}
|
||||
Utils
|
||||
)
|
||||
|
||||
target_link_libraries(orca_stubgen PRIVATE
|
||||
libslic3r_gui
|
||||
pybind11::embed
|
||||
)
|
||||
|
||||
set_target_properties(orca_stubgen PROPERTIES
|
||||
OUTPUT_NAME orca
|
||||
PREFIX ""
|
||||
)
|
||||
endif()
|
||||
|
||||
@@ -548,7 +548,7 @@ void GLVolume::render_with_outline(const GUI::Size& cnv_size)
|
||||
glsafe(::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR));
|
||||
glsafe(::glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT32F, cnv_size.get_width(), cnv_size.get_height(), 0, GL_DEPTH_COMPONENT, GL_FLOAT, nullptr));
|
||||
|
||||
glsafe(::glFramebufferTexture2D(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, GL_TEXTURE_2D, depth_tex, 0));
|
||||
glsafe(::glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, GL_TEXTURE_2D, depth_tex, 0));
|
||||
}
|
||||
glsafe(::glClear(GL_DEPTH_BUFFER_BIT));
|
||||
if (tverts_range == std::make_pair<size_t, size_t>(0, -1))
|
||||
|
||||
@@ -42,7 +42,7 @@ static std::string get_humidity_level_img_path(int humidity_percent)
|
||||
}
|
||||
|
||||
if (wxGetApp().dark_mode()) {
|
||||
return "hum_level" + std::to_string(hum_level) + "_no_num_dark";
|
||||
return "hum_level" + std::to_string(hum_level) + "_no_num_dark"; // Orca: use the dark-mode humidity glyph in dark mode
|
||||
} else {
|
||||
return "hum_level" + std::to_string(hum_level) + "_no_num_light";
|
||||
}
|
||||
@@ -86,7 +86,7 @@ FilamentItemPanel::FilamentItemPanel(wxWindow* parent, const wxString& text, con
|
||||
: wxPanel(parent, id)
|
||||
, m_icon_name(icon_name)
|
||||
{
|
||||
SetBackgroundColour(wxColour("#F0F0F1")); // Light gray background
|
||||
SetBackgroundColour(wxColour("#F0F0F1")); // Orca: light-gray panel scheme (#F0F0F1 replaces REF #F7F7F7)
|
||||
SetMinSize(wxSize(FromDIP(64), FromDIP(106))); // Width: 64, Height: 106
|
||||
SetSize(wxSize(FromDIP(64), FromDIP(106))); // Fixed size
|
||||
|
||||
@@ -454,7 +454,7 @@ wxBoxSizer* AMSDryCtrWin::create_normal_state_panel(wxPanel* parent)
|
||||
m_temperature_input->SetBackgroundColour(StateColor::darkModeColorFor(*wxWHITE));
|
||||
m_temperature_input->SetForegroundColour(StateColor::darkModeColorFor(*wxBLACK));
|
||||
|
||||
Label* temp_unit_label = new Label(parent, wxString::FromUTF8("℃"));
|
||||
Label* temp_unit_label = new Label(parent, wxString::FromUTF8(u8"\u2103" /* °C */));
|
||||
temp_unit_label->SetForegroundColour(*wxBLACK);
|
||||
temp_sizer->Add(m_temperature_input, 1, wxRIGHT, FromDIP(1));
|
||||
temp_sizer->Add(temp_unit_label, 0, wxALIGN_CENTER_VERTICAL);
|
||||
@@ -977,7 +977,7 @@ void AMSDryCtrWin::create()
|
||||
// set title icon
|
||||
SetBackgroundColour(StateColor::darkModeColorFor(*wxWHITE));
|
||||
this->SetDoubleBuffered(true);
|
||||
std::string icon_path = (boost::format("%1%/images/OrcaSlicerTitle.ico") % resources_dir()).str();
|
||||
std::string icon_path = (boost::format("%1%/images/OrcaSlicerTitle.ico") % resources_dir()).str(); // Orca: app title icon
|
||||
SetIcon(wxIcon(encode_path(icon_path.c_str()), wxBITMAP_TYPE_ICO));
|
||||
|
||||
SetSize(wxSize(FromDIP(700), FromDIP(500)));
|
||||
@@ -1444,9 +1444,9 @@ int AMSDryCtrWin::update_ams_change(DevAms* dev_ams)
|
||||
|
||||
m_ams_info.m_ams_id = dev_ams->GetAmsId();
|
||||
if (dev_ams->GetAmsType() == DevAmsType::N3F) {
|
||||
m_temperature_input->SetHint("45-65" + wxString::FromUTF8("°C"));
|
||||
m_temperature_input->SetHint("45-65" + wxString::FromUTF8(u8"\u2103" /* °C */));
|
||||
} else if (dev_ams->GetAmsType() == DevAmsType::N3S) {
|
||||
m_temperature_input->SetHint("45-85" + wxString::FromUTF8("°C"));
|
||||
m_temperature_input->SetHint("45-85" + wxString::FromUTF8(u8"\u2103" /* °C */));
|
||||
}
|
||||
|
||||
m_time_input->SetHint("1-24 h");
|
||||
@@ -1467,7 +1467,7 @@ int AMSDryCtrWin::update_dryness_status(DevAms* dev_ams)
|
||||
if (m_ams_info.m_temperature != dev_ams->GetCurrentTemperature()) {
|
||||
updated += 1;
|
||||
m_ams_info.m_temperature = dev_ams->GetCurrentTemperature();
|
||||
m_temperature_data_label->SetLabel(std::to_string(m_ams_info.m_temperature) + wxString::FromUTF8("°C"));
|
||||
m_temperature_data_label->SetLabel(std::to_string(m_ams_info.m_temperature) + wxString::FromUTF8(u8"\u2103" /* °C */));
|
||||
}
|
||||
|
||||
if (is_dry_ctr_idle(dev_ams)) {
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
|
||||
#include "slic3r/GUI/wxExtensions.hpp"
|
||||
#include "slic3r/GUI/DeviceCore/DevFilaSystem.h"
|
||||
#include "slic3r/GUI/I18N.hpp"
|
||||
#include "slic3r/GUI/I18N.hpp" // Orca: explicit _L() catalog include
|
||||
|
||||
#include <chrono>
|
||||
#include <optional>
|
||||
|
||||
@@ -296,11 +296,8 @@ void AMSMaterialsSetting::create_panel_kn(wxWindow* parent)
|
||||
m_ratio_text->SetForegroundColour(wxColour(50, 58, 61));
|
||||
m_ratio_text->SetFont(Label::Head_14);
|
||||
|
||||
std::string language = wxGetApp().app_config->get("language");
|
||||
wxString region = "en";
|
||||
if (language.find("zh") == 0)
|
||||
region = "zh";
|
||||
wxString link_url = wxString::Format("https://wiki.bambulab.com/%s/software/bambu-studio/calibration_pa", region);
|
||||
// Orca: link to the Orca Slicer pressure-advance wiki (region-agnostic).
|
||||
wxString link_url = "https://www.orcaslicer.com/wiki/pressure_advance_calib";
|
||||
m_wiki_ctrl = new HyperLink(parent, _L("Wiki Guide"), link_url);
|
||||
cali_title_sizer->Add(m_ratio_text, 0, wxALIGN_CENTER_VERTICAL);
|
||||
cali_title_sizer->Add(m_wiki_ctrl, 0, wxALIGN_CENTER_VERTICAL);
|
||||
@@ -449,9 +446,19 @@ void AMSMaterialsSetting::update_filament_editing(bool is_printing)
|
||||
m_tip_readonly->Wrap(FromDIP(380));
|
||||
m_tip_readonly->Show(is_printing);
|
||||
}
|
||||
|
||||
if (m_view_only) { // Orca: view-only (2D laser/cut) — lock every edit control and hide apply/reset
|
||||
m_comboBox_filament->Enable(false);
|
||||
m_comboBox_cali_result->Enable(false);
|
||||
m_input_k_val->Enable(false);
|
||||
m_input_n_val->Enable(false);
|
||||
m_button_confirm->Hide();
|
||||
m_button_reset->Hide();
|
||||
}
|
||||
}
|
||||
|
||||
void AMSMaterialsSetting::on_select_reset(wxCommandEvent& event) {
|
||||
if (m_view_only) return; // Orca: view-only never commits
|
||||
MessageDialog msg_dlg(nullptr, _L("Are you sure you want to clear the filament information?"), wxEmptyString, wxICON_WARNING | wxOK | wxCANCEL);
|
||||
auto result = msg_dlg.ShowModal();
|
||||
if (result != wxID_OK)
|
||||
@@ -619,6 +626,8 @@ void AMSMaterialsSetting::on_select_ok(wxCommandEvent &event)
|
||||
if (!obj)
|
||||
return;
|
||||
|
||||
if (m_view_only) return; // Orca: view-only never commits
|
||||
|
||||
//get filament id
|
||||
ams_filament_id = "";
|
||||
ams_setting_id = "";
|
||||
@@ -830,6 +839,7 @@ void AMSMaterialsSetting::on_picker_color(wxCommandEvent& event)
|
||||
|
||||
void AMSMaterialsSetting::on_clr_picker(wxMouseEvent &event)
|
||||
{
|
||||
if (m_view_only) return; // Orca: view-only disables color editing
|
||||
if(!m_is_third)
|
||||
return;
|
||||
|
||||
|
||||
@@ -124,6 +124,9 @@ public:
|
||||
std::string ams_setting_id;
|
||||
|
||||
bool m_is_third;
|
||||
// Orca: view-only mode (laser/cut). When set, the dialog is inspectable but every
|
||||
// editing control is disabled and no command is sent.
|
||||
bool m_view_only = false;
|
||||
wxString m_brand_filament;
|
||||
wxString m_brand_sn;
|
||||
wxString m_brand_tmp;
|
||||
@@ -183,6 +186,7 @@ protected:
|
||||
|
||||
int m_pa_cali_select_id = 0;
|
||||
|
||||
// Orca: native wxComboBox on macOS (the custom ComboBox mis-renders the filament dropdown there)
|
||||
#ifdef __APPLE__
|
||||
wxComboBox *m_comboBox_filament;
|
||||
#else
|
||||
|
||||
@@ -272,7 +272,7 @@ void AMSSetting::create()
|
||||
m_sizer_main->Add(0, 0, 0, wxTOP, FromDIP(10));
|
||||
m_sizer_main->Add(m_static_ams_settings, 0, wxEXPAND | wxLEFT | wxRIGHT, FromDIP(24));
|
||||
m_sizer_main->Add(0, 0, 0, wxTOP, FromDIP(10));
|
||||
m_sizer_main->Add(m_panel_body, 1, wxBottom | wxLEFT | wxRIGHT | wxEXPAND, FromDIP(24));
|
||||
m_sizer_main->Add(m_panel_body, 1, wxBOTTOM | wxLEFT | wxRIGHT | wxEXPAND, FromDIP(24)); // Orca: wxBOTTOM (REF uses the wxEdge enum by mistake)
|
||||
|
||||
this->SetSizer(m_sizer_main);
|
||||
this->Layout();
|
||||
@@ -644,7 +644,7 @@ void AMSSettingTypePanel::Update(const MachineObject* obj)
|
||||
}
|
||||
|
||||
if (ptr->IsSwitching()) {
|
||||
int display_percent = obj->get_upgrade_percent();
|
||||
int display_percent = obj->get_upgrade_percent(); // Orca: read upgrade progress via the kept MachineObject accessor
|
||||
if (display_percent == 100 || display_percent == 0) {
|
||||
display_percent = 1;// special case, sometimes it's switching but percent is 0 or 100
|
||||
}
|
||||
@@ -735,6 +735,7 @@ void AMSSettingTypePanel::OnAmsTypeChanged(wxCommandEvent& event)
|
||||
event.Skip();
|
||||
}
|
||||
|
||||
// Orca: AMSSettingArrangeAMSOrder impl kept compiled-out (feature intentionally not shipped).
|
||||
#if 0 /*used option*/
|
||||
AMSSettingArrangeAMSOrder::AMSSettingArrangeAMSOrder(wxWindow* parent)
|
||||
: wxPanel(parent)
|
||||
|
||||
@@ -129,6 +129,7 @@ private:
|
||||
AnimaIcon* m_switching_icon;
|
||||
};
|
||||
|
||||
// Orca: the firmware AMS-reorder ("Arrange AMS Order") panel is intentionally not shipped.
|
||||
#if 0
|
||||
class AMSSettingArrangeAMSOrder : public wxPanel
|
||||
{
|
||||
|
||||
@@ -0,0 +1,390 @@
|
||||
#include "ActionRegistry.hpp"
|
||||
|
||||
#include "GUI.hpp"
|
||||
#include "GUI_App.hpp"
|
||||
#include "I18N.hpp"
|
||||
#include "slic3r/plugin/PluginManager.hpp"
|
||||
|
||||
#include <libslic3r/AppConfig.hpp>
|
||||
#include <slic3r/plugin/PythonPluginInterface.hpp>
|
||||
|
||||
#include <wx/thread.h>
|
||||
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
#include <ctime>
|
||||
#include <unordered_map>
|
||||
|
||||
namespace Slic3r { namespace GUI {
|
||||
|
||||
namespace {
|
||||
|
||||
constexpr const char* kConfigSection = "speed_dial";
|
||||
|
||||
nlohmann::json parse_config_json(const std::string& value, nlohmann::json fallback)
|
||||
{
|
||||
nlohmann::json parsed = nlohmann::json::parse(value, nullptr, false);
|
||||
return parsed.is_discarded() ? std::move(fallback) : parsed;
|
||||
}
|
||||
|
||||
nlohmann::json read_section(const char* key, nlohmann::json fallback)
|
||||
{
|
||||
return parse_config_json(wxGetApp().app_config->get(kConfigSection, key), std::move(fallback));
|
||||
}
|
||||
|
||||
void write_section(const char* key, const nlohmann::json& j)
|
||||
{
|
||||
wxGetApp().app_config->set(kConfigSection, key, j.dump());
|
||||
}
|
||||
|
||||
std::vector<std::string> read_string_array(const char* key)
|
||||
{
|
||||
auto j = read_section(key, nlohmann::json::array());
|
||||
std::vector<std::string> v;
|
||||
for (auto& e : j)
|
||||
if (e.is_string())
|
||||
v.push_back(e.get<std::string>());
|
||||
return v;
|
||||
}
|
||||
|
||||
// frecency = frequency + recency; score halves every 30 idle days.
|
||||
double frecency_score(int count, long long last, long long now)
|
||||
{
|
||||
if (count <= 0)
|
||||
return 0.0;
|
||||
constexpr double HALF_LIFE_DAYS = 30.0;
|
||||
double age = std::max(0.0, double(now - last) / 86400.0);
|
||||
return count * std::pow(2.0, -age / HALF_LIFE_DAYS);
|
||||
}
|
||||
|
||||
// ---- script-plugin action source (the one and only source) ------------------
|
||||
|
||||
std::string find_loaded_source_name(PluginManager& manager, const std::string& plugin_key)
|
||||
{
|
||||
PluginDescriptor descriptor;
|
||||
if (manager.try_get_plugin_descriptor(plugin_key, descriptor) && !descriptor.name.empty())
|
||||
return descriptor.name;
|
||||
return plugin_key;
|
||||
}
|
||||
|
||||
// A runnable script capability exposed as a speed-dial action. source_key = plugin_key
|
||||
// (identity), so a plugin display-name change does not re-key the action.
|
||||
struct PluginScriptAction : AppAction
|
||||
{
|
||||
static constexpr const char* kIdPrefix = "plugin_script_action";
|
||||
|
||||
std::string plugin_key;
|
||||
std::string capability;
|
||||
|
||||
// The id an action for (plugin_key, capability) would have - lets refresh_capability
|
||||
// remove a gone capability without materialising the action.
|
||||
static std::string id_for(const std::string& plugin_key, const std::string& capability)
|
||||
{
|
||||
return AppAction::compose_id(kIdPrefix, capability.empty() ? plugin_key : capability, plugin_key);
|
||||
}
|
||||
|
||||
PluginScriptAction(std::string plugin_key_in, std::string capability_in, std::string source_name)
|
||||
: AppAction(kIdPrefix,
|
||||
capability_in.empty() ? plugin_key_in : capability_in, // title
|
||||
plugin_key_in, // source_key
|
||||
std::move(source_name)),
|
||||
plugin_key(std::move(plugin_key_in)), capability(std::move(capability_in))
|
||||
{}
|
||||
|
||||
AppActionRunResult run() const override
|
||||
{
|
||||
std::string error;
|
||||
const ExecutionResult result = PluginManager::instance().run_script_capability(plugin_key, capability, error);
|
||||
if (!error.empty())
|
||||
return {AppActionRunResult::Level::Error, from_u8(error)};
|
||||
|
||||
const bool skipped = result.status == PluginResult::Skipped;
|
||||
const wxString fallback = skipped ? _L("Script plugin skipped.") : _L("Script plugin finished.");
|
||||
return {skipped ? AppActionRunResult::Level::Info : AppActionRunResult::Level::Success,
|
||||
result.message.empty() ? fallback : from_u8(result.message)};
|
||||
}
|
||||
};
|
||||
|
||||
// Builds an action for a capability, or nullptr if it is not a currently-loaded,
|
||||
// enabled script capability.
|
||||
std::unique_ptr<AppAction> make_action(const std::string& plugin_key, const std::string& capability,
|
||||
const std::string& source_name)
|
||||
{
|
||||
PluginManager& manager = PluginManager::instance();
|
||||
if (!manager.is_plugin_loaded(plugin_key))
|
||||
return nullptr;
|
||||
// only_enabled defaults true, so a disabled capability resolves to nullptr here.
|
||||
if (!manager.get_plugin_capability({PluginCapabilityType::Script, capability, plugin_key}))
|
||||
return nullptr;
|
||||
return std::make_unique<PluginScriptAction>(plugin_key, capability, source_name);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
void ActionRegistry::init()
|
||||
{
|
||||
assert(wxThread::IsMain());
|
||||
assert(!m_started);
|
||||
m_started = true;
|
||||
|
||||
PluginManager& manager = PluginManager::instance();
|
||||
|
||||
auto on_source = [this](const std::string& plugin_key, ActionChange change) {
|
||||
if (!wxTheApp || wxGetApp().is_closing())
|
||||
return;
|
||||
wxGetApp().CallAfter([this, plugin_key, change] {
|
||||
if (!wxGetApp().is_closing())
|
||||
this->refresh_source(plugin_key, change);
|
||||
});
|
||||
};
|
||||
auto on_capability = [this](const PluginCapabilityId& capability, ActionChange change) {
|
||||
if (capability.type != PluginCapabilityType::Script || !wxTheApp || wxGetApp().is_closing())
|
||||
return;
|
||||
const std::string plugin_key = capability.plugin_key;
|
||||
const std::string name = capability.name;
|
||||
wxGetApp().CallAfter([this, plugin_key, name, change] {
|
||||
if (!wxGetApp().is_closing())
|
||||
this->refresh_capability(plugin_key, name, change);
|
||||
});
|
||||
};
|
||||
|
||||
// Subscribe before enumerating so a concurrent load cannot land between the initial
|
||||
// snapshot and callback registration. Duplicate notifications are safe: upsert is by
|
||||
// id and the m_actions scan in refresh_source is idempotent.
|
||||
manager.subscribe_on_load_callback(
|
||||
[on_source](const std::string& key) { on_source(key, ActionChange::Added); });
|
||||
manager.subscribe_on_unload_callback(
|
||||
[on_source](const std::string& key) { on_source(key, ActionChange::Removed); });
|
||||
manager.subscribe_on_capability_load_callback(
|
||||
[on_capability](const PluginCapabilityId& capability) {
|
||||
on_capability(capability, ActionChange::Added);
|
||||
});
|
||||
manager.subscribe_on_capability_unload_callback(
|
||||
[on_capability](const PluginCapabilityId& capability) {
|
||||
on_capability(capability, ActionChange::Removed);
|
||||
});
|
||||
|
||||
// enumerate current script capabilities
|
||||
std::unordered_map<std::string, std::string> source_names;
|
||||
for (const PluginDescriptor& desc : manager.get_plugin_descriptors())
|
||||
if (!desc.name.empty())
|
||||
source_names.emplace(desc.plugin_key, desc.name);
|
||||
|
||||
for (const auto& capability : manager.get_plugin_capabilities("", PluginCapabilityType::Script)) {
|
||||
if (!capability)
|
||||
continue;
|
||||
const std::string& key = capability->audit_plugin_key();
|
||||
auto it = source_names.find(key);
|
||||
const std::string& source_name = it == source_names.end() ? key : it->second;
|
||||
if (auto action = make_action(key, capability->name(), source_name))
|
||||
upsert(std::move(action));
|
||||
}
|
||||
}
|
||||
|
||||
void ActionRegistry::refresh_source(const std::string& plugin_key, ActionChange change)
|
||||
{
|
||||
assert(wxThread::IsMain());
|
||||
|
||||
// Remove this source's current actions. Collect first - erasing from m_actions while
|
||||
// iterating invalidates the iterator. why: m_actions (not the loader) is the source of
|
||||
// truth, so this is correct even after the plugin has already unloaded.
|
||||
std::vector<std::string> stale;
|
||||
for (const auto& [id, action] : m_actions)
|
||||
if (action->source_key() == plugin_key)
|
||||
stale.push_back(id);
|
||||
for (const std::string& id : stale)
|
||||
remove(id);
|
||||
|
||||
if (change == ActionChange::Removed)
|
||||
return;
|
||||
|
||||
PluginManager& manager = PluginManager::instance();
|
||||
const std::string source_name = find_loaded_source_name(manager, plugin_key);
|
||||
for (const auto& capability : manager.get_plugin_capabilities(plugin_key, PluginCapabilityType::Script)) {
|
||||
if (!capability)
|
||||
continue;
|
||||
if (auto action = make_action(plugin_key, capability->name(), source_name))
|
||||
upsert(std::move(action));
|
||||
}
|
||||
}
|
||||
|
||||
void ActionRegistry::refresh_capability(const std::string& plugin_key, const std::string& capability,
|
||||
ActionChange change)
|
||||
{
|
||||
assert(wxThread::IsMain());
|
||||
|
||||
const std::string id = PluginScriptAction::id_for(plugin_key, capability);
|
||||
if (change == ActionChange::Removed) {
|
||||
remove(id);
|
||||
return;
|
||||
}
|
||||
|
||||
PluginManager& manager = PluginManager::instance();
|
||||
if (auto action = make_action(plugin_key, capability, find_loaded_source_name(manager, plugin_key)))
|
||||
upsert(std::move(action));
|
||||
else
|
||||
remove(id);
|
||||
}
|
||||
|
||||
void ActionRegistry::upsert(std::unique_ptr<AppAction> action)
|
||||
{
|
||||
assert(wxThread::IsMain());
|
||||
if (!action)
|
||||
return;
|
||||
|
||||
seed_state(*action);
|
||||
std::string id = action->id();
|
||||
std::shared_ptr<AppAction> stored = std::move(action);
|
||||
m_actions.insert_or_assign(std::move(id), std::move(stored));
|
||||
}
|
||||
|
||||
void ActionRegistry::remove(const std::string& id)
|
||||
{
|
||||
assert(wxThread::IsMain());
|
||||
m_actions.erase(id);
|
||||
}
|
||||
|
||||
void ActionRegistry::seed_state(AppAction& a) const
|
||||
{
|
||||
auto favs = read_string_array("favourite_actions");
|
||||
a.favourite = std::find(favs.begin(), favs.end(), a.id()) != favs.end();
|
||||
|
||||
nlohmann::json stats = read_section("stats", nlohmann::json::object());
|
||||
auto it = stats.find(a.id());
|
||||
if (it != stats.end() && it->is_object()) {
|
||||
a.count = it->value("count", 0);
|
||||
a.last = it->value("last", 0LL);
|
||||
} else {
|
||||
a.count = 0;
|
||||
a.last = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// ---- read surface -----------------------------------------------------------
|
||||
|
||||
const AppAction* ActionRegistry::by_id(const std::string& id) const
|
||||
{
|
||||
assert(wxThread::IsMain());
|
||||
auto it = m_actions.find(id);
|
||||
return it == m_actions.end() ? nullptr : it->second.get();
|
||||
}
|
||||
|
||||
AppAction* ActionRegistry::find(const std::string& id)
|
||||
{
|
||||
return const_cast<AppAction*>(by_id(id));
|
||||
}
|
||||
|
||||
// ---- dispatch + write-through ----------------------------------------------
|
||||
|
||||
AppActionRunResult ActionRegistry::run(const std::string& id)
|
||||
{
|
||||
assert(wxThread::IsMain());
|
||||
auto it = m_actions.find(id);
|
||||
if (it == m_actions.end())
|
||||
return {}; // default Info, empty message
|
||||
// why: hold a shared_ptr keep-alive, never a bare map entry. A runner may pump a
|
||||
// nested event loop; a queued source refresh can erase the entry while the
|
||||
// keep-alive preserves the action until run returns.
|
||||
std::shared_ptr<AppAction> keep = it->second;
|
||||
AppActionRunResult o = keep->run();
|
||||
if (o.level == AppActionRunResult::Level::Busy)
|
||||
return o;
|
||||
|
||||
// Bump stats (write-through). Re-read to avoid clobbering a concurrent field.
|
||||
nlohmann::json stats = read_section("stats", nlohmann::json::object());
|
||||
if (!stats.is_object()) // corrupt (valid-JSON, non-object) value degrades to empty
|
||||
stats = nlohmann::json::object();
|
||||
nlohmann::json& e = stats[id];
|
||||
if (!e.is_object())
|
||||
e = nlohmann::json::object();
|
||||
e["count"] = e.value("count", 0) + 1;
|
||||
e["last"] = (long long) std::time(nullptr);
|
||||
write_section("stats", stats);
|
||||
if (AppAction* live = find(id)) { live->count = e["count"]; live->last = e["last"]; }
|
||||
return o;
|
||||
}
|
||||
|
||||
void ActionRegistry::set_favourite(const std::string& id, bool on)
|
||||
{
|
||||
assert(wxThread::IsMain());
|
||||
auto favs = read_string_array("favourite_actions");
|
||||
auto it = std::find(favs.begin(), favs.end(), id);
|
||||
if (on && it == favs.end())
|
||||
favs.push_back(id);
|
||||
if (!on && it != favs.end())
|
||||
favs.erase(it);
|
||||
write_section("favourite_actions", nlohmann::json(favs));
|
||||
if (AppAction* live = find(id))
|
||||
live->favourite = on;
|
||||
}
|
||||
|
||||
void ActionRegistry::reorder_favourites(const std::vector<std::string>& ids)
|
||||
{
|
||||
assert(wxThread::IsMain());
|
||||
auto cur = read_string_array("favourite_actions");
|
||||
std::vector<std::string> next;
|
||||
// keep the requested order, but only ids that are actually favourites (guard a bad payload)
|
||||
for (const auto& id : ids)
|
||||
if (std::find(cur.begin(), cur.end(), id) != cur.end() &&
|
||||
std::find(next.begin(), next.end(), id) == next.end())
|
||||
next.push_back(id);
|
||||
// why: don't drop favourites the page omitted (e.g. pins with no live action hidden from the bar)
|
||||
for (const auto& id : cur)
|
||||
if (std::find(next.begin(), next.end(), id) == next.end())
|
||||
next.push_back(id);
|
||||
write_section("favourite_actions", nlohmann::json(next));
|
||||
}
|
||||
|
||||
bool ActionRegistry::should_ask(const std::string& id) const
|
||||
{
|
||||
assert(wxThread::IsMain());
|
||||
auto arr = read_string_array("ask_suppressed");
|
||||
return std::find(arr.begin(), arr.end(), id) == arr.end();
|
||||
}
|
||||
|
||||
void ActionRegistry::suppress_ask(const std::string& id)
|
||||
{
|
||||
assert(wxThread::IsMain());
|
||||
auto arr = read_string_array("ask_suppressed");
|
||||
if (std::find(arr.begin(), arr.end(), id) == arr.end())
|
||||
arr.push_back(id);
|
||||
write_section("ask_suppressed", nlohmann::json(arr));
|
||||
}
|
||||
|
||||
// ---- snapshot ---------------------------------------------------------------
|
||||
|
||||
nlohmann::json ActionRegistry::snapshot() const
|
||||
{
|
||||
assert(wxThread::IsMain());
|
||||
std::vector<const AppAction*> sorted;
|
||||
sorted.reserve(m_actions.size());
|
||||
for (const auto& entry : m_actions)
|
||||
sorted.push_back(entry.second.get());
|
||||
|
||||
const long long now = (long long) std::time(nullptr);
|
||||
std::sort(sorted.begin(), sorted.end(), [&](const AppAction* a, const AppAction* b) {
|
||||
double sa = frecency_score(a->count, a->last, now);
|
||||
double sb = frecency_score(b->count, b->last, now);
|
||||
if (sa != sb)
|
||||
return sa > sb;
|
||||
if (a->title() != b->title())
|
||||
return a->title() < b->title();
|
||||
if (a->source_name() != b->source_name())
|
||||
return a->source_name() < b->source_name();
|
||||
return a->id() < b->id();
|
||||
});
|
||||
|
||||
nlohmann::json actions = nlohmann::json::array();
|
||||
for (const AppAction* a : sorted)
|
||||
actions.push_back({{"id", a->id()},
|
||||
{"title", a->title()},
|
||||
{"source", a->source_name()},
|
||||
{"shortcut", ""}});
|
||||
// why: favourites is the ORDERED pin list - it must come from favourite_actions
|
||||
// as stored, not be re-derived from the frecency-sorted actions (that would
|
||||
// reorder the favourites bar). The page (js) filters out ids with no live action itself.
|
||||
nlohmann::json favourites(read_string_array("favourite_actions"));
|
||||
return {{"actions", std::move(actions)}, {"favourites", std::move(favourites)}};
|
||||
}
|
||||
|
||||
}} // namespace Slic3r::GUI
|
||||
@@ -0,0 +1,135 @@
|
||||
#pragma once
|
||||
|
||||
#include <nlohmann/json.hpp>
|
||||
|
||||
#include <wx/string.h>
|
||||
#include <wx/thread.h>
|
||||
|
||||
#include <cassert>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <unordered_map>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
namespace Slic3r { namespace GUI {
|
||||
|
||||
// How a source's action set changed. Drives the registry's refresh handlers.
|
||||
enum class ActionChange { Added, Removed };
|
||||
|
||||
// Result of running an AppAction, in the action layer's own vocabulary. Concrete
|
||||
// actions translate their runner-specific result into this generic shape.
|
||||
struct AppActionRunResult
|
||||
{
|
||||
enum class Level { Success, Info, Error, Busy };
|
||||
|
||||
Level level = Level::Info;
|
||||
wxString message; // empty = "nothing worth showing"
|
||||
};
|
||||
|
||||
// A speed-dial action: identity + user-state seeded from config + how to run itself.
|
||||
// Abstract base - the only virtual is run(); concrete subclasses know how to run
|
||||
// and what their source is.
|
||||
// note: named AppAction, not Action - Slic3r::GUI::Action is already taken by
|
||||
// UnsavedChangesDialog's exit-action enum, and this header reaches most GUI TUs.
|
||||
struct AppAction
|
||||
{
|
||||
const std::string& id() const { return m_id; }
|
||||
const std::string& title() const { return m_title; }
|
||||
const std::string& source_key() const { return m_source_key; } // stable source identity
|
||||
const std::string& source_name() const { return m_source_name; } // source display name
|
||||
|
||||
// Builds the stable id "<prefix>:<title>:<source_key>". why: one place owns the
|
||||
// format - both the base ctor and the one raw-key lookup (removing a capability
|
||||
// without an action object) go through this; ids are never parsed back apart.
|
||||
static std::string compose_id(std::string_view prefix, std::string_view title, std::string_view source_key)
|
||||
{
|
||||
std::string out;
|
||||
out.reserve(prefix.size() + title.size() + source_key.size() + 2);
|
||||
out.append(prefix).append(1, ':').append(title).append(1, ':').append(source_key);
|
||||
return out;
|
||||
}
|
||||
|
||||
// seeded from AppConfig for the snapshot / sort:
|
||||
bool favourite = false;
|
||||
int count = 0;
|
||||
long long last = 0; // epoch seconds
|
||||
|
||||
virtual ~AppAction() = default;
|
||||
virtual AppActionRunResult run() const = 0; // re-resolves + runs (UI thread)
|
||||
|
||||
protected:
|
||||
// The definition is constructor-set and immutable. Refreshes replace an action
|
||||
// instead of mutating identity after the registry has indexed it by id.
|
||||
// why: source_key (not the display name) carries identity, so renaming the source's
|
||||
// display name leaves the id - and its persisted stats/favourite - intact.
|
||||
AppAction(std::string_view prefix, std::string title, std::string source_key, std::string source_name)
|
||||
: m_id(compose_id(prefix, title, source_key)),
|
||||
m_title(std::move(title)),
|
||||
m_source_key(std::move(source_key)),
|
||||
m_source_name(std::move(source_name)) {}
|
||||
|
||||
private:
|
||||
std::string m_id; // <prefix>:<title>:<source_key> - stable identity + AppConfig key
|
||||
std::string m_title; // display name
|
||||
std::string m_source_key; // stable identity of the action's source (e.g. plugin_key)
|
||||
std::string m_source_name; // display name of the action's source
|
||||
};
|
||||
|
||||
// Self-contained sink and single owner of runnable actions for the app session.
|
||||
//
|
||||
// Workflow:
|
||||
// 1. init() (once, UI thread) subscribes to the plugin loader and enumerates the
|
||||
// current script capabilities into actions.
|
||||
// 2. Loader load/unload callbacks route through refresh_source()/refresh_capability(),
|
||||
// which upsert()/remove() actions. The registry keeps the only action list and
|
||||
// restores persisted user state as actions arrive.
|
||||
// 3. Consumers use by_id(), snapshot(), and run() without knowing the source.
|
||||
//
|
||||
// note: there is exactly one source (script plugins), so it lives inline here rather
|
||||
// than behind a polymorphic source interface.
|
||||
class ActionRegistry
|
||||
{
|
||||
public:
|
||||
// Subscribes to the plugin loader and enumerates its current actions. Call once
|
||||
// on the UI thread after the plugin system is up; wires the initial list and live
|
||||
// updates together.
|
||||
void init();
|
||||
|
||||
// Takes ownership, seeds persisted state, then inserts the action or replaces
|
||||
// the action with the same id. A null action is ignored.
|
||||
void upsert(std::unique_ptr<AppAction> action);
|
||||
|
||||
// Removes the action with this id. Missing ids are a harmless no-op.
|
||||
void remove(const std::string& id);
|
||||
|
||||
// Always-clean read surface. UI thread only.
|
||||
const AppAction* by_id(const std::string& id) const;
|
||||
|
||||
// Dispatch + write-through (registry is the only thing that touches AppConfig).
|
||||
AppActionRunResult run(const std::string& id); // runs + bumps stats
|
||||
void set_favourite(const std::string& id, bool on);
|
||||
void reorder_favourites(const std::vector<std::string>& ids); // persist a new bar order
|
||||
|
||||
// Run-confirm gate, keyed by action id (per-action "don't ask again").
|
||||
bool should_ask(const std::string& id) const;
|
||||
void suppress_ask(const std::string& id);
|
||||
|
||||
// Flat, frecency-sorted snapshot for the webview: {actions:[...], favourites:[...]}.
|
||||
nlohmann::json snapshot() const;
|
||||
|
||||
private:
|
||||
void seed_state(AppAction& a) const; // favourite/stats from config
|
||||
AppAction* find(const std::string& id);
|
||||
|
||||
// Loader callbacks (marshalled to the UI thread) land here. refresh_source rebuilds
|
||||
// one plugin's whole action set; refresh_capability touches a single capability.
|
||||
void refresh_source(const std::string& plugin_key, ActionChange change);
|
||||
void refresh_capability(const std::string& plugin_key, const std::string& capability, ActionChange change);
|
||||
|
||||
bool m_started = false; // init() runs exactly once; guards double-subscription
|
||||
std::unordered_map<std::string, std::shared_ptr<AppAction>> m_actions; // UI-thread confined; no lock
|
||||
};
|
||||
|
||||
}} // namespace Slic3r::GUI
|
||||
+675
-939
File diff suppressed because it is too large
Load Diff
@@ -34,23 +34,28 @@
|
||||
#include "Widgets/CheckBox.hpp"
|
||||
#include "Widgets/ComboBox.hpp"
|
||||
#include "Widgets/ScrolledWindow.hpp"
|
||||
#include "Widgets/SwitchButton.hpp"
|
||||
#include "Widgets/SwitchButton.hpp" // Orca: SwitchBoard (m_nozzle_btn_panel); not transitively available here
|
||||
#include "Widgets/PopupWindow.hpp"
|
||||
#include <wx/simplebook.h>
|
||||
#include <wx/hashmap.h>
|
||||
|
||||
#include "slic3r/GUI/DeviceCore/DevUtil.h"
|
||||
|
||||
#include <optional>
|
||||
|
||||
#define MAPPING_ITEM_INVALID_REMAIN -1
|
||||
|
||||
namespace Slic3r {
|
||||
// Previous definitions
|
||||
namespace Slic3r
|
||||
{
|
||||
class DevNozzleRack;
|
||||
namespace GUI { class wgtDeviceNozzleRackSelect; }
|
||||
}
|
||||
namespace GUI
|
||||
{
|
||||
class wgtDeviceNozzleRackSelect;
|
||||
class wgtMsgPanel;
|
||||
};
|
||||
};
|
||||
|
||||
namespace Slic3r { namespace GUI {
|
||||
namespace Slic3r {
|
||||
namespace GUI {
|
||||
|
||||
|
||||
#define AMS_TOTAL_COUNT 4
|
||||
@@ -64,7 +69,8 @@ enum TrayType {
|
||||
enum ShowType {
|
||||
LEFT, // only show left ams and left ext
|
||||
RIGHT, //only show right ams and right ext
|
||||
LEFT_AND_RIGHT //show left and right ams at the same time
|
||||
LEFT_AND_RIGHT, //show left and right ams at the same time
|
||||
LEFT_AND_RIGHT_DYNAMIC //show all left and right at one panel when use_dynamic_switch
|
||||
};
|
||||
|
||||
struct TrayData
|
||||
@@ -85,26 +91,59 @@ struct TrayData
|
||||
class MaterialItem: public wxPanel
|
||||
{
|
||||
protected:
|
||||
int m_text_pos_x = 0;
|
||||
int m_text_pos_y = -1;
|
||||
bool m_dropdown_allow_painted = true;
|
||||
wxString m_mapping_text;
|
||||
|
||||
public:
|
||||
MaterialItem(wxWindow *parent, wxColour mcolour, wxString mname);
|
||||
// Orca: filament_id defaults to empty so the existing 3-arg callers keep compiling;
|
||||
// m_filament_id is otherwise unused here.
|
||||
MaterialItem(wxWindow *parent, wxColour mcolour, wxString mname, std::string filament_id = std::string());
|
||||
~MaterialItem();
|
||||
|
||||
wxPanel* m_main_panel;
|
||||
void allow_paint_dropdown(bool flag);
|
||||
|
||||
void set_ams_text(const wxString& txt);
|
||||
void set_ams_info(wxColour col, wxString txt,
|
||||
int ctype = 0, std::vector<wxColour> cols = std::vector<wxColour>(),
|
||||
bool record_back_info = false);
|
||||
|
||||
void set_material_cols(int ctype, const std::vector<wxColour>& cols);
|
||||
|
||||
void reset_ams_info();
|
||||
virtual void reset_valid_info();
|
||||
|
||||
void set_nozzle_info(const wxString& mapped_nozzle_str);
|
||||
|
||||
void disable();
|
||||
void enable();
|
||||
void on_normal();
|
||||
void on_selected();
|
||||
void on_warning();
|
||||
|
||||
bool is_selected() const { return m_selected;}
|
||||
bool is_warning() const { return m_warning;}
|
||||
|
||||
void msw_rescale();
|
||||
|
||||
protected:
|
||||
void messure_size();
|
||||
|
||||
public:
|
||||
std::string m_filament_id;
|
||||
|
||||
wxColour m_material_coloul;
|
||||
int m_material_ctype = 0;
|
||||
std::vector<wxColour> m_material_cols;
|
||||
wxString m_material_name;
|
||||
wxString m_mapped_nozzle_str;
|
||||
|
||||
//info
|
||||
wxColour m_ams_coloul;
|
||||
wxString m_ams_name;
|
||||
// Physical nozzle(s) the print-dispatch mapping assigned to this filament (e.g. "R1", "L R").
|
||||
// Empty on printers without a nozzle rack or filament switcher; when set, the card grows a row.
|
||||
wxString m_mapped_nozzle_str;
|
||||
int m_ams_ctype = 0;
|
||||
std::vector<wxColour> m_ams_cols = std::vector<wxColour>();
|
||||
|
||||
//reset
|
||||
wxColour m_back_ams_coloul;
|
||||
wxString m_back_ams_name;
|
||||
@@ -124,32 +163,17 @@ public:
|
||||
bool m_match {true};
|
||||
bool m_enable {true};
|
||||
|
||||
void msw_rescale();
|
||||
void allow_paint_dropdown(bool flag);
|
||||
void set_ams_info(wxColour col, wxString txt, int ctype=0, std::vector<wxColour> cols= std::vector<wxColour>(),bool record_back_info = false);
|
||||
void reset_ams_info();
|
||||
// Set the mapped-nozzle label ("R1", "L R", ...); grows/shrinks the card as the row appears/clears.
|
||||
void set_nozzle_info(const wxString& mapped_nozzle_str);
|
||||
// Size the card: base swatch height, plus one row when a nozzle label is present.
|
||||
void messure_size();
|
||||
|
||||
void disable();
|
||||
void enable();
|
||||
void on_normal();
|
||||
void on_selected();
|
||||
void on_warning();
|
||||
|
||||
void paintEvent(wxPaintEvent &evt);
|
||||
virtual void render(wxDC &dc);
|
||||
void match(bool mat);
|
||||
virtual void doRender(wxDC &dc);
|
||||
virtual void reset_valid_info();
|
||||
};
|
||||
|
||||
class MaterialSyncItem : public MaterialItem
|
||||
{
|
||||
public:
|
||||
MaterialSyncItem(wxWindow *parent, wxColour mcolour, wxString mname);
|
||||
// Orca: filament_id defaults to empty for existing 3-arg callers (see MaterialItem).
|
||||
MaterialSyncItem(wxWindow *parent, wxColour mcolour, wxString mname, std::string filament_id = std::string());
|
||||
~MaterialSyncItem();
|
||||
int get_real_offset();
|
||||
void render(wxDC &dc) override;
|
||||
@@ -181,7 +205,7 @@ public:
|
||||
public:
|
||||
void update_data(TrayData data);
|
||||
void send_event(int fliament_id);
|
||||
void set_data(const wxString& tag_name, wxColour colour, wxString name, bool remain_detect, TrayData data, bool unmatch = false);
|
||||
void set_data(const wxString& tag_name, wxColour colour, wxString name, bool remain_detect, TrayData data, bool unmatch = false, std::optional<wxString> tooltip_opt = std::nullopt);
|
||||
void set_checked(bool checked);
|
||||
void set_tray_index(wxString t_index) { m_tray_index = t_index; };
|
||||
|
||||
@@ -222,13 +246,38 @@ protected:
|
||||
void doRender(wxDC& dc);
|
||||
};
|
||||
|
||||
class DevIconLabel : public wxPanel
|
||||
{
|
||||
public:
|
||||
DevIconLabel(wxWindow* parent, const wxString& icon, const wxString& label);
|
||||
|
||||
public:
|
||||
void SetAllBackgroundColor(const wxColour& color);
|
||||
|
||||
Label* GetLabelItem() const { return m_label; }
|
||||
void SetLabel(const wxString& label);
|
||||
void SetIcon(const wxString& icon);
|
||||
|
||||
void Rescale();
|
||||
|
||||
private:
|
||||
void CreateGui();
|
||||
|
||||
private:
|
||||
Label* m_label{ nullptr };
|
||||
wxString m_icon_str;
|
||||
wxStaticBitmap* m_icon{ nullptr };
|
||||
};
|
||||
|
||||
class AmsMapingPopup : public PopupWindow
|
||||
{
|
||||
bool m_use_in_sync_dialog = false;
|
||||
bool m_ams_remain_detect_flag = false;
|
||||
bool m_ext_mapping_filatype_check = true;
|
||||
wxStaticText* m_title_text{ nullptr };
|
||||
|
||||
wgtDeviceNozzleRackSelect *m_rack_nozzle_select{nullptr};
|
||||
DevIconLabel* m_flush_warning_panel;
|
||||
|
||||
public:
|
||||
AmsMapingPopup(wxWindow *parent,bool use_in_sync_dialog = false);
|
||||
~AmsMapingPopup() {};
|
||||
@@ -244,11 +293,11 @@ public:
|
||||
bool m_has_unmatch_filament {false};
|
||||
int m_current_filament_id;
|
||||
ShowType m_show_type{ShowType::RIGHT};
|
||||
// Orca: set from update() — true when a Filament Track Switch is installed, which makes the
|
||||
// external-spool slots un-pickable (false on any printer without a switch, so behavior is unchanged).
|
||||
bool m_fila_switch_installed{false};
|
||||
std::string m_tag_material;
|
||||
wxScrolledWindow *m_scrolled_window{nullptr};
|
||||
wxBoxSizer *m_sizer_main{nullptr};
|
||||
wxBoxSizer *m_sizer_main_h{nullptr};
|
||||
wxBoxSizer *m_sizer_ams_v{nullptr};
|
||||
wxBoxSizer *m_sizer_ams{nullptr};
|
||||
wxBoxSizer *m_sizer_ams_left{nullptr};
|
||||
wxBoxSizer *m_sizer_ams_right{nullptr};
|
||||
@@ -262,26 +311,27 @@ public:
|
||||
MappingItem* m_right_extra_slot{nullptr};
|
||||
|
||||
wxPanel * m_left_marea_panel{nullptr};
|
||||
wxPanel * m_right_marea_panel{nullptr};
|
||||
wxPanel* m_right_marea_panel{ nullptr }; // used as right if both left and right sides shown. used as single panel if only one side shown.
|
||||
wxPanel * m_left_first_text_panel{nullptr};
|
||||
wxPanel * m_right_first_text_panel{nullptr};
|
||||
wgtMsgPanel* m_ams_tips_msg_panel{nullptr};
|
||||
wxPanel * m_split_line_panel{nullptr};
|
||||
wxBoxSizer * m_left_split_ams_sizer{nullptr};
|
||||
wxBoxSizer * m_right_split_ams_sizer{nullptr};
|
||||
wxBoxSizer * m_right_split_ext_sizer{ nullptr };
|
||||
Label * m_left_tips{nullptr};
|
||||
Label * m_right_tips{nullptr};
|
||||
|
||||
ScalableButton* m_reset_btn{nullptr};
|
||||
wxString m_single_tip_text;
|
||||
wxString m_left_tip_text;
|
||||
wxString m_right_tip_text;
|
||||
wxString m_ams_tips_panel_text;
|
||||
wxBoxSizer* m_sizer_split_ams_left;
|
||||
wxBoxSizer* m_sizer_split_ams_right;
|
||||
bool m_mapping_from_multi_machines {false};
|
||||
|
||||
// Rack nozzle manual-pick (rack printers only; hidden by default via Show(false)).
|
||||
wgtDeviceNozzleRackSelect* m_rack_nozzle_select{nullptr};
|
||||
Label* m_flush_warning_panel{nullptr}; // Orca: uses a plain Label for the flush warning
|
||||
std::weak_ptr<DevNozzleRack> m_rack;
|
||||
|
||||
bool get_use_in_sync_dialog() { return m_use_in_sync_dialog; }
|
||||
void set_sizer_title(wxBoxSizer *sizer, wxString text);
|
||||
wxBoxSizer* create_split_sizer(wxWindow* parent, wxString text);
|
||||
void set_send_win(wxWindow* win) {send_win = win;};
|
||||
@@ -289,8 +339,6 @@ public:
|
||||
void set_tag_texture(std::string texture);
|
||||
void update(MachineObject* obj, const std::vector<FilamentInfo>& ams_mapping_result, bool use_dynamic_switch = false, std::optional<PrintFromType> print_type = std::nullopt);
|
||||
void update_rack_select(MachineObject* obj, bool use_dynamic_switch, std::optional<PrintFromType> print_type);
|
||||
void update_flush_waste(MachineObject* obj);
|
||||
void update_title(MachineObject* obj);
|
||||
void update_items_check_state(const std::vector<FilamentInfo>& ams_mapping_result);
|
||||
void update_ams_data_multi_machines();
|
||||
void add_ams_mapping(std::vector<TrayData> tray_data, bool remain_detect_flag, wxWindow *container, wxBoxSizer *sizer);
|
||||
@@ -304,15 +352,24 @@ public:
|
||||
void paintEvent(wxPaintEvent &evt);
|
||||
void set_parent_item(MaterialItem* item) {m_parent_item = item;};
|
||||
void set_show_type(ShowType type) { m_show_type = type; };
|
||||
// Orca: kept as a no-op for SelectMachine — its only caller passes false, which
|
||||
// equals the update path's default (all slots shown).
|
||||
void set_only_show_ext_spool(bool /*flag*/) {}
|
||||
// Orca: dropped by the reference but still consumed by SelectMachine.
|
||||
std::vector<TrayData> parse_ams_mapping(const std::map<std::string, DevAms*, NumericStrCompare>& amsList);
|
||||
|
||||
#ifdef __APPLE__
|
||||
void on_mouse_move(wxMouseEvent &evt);
|
||||
wxPopupWindow * m_tip_popup{nullptr};
|
||||
Label* m_tip_label{nullptr};
|
||||
#endif
|
||||
|
||||
using ResetCallback = std::function<void(const std::string&)>;
|
||||
void reset_ams_info();
|
||||
void set_reset_callback(ResetCallback callback);
|
||||
void show_reset_button();
|
||||
void set_material_index_str(std::string str) { m_material_index = str; }
|
||||
const std::string &get_material_index_str() { return m_material_index; }
|
||||
void set_only_show_ext_spool(bool flag);
|
||||
|
||||
public:
|
||||
void msw_rescale();
|
||||
@@ -320,11 +377,20 @@ public:
|
||||
void EnableExtMappingFilaTypeCheck(bool to_check = true) { m_ext_mapping_filatype_check = to_check;} ;
|
||||
|
||||
private:
|
||||
// update
|
||||
void update_title(MachineObject* obj);
|
||||
void update_ams_tips(MachineObject* obj);
|
||||
void update_mapping_items(MachineObject* obj, const std::vector<FilamentInfo>& ams_mapping_result, bool use_dynamic_switch);
|
||||
|
||||
// events
|
||||
void OnNozzleMappingSelected(wxCommandEvent& evt);
|
||||
void update_flush_waste(MachineObject* obj);
|
||||
|
||||
private:
|
||||
std::weak_ptr<DevNozzleRack> m_rack;
|
||||
|
||||
ResetCallback m_reset_callback{nullptr};
|
||||
std::string m_material_index;
|
||||
bool m_only_show_ext_spool{false};
|
||||
};
|
||||
|
||||
class AmsMapingTipPopup : public PopupWindow
|
||||
|
||||
@@ -0,0 +1,708 @@
|
||||
/*****************************************************************//**
|
||||
* \file AmsMappingPopupUpdate.cpp
|
||||
* \brief do update for AmsMappingPopup
|
||||
* \note moved from AmsMappingPopup.cpp to here to reduce the code size of AmsMappingPopup.cpp
|
||||
*
|
||||
* \author xin.zhang
|
||||
* \date February 2026
|
||||
*********************************************************************/
|
||||
#include "AmsMappingPopup.hpp"
|
||||
#include "I18N.hpp"
|
||||
#include "DeviceCore/DevConfigUtil.h"
|
||||
|
||||
#include "libslic3r/Utils.hpp"
|
||||
#include "slic3r/Utils/WxFontUtils.hpp"
|
||||
#include "GUI.hpp"
|
||||
#include "GUI_App.hpp"
|
||||
#include "GUI_Preview.hpp"
|
||||
#include "MainFrame.hpp"
|
||||
#include "format.hpp"
|
||||
#include "Widgets/ProgressDialog.hpp"
|
||||
#include "Widgets/RoundedRectangle.hpp"
|
||||
#include "Widgets/StaticBox.hpp"
|
||||
|
||||
#include <wx/progdlg.h>
|
||||
#include <wx/clipbrd.h>
|
||||
#include <wx/dcgraph.h>
|
||||
#include <wx/graphics.h>
|
||||
#include <miniz.h>
|
||||
#include <algorithm>
|
||||
#include <optional>
|
||||
#include "Plater.hpp"
|
||||
#include "BitmapCache.hpp"
|
||||
|
||||
#include "DeviceCore/DevFilaSystem.h"
|
||||
#include "DeviceCore/DevFilaSwitch.h"
|
||||
#include "DeviceCore/DevMappingNozzle.h"
|
||||
#include "DeviceCore/DevNozzleRack.h" // Orca: full type for GetNozzleSystem()->GetNozzleRack()->IsSupported()
|
||||
|
||||
#include "DeviceTab/wgtDeviceNozzleSelect.h"
|
||||
#include "DeviceTab/wgtMsgPanel.h"
|
||||
|
||||
namespace Slic3r::GUI {
|
||||
|
||||
|
||||
static void _add_containers(const AmsMapingPopup* win,
|
||||
std::list<MappingContainer*>& one_slot_containers,
|
||||
const std::vector<MappingContainer*>& four_slots_containers,
|
||||
wxBoxSizer* target_sizer)
|
||||
{
|
||||
for (auto container : four_slots_containers) { target_sizer->Add(container, 0, wxTOP, win->FromDIP(5)); }
|
||||
|
||||
while (!one_slot_containers.empty()) {
|
||||
wxBoxSizer* sizer = new wxBoxSizer(wxHORIZONTAL);
|
||||
for (int i = 0; i < 3; i++) {
|
||||
if (one_slot_containers.empty()) { break; }
|
||||
|
||||
sizer->Add(one_slot_containers.front(), 0, wxLEFT, (i == 0) ? 0 : win->FromDIP(5));
|
||||
one_slot_containers.pop_front();
|
||||
}
|
||||
|
||||
target_sizer->Add(sizer, 0, wxTOP, win->FromDIP(5));
|
||||
}
|
||||
}
|
||||
|
||||
void AmsMapingPopup::update(MachineObject* obj,
|
||||
const std::vector<FilamentInfo>& ams_mapping_result,
|
||||
bool use_dynamic_switch,
|
||||
std::optional<PrintFromType> print_type)
|
||||
{
|
||||
if (!obj) { return; } // Orca: guard before use (REF logs before its null check)
|
||||
|
||||
BOOST_LOG_TRIVIAL(info) << "ams_mapping total count " << obj->GetFilaSystem()->GetAmsCount();
|
||||
|
||||
for (auto& ams_container : m_amsmapping_container_list) {
|
||||
ams_container->Destroy();
|
||||
}
|
||||
|
||||
m_amsmapping_container_list.clear();
|
||||
m_amsmapping_container_sizer_list.clear();
|
||||
m_mapping_item_list.clear();
|
||||
|
||||
/*title*/
|
||||
update_title(obj);
|
||||
|
||||
/*ams mapping*/
|
||||
update_ams_tips(obj);
|
||||
update_mapping_items(obj, ams_mapping_result, use_dynamic_switch);
|
||||
|
||||
/*rack*/
|
||||
update_rack_select(obj, use_dynamic_switch, print_type);
|
||||
update_flush_waste(obj);
|
||||
|
||||
if (wxGetApp().dark_mode() && m_reset_btn->GetName() != "erase_dark") {
|
||||
m_reset_btn->SetName("erase_dark");
|
||||
m_reset_btn->SetBitmap(ScalableBitmap(m_right_first_text_panel, "erase_dark", 14).bmp());
|
||||
} else if (!wxGetApp().dark_mode() && m_reset_btn->GetName() != "erase") {
|
||||
m_reset_btn->SetName("erase");
|
||||
m_reset_btn->SetBitmap(ScalableBitmap(m_right_first_text_panel, "erase", 14).bmp());
|
||||
}
|
||||
|
||||
size_t extruder_num = obj->GetExtderSystem()->GetTotalExtderCount();
|
||||
if (extruder_num == 1) {
|
||||
m_left_marea_panel->Hide();
|
||||
m_left_extra_slot->Hide();
|
||||
m_split_line_panel->Hide();
|
||||
m_right_marea_panel->Show();
|
||||
m_right_marea_panel->Enable(true);
|
||||
m_right_extra_slot->Show();
|
||||
m_right_extra_slot->Enable(true);
|
||||
m_right_split_ext_sizer->Show(true);
|
||||
set_sizer_title(m_right_split_ams_sizer, _L("AMS"));
|
||||
} else if (extruder_num > 1) {
|
||||
if (m_show_type == ShowType::LEFT_AND_RIGHT_DYNAMIC) {
|
||||
m_left_marea_panel->Show(false);
|
||||
m_left_extra_slot->Show(false);
|
||||
m_right_extra_slot->Show(false);
|
||||
m_left_split_ams_sizer->Show(false);
|
||||
m_right_split_ams_sizer->Show(false);
|
||||
m_right_split_ext_sizer->Show(false);
|
||||
m_split_line_panel->Hide();
|
||||
|
||||
m_right_marea_panel->Show();
|
||||
m_right_marea_panel->Enable(true);
|
||||
} else {
|
||||
|
||||
m_left_marea_panel->Show();
|
||||
m_left_extra_slot->Show();
|
||||
m_split_line_panel->Show();
|
||||
m_right_marea_panel->Show();
|
||||
m_right_extra_slot->Show();
|
||||
m_right_split_ext_sizer->Show(true);
|
||||
m_left_tips->SetLabel(m_left_tip_text);
|
||||
m_right_tips->SetLabel(m_right_tip_text);
|
||||
set_sizer_title(m_left_split_ams_sizer, _L("Left AMS"));
|
||||
set_sizer_title(m_right_split_ams_sizer, _L("Right AMS"));
|
||||
if (m_show_type == ShowType::LEFT) {
|
||||
m_left_marea_panel->Enable(true);
|
||||
m_left_extra_slot->Enable(true);
|
||||
m_right_marea_panel->Enable(false);
|
||||
m_right_extra_slot->Enable(false);
|
||||
if (m_use_in_sync_dialog) {
|
||||
m_left_tips->SetLabel(m_single_tip_text);
|
||||
m_right_tips->SetLabel("");
|
||||
}
|
||||
} else if (m_show_type == ShowType::RIGHT) {
|
||||
m_left_marea_panel->Enable(false);
|
||||
m_left_extra_slot->Enable(false);
|
||||
m_right_marea_panel->Enable(true);
|
||||
m_right_extra_slot->Enable(true);
|
||||
if (m_use_in_sync_dialog) {
|
||||
// m_right_tips->SetLabel(m_single_tip_text);
|
||||
m_left_tips->SetLabel("");
|
||||
}
|
||||
} else if (m_show_type == ShowType::LEFT_AND_RIGHT) {
|
||||
m_left_marea_panel->Enable(true);
|
||||
m_left_extra_slot->Enable(true);
|
||||
m_right_marea_panel->Enable(true);
|
||||
m_right_extra_slot->Enable(true);
|
||||
if (m_use_in_sync_dialog) {
|
||||
m_left_tips->SetLabel(m_single_tip_text);
|
||||
m_right_tips->SetLabel("");
|
||||
}
|
||||
} else if (m_show_type == ShowType::LEFT_AND_RIGHT_DYNAMIC) {
|
||||
m_left_marea_panel->Show(false);
|
||||
m_left_extra_slot->Show(false);
|
||||
m_right_extra_slot->Show(false);
|
||||
m_left_split_ams_sizer->Show(false);
|
||||
m_right_split_ams_sizer->Show(false);
|
||||
m_right_split_ext_sizer->Show(false);
|
||||
m_split_line_panel->Hide();
|
||||
|
||||
m_right_marea_panel->Show();
|
||||
m_right_marea_panel->Enable(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Layout();
|
||||
Fit();
|
||||
Refresh();
|
||||
}
|
||||
|
||||
// Orca: the kept fila model has no tray-level ams_type; the owning AMS type is passed in
|
||||
// (DevAms::GetAmsType() for AMS trays, DevAmsType::EXT_SPOOL for the virtual/ext slots).
|
||||
static std::optional<TrayData> sGetTrayData(DevAmsTray* tray,
|
||||
const std::string& ams_id_str,
|
||||
DevAmsType ams_type)
|
||||
{
|
||||
int ams_id = 0;
|
||||
int tray_id = 0;
|
||||
try {
|
||||
ams_id = std::stoi(ams_id_str);
|
||||
tray_id = atoi(tray->id.c_str());
|
||||
} catch (const std::exception& e) {
|
||||
BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << ":[error] invalid ams_id or tray_id: " << e.what();
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
TrayData td;
|
||||
td.ams_id = ams_id;
|
||||
if (ams_type != DevAmsType::EXT_SPOOL) {
|
||||
td.slot_id = tray_id;
|
||||
} else {
|
||||
td.slot_id = 0;
|
||||
}
|
||||
|
||||
if (ams_type == DevAmsType::AMS ||
|
||||
ams_type == DevAmsType::AMS_LITE ||
|
||||
ams_type == DevAmsType::N3F) {
|
||||
td.id = ams_id * 4 + tray_id;
|
||||
} else if (ams_type == DevAmsType::N3S) {
|
||||
td.id = ams_id + tray_id;
|
||||
} else if(ams_type == DevAmsType::EXT_SPOOL){
|
||||
td.id = tray_id;
|
||||
}
|
||||
|
||||
if (ams_type != EXT_SPOOL && !tray->is_exists) {
|
||||
td.type = EMPTY;
|
||||
} else {
|
||||
if (!tray->is_tray_info_ready()) {
|
||||
td.type = THIRD;
|
||||
} else {
|
||||
td.type = NORMAL;
|
||||
td.remain = tray->remain;
|
||||
td.colour = DevAmsTray::decode_color(tray->color);
|
||||
td.name = tray->get_display_filament_type();
|
||||
td.filament_type = tray->get_filament_type();
|
||||
td.ctype = tray->ctype;
|
||||
for (const auto& col : tray->cols) {
|
||||
td.material_cols.push_back(DevAmsTray::decode_color(col));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return td;
|
||||
}
|
||||
|
||||
void AmsMapingPopup::update_mapping_items(MachineObject* obj, const std::vector<FilamentInfo>& ams_mapping_result, bool use_dynamic_switch)
|
||||
{
|
||||
std::list<MappingContainer*> left_one_slot_containers;
|
||||
std::list<MappingContainer*> right_one_slot_containers;
|
||||
std::vector<MappingContainer*> left_four_slots_containers;
|
||||
std::vector<MappingContainer*> right_four_slot_containers;
|
||||
|
||||
const auto& ams_list = obj->GetFilaSystem()->GetAmsList();
|
||||
for (auto ams_iter = ams_list.begin(); ams_iter != ams_list.end(); ams_iter++) {
|
||||
const auto& extruder_set = ams_iter->second->GetBindedExtruderSet();
|
||||
if (extruder_set.size() <= 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
wxPanel* target_panel = nullptr;
|
||||
if (m_show_type == ShowType::LEFT_AND_RIGHT_DYNAMIC) {
|
||||
target_panel = m_right_marea_panel;
|
||||
} else {
|
||||
int extruder_id = *extruder_set.begin();
|
||||
if (extruder_id == MAIN_EXTRUDER_ID) {
|
||||
target_panel = m_right_marea_panel;
|
||||
} else if (extruder_id == DEPUTY_EXTRUDER_ID) {
|
||||
target_panel = m_left_marea_panel;
|
||||
}
|
||||
}
|
||||
|
||||
if (!target_panel) {
|
||||
continue;
|
||||
}
|
||||
|
||||
auto sizer_mapping_list = new wxBoxSizer(wxHORIZONTAL);
|
||||
auto ams_mapping_item_container = new MappingContainer(target_panel, ams_iter->second->GetDisplayName(), ams_iter->second->GetSlotCount());
|
||||
ams_mapping_item_container->SetName(target_panel->GetName());
|
||||
ams_mapping_item_container->SetSizer(sizer_mapping_list);
|
||||
ams_mapping_item_container->Layout();
|
||||
|
||||
BOOST_LOG_TRIVIAL(trace) << "ams_mapping ams id " << ams_iter->first.c_str();
|
||||
|
||||
DevAms* ams_group = ams_iter->second;
|
||||
auto ams_type = ams_group->GetAmsType();
|
||||
std::vector<TrayData> tray_datas;
|
||||
std::map<std::string, DevAmsTray*>::const_iterator tray_iter;
|
||||
for (tray_iter = ams_group->GetTrays().cbegin(); tray_iter != ams_group->GetTrays().cend(); tray_iter++) {
|
||||
const auto& td_opt = sGetTrayData(tray_iter->second, ams_group->GetAmsId(), ams_type);
|
||||
if (td_opt.has_value()) {
|
||||
tray_datas.push_back(td_opt.value());
|
||||
}
|
||||
}
|
||||
|
||||
ams_mapping_item_container->Show();
|
||||
add_ams_mapping(tray_datas, obj->GetFilaSystem()->IsDetectRemainEnabled(), ams_mapping_item_container, sizer_mapping_list);
|
||||
m_amsmapping_container_sizer_list.push_back(sizer_mapping_list);
|
||||
m_amsmapping_container_list.push_back(ams_mapping_item_container);
|
||||
|
||||
if (target_panel == m_right_marea_panel) {
|
||||
if (ams_mapping_item_container->get_slots_num() == 1) {
|
||||
right_one_slot_containers.push_back(ams_mapping_item_container);
|
||||
} else {
|
||||
right_four_slot_containers.push_back(ams_mapping_item_container);
|
||||
}
|
||||
} else if (target_panel == m_left_marea_panel) {
|
||||
if (ams_mapping_item_container->get_slots_num() == 1) {
|
||||
left_one_slot_containers.push_back(ams_mapping_item_container);
|
||||
} else {
|
||||
left_four_slots_containers.push_back(ams_mapping_item_container);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = obj->vt_slot.size() - 1; i >= 0; i--) {
|
||||
DevAmsTray* tray_data = &obj->vt_slot[i];
|
||||
const auto& td_opt = sGetTrayData(tray_data, tray_data->id, DevAmsType::EXT_SPOOL);
|
||||
if (!td_opt.has_value()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (m_show_type == ShowType::LEFT_AND_RIGHT_DYNAMIC) {
|
||||
auto sizer_mapping_list = new wxBoxSizer(wxHORIZONTAL);
|
||||
const auto& shown_name = td_opt->ams_id == VIRTUAL_TRAY_MAIN_ID ? "Ext-R" : "Ext-L";
|
||||
auto ams_mapping_item_container = new MappingContainer(m_right_marea_panel, shown_name, 1);
|
||||
ams_mapping_item_container->SetName(m_right_marea_panel->GetName());
|
||||
ams_mapping_item_container->SetSizer(sizer_mapping_list);
|
||||
ams_mapping_item_container->Layout();
|
||||
|
||||
std::vector<TrayData> tray_datas;
|
||||
tray_datas.push_back(td_opt.value());
|
||||
add_ams_mapping(tray_datas, false, ams_mapping_item_container, sizer_mapping_list);
|
||||
m_amsmapping_container_sizer_list.push_back(sizer_mapping_list);
|
||||
m_amsmapping_container_list.push_back(ams_mapping_item_container);
|
||||
right_one_slot_containers.push_back(ams_mapping_item_container);
|
||||
} else {
|
||||
if (td_opt->ams_id == VIRTUAL_TRAY_MAIN_ID) {
|
||||
m_right_extra_slot->send_win = send_win;
|
||||
add_ext_ams_mapping(td_opt.value(), m_right_extra_slot);
|
||||
} else if (td_opt->ams_id == VIRTUAL_TRAY_DEPUTY_ID) {
|
||||
m_left_extra_slot->send_win = send_win;
|
||||
add_ext_ams_mapping(td_opt.value(), m_left_extra_slot);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_add_containers(this, left_one_slot_containers, left_four_slots_containers, m_sizer_ams_basket_left);
|
||||
_add_containers(this, right_one_slot_containers, right_four_slot_containers, m_sizer_ams_basket_right);
|
||||
if (m_show_type != ShowType::LEFT_AND_RIGHT_DYNAMIC){
|
||||
m_left_split_ams_sizer->Show(!left_one_slot_containers.empty() || !left_four_slots_containers.empty());
|
||||
m_right_split_ams_sizer->Show(!right_one_slot_containers.empty() || !right_four_slot_containers.empty());
|
||||
} else {
|
||||
m_left_split_ams_sizer->Show(false);
|
||||
m_right_split_ams_sizer->Show(false);
|
||||
}
|
||||
|
||||
update_items_check_state(ams_mapping_result);
|
||||
}
|
||||
|
||||
void AmsMapingPopup::update_ams_data_multi_machines()
|
||||
{
|
||||
m_mapping_from_multi_machines = true;
|
||||
|
||||
std::vector<TrayData> tray_datas;
|
||||
for (int i = 0; i < 4; ++i) {
|
||||
TrayData td;
|
||||
td.id = i;
|
||||
td.type = EMPTY;
|
||||
td.colour = wxColour(166, 169, 170);
|
||||
td.name = "";
|
||||
td.filament_type = "";
|
||||
td.ctype = 0;
|
||||
tray_datas.push_back(td);
|
||||
}
|
||||
|
||||
for (auto& ams_container : m_amsmapping_container_list) {
|
||||
ams_container->Destroy();
|
||||
}
|
||||
|
||||
m_amsmapping_container_list.clear();
|
||||
m_amsmapping_container_sizer_list.clear();
|
||||
m_mapping_item_list.clear();
|
||||
|
||||
if (wxGetApp().dark_mode() && m_reset_btn->GetName() != "erase_dark") {
|
||||
m_reset_btn->SetName("erase_dark");
|
||||
m_reset_btn->SetBitmap(ScalableBitmap(m_right_first_text_panel, "erase_dark", 14).bmp());
|
||||
} else if (!wxGetApp().dark_mode() && m_reset_btn->GetName() != "erase") {
|
||||
m_reset_btn->SetName("erase");
|
||||
m_reset_btn->SetBitmap(ScalableBitmap(m_right_first_text_panel, "erase", 14).bmp());
|
||||
}
|
||||
|
||||
size_t nozzle_nums = 1;
|
||||
m_show_type = ShowType::RIGHT;
|
||||
|
||||
m_left_marea_panel->Hide();
|
||||
m_left_extra_slot->Hide();
|
||||
// m_left_marea_panel->Show();
|
||||
m_right_marea_panel->Show();
|
||||
set_sizer_title(m_right_split_ams_sizer, _L("AMS"));
|
||||
// m_right_tips->SetLabel(m_single_tip_text);
|
||||
m_right_extra_slot->Hide();
|
||||
m_left_extra_slot->Hide();
|
||||
|
||||
|
||||
|
||||
/*ams*/
|
||||
bool has_left_ams = false, has_right_ams = false;
|
||||
std::list<MappingContainer*> left_one_slot_containers;
|
||||
std::list<MappingContainer*> right_one_slot_containers;
|
||||
std::vector<MappingContainer*> left_four_slots_containers;
|
||||
std::vector<MappingContainer*> right_four_slot_containers;
|
||||
for (int i = 0; i < 1; i++) {
|
||||
int ams_indx = 0;
|
||||
int ams_type = 1;
|
||||
int nozzle_id = 0;
|
||||
|
||||
if (ams_type >= 1 || ams_type <= 3) { // 1:ams 2:ams-lite 3:n3f
|
||||
|
||||
auto sizer_mapping_list = new wxBoxSizer(wxHORIZONTAL);
|
||||
auto ams_mapping_item_container = new MappingContainer(nozzle_id == 0 ? m_right_marea_panel : m_left_marea_panel, "AMS-1", 4);
|
||||
ams_mapping_item_container->SetName(nozzle_id == 0 ? m_right_marea_panel->GetName() : m_left_marea_panel->GetName());
|
||||
ams_mapping_item_container->SetSizer(sizer_mapping_list);
|
||||
ams_mapping_item_container->Layout();
|
||||
|
||||
m_has_unmatch_filament = false;
|
||||
ams_mapping_item_container->Show();
|
||||
add_ams_mapping(tray_datas, false, ams_mapping_item_container, sizer_mapping_list);
|
||||
m_amsmapping_container_sizer_list.push_back(sizer_mapping_list);
|
||||
m_amsmapping_container_list.push_back(ams_mapping_item_container);
|
||||
|
||||
if (nozzle_id == 0) {
|
||||
has_right_ams = true;
|
||||
if (ams_mapping_item_container->get_slots_num() == 1) {
|
||||
right_one_slot_containers.push_back(ams_mapping_item_container);
|
||||
} else {
|
||||
right_four_slot_containers.push_back(ams_mapping_item_container);
|
||||
}
|
||||
} else if (nozzle_id == 1) {
|
||||
has_left_ams = true;
|
||||
if (ams_mapping_item_container->get_slots_num() == 1) {
|
||||
left_one_slot_containers.push_back(ams_mapping_item_container);
|
||||
} else {
|
||||
left_four_slots_containers.push_back(ams_mapping_item_container);
|
||||
}
|
||||
}
|
||||
} else if (ams_type == 4) { // 4:n3s
|
||||
}
|
||||
}
|
||||
|
||||
_add_containers(this, left_one_slot_containers, left_four_slots_containers, m_sizer_ams_basket_left);
|
||||
_add_containers(this, right_one_slot_containers, right_four_slot_containers, m_sizer_ams_basket_right);
|
||||
m_left_split_ams_sizer->Show(has_left_ams);
|
||||
m_right_split_ams_sizer->Show(has_right_ams);
|
||||
//update_items_check_state(ams_mapping_result);
|
||||
|
||||
Layout();
|
||||
Fit();
|
||||
}
|
||||
|
||||
|
||||
void AmsMapingPopup::update_title(MachineObject* obj)
|
||||
{
|
||||
const auto& full_config = wxGetApp().preset_bundle->full_config();
|
||||
size_t nozzle_nums = full_config.option<ConfigOptionFloatsNullable>("nozzle_diameter")->values.size();
|
||||
if (nozzle_nums > 1) {
|
||||
m_split_line_panel->Show();
|
||||
if (m_show_type == ShowType::LEFT) {
|
||||
wxString nozzle_name = obj ? _L(DevPrinterConfigUtil::get_toolhead_display_name(
|
||||
obj->printer_type, DEPUTY_EXTRUDER_ID, ToolHeadComponent::Nozzle, ToolHeadNameCase::LowerCase)) : _L("left nozzle");
|
||||
m_title_text->SetLabelText(wxString::Format(_L("Please select the filament installed on the %s."), nozzle_name));
|
||||
return;
|
||||
} else if (m_show_type == ShowType::RIGHT) {
|
||||
wxString nozzle_name = obj ? _L(DevPrinterConfigUtil::get_toolhead_display_name(
|
||||
obj->printer_type, MAIN_EXTRUDER_ID, ToolHeadComponent::Nozzle, ToolHeadNameCase::LowerCase)) : _L("right nozzle");
|
||||
m_title_text->SetLabelText(wxString::Format(_L("Please select the filament installed on the %s."), nozzle_name));
|
||||
return;
|
||||
} else if (m_show_type == ShowType::LEFT_AND_RIGHT_DYNAMIC) {
|
||||
m_title_text->SetLabelText(_L("Please select the filament."));
|
||||
return;
|
||||
}
|
||||
} else if (nozzle_nums == 1) {
|
||||
m_split_line_panel->Hide();
|
||||
}
|
||||
|
||||
m_title_text->SetLabelText(_L("Nozzle"));
|
||||
}
|
||||
|
||||
|
||||
void AmsMapingPopup::update_ams_tips(MachineObject* obj)
|
||||
{
|
||||
const auto& full_config = wxGetApp().preset_bundle->full_config();
|
||||
size_t nozzle_nums = full_config.option<ConfigOptionFloatsNullable>("nozzle_diameter")->values.size();
|
||||
if (m_ams_tips_msg_panel) {
|
||||
m_ams_tips_msg_panel->Clear();
|
||||
if (nozzle_nums == 2 && m_show_type != ShowType::LEFT_AND_RIGHT) {
|
||||
m_ams_tips_msg_panel->AddMessage(_L("To learn about the filaments matching rules."),
|
||||
"#FF6F00", ""); // Orca: dropped vendor help link
|
||||
}
|
||||
|
||||
if (obj && obj->GetFilaSwitch()->IsInstalled()) {
|
||||
const auto& msg = _L("External spools is not supported since Filament Track Switch has been installed. If you want to use external spool, please uninstall it.");
|
||||
m_ams_tips_msg_panel->AddMessage(msg, "#FF6F00", "");
|
||||
}
|
||||
|
||||
m_ams_tips_msg_panel->Layout();
|
||||
m_ams_tips_msg_panel->Fit();
|
||||
m_ams_tips_msg_panel->Show(m_ams_tips_msg_panel->GetMessageCount() > 0);
|
||||
}
|
||||
}
|
||||
|
||||
void AmsMapingPopup::update_rack_select(MachineObject* obj, bool use_dynamic_switch, std::optional<PrintFromType> print_from_type)
|
||||
{
|
||||
// Orca: MachineObject has no GetNozzleRack() convenience; route through the nozzle system instead.
|
||||
m_rack = obj ? obj->GetNozzleSystem()->GetNozzleRack() : nullptr;
|
||||
|
||||
bool show_rack_select_area = false;
|
||||
if (!m_mapping_from_multi_machines && !m_use_in_sync_dialog &&
|
||||
obj && obj->GetNozzleSystem()->GetNozzleRack()->IsSupported() &&
|
||||
print_from_type.has_value() && print_from_type.value() == PrintFromType::FROM_NORMAL) {
|
||||
const auto& nozzle_pos_vec = obj->get_nozzle_mapping_result()->GetMappedNozzlePosVecByFilaId(m_current_filament_id);
|
||||
m_rack_nozzle_select->UpdatSelectedNozzles(obj->GetNozzleSystem()->GetNozzleRack(), nozzle_pos_vec, use_dynamic_switch, print_from_type);
|
||||
show_rack_select_area = true;
|
||||
}
|
||||
|
||||
if (show_rack_select_area != m_rack_nozzle_select->IsShown()) {
|
||||
m_right_tip_text = show_rack_select_area ? _L("Select Filament && Hotends") : _L("Select Filament");
|
||||
m_right_tips->SetLabel(m_right_tip_text);
|
||||
m_rack_nozzle_select->Show(show_rack_select_area);
|
||||
Layout();
|
||||
Fit();
|
||||
}
|
||||
}
|
||||
|
||||
void AmsMapingPopup::update_items_check_state(const std::vector<FilamentInfo>& ams_mapping_result)
|
||||
{
|
||||
/*update check states*/
|
||||
if (m_parent_item) {
|
||||
auto update_item_check_state = [&ams_mapping_result, this](MappingItem* item) {
|
||||
if (item) {
|
||||
for (const auto& mapping_res : ams_mapping_result) {
|
||||
if (mapping_res.id == this->m_current_filament_id) {
|
||||
if (mapping_res.ams_id == std::to_string(item->m_ams_id) &&
|
||||
mapping_res.slot_id == std::to_string(item->m_slot_id)) {
|
||||
item->set_checked(true);
|
||||
} else {
|
||||
item->set_checked(false);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
item->set_checked(false);
|
||||
}
|
||||
};
|
||||
|
||||
update_item_check_state(m_left_extra_slot);
|
||||
update_item_check_state(m_right_extra_slot);
|
||||
for (auto mapping_item : m_mapping_item_list) {
|
||||
update_item_check_state(mapping_item);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void AmsMapingPopup::add_ams_mapping(std::vector<TrayData> tray_data,
|
||||
bool remain_detect_flag,
|
||||
wxWindow* container,
|
||||
wxBoxSizer* sizer)
|
||||
{
|
||||
sizer->Add(0, 0, 0, wxLEFT, FromDIP(6));
|
||||
|
||||
for (auto i = 0; i < tray_data.size(); i++) {
|
||||
|
||||
// set button
|
||||
MappingItem* m_mapping_item = new MappingItem(container);
|
||||
m_mapping_item->send_win = send_win;
|
||||
m_mapping_item->m_ams_id = tray_data[i].ams_id;
|
||||
m_mapping_item->m_slot_id = tray_data[i].slot_id;
|
||||
// Orca: transition_tridid here has no total-extruder-count overload; label the virtual (ext)
|
||||
// slots Ext-R / Ext-L inline in the two-nozzle left+right views (matches that overload's result).
|
||||
if ((m_show_type == ShowType::LEFT_AND_RIGHT || m_show_type == ShowType::LEFT_AND_RIGHT_DYNAMIC)
|
||||
&& (tray_data[i].id == VIRTUAL_TRAY_MAIN_ID || tray_data[i].id == VIRTUAL_TRAY_DEPUTY_ID)) {
|
||||
m_mapping_item->set_tray_index(tray_data[i].id == VIRTUAL_TRAY_MAIN_ID ? wxString("Ext-R") : wxString("Ext-L"));
|
||||
} else {
|
||||
m_mapping_item->set_tray_index(wxGetApp().transition_tridid(tray_data[i].id));
|
||||
}
|
||||
|
||||
m_mapping_item->SetSize(wxSize(FromDIP(48), FromDIP(60)));
|
||||
m_mapping_item->SetMinSize(wxSize(FromDIP(48), FromDIP(60)));
|
||||
m_mapping_item->SetMaxSize(wxSize(FromDIP(48), FromDIP(60)));
|
||||
|
||||
// traversal if can pick the item or not
|
||||
bool can_pick_the_item = true;
|
||||
std::optional<wxString> item_tooltip_msg;
|
||||
// check filament area
|
||||
if (m_use_in_sync_dialog) {
|
||||
if (can_pick_the_item) {
|
||||
if (m_show_type == ShowType::LEFT_AND_RIGHT_DYNAMIC) {
|
||||
can_pick_the_item = !devPrinterUtil::IsVirtualSlot(m_mapping_item->m_ams_id);
|
||||
if (!can_pick_the_item) {
|
||||
item_tooltip_msg = _L(
|
||||
"External spools is not supported since Filament Track Switch has been installed. If you want to use external spool, please uninstall it.");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (can_pick_the_item) {
|
||||
// LEFT_AND_RIGHT — the fallback when a filament has no entry in the
|
||||
// nozzle map — keeps both panels pickable.
|
||||
auto parent = container->GetParent();
|
||||
if (parent == m_left_marea_panel) {
|
||||
can_pick_the_item = (m_show_type == ShowType::LEFT || m_show_type == ShowType::LEFT_AND_RIGHT);
|
||||
} else if (parent == m_right_marea_panel) {
|
||||
if (m_show_type == ShowType::LEFT_AND_RIGHT_DYNAMIC) {
|
||||
can_pick_the_item = !devPrinterUtil::IsVirtualSlot(m_mapping_item->m_ams_id);
|
||||
if (!can_pick_the_item) {
|
||||
item_tooltip_msg = _L(
|
||||
"External spools is not supported since Filament Track Switch has been installed. If you want to use external spool, please uninstall it.");
|
||||
}
|
||||
} else if (m_show_type != ShowType::RIGHT && m_show_type != ShowType::LEFT_AND_RIGHT) {
|
||||
can_pick_the_item = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// check filament type
|
||||
if (can_pick_the_item) {
|
||||
if (tray_data[i].type == NORMAL && !is_match_material(tray_data[i].filament_type)){
|
||||
can_pick_the_item = false;
|
||||
} else if(tray_data[i].type == EMPTY){
|
||||
// Mapping to an empty slot is only allowed from the multi-machine send page,
|
||||
// which maps onto EMPTY placeholder trays.
|
||||
can_pick_the_item = m_mapping_from_multi_machines;
|
||||
}
|
||||
}
|
||||
|
||||
wxColour display_color;
|
||||
if (can_pick_the_item) {
|
||||
display_color = tray_data[i].type == NORMAL ? tray_data[i].colour : wxColour(0xCE, 0xCE, 0xCE);
|
||||
} else {
|
||||
display_color = wxColour(0xEE, 0xEE, 0xEE);
|
||||
}
|
||||
|
||||
wxString display_name;
|
||||
if (tray_data[i].type == THIRD) {
|
||||
display_name = "?";
|
||||
} else if (tray_data[i].type == EMPTY) {
|
||||
display_name = "-";
|
||||
} else {
|
||||
display_name = tray_data[i].name;
|
||||
}
|
||||
|
||||
m_mapping_item->set_data(m_tag_material, display_color, display_name, remain_detect_flag, tray_data[i], !can_pick_the_item, item_tooltip_msg);
|
||||
m_mapping_item->Bind(wxEVT_LEFT_DOWN, [this, can_pick_the_item, m_mapping_item](wxMouseEvent& e) {
|
||||
if (can_pick_the_item) {
|
||||
m_mapping_item->send_event(m_current_filament_id);
|
||||
Dismiss();
|
||||
}
|
||||
});
|
||||
|
||||
sizer->Add(0, 0, 0, wxRIGHT, FromDIP(6));
|
||||
sizer->Add(m_mapping_item, 0, wxTOP, FromDIP(1));
|
||||
m_mapping_item_list.push_back(m_mapping_item);
|
||||
}
|
||||
}
|
||||
|
||||
void AmsMapingPopup::add_ext_ams_mapping(TrayData tray_data, MappingItem* item)
|
||||
{
|
||||
#ifdef __APPLE__
|
||||
m_mapping_item_list.push_back(item);
|
||||
#endif
|
||||
// set button
|
||||
if (tray_data.type == NORMAL) {
|
||||
if (is_match_material(tray_data.filament_type)) {
|
||||
item->set_data(m_tag_material, tray_data.colour, tray_data.name, false, tray_data);
|
||||
} else {
|
||||
item->set_data(m_tag_material, m_ext_mapping_filatype_check ? wxColour(0xEE, 0xEE, 0xEE) : tray_data.colour, tray_data.name, false, tray_data, true);
|
||||
m_has_unmatch_filament = true;
|
||||
}
|
||||
|
||||
item->Bind(wxEVT_LEFT_DOWN, [this, tray_data, item](wxMouseEvent& e) {
|
||||
if (!item->GetParent() || !item->GetParent()->IsEnabled()) return;
|
||||
if (m_ext_mapping_filatype_check && !is_match_material(tray_data.filament_type)) return;
|
||||
item->send_event(m_current_filament_id);
|
||||
Dismiss();
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
// temp
|
||||
if (tray_data.type == EMPTY) {
|
||||
item->set_data(m_tag_material, wxColour(0xCE, 0xCE, 0xCE), "-", false, tray_data);
|
||||
item->Bind(wxEVT_LEFT_DOWN, [this, tray_data, item](wxMouseEvent& e) {
|
||||
if (!item->GetParent() || !item->GetParent()->IsEnabled()) return;
|
||||
item->send_event(m_current_filament_id);
|
||||
Dismiss();
|
||||
});
|
||||
}
|
||||
|
||||
// third party
|
||||
if (tray_data.type == THIRD) {
|
||||
item->set_data(m_tag_material, tray_data.colour, "?", false, tray_data);
|
||||
//item->set_data(wxColour(0xCE, 0xCE, 0xCE), "?", tray_data);
|
||||
item->Bind(wxEVT_LEFT_DOWN, [this, tray_data, item](wxMouseEvent& e) {
|
||||
if (!item->GetParent() || !item->GetParent()->IsEnabled()) return;
|
||||
item->send_event(m_current_filament_id);
|
||||
Dismiss();
|
||||
});
|
||||
}
|
||||
|
||||
item->set_tray_index("Ext");
|
||||
}
|
||||
|
||||
} // namespace Slic3r::GUI
|
||||
File diff suppressed because it is too large
Load Diff
@@ -813,7 +813,7 @@ PingCodeBindDialog::~PingCodeBindDialog() {
|
||||
|
||||
m_simplebook->SetSelection(0);
|
||||
auto m_bind_job = std::make_unique<BindJob>(
|
||||
m_machine_info->get_dev_id(), m_machine_info->get_dev_ip(), m_machine_info->bind_sec_link, m_machine_info->bind_ssdp_version);
|
||||
m_machine_info->get_dev_id(), m_machine_info->get_dev_ip(), m_machine_info->get_show_printer_type(), m_machine_info->bind_sec_link, m_machine_info->bind_ssdp_version);
|
||||
|
||||
if (m_machine_info && (m_machine_info->get_printer_series() == PrinterSeries::SERIES_X1)) {
|
||||
m_bind_job->set_improved(false);
|
||||
|
||||
@@ -102,7 +102,7 @@ CameraPopup::CameraPopup(wxWindow *parent)
|
||||
top_sizer->Add(0, 0, wxALL, 0);
|
||||
}
|
||||
|
||||
// custom IP camera
|
||||
// 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));
|
||||
@@ -132,7 +132,7 @@ CameraPopup::CameraPopup(wxWindow *parent)
|
||||
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::Format(L"https://wiki.bambulab.com/%s/software/bambu-studio/virtual-camera", L"en");
|
||||
auto url = wxString(L"https://www.orcaslicer.com/wiki/"); // Orca: neutral wiki link (vendor URL removed)
|
||||
auto text = _L("Show \"Live Video\" guide page.");
|
||||
|
||||
wxBoxSizer* link_sizer = new wxBoxSizer(wxVERTICAL);
|
||||
@@ -479,7 +479,7 @@ void CameraPopup::OnLeftUp(wxMouseEvent &event)
|
||||
//hyper link
|
||||
auto h_rect = vcamera_guide_link->ClientToScreen(wxPoint(0, 0));
|
||||
if (mouse_pos.x > h_rect.x && mouse_pos.y > h_rect.y && mouse_pos.x < (h_rect.x + vcamera_guide_link->GetSize().x) && mouse_pos.y < (h_rect.y + vcamera_guide_link->GetSize().y)) {
|
||||
auto url = wxString::Format(L"https://wiki.bambulab.com/%s/software/bambu-studio/virtual-camera", L"en");
|
||||
auto url = wxString(L"https://www.orcaslicer.com/wiki/"); // Orca: neutral wiki link (vendor URL removed)
|
||||
wxLaunchDefaultBrowser(url);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -60,6 +60,19 @@ list(APPEND SLIC3R_GUI_SOURCES
|
||||
GUI/DeviceCore/DevUtil.cpp
|
||||
GUI/DeviceCore/DevUtilBackend.h
|
||||
GUI/DeviceCore/DevUtilBackend.cpp
|
||||
GUI/DeviceCore/DevAxis.h
|
||||
GUI/DeviceCore/DevAxis.cpp
|
||||
GUI/DeviceCore/DevAxisCtrl.cpp
|
||||
GUI/DeviceCore/DevCalib.h
|
||||
GUI/DeviceCore/DevCalib.cpp
|
||||
GUI/DeviceCore/DevChamber.h
|
||||
GUI/DeviceCore/DevChamber.cpp
|
||||
GUI/DeviceCore/DevChamberCtrl.cpp
|
||||
GUI/DeviceCore/DevStatus.h
|
||||
GUI/DeviceCore/DevStatus.cpp
|
||||
GUI/DeviceCore/DevUpgrade.h
|
||||
GUI/DeviceCore/DevUpgrade.cpp
|
||||
GUI/DeviceCore/DevUpgradeCtrl.cpp
|
||||
|
||||
)
|
||||
set(SLIC3R_GUI_SOURCES ${SLIC3R_GUI_SOURCES} PARENT_SCOPE)
|
||||
@@ -0,0 +1,25 @@
|
||||
#include "DevAxis.h"
|
||||
#include "DevUtil.h"
|
||||
|
||||
#include "slic3r/GUI/DeviceManager.hpp"
|
||||
|
||||
namespace Slic3r
|
||||
{
|
||||
|
||||
void DevAxis::ParseAxis(const json& print_json)
|
||||
{
|
||||
DevJsonValParser::ParseVal(print_json, "home_flag", m_home_flag);
|
||||
|
||||
if (print_json.contains("fun")) {
|
||||
const std::string& fun = print_json["fun"].get<std::string>();
|
||||
m_is_support_mqtt_homing = DevUtil::get_flag_bits(fun, 32);
|
||||
m_is_support_mqtt_axis_ctrl = DevUtil::get_flag_bits(fun, 38);
|
||||
}
|
||||
}
|
||||
|
||||
bool DevAxis::IsArchCoreXY() const
|
||||
{
|
||||
return m_owner->get_printer_arch() == PrinterArch::ARCH_CORE_XY;
|
||||
}
|
||||
|
||||
} // namespace Slic3r
|
||||
@@ -0,0 +1,39 @@
|
||||
#pragma once
|
||||
#include <nlohmann/json.hpp>
|
||||
#include "slic3r/Utils/json_diff.hpp"
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
class MachineObject;
|
||||
|
||||
class DevAxis
|
||||
{
|
||||
public:
|
||||
static std::shared_ptr<DevAxis> Create(MachineObject *obj) { return std::shared_ptr<DevAxis>( new DevAxis(obj)); }
|
||||
virtual ~DevAxis() = default;
|
||||
|
||||
public:
|
||||
bool IsAxisAtHomeX() const { return m_home_flag == 0 ? true : (m_home_flag & 1) == 1; }
|
||||
bool IsAxisAtHomeY() const { return m_home_flag == 0 ? true : ((m_home_flag >> 1) & 1) == 1; }
|
||||
bool IsAxisAtHomeZ() const { return m_home_flag == 0 ? true : ((m_home_flag >> 2) & 1) == 1; }
|
||||
|
||||
bool IsArchCoreXY() const;
|
||||
|
||||
public:
|
||||
void ParseAxis(const json &print_json);
|
||||
|
||||
int Ctrl_GoHome();
|
||||
int Ctrl_Axis(std::string axis, double unit = 1.0f, double input_val = 1.0f, int speed = 3000); // xyz e
|
||||
|
||||
protected:
|
||||
DevAxis(MachineObject *obj) noexcept : m_owner(obj) {}
|
||||
|
||||
private:
|
||||
MachineObject *m_owner = nullptr;
|
||||
|
||||
int m_home_flag = 0; // bit0:X, bit1:Y, bit2:Z
|
||||
bool m_is_support_mqtt_axis_ctrl = false;
|
||||
bool m_is_support_mqtt_homing = false;
|
||||
};
|
||||
|
||||
} // namespace Slic3r
|
||||
@@ -0,0 +1,60 @@
|
||||
#include "DevAxis.h"
|
||||
|
||||
#include "slic3r/GUI/DeviceManager.hpp"
|
||||
#include "slic3r/Utils/NetworkAgent.hpp"
|
||||
|
||||
namespace Slic3r
|
||||
{
|
||||
|
||||
int DevAxis::Ctrl_GoHome()
|
||||
{
|
||||
if (m_is_support_mqtt_homing) {
|
||||
json j;
|
||||
j["print"]["command"] = "back_to_center";
|
||||
j["print"]["sequence_id"] = std::to_string(MachineObject::m_sequence_id++);
|
||||
return m_owner->publish_json(j);
|
||||
}
|
||||
|
||||
// gcode command
|
||||
return m_owner->is_in_printing() ? m_owner->publish_gcode("G28 X\n") : m_owner->publish_gcode("G28 \n");
|
||||
}
|
||||
|
||||
int DevAxis::Ctrl_Axis(std::string axis, double unit, double input_val, int speed)
|
||||
{
|
||||
if (m_is_support_mqtt_axis_ctrl) {
|
||||
int dir = input_val > 0 ? 1 : -1;
|
||||
if (!IsArchCoreXY()) {
|
||||
if (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 m_owner->publish_json(j);
|
||||
}
|
||||
|
||||
double value = input_val;
|
||||
if (!IsArchCoreXY()) {
|
||||
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;
|
||||
}
|
||||
|
||||
// Orca: strip analytics telemetry (matches MachineObject::command_axis_control)
|
||||
return m_owner->publish_gcode(cmd);
|
||||
}
|
||||
|
||||
} // namespace Slic3r
|
||||
@@ -0,0 +1,337 @@
|
||||
#include <boost/log/trivial.hpp>
|
||||
#include "slic3r/GUI/GUI_App.hpp"
|
||||
|
||||
#include "slic3r/GUI/UserNotification.hpp"
|
||||
#include "libslic3r/PrintConfig.hpp"
|
||||
|
||||
#include <wx/dir.h>
|
||||
#include "fast_float/fast_float.h"
|
||||
|
||||
#include "DevCalib.h"
|
||||
#include "DevDefs.h"
|
||||
#include "DevFilaSystem.h"
|
||||
#include "DevConfig.h"
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
static float string_to_float(const std::string &str_value)
|
||||
{
|
||||
float value = 0.0;
|
||||
fast_float::from_chars(str_value.c_str(), str_value.c_str() + str_value.size(), value);
|
||||
return value;
|
||||
}
|
||||
|
||||
static NozzleVolumeType convert_to_nozzle_type(const std::string &str)
|
||||
{
|
||||
if (str.size() < 8) {
|
||||
assert(false && "invalid nozzle info");
|
||||
return NozzleVolumeType::nvtStandard;
|
||||
}
|
||||
|
||||
if (str[1] == 'S')
|
||||
return NozzleVolumeType::nvtStandard;
|
||||
else if (str[1] == 'H')
|
||||
return NozzleVolumeType::nvtHighFlow;
|
||||
else if (str[1] == 'U')
|
||||
return NozzleVolumeType::nvtTPUHighFlow;
|
||||
// Orca: no nvtE3DHighFlow in Orca's NozzleVolumeType; map 'B' to Standard
|
||||
else
|
||||
return NozzleVolumeType::nvtStandard;
|
||||
}
|
||||
|
||||
static float get_number_flexible(const json& j, const std::string& key, float def = 0.0f)
|
||||
{
|
||||
if (!j.contains(key)) return def;
|
||||
|
||||
const auto& v = j[key];
|
||||
|
||||
if (v.is_number_float()) return v.get<float>();
|
||||
if (v.is_number_integer()) return static_cast<float>(v.get<int>());
|
||||
if (v.is_string()) return string_to_float(v.get<std::string>());
|
||||
|
||||
return def;
|
||||
}
|
||||
|
||||
void from_json(const json& j, PACalibResult& cali) {
|
||||
cali.extruder_id = j.value("extruder_id", 0);
|
||||
cali.nozzle_volume_type = convert_to_nozzle_type(j.value("nozzle_id", "HS00-0.4"));
|
||||
cali.tray_id = j.value("tray_id",0);
|
||||
cali.ams_id = j.value("ams_id",0);
|
||||
cali.slot_id = j.value("slot_id",0);
|
||||
cali.cali_idx = j.value("cali_idx",-1);
|
||||
cali.nozzle_pos_id = j.value("nozzle_pos",-1);
|
||||
cali.nozzle_diameter = get_number_flexible(j, "nozzle_diameter", 0.4f);
|
||||
cali.nozzle_sn = j.value("nozzle_sn","");
|
||||
cali.filament_id = j.value("filament_id","");
|
||||
cali.setting_id = j.value("setting_id","");
|
||||
cali.name = j.value("name","");
|
||||
cali.k_value = get_number_flexible(j, "k_value", 0.0f);
|
||||
cali.n_coef = get_number_flexible(j, "n_coef", 0.0f);
|
||||
cali.confidence = j.value("confidence", 0);
|
||||
}
|
||||
|
||||
void from_json(const json& j, FlowRatioCalibResult& cali)
|
||||
{
|
||||
cali.tray_id = j.value("tray_id", 0);
|
||||
cali.nozzle_diameter = string_to_float(j.value("nozzle_diameter", ""));
|
||||
cali.filament_id = j.value("filament_id", "");
|
||||
cali.setting_id = j.value("setting_id", "");
|
||||
cali.flow_ratio = string_to_float(j.value("flow_ratio", ""));
|
||||
cali.confidence = j.value("confidence", 0);
|
||||
}
|
||||
|
||||
void DevCalib::ParseCalibVersion(const json& j, DevCalib* system)
|
||||
{
|
||||
if(system) system->m_calib_version = j.value("cali_version", -1);
|
||||
}
|
||||
|
||||
bool DevCalib::IsVersionExpired() const
|
||||
{
|
||||
if (m_last_calib_version.has_value())
|
||||
return m_last_calib_version.value() != m_calib_version;
|
||||
else
|
||||
return true;
|
||||
}
|
||||
|
||||
void DevCalib::ParseSupportNewAutoCalib(int flag, DevCalib* system)
|
||||
{
|
||||
if(system) system->m_support_new_auto_cali = flag;
|
||||
}
|
||||
|
||||
void DevCalib::RequestPAResult()
|
||||
{
|
||||
m_pa_results_status = CalibStatus::REQUEST;
|
||||
}
|
||||
|
||||
void DevCalib::ResetPAResult()
|
||||
{
|
||||
m_pa_calib_results.clear();
|
||||
m_pa_results_status = CalibStatus::IDLE;
|
||||
}
|
||||
|
||||
int DevCalib::RequestPAHistory(const PACalibExtruderInfo &calib_info)
|
||||
{
|
||||
m_pa_calib_tab.clear();
|
||||
m_pa_table_status = CalibStatus::REQUEST;
|
||||
|
||||
return GetOwner()->command_get_pa_calibration_tab(calib_info);
|
||||
}
|
||||
|
||||
void DevCalib::ResetPAHistory()
|
||||
{
|
||||
m_pa_calib_tab.clear();
|
||||
m_pa_table_status = CalibStatus::IDLE;
|
||||
}
|
||||
|
||||
void DevCalib::RequestFlowRateResult()
|
||||
{
|
||||
m_flow_results_status = CalibStatus::REQUEST;
|
||||
}
|
||||
|
||||
void DevCalib::ResetFlowRateResult()
|
||||
{
|
||||
m_flow_ratio_results.clear();
|
||||
m_flow_results_status = CalibStatus::IDLE;
|
||||
}
|
||||
|
||||
void calib_fail_message(MachineObject* obj, std::string cali_mode, std::string reason){
|
||||
wxString info;
|
||||
if (reason == "invalid nozzle_diameter" || reason == "nozzle_diameter is not supported") {
|
||||
info = _L("This calibration does not support the currently selected nozzle diameter");
|
||||
} else if (reason == "invalid handle_flowrate_cali param") {
|
||||
info = _L("Current flowrate cali param is invalid");
|
||||
} else if (reason == "nozzle_diameter is not matched") {
|
||||
info = _L("Selected diameter and machine diameter do not match");
|
||||
} else if (reason == "generate auto filament cali gcode failure") {
|
||||
info = _L("Failed to generate cali gcode");
|
||||
} else {
|
||||
info = wxString(reason);
|
||||
}
|
||||
|
||||
GUI::wxGetApp().push_notification(obj, info, _L("Calibration error"), UserNotificationStyle::UNS_WARNING_CONFIRM);
|
||||
BOOST_LOG_TRIVIAL(info) << cali_mode << " result fail, reason = " << reason;
|
||||
}
|
||||
|
||||
void DevCalib::ExtrusionCalibSetParse(const json & jj){
|
||||
int tray_id = jj.value("tray_id", -1);
|
||||
|
||||
auto tray_ams_slot_map = GetOwner()->GetFilaSystem()->GetTrayIndexMap();
|
||||
int ams_id = tray_ams_slot_map.find(tray_id) != tray_ams_slot_map.end() ? tray_ams_slot_map[tray_id].first : -1;
|
||||
int slot_id = tray_ams_slot_map.find(tray_id) != tray_ams_slot_map.end() ? tray_ams_slot_map[tray_id].second : -1;
|
||||
|
||||
if(tray_id == VIRTUAL_TRAY_MAIN_ID) {
|
||||
GetOwner()->vt_slot[MAIN_EXTRUDER_ID].k = jj.value("k_value", GetOwner()->vt_slot[MAIN_EXTRUDER_ID].k);
|
||||
GetOwner()->vt_slot[MAIN_EXTRUDER_ID].n = jj.value("n_value", GetOwner()->vt_slot[MAIN_EXTRUDER_ID].n);
|
||||
}else{
|
||||
auto tray_item = GetOwner()->GetFilaSystem()->GetAmsTray(std::to_string(ams_id), std::to_string(slot_id));
|
||||
if (tray_item) {
|
||||
tray_item->k = jj.value("k_value", tray_item->k);
|
||||
tray_item->n = jj.value("n_coef", tray_item->n);
|
||||
}
|
||||
}
|
||||
|
||||
GetOwner()->extrusion_cali_set_tray_id = tray_id;
|
||||
GetOwner()->extrusion_cali_set_hold_start = std::chrono::system_clock::now();
|
||||
}
|
||||
|
||||
/* calib select ack parse */
|
||||
void DevCalib::ExtrusionCalibSelectParse(const json &jj){
|
||||
try{
|
||||
int tray_id = jj.value("tray_id", -1);
|
||||
|
||||
auto tray_ams_slot_map = GetOwner()->GetFilaSystem()->GetTrayIndexMap();
|
||||
int default_ams_id = tray_ams_slot_map.find(tray_id) != tray_ams_slot_map.end() ? tray_ams_slot_map[tray_id].first : -1;
|
||||
int default_slot_id = tray_ams_slot_map.find(tray_id) != tray_ams_slot_map.end() ? tray_ams_slot_map[tray_id].second : -1;
|
||||
|
||||
int ams_id = jj.value("ams_id", default_ams_id);
|
||||
int slot_id = jj.value("slot_id", default_slot_id);
|
||||
|
||||
BOOST_LOG_TRIVIAL(trace) << "extrusion_cali_sel: illegal ams_id = " << ams_id << "slot_id = " << slot_id;
|
||||
|
||||
std::vector<DevAmsTray> &vt_slot = GetOwner()->vt_slot;
|
||||
if (ams_id == VIRTUAL_TRAY_MAIN_ID && vt_slot.size() > 0) {
|
||||
vt_slot[MAIN_EXTRUDER_ID].cali_idx = jj.value("cali_idx", vt_slot[MAIN_EXTRUDER_ID].cali_idx);
|
||||
vt_slot[MAIN_EXTRUDER_ID].set_hold_count();
|
||||
} else if (ams_id == VIRTUAL_TRAY_DEPUTY_ID && vt_slot.size() > 1) {
|
||||
vt_slot[DEPUTY_EXTRUDER_ID].cali_idx = jj.value("cali_idx", vt_slot[DEPUTY_EXTRUDER_ID].cali_idx);
|
||||
vt_slot[DEPUTY_EXTRUDER_ID].set_hold_count();
|
||||
} else {
|
||||
auto tray_item = GetOwner()->GetFilaSystem()->GetAmsTray(std::to_string(ams_id), std::to_string(slot_id));
|
||||
if (tray_item) {
|
||||
tray_item->cali_idx = jj.value("cali_idx", tray_item->cali_idx);
|
||||
tray_item->set_hold_count();
|
||||
}
|
||||
}
|
||||
} catch(...){
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
void DevCalib::ExtrusionCalibGetTableParse(const json &jj){
|
||||
if (GetPAHistoryStatus() == CalibStatus::REQUEST ) {
|
||||
m_pa_table_status = CalibStatus::WAITING;
|
||||
|
||||
/* request success */
|
||||
if (!(jj.contains("result") && jj.contains("reason") && jj["result"].get<std::string>() == "fail")) {
|
||||
SyncCalibVersion();
|
||||
m_pa_table_status = CalibStatus::FINISHED;
|
||||
}
|
||||
|
||||
try{
|
||||
json filaments_json;
|
||||
if(jj.contains("filaments"))
|
||||
{
|
||||
/* fill item->nozzle_diameter with command->nozzle_diameter */
|
||||
filaments_json = jj["filaments"];
|
||||
for (auto &f : filaments_json) {
|
||||
if (!f.contains("nozzle_diameter") && jj.contains("nozzle_diameter")) {
|
||||
f["nozzle_diameter"] = jj["nozzle_diameter"];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
m_pa_calib_tab = filaments_json.get<std::vector<PACalibResult>>();
|
||||
|
||||
/* filter invalid pa_calib_tab */
|
||||
m_pa_calib_tab.erase(std::remove_if(m_pa_calib_tab.begin(), m_pa_calib_tab.end(), [](auto &res) { return res.k_value < 0.0f || res.k_value > 10.0f; }), m_pa_calib_tab.end());
|
||||
|
||||
if (m_pa_calib_tab.empty()) { BOOST_LOG_TRIVIAL(info) << "empty pa calib history"; }
|
||||
}catch(...){
|
||||
m_pa_calib_tab.clear();
|
||||
BOOST_LOG_TRIVIAL(error) << "pa calib history missing fields, current json:\n "<< jj.dump();
|
||||
}
|
||||
// notify cali history to update
|
||||
}
|
||||
}
|
||||
|
||||
void DevCalib::ExtrusionCalibGetResultParse(const json &jj)
|
||||
{
|
||||
m_pa_results_status = CalibStatus::WAITING;
|
||||
|
||||
if (!(jj.contains("result") && jj.contains("reason") && jj["result"].get<std::string>() == "fail" && jj.contains("err_code"))) {
|
||||
m_pa_results_status = CalibStatus::FINISHED;
|
||||
}
|
||||
|
||||
try {
|
||||
json filaments_json;
|
||||
if(jj.contains("filaments"))
|
||||
{
|
||||
/* fill item->nozzle_diameter with command->nozzle_diameter */
|
||||
filaments_json = jj["filaments"];
|
||||
for (auto &f : filaments_json) {
|
||||
if (!f.contains("nozzle_diameter") && jj.contains("nozzle_diameter") ) {
|
||||
f["nozzle_diameter"] = jj["nozzle_diameter"];
|
||||
}
|
||||
|
||||
if (IsSupportNewAutoCali()) {
|
||||
auto ams_id = f.value("ams_id", 0);
|
||||
auto slot_id = f.value("slot_id", 0);
|
||||
if(f.contains("tray_id")){
|
||||
// Orca: Orca's DevFilaSystem has no GetTrayIdByAmsSlotId; mirror its
|
||||
// semantics via a reverse lookup over GetTrayIndexMap() (correct for
|
||||
// non-4-slot AMS layouts, unlike a fixed ams_id*4+slot_id formula).
|
||||
int mapped_tray_id = -1;
|
||||
const auto tray_ams_slot_map = GetOwner()->GetFilaSystem()->GetTrayIndexMap();
|
||||
for (const auto& item : tray_ams_slot_map) {
|
||||
if (item.second.first == ams_id && item.second.second == slot_id) {
|
||||
mapped_tray_id = item.first;
|
||||
break;
|
||||
}
|
||||
}
|
||||
f["tray_id"] = mapped_tray_id;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
m_pa_calib_results = filaments_json.get<std::vector<PACalibResult>>();
|
||||
|
||||
m_pa_calib_results.erase(std::remove_if(m_pa_calib_results.begin(), m_pa_calib_results.end(), [](auto &res) { return res.k_value < 0.0f || res.k_value > 10.0f; }), m_pa_calib_results.end());
|
||||
|
||||
if (m_pa_calib_results.empty()) { BOOST_LOG_TRIVIAL(info) << "empty pa calib result"; }
|
||||
} catch (...) {
|
||||
m_pa_calib_results.clear();
|
||||
BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << "pa calibration results missing fileds, current json: \n"<<jj.dump();
|
||||
}
|
||||
}
|
||||
|
||||
void DevCalib::FlowrateGetResultParse(const json &jj){
|
||||
m_flow_results_status = CalibStatus::FINISHED;
|
||||
m_flow_ratio_results.clear();
|
||||
|
||||
if(!jj.contains("filaments")) return;
|
||||
|
||||
try {
|
||||
m_flow_ratio_results = jj["filaments"].get<std::vector<FlowRatioCalibResult>>();
|
||||
} catch (...) {
|
||||
BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << "flow ratio calibration results missing fileds, current json:\n"<<jj.dump();
|
||||
}
|
||||
}
|
||||
|
||||
void DevCalib::ParseV1_0(const json &jj, DevCalib *system, bool key_field_only)
|
||||
{
|
||||
if(!jj.contains("command")) return;
|
||||
|
||||
if (jj["command"].get<std::string>() == "extrusion_cali" || jj["command"].get<std::string>() == "flowrate_cali") {
|
||||
if (jj.contains("result")) {
|
||||
if (jj["result"].get<std::string>() == "success") {
|
||||
} else if (jj["result"].get<std::string>() == "fail") {
|
||||
std::string cali_mode = jj["command"].get<std::string>();
|
||||
std::string reason = jj["reason"].get<std::string>();
|
||||
calib_fail_message(system->GetOwner(), cali_mode, reason);
|
||||
}
|
||||
}
|
||||
} else if (jj["command"].get<std::string>() == "extrusion_cali_set") {
|
||||
system->ExtrusionCalibSetParse(jj);
|
||||
} else if (jj["command"].get<std::string>() == "extrusion_cali_sel") {
|
||||
system->ExtrusionCalibSelectParse(jj);
|
||||
} else if (jj["command"].get<std::string>() == "extrusion_cali_get") {
|
||||
system->ExtrusionCalibGetTableParse(jj);
|
||||
} else if (jj["command"].get<std::string>() == "extrusion_cali_get_result") {
|
||||
system->ExtrusionCalibGetResultParse(jj);
|
||||
} else if (jj["command"].get<std::string>() == "flowrate_get_result" && !key_field_only) {
|
||||
system->FlowrateGetResultParse(jj);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace Slic3r
|
||||
@@ -0,0 +1,127 @@
|
||||
#pragma once
|
||||
#include <nlohmann/json.hpp>
|
||||
#include "slic3r/Utils/json_diff.hpp"
|
||||
|
||||
|
||||
#include "DevDefs.h"
|
||||
#include "libslic3r/calib.hpp" // Orca: lowercase filename (Linux case-sensitive)
|
||||
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
class MachineObject;
|
||||
|
||||
|
||||
enum class CalibStatus{
|
||||
IDLE = 0,
|
||||
REQUEST,
|
||||
WAITING,
|
||||
FINISHED,
|
||||
};
|
||||
|
||||
enum class ManualPaCaliMethod {
|
||||
PA_LINE = 0,
|
||||
PA_PATTERN,
|
||||
};
|
||||
|
||||
class DevCalib
|
||||
{
|
||||
protected:
|
||||
bool m_support_new_auto_cali{false};
|
||||
int m_calib_version {-1};
|
||||
std::optional<int> m_last_calib_version;
|
||||
|
||||
public:
|
||||
DevCalib(MachineObject *obj) : m_owner(obj){};
|
||||
MachineObject* GetOwner() const {return m_owner; };
|
||||
|
||||
void RequestPAResult();
|
||||
CalibStatus GetPAResultStatus() const {return m_pa_results_status;}
|
||||
bool IsPAResultReady() const { return m_pa_results_status == CalibStatus::FINISHED;}
|
||||
void ResetPAResult();
|
||||
|
||||
/* calib history */
|
||||
int RequestPAHistory(const PACalibExtruderInfo &calib_info);
|
||||
CalibStatus GetPAHistoryStatus() const {return m_pa_table_status;}
|
||||
bool IsPAHistoryReady() const { return m_pa_table_status == CalibStatus::FINISHED;}
|
||||
void ResetPAHistory();
|
||||
|
||||
void RequestFlowRateResult();
|
||||
CalibStatus GetFlowRateResultStatus() const {return m_flow_results_status;}
|
||||
bool IsFlowRateReady() const { return m_flow_results_status == CalibStatus::FINISHED;}
|
||||
void ResetFlowRateResult();
|
||||
|
||||
int GetCalibVersion() const {return m_calib_version;}
|
||||
void SyncCalibVersion() { if (IsVersionInited()) { m_last_calib_version = m_calib_version; } }
|
||||
void ResetCalibVersion() {m_last_calib_version.reset();}
|
||||
bool IsVersionExpired() const;
|
||||
bool IsVersionInited() const { return m_calib_version > -1;}
|
||||
|
||||
bool IsSupportNewAutoCali() const {return m_support_new_auto_cali;}
|
||||
|
||||
public:
|
||||
void SetStashCalibFinished(bool finished) {m_calib_finished = finished;}
|
||||
bool GetStashCalibFinished() { return m_calib_finished;}
|
||||
|
||||
void SetStashFlowRatio(float ratio) { m_flow_ratio = ratio;}
|
||||
float GetStashFlowRatio() const {return m_flow_ratio;}
|
||||
|
||||
FlowRatioCalibrationType GetFlowRatioCalibType() {return m_flow_ratio_calibration_type;}
|
||||
void SetFlowRatioCalibType(const FlowRatioCalibrationType &type) { m_flow_ratio_calibration_type = type; }
|
||||
|
||||
ManualPaCaliMethod GetManualPaCalibMethod() {return m_manual_pa_cali_method;}
|
||||
void SetManualPaCalibMethod(const ManualPaCaliMethod& method) { m_manual_pa_cali_method = method;}
|
||||
|
||||
NozzleDiameterType GetSelectedNozzleDiameter() {return m_selected_nozzle_diameter;}
|
||||
void SetSelectedNozzleDiameter(const NozzleDiameterType& diameter) { m_selected_nozzle_diameter = diameter;}
|
||||
|
||||
std::vector<CaliPresetInfo> GetSelectedCalibPreset() {return m_selected_calib_preset;}
|
||||
void ResetSelectedCalibPreset() { m_selected_calib_preset.clear();}
|
||||
void SetSelectedCalibPreset(const std::vector<CaliPresetInfo>& preset) { m_selected_calib_preset = preset;}
|
||||
|
||||
std::vector<PACalibResult> GetPAHistory() const {return m_pa_calib_tab; }
|
||||
std::vector<PACalibResult> GetPAResult() const {return m_pa_calib_results; }
|
||||
std::vector<FlowRatioCalibResult> GetFlowRatioResult() const {return m_flow_ratio_results; }
|
||||
|
||||
protected:
|
||||
void ExtrusionCalibSetParse(const json &jj);
|
||||
void ExtrusionCalibSelectParse(const json &jj);
|
||||
void ExtrusionCalibGetTableParse(const json &jj);
|
||||
void ExtrusionCalibGetResultParse(const json &jj);
|
||||
void FlowrateGetResultParse(const json &jj);
|
||||
|
||||
private:
|
||||
MachineObject* m_owner{nullptr};
|
||||
|
||||
std::vector<PACalibResult> m_pa_calib_tab;
|
||||
std::vector<PACalibResult> m_pa_calib_results;
|
||||
std::vector<FlowRatioCalibResult> m_flow_ratio_results;
|
||||
|
||||
CalibStatus m_pa_results_status{CalibStatus::IDLE};
|
||||
CalibStatus m_pa_table_status{CalibStatus::IDLE};
|
||||
CalibStatus m_flow_results_status{CalibStatus::IDLE};
|
||||
|
||||
FlowRatioCalibrationType m_flow_ratio_calibration_type{FlowRatioCalibrationType::COMPLETE_CALIBRATION};
|
||||
ManualPaCaliMethod m_manual_pa_cali_method{ManualPaCaliMethod::PA_LINE};
|
||||
|
||||
// calibration page selected info
|
||||
// 1: record when start calibration in preset page
|
||||
// 2: reset when start calibration in start page
|
||||
// 3: save tray_id, filament_id, setting_id, and name, nozzle_dia
|
||||
// std::vector<CaliPresetInfo> selected_cali_preset;
|
||||
NozzleDiameterType m_selected_nozzle_diameter{NozzleDiameterType::NONE_DIAMETER_TYPE};
|
||||
std::vector<CaliPresetInfo> m_selected_calib_preset;
|
||||
|
||||
// stash calibrating info
|
||||
float m_flow_ratio { 0.0 };
|
||||
bool m_calib_finished{false};
|
||||
|
||||
public:
|
||||
static void ParseCalibVersion(const json& j, DevCalib* system);
|
||||
|
||||
static void ParseSupportNewAutoCalib(int flag, DevCalib* system);
|
||||
|
||||
static void ParseV1_0(const json& print_json, DevCalib* system, bool key_field_only);
|
||||
};
|
||||
|
||||
} // namespace Slic3r
|
||||
@@ -0,0 +1,50 @@
|
||||
#include "DevChamber.h"
|
||||
|
||||
#include "DevConfig.h"
|
||||
#include "DevUtil.h"
|
||||
|
||||
#include "slic3r/GUI/DeviceManager.hpp"
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
bool DevChamber::HasChamber() const { return m_owner->GetConfig()->HasChamber(); }
|
||||
|
||||
bool DevChamber::SupportChamberTempDisplay() const { return m_owner->GetConfig()->SupportChamberTempDisplay();}
|
||||
|
||||
bool DevChamber::SupportChamberEdit() const { return m_owner->GetConfig()->SupportChamberEdit(); }
|
||||
|
||||
int DevChamber::GetChamberTempEditMin() const { return m_owner->GetConfig()->GetChamberTempEditMin(); }
|
||||
|
||||
int DevChamber::GetChamberTempEditMax() const { return m_owner->GetConfig()->GetChamberTempEditMax(); }
|
||||
|
||||
int DevChamber::GetChamberTempSwitchHeat() const { return m_owner->GetConfig()->GetChamberTempSwitchHeat(); }
|
||||
|
||||
void DevChamber::ParseChamber(const json &print_json)
|
||||
{
|
||||
ParseChamberV1_0(print_json);
|
||||
ParseChamberV2_0(print_json);
|
||||
}
|
||||
|
||||
void DevChamber::ParseChamberV1_0(const json &print_json)
|
||||
{
|
||||
DevJsonValParser::ParseVal(print_json, "chamber_temper", m_temp);
|
||||
DevJsonValParser::ParseVal(print_json, "ctt", m_temp_target);
|
||||
}
|
||||
|
||||
void DevChamber::ParseChamberV2_0(const json &print_json)
|
||||
{
|
||||
if (print_json.contains("device")) {
|
||||
const json &device_jj = print_json["device"];
|
||||
if (device_jj.contains("ctc")) {
|
||||
const json &ctc = device_jj["ctc"];
|
||||
int state = DevUtil::get_flag_bits(ctc["state"].get<int>(), 0, 4);
|
||||
if (ctc.contains("info")) {
|
||||
const json &info = ctc["info"];
|
||||
m_temp = DevUtil::get_flag_bits(info["temp"].get<int>(), 0, 16);
|
||||
m_temp_target = DevUtil::get_flag_bits(info["temp"].get<int>(), 16, 16);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace Slic3r
|
||||
@@ -0,0 +1,44 @@
|
||||
#pragma once
|
||||
#include <nlohmann/json.hpp>
|
||||
#include "slic3r/Utils/json_diff.hpp"
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
class MachineObject;
|
||||
|
||||
class DevChamber
|
||||
{
|
||||
public:
|
||||
static std::shared_ptr<DevChamber> Create(MachineObject *obj) { return std::shared_ptr<DevChamber>( new DevChamber(obj)); }
|
||||
|
||||
public: // getter
|
||||
bool HasChamber() const;
|
||||
bool SupportChamberTempDisplay() const;
|
||||
bool SupportChamberEdit() const;
|
||||
int GetChamberTempEditMin() const;
|
||||
int GetChamberTempEditMax() const;
|
||||
int GetChamberTempSwitchHeat() const;
|
||||
|
||||
float GetChamberTemp() const { return m_temp; };
|
||||
float GetChamberTempTarget() const { return m_temp_target; };
|
||||
|
||||
public:
|
||||
// setter
|
||||
void ParseChamber(const json &print_json);
|
||||
|
||||
void ParseChamberV1_0(const json& print_json);
|
||||
void ParseChamberV2_0(const json& print_json);
|
||||
|
||||
// control
|
||||
int CtrlSetChamberTemp(int temp);
|
||||
|
||||
protected:
|
||||
DevChamber(MachineObject *obj) : m_owner(obj) {}
|
||||
|
||||
private:
|
||||
float m_temp = 0.0f;
|
||||
float m_temp_target = 0.0f;
|
||||
MachineObject* m_owner = nullptr;
|
||||
};
|
||||
|
||||
} // namespace Slic3r
|
||||
@@ -0,0 +1,16 @@
|
||||
#include "DevChamber.h"
|
||||
#include "slic3r/GUI/DeviceManager.hpp"
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
int DevChamber::CtrlSetChamberTemp(int temp)
|
||||
{
|
||||
json j;
|
||||
j["print"]["sequence_id"] = std::to_string(MachineObject::m_sequence_id++);
|
||||
j["print"]["command"] = "set_ctt";
|
||||
j["print"]["ctt_val"] = temp;
|
||||
|
||||
return m_owner->publish_json(j, 1);
|
||||
}
|
||||
|
||||
} // namespace Slic3r
|
||||
@@ -8,17 +8,30 @@ using namespace nlohmann;
|
||||
namespace Slic3r
|
||||
{
|
||||
|
||||
bool DevConfig::SupportChamberTempDisplay() const
|
||||
{
|
||||
if (!m_support_chamber_temp_display.has_value()) {
|
||||
return m_has_chamber;
|
||||
}
|
||||
|
||||
return m_support_chamber_temp_display.value();
|
||||
}
|
||||
|
||||
void DevConfig::ParseConfig(const json& print_json)
|
||||
{
|
||||
ParseChamberConfig(print_json);
|
||||
ParsePrintOptionsConfig(print_json);
|
||||
ParseCalibrationConfig(print_json);
|
||||
|
||||
}
|
||||
|
||||
void DevConfig::ParseChamberConfig(const json& print_json)
|
||||
{
|
||||
DevJsonValParser::ParseVal(print_json, "support_chamber", m_has_chamber);
|
||||
|
||||
if (print_json.contains("support_chamber_temp_display")) {
|
||||
m_support_chamber_temp_display = DevJsonValParser::GetVal<bool>(print_json, "support_chamber_temp_display");
|
||||
}
|
||||
|
||||
DevJsonValParser::ParseVal(print_json, "support_chamber_temp_edit", m_support_chamber_edit);
|
||||
if (m_support_chamber_edit)
|
||||
{
|
||||
|
||||
@@ -3,8 +3,9 @@
|
||||
|
||||
#include "slic3r/Utils/json_diff.hpp"
|
||||
#include <wx/string.h>
|
||||
#include <limits>
|
||||
|
||||
#include <limits>
|
||||
#include <optional>
|
||||
|
||||
namespace Slic3r
|
||||
{
|
||||
@@ -22,6 +23,7 @@ public:
|
||||
public:
|
||||
// chamber
|
||||
bool HasChamber() const { return m_has_chamber; }
|
||||
bool SupportChamberTempDisplay() const;
|
||||
bool SupportChamberEdit() const { return m_support_chamber_edit; }
|
||||
int GetChamberTempEditMin() const { return m_chamber_temp_edit_min; }
|
||||
int GetChamberTempEditMax() const { return m_chamber_temp_edit_max; }
|
||||
@@ -57,6 +59,7 @@ private:
|
||||
/*configure vals*/
|
||||
// chamber
|
||||
bool m_has_chamber = false; // whether the machine has a chamber
|
||||
std::optional<bool> m_support_chamber_temp_display;
|
||||
bool m_support_chamber_edit = false;
|
||||
int m_chamber_temp_edit_min = 0;
|
||||
int m_chamber_temp_edit_max = 60;
|
||||
|
||||
@@ -1,16 +1,41 @@
|
||||
#include "DevConfigUtil.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cctype>
|
||||
|
||||
#include <wx/dir.h>
|
||||
#include <boost/filesystem.hpp>
|
||||
#include <boost/filesystem/operations.hpp>
|
||||
#include "../I18N.hpp"
|
||||
|
||||
using namespace nlohmann;
|
||||
|
||||
namespace Slic3r
|
||||
{
|
||||
|
||||
// Translation markers for xgettext extraction.
|
||||
// These strings are configured in printers/*.json (tool_head_display_names)
|
||||
// and passed to _L() at runtime. xgettext cannot scan dynamic strings,
|
||||
// so we mark them here with L() for extraction into .pot file.
|
||||
// This block is never executed at runtime.
|
||||
static void _toolhead_translation_markers()
|
||||
{
|
||||
// Dynamic toolhead display names from JSON config — xgettext cannot scan these
|
||||
L("Main Extruder"); L("Main extruder"); L("main extruder");
|
||||
L("Auxiliary Extruder"); L("Auxiliary extruder"); L("auxiliary extruder");
|
||||
L("Left Extruder"); L("Left extruder"); L("left extruder");
|
||||
L("Right Extruder"); L("Right extruder"); L("right extruder");
|
||||
L("Main Nozzle"); L("Main nozzle"); L("main nozzle");
|
||||
L("Auxiliary Nozzle"); L("Auxiliary nozzle"); L("auxiliary nozzle");
|
||||
L("Left Nozzle"); L("Left nozzle"); L("left nozzle");
|
||||
L("Right Nozzle"); L("Right nozzle"); L("right nozzle");
|
||||
L("Main Hotend"); L("Main hotend"); L("main hotend");
|
||||
L("Auxiliary Hotend"); L("Auxiliary hotend"); L("auxiliary hotend");
|
||||
L("Left Hotend"); L("Left hotend"); L("left hotend");
|
||||
L("Right Hotend"); L("Right hotend"); L("right hotend");
|
||||
// standalone position words (short_name=true runtime results)
|
||||
L("main"); L("auxiliary");
|
||||
L("Main"); L("Auxiliary");
|
||||
L("left"); L("right");
|
||||
L("Left"); L("Right");
|
||||
}
|
||||
|
||||
std::string DevPrinterConfigUtil::m_resource_file_path = "";
|
||||
|
||||
|
||||
@@ -91,7 +116,7 @@ std::string DevPrinterConfigUtil::get_filament_load_img(const std::string &type_
|
||||
{
|
||||
if (has_nozzle_rack)
|
||||
{
|
||||
const auto &rack_vec = get_value_from_config<std::vector<std::string>>(type_str, "filament_load_image_nozzle_rack");
|
||||
const auto &rack_vec = get_value_from_config<std::vector<std::string>>(type_str, "filament_load_image_nozzle_rack") ;
|
||||
if (!rack_vec.empty())
|
||||
{
|
||||
if (ext_id >= 0 && static_cast<size_t>(ext_id) < rack_vec.size())
|
||||
@@ -111,7 +136,6 @@ std::string DevPrinterConfigUtil::get_filament_load_img(const std::string &type_
|
||||
|
||||
std::string DevPrinterConfigUtil::get_fan_text(const std::string& type_str, const std::string& key)
|
||||
{
|
||||
std::vector<std::string> filaments;
|
||||
std::string config_file = m_resource_file_path + "/printers/" + type_str + ".json";
|
||||
boost::nowide::ifstream json_file(config_file.c_str());
|
||||
try
|
||||
@@ -134,6 +158,25 @@ std::string DevPrinterConfigUtil::get_fan_text(const std::string& type_str, cons
|
||||
return std::string();
|
||||
}
|
||||
|
||||
std::vector<std::string> DevPrinterConfigUtil::get_fan_text_params(const std::string& type_str, const std::string& key)
|
||||
{
|
||||
std::string config_file = m_resource_file_path + "/printers/" + type_str + ".json";
|
||||
boost::nowide::ifstream json_file(config_file.c_str());
|
||||
try {
|
||||
json jj;
|
||||
if (json_file.is_open()) {
|
||||
json_file >> jj;
|
||||
if (jj.contains("00.00.00.00")) {
|
||||
json const& printer = jj["00.00.00.00"];
|
||||
if (printer.contains("fan") && printer["fan"].contains(key)) {
|
||||
return printer["fan"][key].get<std::vector<std::string>>();
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (...) {}
|
||||
return std::vector<std::string>();
|
||||
}
|
||||
|
||||
std::string DevPrinterConfigUtil::get_fan_text(const std::string& type_str, int airduct_mode, int airduct_func, int submode)
|
||||
{
|
||||
std::vector<std::string> filaments;
|
||||
@@ -181,6 +224,36 @@ std::string DevPrinterConfigUtil::get_fan_text(const std::string& type_str, int
|
||||
return std::string();
|
||||
}
|
||||
|
||||
std::string DevPrinterConfigUtil::get_fan_mode_text(const std::string& type_str, int airduct_mode, const std::string& key)
|
||||
{
|
||||
std::vector<std::string> filaments;
|
||||
std::string config_file = m_resource_file_path + "/printers/" + type_str + ".json";
|
||||
boost::nowide::ifstream json_file(config_file.c_str());
|
||||
try {
|
||||
json jj;
|
||||
if (json_file.is_open()) {
|
||||
json_file >> jj;
|
||||
if (jj.contains("00.00.00.00")) {
|
||||
json const& printer = jj["00.00.00.00"];
|
||||
if (!printer.contains("fan")) {
|
||||
return std::string();
|
||||
}
|
||||
|
||||
json const& fan_item = printer["fan"];
|
||||
const auto& airduct_mode_str = std::to_string(airduct_mode);
|
||||
if (!fan_item.contains(airduct_mode_str)) {
|
||||
return std::string();
|
||||
}
|
||||
|
||||
if (fan_item[airduct_mode_str].contains(key)) {
|
||||
return fan_item[airduct_mode_str][key].get<std::string>();
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (...) {}
|
||||
return std::string();
|
||||
}
|
||||
|
||||
std::map<std::string, std::vector<std::string>> DevPrinterConfigUtil::get_all_subseries(std::string type_str)
|
||||
{
|
||||
std::map<std::string, std::vector<std::string>> subseries;
|
||||
@@ -284,18 +357,29 @@ std::string DevPrinterConfigUtil::get_toolhead_display_name(
|
||||
{ ToolHeadComponent::Hotend, "hotend" }
|
||||
};
|
||||
|
||||
const int case_index = static_cast<int>(name_case);
|
||||
const std::string role_key = std::to_string(ext_id);
|
||||
int case_index = static_cast<int>(name_case); // 0, 1, 2
|
||||
|
||||
// Try to read from printer config json
|
||||
auto names_json = get_value_from_config<json>(type_str, "tool_head_display_names");
|
||||
std::string role_key = std::to_string(ext_id); // "0" or "1"
|
||||
const std::string& comp_key = comp_keys.at(component);
|
||||
|
||||
std::string result;
|
||||
auto names_json = get_value_from_config<json>(type_str, "tool_head_display_names");
|
||||
if (!names_json.is_null() && names_json.contains(role_key) && names_json[role_key].contains(comp_key)) {
|
||||
|
||||
if (!names_json.is_null()
|
||||
&& names_json.contains(role_key)
|
||||
&& names_json[role_key].contains(comp_key))
|
||||
{
|
||||
auto& arr = names_json[role_key][comp_key];
|
||||
if (arr.is_array() && case_index < static_cast<int>(arr.size()))
|
||||
if (arr.is_array() && case_index < static_cast<int>(arr.size())) {
|
||||
result = arr[case_index].get<std::string>();
|
||||
}
|
||||
}
|
||||
|
||||
// Orca: models that ship no tool_head_display_names (e.g. H2D/H2S) must still get distinct
|
||||
// per-extruder labels, so fall back to the previous programmatic Left/Right construction.
|
||||
// Engages only when the config lookup above yields nothing, so models that ship the key
|
||||
// behave exactly like the reference.
|
||||
if (result.empty()) {
|
||||
const std::string side = ext_id == DEPUTY_EXTRUDER_ID ? "Left" : "Right";
|
||||
const std::string component_name = component == ToolHeadComponent::Extruder ? "Extruder" :
|
||||
@@ -307,13 +391,15 @@ std::string DevPrinterConfigUtil::get_toolhead_display_name(
|
||||
std::transform(result.begin(), result.end(), result.begin(), [](unsigned char c) { return static_cast<char>(std::tolower(c)); });
|
||||
}
|
||||
|
||||
// short_name: return only the role prefix (e.g. "Main" from "Main Nozzle")
|
||||
if (short_name) {
|
||||
auto sp = result.find(' ');
|
||||
if (sp != std::string::npos)
|
||||
if (sp != std::string::npos) {
|
||||
result = result.substr(0, sp);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
};
|
||||
};
|
||||
@@ -11,6 +11,8 @@
|
||||
#include <string>
|
||||
#include <map>
|
||||
#include <vector>
|
||||
#include <algorithm>
|
||||
#include <cctype>
|
||||
|
||||
#include <boost/log/trivial.hpp>
|
||||
#include <boost/nowide/fstream.hpp>
|
||||
@@ -23,6 +25,20 @@
|
||||
namespace Slic3r
|
||||
{
|
||||
|
||||
/// Toolhead component type (extruder / nozzle / hotend)
|
||||
enum class ToolHeadComponent {
|
||||
Extruder,
|
||||
Nozzle,
|
||||
Hotend
|
||||
};
|
||||
|
||||
/// Name case style for toolhead display names
|
||||
enum class ToolHeadNameCase {
|
||||
TitleCase = 0, // [0] "Main Extruder" — panel titles, section headers
|
||||
SentenceCase = 1, // [1] "Main extruder" — static box labels
|
||||
LowerCase = 2 // [2] "main extruder" — inline text, sentence concatenation
|
||||
};
|
||||
|
||||
class dePrinterConfigFactory
|
||||
{
|
||||
public:
|
||||
@@ -30,17 +46,6 @@ public:
|
||||
~dePrinterConfigFactory() = default;
|
||||
};
|
||||
|
||||
enum class ToolHeadComponent {
|
||||
Extruder,
|
||||
Nozzle,
|
||||
Hotend
|
||||
};
|
||||
|
||||
enum class ToolHeadNameCase {
|
||||
TitleCase = 0,
|
||||
SentenceCase = 1,
|
||||
LowerCase = 2
|
||||
};
|
||||
|
||||
class DevPrinterConfigUtil
|
||||
{
|
||||
@@ -74,14 +79,19 @@ public:
|
||||
static std::string get_printer_use_ams_type(std::string type_str) { return get_value_from_config<std::string>(type_str, "use_ams_type"); }
|
||||
static std::string get_printer_ams_img(const std::string& type_str) { return get_value_from_config<std::string>(type_str, "printer_use_ams_image"); }
|
||||
static std::string get_printer_ext_img(const std::string& type_str, int pos);//printer_ext_image
|
||||
static bool support_ams_fila_change_abort(std::string type_str) { return get_value_from_config<bool>(type_str, "print", "support_ams_filament_change_abort"); }
|
||||
static std::string get_filament_load_img(const std::string &type_str, int ext_id, bool has_nozzle_rack = false);
|
||||
|
||||
/*fan*/
|
||||
static std::string get_fan_text(const std::string& type_str, const std::string& key);
|
||||
static std::string get_fan_text(const std::string& type_str, const std::string& key);
|
||||
static std::vector<std::string> get_fan_text_params(const std::string& type_str, const std::string& key);
|
||||
static std::string get_fan_text(const std::string& type_str, int airduct_mode, int airduct_func, int submode);
|
||||
static std::string get_fan_mode_text(const std::string& type_str, int airduct_mode, const std::string& key);
|
||||
|
||||
/*extruder*/
|
||||
static bool get_printer_can_set_nozzle(std::string type_str) { return get_value_from_config<bool>(type_str, "enable_set_nozzle_info"); }// can set nozzle from studio
|
||||
|
||||
/*toolhead display names (extruder / nozzle / hotend)*/
|
||||
static std::string get_toolhead_display_name(
|
||||
const std::string& type_str,
|
||||
int ext_id,
|
||||
@@ -90,10 +100,15 @@ public:
|
||||
bool short_name = false);
|
||||
|
||||
/*print job*/
|
||||
static bool support_print_check_firmware_for_tpu_left(std::string type_str){ return get_value_from_config<bool>(type_str, "print", "support_print_check_firmware_for_tpu_left"); }
|
||||
static bool support_user_first_setup_tpu_check(std::string type_str){ return get_value_from_config<bool>(type_str, "print", "support_user_first_setup_tpu_check"); }
|
||||
static std::string support_user_first_setup_tpu_check_url(std::string type_str){ return get_value_from_config<std::string>(type_str, "print", "support_user_first_setup_tpu_check_url"); }
|
||||
static bool support_ams_ext_mix_print(std::string type_str) { return get_value_from_config<bool>(type_str, "print", "support_ams_ext_mix_print"); }
|
||||
static bool support_print_time_estimate_warning(std::string type_str) { return get_value_from_config<bool>(type_str, "print", "support_print_time_estimate_warning"); }
|
||||
|
||||
/*calibration*/
|
||||
static std::vector<std::string> get_unsupport_auto_cali_filaments(std::string type_str) { return get_value_from_config<std::vector<std::string>>(type_str, "auto_cali_not_support_filaments"); }
|
||||
static bool support_disable_cali_flow_type(std::string type_str) { return get_value_from_config<bool>(type_str,"support_disable_cali_flow_type"); }
|
||||
|
||||
/*detection*/
|
||||
static bool support_wrapping_detection(const std::string& type_str) { return get_value_from_config<bool>(type_str, "support_wrapping_detection"); }
|
||||
@@ -103,6 +118,10 @@ public:
|
||||
|
||||
/*print check*/
|
||||
static bool support_print_check_extension_fan_f000_mounted(const std::string& type_str) { return get_value_from_config<bool>(type_str, "print", "support_print_check_extension_fan_f000_mounted"); }
|
||||
static std::string air_print_detection_position(const std::string &type_str) { return get_value_from_config<std::string>(type_str, "air_print_detection_position"); }
|
||||
|
||||
/*bed*/
|
||||
static int get_bed_temperature_limit(const std::string &type_str) { return get_value_from_config<int>(type_str, "print", "bed_temperature_limit"); }
|
||||
|
||||
public:
|
||||
template<typename T>
|
||||
@@ -126,7 +145,7 @@ public:
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (...) { assert(0 && "get_value_from_config failed"); BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << " failed"; }// there are file errors
|
||||
catch (...) { assert(0 && "get_value_from_config failed"); BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << " failed"; }// there are file errors
|
||||
return T();
|
||||
};
|
||||
|
||||
@@ -178,7 +197,7 @@ public:
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (...) { assert(0 && "get_json_from_config failed"); BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << " failed"; }// there are file errors
|
||||
catch (...) { assert(0 && "get_json_from_config failed"); BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << " failed"; }// there are file errors
|
||||
return nlohmann::json();
|
||||
}
|
||||
|
||||
@@ -187,35 +206,25 @@ private:
|
||||
};
|
||||
|
||||
/*special transform*/
|
||||
static std::string _parse_printer_type(const std::string& type_str)
|
||||
static std::string _parse_printer_type(const std::string &type_str)
|
||||
{
|
||||
if (type_str.compare("3DPrinter-X1") == 0)
|
||||
{
|
||||
return "BL-P002";
|
||||
}
|
||||
else if (type_str.compare("3DPrinter-X1-Carbon") == 0)
|
||||
{
|
||||
return "BL-P001";
|
||||
}
|
||||
else if (type_str.compare("BL-P001") == 0)
|
||||
{
|
||||
return type_str;
|
||||
}
|
||||
else if (type_str.compare("BL-P002") == 0)
|
||||
{
|
||||
return type_str;
|
||||
}
|
||||
else
|
||||
{
|
||||
std::string result = DevPrinterConfigUtil::get_printer_type(type_str);
|
||||
if (!result.empty())
|
||||
{
|
||||
return result;
|
||||
}
|
||||
}
|
||||
static std::unordered_map<std::string, std::string> s_printer_type_lazy_cache;
|
||||
|
||||
if (type_str == "3DPrinter-X1") return "BL-P002";
|
||||
if (type_str == "3DPrinter-X1-Carbon") return "BL-P001";
|
||||
if (type_str == "BL-P001" || type_str == "BL-P002") return type_str;
|
||||
|
||||
auto cache_it = s_printer_type_lazy_cache.find(type_str);
|
||||
if (cache_it != s_printer_type_lazy_cache.end()) {
|
||||
return cache_it->second;
|
||||
}
|
||||
std::string result = DevPrinterConfigUtil::get_printer_type(type_str);
|
||||
if (!result.empty()) {
|
||||
s_printer_type_lazy_cache[type_str] = result;
|
||||
return result;
|
||||
}
|
||||
BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << " Unsupported printer type: " << type_str;
|
||||
return type_str;
|
||||
}
|
||||
|
||||
};// namespace Slic3r
|
||||
};// namespace Slic3r
|
||||
@@ -10,12 +10,21 @@
|
||||
|
||||
#pragma once
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include <nlohmann/json.hpp>
|
||||
|
||||
// Reserved for future usage
|
||||
#define DEV_RESERVED_FOR_FUTURE(...) /* stripped */
|
||||
|
||||
// Previous definitions
|
||||
namespace Slic3r
|
||||
{
|
||||
class MachineObject;
|
||||
}
|
||||
|
||||
enum PrinterArch
|
||||
{
|
||||
ARCH_CORE_XY,
|
||||
ARCH_I3,
|
||||
ARCH_CORE_XY,// move hotbed
|
||||
ARCH_I3,//move z
|
||||
};
|
||||
|
||||
enum PrinterSeries
|
||||
@@ -36,6 +45,7 @@ enum AmsStatusMain
|
||||
AMS_STATUS_MAIN_RFID_IDENTIFYING = 0x02,
|
||||
AMS_STATUS_MAIN_ASSIST = 0x03,
|
||||
AMS_STATUS_MAIN_CALIBRATION = 0x04,
|
||||
AMS_STATUS_MAIN_COLD_PULL = 0x07,
|
||||
AMS_STATUS_MAIN_SELF_CHECK = 0x10,
|
||||
AMS_STATUS_MAIN_DEBUG = 0x20,
|
||||
AMS_STATUS_MAIN_UNKNOWN = 0xFF,
|
||||
@@ -51,10 +61,6 @@ enum DevAmsType : int
|
||||
AMS_LITE_MIXED = 5, // AMS-Lite for N9
|
||||
};
|
||||
|
||||
// Device-numbered filament-change step codes. Newer firmware reports the exact change-step
|
||||
// sequence through the AMS (ams.cfs), keyed by these codes, instead of the client hardcoding
|
||||
// steps per model. Distinct from the legacy GUI-side Slic3r::GUI::FilamentStep enum.
|
||||
// STEP_CHECK_POSITION and STEP_CONFIRM_EXTRUDED share code 0x08 by device design.
|
||||
enum DevFilamentStep
|
||||
{
|
||||
STEP_IDLE = 0x00,
|
||||
@@ -83,37 +89,35 @@ enum DevFilamentStep
|
||||
#define VIRTUAL_AMS_MAIN_ID_STR "255"
|
||||
#define VIRTUAL_AMS_DEPUTY_ID_STR "254"
|
||||
|
||||
// Tray index offset for the AMS-Lite-mixed unit (A2L / N9). Its 4 trays occupy
|
||||
// global tray indices 24..27.
|
||||
#define AMS_LITE_MIXED_TRAY_INDEX_OFFSET 24
|
||||
// Tray index offsets for different AMS types
|
||||
#define AMS_LITE_MIXED_TRAY_INDEX_OFFSET 24 // Offset for AMS_LITE_MIXED tray index (24)
|
||||
|
||||
#define INVALID_AMS_TEMPERATURE std::numeric_limits<float>::min()
|
||||
|
||||
// (ams_id, slot_id) pair.
|
||||
using DevAmsSlotId = std::pair<int, int>;
|
||||
|
||||
/* Extruder*/
|
||||
#define MAIN_EXTRUDER_ID 0
|
||||
#define DEPUTY_EXTRUDER_ID 1
|
||||
#define UNIQUE_EXTRUDER_ID MAIN_EXTRUDER_ID
|
||||
#define INVALID_EXTRUDER_ID -1
|
||||
|
||||
/* Logical extruder ids (multi-nozzle). */
|
||||
// see PartPlate::get_physical_extruder_by_logical_extruder
|
||||
#define LOGIC_UNIQUE_EXTRUDER_ID 0
|
||||
#define LOGIC_L_EXTRUDER_ID 0
|
||||
#define LOGIC_R_EXTRUDER_ID 1
|
||||
|
||||
// <ams_id, slot_id>
|
||||
using DevAmsSlotId = std::pair<int, int>;
|
||||
|
||||
/* Nozzle*/
|
||||
enum NozzleFlowType
|
||||
enum NozzleFlowType : int
|
||||
{
|
||||
NONE_FLOWTYPE,
|
||||
S_FLOW,
|
||||
H_FLOW,
|
||||
U_FLOW, // TPU 1.75 High Flow (device-reported; maps to nvtTPUHighFlow)
|
||||
U_FLOW, // TPU 1.75 High Flow
|
||||
E_FLOW, // E3D High Flow
|
||||
};
|
||||
|
||||
// Discrete nozzle diameters reported by the device.
|
||||
/* 0.2mm 0.4mm 0.6mm 0.8mm */
|
||||
enum NozzleDiameterType : int
|
||||
{
|
||||
NONE_DIAMETER_TYPE,
|
||||
@@ -135,7 +139,7 @@ enum DevPrintingSpeedLevel
|
||||
};
|
||||
|
||||
/*Upgrade*/
|
||||
enum class DevFirmwareUpgradingState : int
|
||||
enum class DevFirmwareUpgradeState : int
|
||||
{
|
||||
DC = -1,
|
||||
UpgradingUnavaliable = 0,
|
||||
@@ -144,6 +148,10 @@ enum class DevFirmwareUpgradingState : int
|
||||
UpgradingFinished = 3
|
||||
};
|
||||
|
||||
// Orca: DeviceManager/UpgradePanel keep the Orca spelling of this enum; the alias to the
|
||||
// reference name is a permanent compatibility surface.
|
||||
using DevFirmwareUpgradingState = DevFirmwareUpgradeState;
|
||||
|
||||
class devPrinterUtil
|
||||
{
|
||||
public:
|
||||
@@ -157,9 +165,6 @@ public:
|
||||
|
||||
namespace GUI
|
||||
{
|
||||
// Print source. Defined here (rather than in SelectMachine.hpp) so the shared device-mapping
|
||||
// GUI headers — AmsMappingPopup, wgtDeviceNozzleSelect — can name it without pulling in
|
||||
// SelectMachine.hpp, which would create an include cycle.
|
||||
enum PrintFromType
|
||||
{
|
||||
FROM_NORMAL,
|
||||
@@ -169,8 +174,6 @@ enum PrintFromType
|
||||
|
||||
};// namespace Slic3r
|
||||
|
||||
// A nozzle requirement of the sliced plate (or an installed nozzle's identity), compared in the
|
||||
// pre-print slicing-vs-installed nozzle checks.
|
||||
struct NozzleDef
|
||||
{
|
||||
float nozzle_diameter;
|
||||
@@ -190,4 +193,7 @@ template<> struct std::hash<NozzleDef>
|
||||
size_t h2 = std::hash<int>{}(v.nozzle_flow_type);
|
||||
return h1 ^ (h2 + 0x9e3779b9 + (h1 << 6) + (h1 >> 2));
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
// key(extruder_id) -> { key1(nozzle type info), val1( number of the nozzle type)}
|
||||
using ExtruderNozzleInfos = std::unordered_map<int, std::unordered_map<NozzleDef, int>>;
|
||||
@@ -271,7 +271,7 @@ namespace Slic3r
|
||||
system->m_current_loading_extder_id = INVALID_EXTRUDER_ID;
|
||||
if (!system->m_extders[MAIN_EXTRUDER_ID].m_star.ams_id.empty())
|
||||
{
|
||||
system->m_current_busy_for_loading = (system->m_extders[MAIN_EXTRUDER_ID].m_snow == system->m_extders[MAIN_EXTRUDER_ID].m_star);
|
||||
system->m_current_busy_for_loading = (system->m_extders[MAIN_EXTRUDER_ID].m_snow != system->m_extders[MAIN_EXTRUDER_ID].m_star);
|
||||
if (system->m_current_busy_for_loading)
|
||||
{
|
||||
system->m_current_loading_extder_id = MAIN_EXTRUDER_ID;
|
||||
|
||||
@@ -22,6 +22,7 @@ struct DevAmsSlotInfo
|
||||
std::string slot_id;
|
||||
|
||||
public:
|
||||
bool operator!=(const DevAmsSlotInfo& other) const { return !(*this == other); }
|
||||
bool operator==(const DevAmsSlotInfo& other) const { return ams_id == other.ams_id && slot_id == other.slot_id;}
|
||||
};
|
||||
|
||||
|
||||
@@ -46,20 +46,10 @@ void Slic3r::DevFan::converse_to_duct(bool is_suppt_part_fun, bool is_suppt_aux_
|
||||
}
|
||||
}
|
||||
|
||||
static std::string _get_string_from_fantype(int type)
|
||||
{
|
||||
switch (type) {
|
||||
case 1: return "cooling_fan";
|
||||
case 2: return "big_cooling_fan";
|
||||
case 3: return "chamber_fan";
|
||||
default: return "";
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
// Old protocol
|
||||
int Slic3r::DevFan::command_control_fan(int fan_type, int val)
|
||||
{
|
||||
// Orca: strip analytics telemetry
|
||||
std::string gcode = (boost::format("M106 P%1% S%2% \n") % (int) fan_type % (val)).str();
|
||||
return m_owner->publish_gcode(gcode);
|
||||
}
|
||||
@@ -166,8 +156,6 @@ void Slic3r::DevFan::ParseV3_0(const json &device)
|
||||
m_air_duct_data.modes.clear();
|
||||
m_air_duct_data.parts.clear();
|
||||
|
||||
m_air_duct_data.curren_mode = device["airduct"]["modeCur"].get<int>();
|
||||
|
||||
const json &airduct = device["airduct"];
|
||||
if (airduct.contains("modeCur")) { m_air_duct_data.curren_mode = airduct["modeCur"].get<int>(); }
|
||||
if (airduct.contains("subMode")) { m_air_duct_data.m_sub_mode = airduct["subMode"].get<int>(); }
|
||||
@@ -193,7 +181,7 @@ void Slic3r::DevFan::ParseV3_0(const json &device)
|
||||
}
|
||||
}
|
||||
|
||||
if (AIR_DUCT(mode.id) == AIR_DUCT::AIR_DUCT_EXHAUST) { continue; } /*STUDIO-12796*/
|
||||
if (AIR_DUCT(mode.id) == AIR_DUCT::AIR_DUCT_EXHAUST) { continue; }
|
||||
m_air_duct_data.modes[mode.id] = mode;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,8 +11,6 @@ class MachineObject;
|
||||
enum AirDuctType { AIR_FAN_TYPE, AIR_DOOR_TYPE };
|
||||
typedef std::function<void(const json &)> CommandCallBack;
|
||||
|
||||
|
||||
|
||||
enum AIR_FUN : int {
|
||||
FAN_HEAT_BREAK_0_IDX = 0,
|
||||
FAN_COOLING_0_AIRDOOR = 1,
|
||||
@@ -91,6 +89,13 @@ public:
|
||||
|
||||
bool IsSupportCoolingFilter() const { return m_support_cooling_filter; }
|
||||
bool IsCoolingFilerOn() const { return m_sub_mode == 1; }
|
||||
bool IsExaustFanExit() const
|
||||
{
|
||||
for (auto &p : parts) {
|
||||
if (p.id == int(AIR_FUN::FAN_CHAMBER_0_IDX)) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
class DevFan
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#pragma once
|
||||
#include <optional>
|
||||
#include <unordered_map> // Orca: for m_firmwares / GetSuppotedFirmwares() — the unordered_map AMSSetting.cpp mirrors and iterates
|
||||
#include <nlohmann/json.hpp>
|
||||
#include "DevCtrl.h"
|
||||
|
||||
@@ -75,7 +76,7 @@ public:
|
||||
|
||||
DevAmsSystemIdx GetCurrentFirmwareIdxSel() const { return m_current_firmware_sel.m_firmare_idx; };
|
||||
DevAmsSystemIdx GetCurrentFirmwareIdxRun() const { return m_current_firmware_run.m_firmare_idx; };
|
||||
std::unordered_map<int, DevAmsSystemFirmware> GetSuppotedFirmwares() const { return m_firmwares;};
|
||||
std::unordered_map<int, DevAmsSystemFirmware> GetSuppotedFirmwares() const { return m_firmwares;}; // Orca: unordered (mirrored by AMSSetting.cpp)
|
||||
|
||||
bool IsSwitching() const { return m_status == "SWITCHING";};
|
||||
bool IsIdle() const { return m_status == "IDLE";};
|
||||
@@ -94,7 +95,7 @@ private:
|
||||
|
||||
DevAmsSystemFirmware m_current_firmware_run;
|
||||
DevAmsSystemFirmware m_current_firmware_sel;
|
||||
std::unordered_map<int, DevAmsSystemFirmware> m_firmwares;
|
||||
std::unordered_map<int, DevAmsSystemFirmware> m_firmwares; // Orca: unordered (mirrored by AMSSetting.cpp)
|
||||
|
||||
DevCtrlInfo m_ctrl_switching;
|
||||
};
|
||||
|
||||
@@ -9,19 +9,6 @@ namespace Slic3r {
|
||||
|
||||
DevFilaSwitch::DevFilaSwitch(MachineObject *owner) { m_owner = owner; }
|
||||
|
||||
bool DevFilaSwitch::IsReady() const
|
||||
{
|
||||
if (!m_is_installed) { return false; }
|
||||
|
||||
const auto& ams_list = m_owner->GetFilaSystem()->GetAmsList();
|
||||
for (const auto& ams_item : ams_list) {
|
||||
if (ams_item.second->GetBindedExtruderSet().empty()) { return false; }
|
||||
if (!ams_item.second->GetSwitcherPos().has_value()) { return false; }
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void DevFilaSwitch::Reset()
|
||||
{
|
||||
m_is_installed = false;
|
||||
@@ -34,6 +21,24 @@ void DevFilaSwitch::Reset()
|
||||
m_cali_status = CaliStatus::CALI_IDLE;
|
||||
}
|
||||
|
||||
bool DevFilaSwitch::IsReady() const
|
||||
{
|
||||
if (!m_is_installed) {
|
||||
BOOST_LOG_TRIVIAL(debug) << "[FilaSwitch] IsReady: not installed";
|
||||
return false;
|
||||
}
|
||||
|
||||
const auto& ams_list = m_owner->GetFilaSystem()->GetAmsList();
|
||||
for (const auto& ams_item : ams_list) {
|
||||
if (ams_item.second->GetBindedExtruderSet().size() == 0) { return false; }
|
||||
if (!ams_item.second->GetSwitcherPos().has_value()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
std::optional<DevAmsTray> DevFilaSwitch::GetInA_Slot() const
|
||||
{
|
||||
auto ams_id_opt = GetInA_SlotId();
|
||||
@@ -128,4 +133,4 @@ void DevFilaSwitch::ParseFilaSwitchInfo(const nlohmann::json &print_jj)
|
||||
}
|
||||
}
|
||||
|
||||
}; // namespace Slic3r
|
||||
}; // namespace Slic3r
|
||||
@@ -1,7 +1,6 @@
|
||||
#pragma once
|
||||
#include "DevDefs.h"
|
||||
#include <optional>
|
||||
#include <nlohmann/json.hpp>
|
||||
|
||||
namespace Slic3r
|
||||
{
|
||||
@@ -10,16 +9,11 @@ class MachineObject;
|
||||
class DevExtder;
|
||||
class DevAmsTray;
|
||||
|
||||
// Filament Track Switch (H2-family hardware).
|
||||
// The filament blacklist consumes IsInstalled() for the `has_filament_switch`
|
||||
// rule (e.g. PLA Silk). m_is_installed defaults to false and is only raised when
|
||||
// the device reports the switch over MQTT (aux bit 29), so the rule stays inert on
|
||||
// every printer that does not report an installed switch.
|
||||
class DevFilaSwitch
|
||||
{
|
||||
public:
|
||||
enum class CaliStatus : int
|
||||
{
|
||||
{
|
||||
CALI_IDLE = 0,
|
||||
CALI_STEPING = 1,
|
||||
};
|
||||
@@ -48,10 +42,6 @@ public:
|
||||
|
||||
public:
|
||||
bool IsInstalled() const { return m_is_installed; };
|
||||
|
||||
// Ready once installed and every AMS has resolved both its extruder binding and its
|
||||
// switcher track position from the device. The send dialog and sidebar sync UX only
|
||||
// treat the switch as usable when this is true.
|
||||
bool IsReady() const;
|
||||
|
||||
std::optional<bool> IsInA_HasFilament() const { return m_in_a_has_filament; };
|
||||
@@ -88,4 +78,4 @@ private:
|
||||
|
||||
CaliStatus m_cali_status = CaliStatus::CALI_IDLE;
|
||||
};
|
||||
};
|
||||
};
|
||||
@@ -839,4 +839,154 @@ void DevFilaSystemParser::ParseV1_0(const json& jj, MachineObject* obj, DevFilaS
|
||||
}
|
||||
}
|
||||
|
||||
static DevAms::AmsType ams_type_from_string(const std::string& s)
|
||||
{
|
||||
if (s == "ams_lite" || s == "ams-lite") return DevAms::AMS_LITE;
|
||||
if (s == "n3f") return DevAms::N3F;
|
||||
if (s == "n3s") return DevAms::N3S;
|
||||
return DevAms::AMS; // default
|
||||
}
|
||||
|
||||
void DevFilaSystemParser::ParseAgentFilament(const json& data, MachineObject* obj, DevFilaSystem* system)
|
||||
{
|
||||
if (!system || !data.is_object())
|
||||
return;
|
||||
|
||||
// --- AMS units ---
|
||||
if (data.contains("units") && data["units"].is_array())
|
||||
{
|
||||
std::set<std::string> seen_units;
|
||||
|
||||
for (const auto& u : data["units"])
|
||||
{
|
||||
if (!u.is_object() || !u.contains("id"))
|
||||
continue;
|
||||
const std::string ams_id = u.value("id", std::string());
|
||||
if (ams_id.empty())
|
||||
continue;
|
||||
seen_units.insert(ams_id);
|
||||
|
||||
const int ext_id = u.value("extruder", MAIN_EXTRUDER_ID);
|
||||
const DevAms::AmsType type = ams_type_from_string(u.value("type", std::string("ams")));
|
||||
|
||||
DevAms* ams = nullptr;
|
||||
auto it = system->amsList.find(ams_id);
|
||||
if (it == system->amsList.end())
|
||||
{
|
||||
ams = new DevAms(ams_id, ext_id, type);
|
||||
system->amsList.insert(std::make_pair(ams_id, ams));
|
||||
}
|
||||
else
|
||||
{
|
||||
ams = it->second;
|
||||
ams->m_ext_id = ext_id;
|
||||
ams->SetAmsType(type);
|
||||
}
|
||||
|
||||
ams->m_exist = true;
|
||||
ams->m_current_temperature = u.value("temperature", (float) INVALID_AMS_TEMPERATURE);
|
||||
ams->m_humidity_percent = u.value("humidity_percent", -1);
|
||||
ams->m_left_dry_time = u.value("dry_time_min", 0);
|
||||
|
||||
// --- slots / trays ---
|
||||
std::set<std::string> seen_slots;
|
||||
if (u.contains("slots") && u["slots"].is_array())
|
||||
{
|
||||
for (const auto& s : u["slots"])
|
||||
{
|
||||
if (!s.is_object())
|
||||
continue;
|
||||
const std::string tray_id = std::to_string(s.value("index", -1));
|
||||
seen_slots.insert(tray_id);
|
||||
|
||||
DevAmsTray* tray = nullptr;
|
||||
auto tit = ams->m_trays.find(tray_id);
|
||||
if (tit == ams->m_trays.end())
|
||||
{
|
||||
tray = new DevAmsTray(tray_id);
|
||||
ams->m_trays.insert(std::make_pair(tray_id, tray));
|
||||
}
|
||||
else
|
||||
{
|
||||
tray = tit->second;
|
||||
}
|
||||
|
||||
tray->is_exists = s.value("loaded", false);
|
||||
tray->m_fila_type = s.value("material", std::string());
|
||||
tray->setting_id = s.value("preset_id", std::string());
|
||||
tray->UpdateColorFromStr(s.value("color", std::string()));
|
||||
tray->nozzle_temp_min = std::to_string(s.value("nozzle_temp_min", 0));
|
||||
tray->nozzle_temp_max = std::to_string(s.value("nozzle_temp_max", 0));
|
||||
tray->remain = s.value("remain_percent", -1);
|
||||
tray->k = s.value("k", 0.0f);
|
||||
if (s.contains("diameter_mm") && s["diameter_mm"].is_number())
|
||||
tray->diameter = std::to_string(s["diameter_mm"].get<double>());
|
||||
if (s.contains("weight_g") && s["weight_g"].is_number())
|
||||
tray->weight = std::to_string(s["weight_g"].get<int>());
|
||||
|
||||
tray->cols.clear();
|
||||
if (s.contains("colors") && s["colors"].is_array())
|
||||
{
|
||||
for (const auto& c : s["colors"])
|
||||
if (c.is_string())
|
||||
tray->cols.push_back(c.get<std::string>());
|
||||
}
|
||||
tray->ctype = tray->cols.size() > 1 ? 1 : 0;
|
||||
}
|
||||
}
|
||||
|
||||
// prune trays no longer reported
|
||||
for (auto tit = ams->m_trays.begin(); tit != ams->m_trays.end();)
|
||||
{
|
||||
if (seen_slots.count(tit->first) == 0)
|
||||
{
|
||||
delete tit->second;
|
||||
tit = ams->m_trays.erase(tit);
|
||||
}
|
||||
else
|
||||
{
|
||||
++tit;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// prune units no longer reported
|
||||
for (auto it = system->amsList.begin(); it != system->amsList.end();)
|
||||
{
|
||||
if (seen_units.count(it->first) == 0)
|
||||
{
|
||||
delete it->second;
|
||||
it = system->amsList.erase(it);
|
||||
}
|
||||
else
|
||||
{
|
||||
++it;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// --- external / direct spools -> obj->vt_slot ---
|
||||
// extruder 0 -> main virtual slot, extruder >0 -> deputy.
|
||||
if (obj && data.contains("external") && data["external"].is_array())
|
||||
{
|
||||
obj->vt_slot.clear();
|
||||
for (const auto& e : data["external"])
|
||||
{
|
||||
if (!e.is_object())
|
||||
continue;
|
||||
const int ext = e.value("extruder", MAIN_EXTRUDER_ID);
|
||||
const int vt_id = (ext == MAIN_EXTRUDER_ID) ? VIRTUAL_TRAY_MAIN_ID : VIRTUAL_TRAY_DEPUTY_ID;
|
||||
DevAmsTray tray(std::to_string(vt_id));
|
||||
tray.is_exists = e.value("loaded", false);
|
||||
tray.m_fila_type = e.value("material", std::string());
|
||||
tray.setting_id = e.value("preset_id", std::string());
|
||||
tray.UpdateColorFromStr(e.value("color", std::string()));
|
||||
tray.nozzle_temp_min = std::to_string(e.value("nozzle_temp_min", 0));
|
||||
tray.nozzle_temp_max = std::to_string(e.value("nozzle_temp_max", 0));
|
||||
tray.remain = e.value("remain_percent", -1);
|
||||
obj->vt_slot.push_back(tray);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -270,10 +270,10 @@ private:
|
||||
AmsType m_ams_type = AmsType::AMS;
|
||||
std::string m_ams_id;
|
||||
int m_ext_id;//extruder id
|
||||
// Orca: keeps the legacy single m_ext_id for existing consumers and carries the
|
||||
// switch-aware binding alongside it (BambuStudio stores only the set). Without a Filament
|
||||
// Track Switch the set is {m_ext_id}; with one installed the device binds both extruders and
|
||||
// records which input track (A/B) feeds this AMS.
|
||||
// Orca: pull-mode / multi-vendor agent model — keeps the legacy single m_ext_id for existing
|
||||
// consumers and carries the switch-aware binding alongside it. Without a Filament Track Switch
|
||||
// the set is {m_ext_id}; with one installed the device binds both extruders and records which
|
||||
// input track (A/B) feeds this AMS.
|
||||
std::set<int> m_binded_extruder_set;
|
||||
std::optional<DevFilaSwitch::SwitchPos> m_binded_switcher_pos;
|
||||
bool m_exist = false;
|
||||
@@ -415,6 +415,8 @@ class DevFilaSystemParser
|
||||
{
|
||||
public:
|
||||
static void ParseV1_0(const json& print_json, MachineObject* obj, DevFilaSystem* system, bool key_field_only);
|
||||
|
||||
static void ParseAgentFilament(const json& data, MachineObject* obj, DevFilaSystem* system);
|
||||
};
|
||||
|
||||
struct DevFilamentDryingPreset
|
||||
|
||||
@@ -28,8 +28,8 @@ int DevFilaSystem::CtrlAmsStartDryingHour(int ams_id,
|
||||
jj_command["print"]["command"] = "ams_filament_drying";
|
||||
jj_command["print"]["sequence_id"] = std::to_string(MachineObject::m_sequence_id++);
|
||||
jj_command["print"]["ams_id"] = ams_id;
|
||||
jj_command["print"]["mode"] = static_cast<int>(DevAms::DryCtrlMode::OnTime);
|
||||
jj_command["print"]["filament"] = filament_type;
|
||||
jj_command["print"]["mode"] = static_cast<int>(DevAms::DryCtrlMode::OnTime); // Orca: cast scoped enum for json
|
||||
jj_command["print"]["filament"] = filament_type; // Orca: fix comma-operator typo
|
||||
jj_command["print"]["temp"] = tag_temp;
|
||||
jj_command["print"]["duration"] = tag_duration_hour;
|
||||
jj_command["print"]["humidity"] = 0;
|
||||
@@ -45,8 +45,8 @@ int DevFilaSystem::CtrlAmsStopDrying(int ams_id) const
|
||||
jj_command["print"]["command"] = "ams_filament_drying";
|
||||
jj_command["print"]["sequence_id"] = std::to_string(MachineObject::m_sequence_id++);
|
||||
jj_command["print"]["ams_id"] = ams_id;
|
||||
jj_command["print"]["mode"] = static_cast<int>(DevAms::DryCtrlMode::Off);
|
||||
jj_command["print"]["filament"] = "";
|
||||
jj_command["print"]["mode"] = static_cast<int>(DevAms::DryCtrlMode::Off); // Orca: cast scoped enum for json
|
||||
jj_command["print"]["filament"] = ""; // Orca: fix comma-operator typo
|
||||
jj_command["print"]["temp"] = 0;
|
||||
jj_command["print"]["duration"] = 0;
|
||||
jj_command["print"]["humidity"] = 0;
|
||||
|
||||
@@ -1,4 +1,7 @@
|
||||
#pragma once
|
||||
#include "DevFirmware.h"
|
||||
#include "slic3r/Utils/json_diff.hpp"
|
||||
|
||||
#include <nlohmann/json.hpp>
|
||||
#include <wx/string.h>
|
||||
#include "slic3r/Utils/json_diff.hpp"
|
||||
@@ -8,6 +11,7 @@ namespace Slic3r {
|
||||
//Previous definitions
|
||||
class MachineObject;
|
||||
|
||||
// Orca: firmware-type enum consumed by DeviceManager/UpgradePanel; no reference-side equivalent yet
|
||||
enum PrinterFirmwareType
|
||||
{
|
||||
FIRMWARE_TYPE_ENGINEER = 0,
|
||||
@@ -15,7 +19,6 @@ enum PrinterFirmwareType
|
||||
FIRMEARE_TYPE_UKNOWN,
|
||||
};
|
||||
|
||||
|
||||
class FirmwareInfo
|
||||
{
|
||||
public:
|
||||
@@ -40,12 +43,18 @@ public:
|
||||
|
||||
public:
|
||||
bool isValid() const { return !sn.empty(); }
|
||||
|
||||
/*type check*/
|
||||
bool isAirPump() const { return product_name.Contains("Air Pump"); }
|
||||
bool isLaszer() const { return product_name.Contains("Laser"); }
|
||||
bool isCuttingModule() const { return product_name.Contains("Cutting Module"); }
|
||||
bool isExtinguishSystem() const { return product_name.Contains("Extinguishing System"); }
|
||||
bool isWTM() const { return name.find("wtm") != std::string::npos; } // nozzle (rack hotend) module firmware
|
||||
bool isFilaTrackSwitch() const { return product_name.Contains("Filament Track"); }
|
||||
bool isRotary() const { return product_name.Contains("Rotary"); }// Rotary Attachment
|
||||
bool isExtinguishSystem() const { return product_name.Contains("Extinguishing System"); }// Auto Fire Extinguishing System
|
||||
bool isWTM() const { return name.find("wtm") != string::npos; } // nozzle
|
||||
bool isExhaustFan() const { return product_name.Contains("Exhaust Fan"); }
|
||||
bool isHmshub() const { return product_name.find("Filament Buffer") != string::npos; }
|
||||
bool isFilaTrackSwitch() const { return product_name.find("Filament Track") != string::npos; }
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
||||
@@ -29,6 +29,7 @@ bool DevHMSItem::parse_hms_info(unsigned attr, unsigned code)
|
||||
std::string DevHMSItem::get_long_error_code() const
|
||||
{
|
||||
char buf[64];
|
||||
// Orca: HMS long-error-code format excludes the reserved byte and keys the Orca HMS wiki lookups
|
||||
::sprintf(buf, "%02X%02X%02X00000%1X%04X",
|
||||
this->m_module_id,
|
||||
this->m_module_num,
|
||||
|
||||
@@ -8,6 +8,9 @@ namespace Slic3r {
|
||||
class MachineObject;
|
||||
|
||||
/* some static info of machine*/ /*TODO*/
|
||||
// Orca: kept as a stub — connection_type/is_lan_mode_printer/is_cloud_mode_printer live inline on
|
||||
// MachineObject (DeviceManager.hpp). Adopting the reference's full DevInfo would duplicate those
|
||||
// definitions and require an m_dev_info member MachineObject does not have.
|
||||
class DevInfo
|
||||
{
|
||||
public:
|
||||
|
||||
@@ -24,27 +24,37 @@ namespace Slic3r
|
||||
|
||||
DevPrinterConfigUtil::InitFilePath(resources_dir());
|
||||
|
||||
// Load saved local machines
|
||||
// Load saved local machines (needs an agent; when built without one the load is
|
||||
// deferred to set_agent()).
|
||||
if (agent) {
|
||||
AppConfig* config = GUI::wxGetApp().app_config;
|
||||
const auto local_machines = config->get_local_machines();
|
||||
for (auto& it : local_machines) {
|
||||
const auto& m = it.second;
|
||||
MachineObject* obj = new MachineObject(this, m_agent, m.dev_name, m.dev_id, m.dev_ip);
|
||||
obj->printer_type = m.printer_type;
|
||||
obj->dev_connection_type = "lan";
|
||||
obj->bind_state = "free";
|
||||
obj->bind_sec_link = "secure";
|
||||
obj->m_is_online = true;
|
||||
obj->last_alive = Slic3r::Utils::get_current_time_utc();
|
||||
obj->set_access_code(config->get("access_code", m.dev_id), false);
|
||||
obj->set_user_access_code(config->get("user_access_code", m.dev_id), false);
|
||||
if (obj->has_access_right()) {
|
||||
localMachineList.insert(std::make_pair(m.dev_id, obj));
|
||||
} else {
|
||||
config->erase_local_machine(m.dev_id);
|
||||
delete obj;
|
||||
}
|
||||
load_local_machines_from_config();
|
||||
}
|
||||
}
|
||||
|
||||
void DeviceManager::load_local_machines_from_config()
|
||||
{
|
||||
AppConfig* config = GUI::wxGetApp().app_config;
|
||||
if (!config)
|
||||
return;
|
||||
const auto local_machines = config->get_local_machines();
|
||||
for (auto& it : local_machines) {
|
||||
const auto& m = it.second;
|
||||
if (localMachineList.count(m.dev_id))
|
||||
continue;
|
||||
MachineObject* obj = new MachineObject(this, m_agent, m.dev_name, m.dev_id, m.dev_ip);
|
||||
obj->printer_type = m.printer_type;
|
||||
obj->dev_connection_type = "lan";
|
||||
obj->bind_state = "free";
|
||||
obj->bind_sec_link = "secure";
|
||||
obj->m_is_online = true;
|
||||
obj->last_alive = Slic3r::Utils::get_current_time_utc();
|
||||
obj->set_access_code(config->get("access_code", m.dev_id), false);
|
||||
obj->set_user_access_code(config->get("user_access_code", m.dev_id), false);
|
||||
if (obj->has_access_right()) {
|
||||
localMachineList.insert(std::make_pair(m.dev_id, obj));
|
||||
} else {
|
||||
config->erase_local_machine(m.dev_id);
|
||||
delete obj;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -98,6 +108,11 @@ namespace Slic3r
|
||||
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << ": updating agent for "
|
||||
<< localMachineList.size() << " local and "
|
||||
<< userMachineList.size() << " user machines";
|
||||
|
||||
// First real agent after an agent-less construction (network plugin wasn't ready at
|
||||
// startup): run the persisted-LAN-printer load the constructor had to skip. See
|
||||
// load_local_machines_from_config().
|
||||
const bool first_real_agent = (m_agent == nullptr && agent != nullptr);
|
||||
m_agent = agent;
|
||||
|
||||
std::lock_guard<std::mutex> lock(listMutex);
|
||||
@@ -111,6 +126,10 @@ namespace Slic3r
|
||||
it.second->set_agent(agent);
|
||||
}
|
||||
}
|
||||
|
||||
if (first_real_agent) {
|
||||
load_local_machines_from_config();
|
||||
}
|
||||
}
|
||||
|
||||
void DeviceManager::EnableMultiMachine(bool enable)
|
||||
|
||||
@@ -96,6 +96,13 @@ public:
|
||||
std::map<std::string, std::vector<std::string>> device_subseries;
|
||||
|
||||
private:
|
||||
// Load the LAN printers persisted in AppConfig into localMachineList. Runs from the
|
||||
// constructor when an agent is available and, for the case where the DeviceManager was
|
||||
// first built without one (network plugin not yet installed at startup), from set_agent()
|
||||
// once a real agent finally arrives - so paired printers survive a plugin install/hot
|
||||
// reload without an app restart.
|
||||
void load_local_machines_from_config();
|
||||
|
||||
void keep_alive();
|
||||
void check_pushing();
|
||||
|
||||
|
||||
@@ -162,8 +162,8 @@ namespace Slic3r
|
||||
}
|
||||
|
||||
//first: left,nozzle=1,map=1 second: right,nozzle=0,map=2
|
||||
bool right_ams_valid = ams->second->GetExtruderId() == 0 && map_opt[MappingOption::USE_RIGHT_AMS];
|
||||
bool left_ams_valid = ams->second->GetExtruderId() == 1 && map_opt[MappingOption::USE_LEFT_AMS];
|
||||
bool right_ams_valid = (ams->second->GetBindedExtruderSet().count(MAIN_EXTRUDER_ID) != 0) && map_opt[MappingOption::USE_RIGHT_AMS];
|
||||
bool left_ams_valid = (ams->second->GetBindedExtruderSet().count(DEPUTY_EXTRUDER_ID) != 0) && map_opt[MappingOption::USE_LEFT_AMS];
|
||||
if (right_ams_valid || left_ams_valid)
|
||||
{
|
||||
tray_filaments.emplace(std::make_pair(tray_index, info));
|
||||
|
||||
@@ -16,10 +16,6 @@
|
||||
|
||||
#include "slic3r/GUI/GUI_App.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
#include <unordered_set>
|
||||
|
||||
#include <boost/format.hpp>
|
||||
#include <boost/lexical_cast.hpp>
|
||||
#include <nlohmann/json.hpp>
|
||||
using namespace nlohmann;
|
||||
@@ -33,30 +29,6 @@ void MachineObject::clear_auto_nozzle_mapping()
|
||||
}
|
||||
}
|
||||
|
||||
static std::string s_get_diameter_str(float diameter)
|
||||
{
|
||||
return (boost::format("%.2f") % diameter).str();
|
||||
}
|
||||
|
||||
static std::string s_get_diameter_str(const std::string& diameter)
|
||||
{
|
||||
try {
|
||||
float dia = boost::lexical_cast<float>(diameter);
|
||||
return s_get_diameter_str(dia);
|
||||
} catch (...) {
|
||||
BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << " failed to boost::lexical_cast: " << diameter;
|
||||
return diameter;
|
||||
}
|
||||
|
||||
try {
|
||||
float dia = std::stof(diameter);
|
||||
return s_get_diameter_str(dia);
|
||||
} catch (...) {
|
||||
BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << " std::stof: " << diameter;
|
||||
return diameter;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// get auto nozzle mapping through AP
|
||||
// warnings:
|
||||
@@ -268,7 +240,7 @@ int DevNozzleMappingCtrl::CtrlGetAutoNozzleMappingV1(Slic3r::GUI::Plater* plater
|
||||
|
||||
void DevNozzleMappingCtrl::ParseAutoNozzleMapping(const json& print_jj)
|
||||
{
|
||||
if (print_jj.contains("command") && print_jj["command"].get<std::string>() == "get_auto_nozzle_mapping") {
|
||||
if (print_jj.contains("command") && print_jj["command"].get<string>() == "get_auto_nozzle_mapping") {
|
||||
if (print_jj.contains("sequence_id") && print_jj["sequence_id"] == m_sequence_id) {
|
||||
Clear();
|
||||
DevJsonValParser::ParseVal(print_jj, "result", m_result);
|
||||
@@ -482,4 +454,4 @@ float DevNozzleMappingCtrl::GetFlushWeight(Slic3r::MachineObject* obj) const
|
||||
return total_flush_volume * 1.26 * 0.001;
|
||||
}
|
||||
|
||||
} // namespace Slic3r
|
||||
} // namespace Slic3r
|
||||
@@ -18,7 +18,7 @@ namespace GUI
|
||||
class Plater;
|
||||
}
|
||||
|
||||
struct FilamentInfo; // libslic3r/ProjectTask.hpp
|
||||
struct FilamentInfo; // Orca: libslic3r/ProjectTask.hpp — forward-decl for CtrlGetAutoNozzleMappingV0 signature
|
||||
|
||||
class MachineObject;
|
||||
class DevNozzleMappingCtrl
|
||||
|
||||
@@ -1,11 +1,32 @@
|
||||
#include "DevPrintOptions.h"
|
||||
#include "DevUtil.h"
|
||||
|
||||
#include <cassert>
|
||||
|
||||
#include "slic3r/GUI/DeviceManager.hpp"
|
||||
|
||||
namespace Slic3r
|
||||
{
|
||||
|
||||
DevPrintOptions::DevPrintOptions(MachineObject* obj) : m_obj(obj)
|
||||
{
|
||||
m_detection_list = {
|
||||
{PrintOptionEnum::AI_Monitoring, &m_ai_monitoring_detection},
|
||||
{PrintOptionEnum::First_Layer_Detection, &m_first_layer_detection},
|
||||
{PrintOptionEnum::Buildplate_Mark_Detection, &m_buildplate_mark_detection},
|
||||
{PrintOptionEnum::Buildplate_Align_Detection, &m_buildplate_align_detection},
|
||||
{PrintOptionEnum::Auto_Recovery_Detection, &m_auto_recovery_detection},
|
||||
{PrintOptionEnum::Allow_Prompt_Sound_Detection, &m_allow_prompt_sound_detection},
|
||||
{PrintOptionEnum::Filament_Tangle_Detection, &m_filament_tangle_detection},
|
||||
{PrintOptionEnum::Idle_Heating_Protect_Detection, &m_idel_heating_protect_detection},
|
||||
{PrintOptionEnum::Purify_Air_At_Print_End, &m_purify_air_at_print_end},
|
||||
{PrintOptionEnum::Snapshot_Detection, &m_snapshot_detection},
|
||||
{PrintOptionEnum::FOD_Check_Detection, &m_fod_check_detection},
|
||||
{PrintOptionEnum::Displacement_Detection, &m_displacement_detection},
|
||||
{PrintOptionEnum::Smart_Nozzle_Blob_Detection, &m_smart_nozzle_blob_detection},
|
||||
};
|
||||
}
|
||||
|
||||
void DevPrintOptionsParser::Parse(DevPrintOptions* opts, const nlohmann::json& print_json)
|
||||
{
|
||||
try
|
||||
@@ -20,6 +41,31 @@ void DevPrintOptionsParser::Parse(DevPrintOptions* opts, const nlohmann::json& p
|
||||
const std::string& cfg = print_json["cfg"].get<std::string>();
|
||||
opts->m_speed_level = (DevPrintingSpeedLevel)DevUtil::get_flag_bits(cfg, 8, 3);
|
||||
}
|
||||
|
||||
// Orca: xcam.cfg is an integer bitfield (distinct from the top-level cfg string) that
|
||||
// carries the plate-align/FOD/displacement current values.
|
||||
if (print_json.contains("xcam") && print_json["xcam"].contains("cfg")) {
|
||||
int xcam_cfg = print_json["xcam"]["cfg"].get<int>();
|
||||
if (time(nullptr) - opts->m_buildplate_align_detection.detect_hold_start > HOLD_TIME_3SEC)
|
||||
opts->m_buildplate_align_detection.current_detect_value = DevUtil::get_flag_bits(xcam_cfg, 20);
|
||||
if (time(nullptr) - opts->m_fod_check_detection.detect_hold_start > HOLD_TIME_3SEC)
|
||||
opts->m_fod_check_detection.current_detect_value = DevUtil::get_flag_bits(xcam_cfg, 21);
|
||||
if (time(nullptr) - opts->m_displacement_detection.detect_hold_start > HOLD_TIME_3SEC)
|
||||
opts->m_displacement_detection.current_detect_value = DevUtil::get_flag_bits(xcam_cfg, 22);
|
||||
}
|
||||
|
||||
// Orca: fun2 support bits (distinct from the top-level cfg/fun fields). ORCA's DevUtil has no
|
||||
// no-border extractor, so the MachineObject helper is used here instead.
|
||||
if (print_json.contains("fun2") && print_json["fun2"].is_string()) {
|
||||
std::string fun2 = print_json["fun2"].get<std::string>();
|
||||
if (!fun2.empty()) {
|
||||
opts->m_buildplate_align_detection.is_support_detect = opts->m_obj->get_flag_bits_no_border(fun2, 2) == 1;
|
||||
opts->m_purify_air_at_print_end.is_support_detect = opts->m_obj->get_flag_bits_no_border(fun2, 4);
|
||||
opts->m_fod_check_detection.is_support_detect = opts->m_obj->get_flag_bits_no_border(fun2, 13);
|
||||
opts->m_displacement_detection.is_support_detect = opts->m_obj->get_flag_bits_no_border(fun2, 14);
|
||||
opts->m_smart_nozzle_blob_detection.is_support_detect = opts->m_obj->get_flag_bits_no_border(fun2, 15);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
@@ -31,32 +77,32 @@ void DevPrintOptionsParser::ParseDetectionV1_0(DevPrintOptions *opts, MachineObj
|
||||
{
|
||||
try {
|
||||
if (print_json.contains("xcam")) {
|
||||
if (time(nullptr) - opts->xcam_ai_monitoring_hold_start > HOLD_TIME_3SEC) {
|
||||
if (time(nullptr) - opts->m_ai_monitoring_detection.detect_hold_start > HOLD_TIME_3SEC) {
|
||||
if (print_json["xcam"].contains("printing_monitor")) {
|
||||
// new protocol
|
||||
opts->xcam_ai_monitoring = print_json["xcam"]["printing_monitor"].get<bool>();
|
||||
opts->m_ai_monitoring_detection.current_detect_value = print_json["xcam"]["printing_monitor"].get<bool>();
|
||||
} else {
|
||||
// old version protocol
|
||||
if (print_json["xcam"].contains("spaghetti_detector")) {
|
||||
opts->xcam_ai_monitoring = print_json["xcam"]["spaghetti_detector"].get<bool>();
|
||||
opts->m_ai_monitoring_detection.current_detect_value = print_json["xcam"]["spaghetti_detector"].get<bool>();
|
||||
if (print_json["xcam"].contains("print_halt")) {
|
||||
bool print_halt = print_json["xcam"]["print_halt"].get<bool>();
|
||||
if (print_halt) { opts->xcam_ai_monitoring_sensitivity = "medium"; }
|
||||
if (print_halt) { opts->m_ai_monitoring_detection.current_detect_sensitivity_value = "medium"; }
|
||||
}
|
||||
}
|
||||
}
|
||||
if (print_json["xcam"].contains("halt_print_sensitivity")) {
|
||||
opts->xcam_ai_monitoring_sensitivity = print_json["xcam"]["halt_print_sensitivity"].get<std::string>();
|
||||
opts->m_ai_monitoring_detection.current_detect_sensitivity_value = print_json["xcam"]["halt_print_sensitivity"].get<std::string>();
|
||||
}
|
||||
}
|
||||
|
||||
if (time(nullptr) - opts->xcam_first_layer_hold_start > HOLD_TIME_3SEC) {
|
||||
if (print_json["xcam"].contains("first_layer_inspector")) { opts->xcam_first_layer_inspector = print_json["xcam"]["first_layer_inspector"].get<bool>(); }
|
||||
if (time(nullptr) - opts->m_first_layer_detection.detect_hold_start > HOLD_TIME_3SEC) {
|
||||
if (print_json["xcam"].contains("first_layer_inspector")) { opts->m_first_layer_detection.current_detect_value = print_json["xcam"]["first_layer_inspector"].get<bool>(); }
|
||||
}
|
||||
|
||||
if (time(nullptr) - opts->xcam_buildplate_marker_hold_start > HOLD_TIME_3SEC) {
|
||||
if (time(nullptr) - opts->m_buildplate_mark_detection.detect_hold_start > HOLD_TIME_3SEC) {
|
||||
if (print_json["xcam"].contains("buildplate_marker_detector")) {
|
||||
opts->xcam_buildplate_marker_detector = print_json["xcam"]["buildplate_marker_detector"].get<bool>();
|
||||
opts->m_buildplate_mark_detection.current_detect_value = print_json["xcam"]["buildplate_marker_detector"].get<bool>();
|
||||
obj->is_support_build_plate_marker_detect = true;
|
||||
} else {
|
||||
obj->is_support_build_plate_marker_detect = false;
|
||||
@@ -66,32 +112,31 @@ void DevPrintOptionsParser::ParseDetectionV1_0(DevPrintOptions *opts, MachineObj
|
||||
} catch (...) {
|
||||
;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void DevPrintOptionsParser::ParseDetectionV1_1(DevPrintOptions *opts, MachineObject *obj, const nlohmann::json &print_json,bool enable)
|
||||
{
|
||||
if (print_json["module_name"].get<std::string>() == "first_layer_inspector") {
|
||||
if (time(nullptr) - opts->xcam_first_layer_hold_start > HOLD_TIME_3SEC) {
|
||||
opts->xcam_first_layer_inspector = enable;
|
||||
if (time(nullptr) - opts->m_first_layer_detection.detect_hold_start > HOLD_TIME_3SEC) {
|
||||
opts->m_first_layer_detection.current_detect_value = enable;
|
||||
}
|
||||
} else if (print_json["module_name"].get<std::string>() == "buildplate_marker_detector") {
|
||||
if (time(nullptr) - opts->xcam_buildplate_marker_hold_start > HOLD_TIME_3SEC) {
|
||||
opts->xcam_buildplate_marker_detector = enable;
|
||||
if (time(nullptr) - opts->m_buildplate_mark_detection.detect_hold_start > HOLD_TIME_3SEC) {
|
||||
opts->m_buildplate_mark_detection.current_detect_value = enable;
|
||||
}
|
||||
} else if (print_json["module_name"].get<std::string>() == "printing_monitor") {
|
||||
if (time(nullptr) - opts->xcam_ai_monitoring_hold_start > HOLD_TIME_3SEC) {
|
||||
opts->xcam_ai_monitoring = enable;
|
||||
if (time(nullptr) - opts->m_ai_monitoring_detection.detect_hold_start > HOLD_TIME_3SEC) {
|
||||
opts->m_ai_monitoring_detection.current_detect_value = enable;
|
||||
if (print_json.contains("halt_print_sensitivity")) {
|
||||
opts->xcam_ai_monitoring_sensitivity = print_json["halt_print_sensitivity"].get<std::string>();
|
||||
opts->m_ai_monitoring_detection.current_detect_sensitivity_value = print_json["halt_print_sensitivity"].get<std::string>();
|
||||
}
|
||||
}
|
||||
} else if (print_json["module_name"].get<std::string>() == "spaghetti_detector") {
|
||||
if (time(nullptr) - opts->xcam_ai_monitoring_hold_start > HOLD_TIME_3SEC) {
|
||||
if (time(nullptr) - opts->m_ai_monitoring_detection.detect_hold_start > HOLD_TIME_3SEC) {
|
||||
// old protocol
|
||||
opts->xcam_ai_monitoring = enable;
|
||||
opts->m_ai_monitoring_detection.current_detect_value = enable;
|
||||
if (print_json.contains("print_halt")) {
|
||||
if (print_json["print_halt"].get<bool>()) { opts->xcam_ai_monitoring_sensitivity = "medium"; }
|
||||
if (print_json["print_halt"].get<bool>()) { opts->m_ai_monitoring_detection.current_detect_sensitivity_value = "medium"; }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -103,12 +148,12 @@ void DevPrintOptionsParser::ParseDetectionV1_2(DevPrintOptions *opts, MachineObj
|
||||
if (print_json.contains("option")) {
|
||||
if (print_json["option"].is_number()) {
|
||||
int option = print_json["option"].get<int>();
|
||||
if (time(nullptr) - opts->xcam_auto_recovery_hold_start > HOLD_TIME_3SEC) { opts->xcam_auto_recovery_step_loss = ((option & 0x01) != 0); }
|
||||
if (time(nullptr) - opts->m_auto_recovery_detection.detect_hold_start > HOLD_TIME_3SEC) { opts->m_auto_recovery_detection.current_detect_value = ((option & 0x01) != 0); }
|
||||
}
|
||||
}
|
||||
|
||||
if (time(nullptr) - opts->xcam_auto_recovery_hold_start > HOLD_TIME_3SEC) {
|
||||
if (print_json.contains("auto_recovery")) { opts->xcam_auto_recovery_step_loss = print_json["auto_recovery"].get<bool>(); }
|
||||
if (time(nullptr) - opts->m_auto_recovery_detection.detect_hold_start > HOLD_TIME_3SEC) {
|
||||
if (print_json.contains("auto_recovery")) { opts->m_auto_recovery_detection.current_detect_value = print_json["auto_recovery"].get<bool>(); }
|
||||
}
|
||||
} catch (...) {}
|
||||
|
||||
@@ -117,38 +162,52 @@ void DevPrintOptionsParser::ParseDetectionV1_2(DevPrintOptions *opts, MachineObj
|
||||
void DevPrintOptionsParser::ParseDetectionV2_0(DevPrintOptions *opts, std::string print_json)
|
||||
{
|
||||
|
||||
if (time(nullptr) - opts->xcam_first_layer_hold_start > HOLD_TIME_3SEC) {
|
||||
opts->xcam_first_layer_inspector = DevUtil::get_flag_bits(print_json, 12);
|
||||
if (time(nullptr) - opts->m_first_layer_detection.detect_hold_start > HOLD_TIME_3SEC) {
|
||||
opts->m_first_layer_detection.current_detect_value = DevUtil::get_flag_bits(print_json, 12);
|
||||
}
|
||||
|
||||
if (time(nullptr) - opts->xcam_ai_monitoring_hold_start > HOLD_COUNT_MAX) {
|
||||
opts->xcam_ai_monitoring = DevUtil::get_flag_bits(print_json, 15);
|
||||
if (time(nullptr) - opts->m_ai_monitoring_detection.detect_hold_start > HOLD_COUNT_MAX) {
|
||||
opts->m_ai_monitoring_detection.current_detect_value = DevUtil::get_flag_bits(print_json, 15);
|
||||
|
||||
switch (DevUtil::get_flag_bits(print_json, 13, 2)) {
|
||||
case 0: opts->xcam_ai_monitoring_sensitivity = "never_halt"; break;
|
||||
case 1: opts->xcam_ai_monitoring_sensitivity = "low"; break;
|
||||
case 2: opts->xcam_ai_monitoring_sensitivity = "medium"; break;
|
||||
case 3: opts->xcam_ai_monitoring_sensitivity = "high"; break;
|
||||
case 0: opts->m_ai_monitoring_detection.current_detect_sensitivity_value = "never_halt"; break;
|
||||
case 1: opts->m_ai_monitoring_detection.current_detect_sensitivity_value = "low"; break;
|
||||
case 2: opts->m_ai_monitoring_detection.current_detect_sensitivity_value = "medium"; break;
|
||||
case 3: opts->m_ai_monitoring_detection.current_detect_sensitivity_value = "high"; break;
|
||||
default: break;
|
||||
}
|
||||
}
|
||||
|
||||
if (time(nullptr) - opts->xcam_auto_recovery_hold_start > HOLD_COUNT_MAX){
|
||||
opts->xcam_auto_recovery_step_loss =DevUtil::get_flag_bits(print_json, 16);
|
||||
if (time(nullptr) - opts->m_auto_recovery_detection.detect_hold_start > HOLD_COUNT_MAX){
|
||||
opts->m_auto_recovery_detection.current_detect_value = DevUtil::get_flag_bits(print_json, 16);
|
||||
}
|
||||
|
||||
if (time(nullptr) - opts->xcam_prompt_sound_hold_start > HOLD_TIME_3SEC) {
|
||||
opts->xcam_allow_prompt_sound = DevUtil::get_flag_bits(print_json, 22);
|
||||
if (time(nullptr) - opts->m_allow_prompt_sound_detection.detect_hold_start > HOLD_TIME_3SEC) {
|
||||
opts->m_allow_prompt_sound_detection.current_detect_value = DevUtil::get_flag_bits(print_json, 22);
|
||||
}
|
||||
|
||||
if (time(nullptr) - opts->xcam_filament_tangle_detect_hold_start > HOLD_TIME_3SEC) {
|
||||
opts->xcam_filament_tangle_detect = DevUtil::get_flag_bits(print_json, 23);
|
||||
if (time(nullptr) - opts->m_filament_tangle_detection.detect_hold_start > HOLD_TIME_3SEC) {
|
||||
opts->m_filament_tangle_detection.current_detect_value = DevUtil::get_flag_bits(print_json, 23);
|
||||
}
|
||||
}
|
||||
|
||||
void DevPrintOptionsParser::ParseDetectionV2_1(DevPrintOptions *opts, std::string cfg) {
|
||||
if (time(nullptr) - opts->idel_heating_protect_hold_strat > HOLD_TIME_3SEC)
|
||||
opts->idel_heating_protect_enabled = DevUtil::get_flag_bits(cfg, 32, 2);
|
||||
if (time(nullptr) - opts->m_idel_heating_protect_detection.detect_hold_start > HOLD_TIME_3SEC)
|
||||
opts->m_idel_heating_protect_detection.current_detect_value = DevUtil::get_flag_bits(cfg, 32, 2);
|
||||
|
||||
// Orca: further top-level cfg-string detection bits, parsed here alongside idle-heating.
|
||||
if (time(nullptr) - opts->m_purify_air_at_print_end.detect_hold_start > HOLD_TIME_3SEC)
|
||||
opts->m_purify_air_at_print_end.current_detect_value = DevUtil::get_flag_bits(cfg, 36, 2);
|
||||
|
||||
// Smart nozzle blob detection mode: cfg bits 43-44 (2 bits), 0=off, 1=on, 2=auto
|
||||
if (time(nullptr) - opts->m_smart_nozzle_blob_detection.detect_hold_start > HOLD_TIME_3SEC)
|
||||
opts->m_smart_nozzle_blob_detection.current_detect_value = DevUtil::get_flag_bits(cfg, 43, 2);
|
||||
|
||||
if (time(nullptr) - opts->m_snapshot_detection.detect_hold_start > HOLD_TIME_3SEC) {
|
||||
opts->m_snapshot_detection.current_detect_value = DevUtil::get_flag_bits(cfg, 38, 2);
|
||||
opts->m_snapshot_detection.is_support_detect =
|
||||
opts->m_snapshot_detection.current_detect_value != 0 && opts->m_snapshot_detection.current_detect_value != 3;
|
||||
}
|
||||
}
|
||||
|
||||
void DevPrintOptions::SetPrintingSpeedLevel(DevPrintingSpeedLevel speed_level)
|
||||
@@ -163,75 +222,85 @@ void DevPrintOptions::SetPrintingSpeedLevel(DevPrintingSpeedLevel speed_level)
|
||||
}
|
||||
}
|
||||
|
||||
PrintOptionData* DevPrintOptions::GetDetectionOption(PrintOptionEnum print_option)
|
||||
{
|
||||
auto it = m_detection_list.find(print_option);
|
||||
if (it != m_detection_list.end())
|
||||
{
|
||||
return it->second;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
int DevPrintOptions::command_xcam_control_ai_monitoring(bool on_off, std::string lvl)
|
||||
{
|
||||
bool print_halt = (lvl == "never_halt") ? false : true;
|
||||
|
||||
xcam_ai_monitoring = on_off;
|
||||
xcam_ai_monitoring_hold_start = time(nullptr);
|
||||
xcam_ai_monitoring_sensitivity = lvl;
|
||||
m_ai_monitoring_detection.current_detect_value = on_off;
|
||||
m_ai_monitoring_detection.detect_hold_start = time(nullptr);
|
||||
m_ai_monitoring_detection.current_detect_sensitivity_value = lvl;
|
||||
return command_xcam_control("printing_monitor", on_off, m_obj, lvl);
|
||||
}
|
||||
|
||||
int DevPrintOptions::command_xcam_control_idelheatingprotect_detector(bool on_off)
|
||||
{
|
||||
idel_heating_protect_enabled = on_off;
|
||||
idel_heating_protect_hold_strat = time(nullptr);
|
||||
m_idel_heating_protect_detection.current_detect_value = on_off;
|
||||
m_idel_heating_protect_detection.detect_hold_start = time(nullptr);
|
||||
return command_set_against_continued_heating_mode(on_off);
|
||||
}
|
||||
|
||||
int DevPrintOptions::command_xcam_control_buildplate_marker_detector(bool on_off)
|
||||
{
|
||||
xcam_buildplate_marker_detector = on_off;
|
||||
xcam_buildplate_marker_hold_start = time(nullptr);
|
||||
m_buildplate_mark_detection.current_detect_value = on_off;
|
||||
m_buildplate_mark_detection.detect_hold_start = time(nullptr);
|
||||
return command_xcam_control("buildplate_marker_detector", on_off ,m_obj);
|
||||
}
|
||||
|
||||
int DevPrintOptions::command_xcam_control_first_layer_inspector(bool on_off, bool print_halt)
|
||||
{
|
||||
xcam_first_layer_inspector = on_off;
|
||||
xcam_first_layer_hold_start = time(nullptr);
|
||||
m_first_layer_detection.current_detect_value = on_off;
|
||||
m_first_layer_detection.detect_hold_start = time(nullptr);
|
||||
return command_xcam_control("first_layer_inspector", on_off, m_obj);
|
||||
}
|
||||
|
||||
int DevPrintOptions::command_xcam_control_auto_recovery_step_loss(bool on_off)
|
||||
{
|
||||
xcam_auto_recovery_step_loss = on_off;
|
||||
xcam_auto_recovery_hold_start = time(nullptr);
|
||||
m_auto_recovery_detection.current_detect_value = on_off;
|
||||
m_auto_recovery_detection.detect_hold_start = time(nullptr);
|
||||
return command_set_printing_option(on_off, m_obj);
|
||||
}
|
||||
|
||||
int DevPrintOptions::command_xcam_control_allow_prompt_sound(bool on_off)
|
||||
{
|
||||
xcam_allow_prompt_sound = on_off;
|
||||
xcam_prompt_sound_hold_start = time(nullptr);
|
||||
m_allow_prompt_sound_detection.current_detect_value = on_off;
|
||||
m_allow_prompt_sound_detection.detect_hold_start = time(nullptr);
|
||||
return command_set_prompt_sound(on_off, m_obj);
|
||||
}
|
||||
|
||||
int DevPrintOptions::command_xcam_control_filament_tangle_detect(bool on_off)
|
||||
{
|
||||
xcam_filament_tangle_detect = on_off;
|
||||
xcam_filament_tangle_detect_hold_start = time(nullptr);
|
||||
m_filament_tangle_detection.current_detect_value = on_off;
|
||||
m_filament_tangle_detection.detect_hold_start = time(nullptr);
|
||||
return command_set_filament_tangle_detect(on_off, m_obj);
|
||||
}
|
||||
|
||||
void DevPrintOptions::parse_auto_recovery_step_loss_status(int flag) {
|
||||
if (time(nullptr) - xcam_auto_recovery_hold_start > HOLD_TIME_3SEC) {
|
||||
xcam_auto_recovery_step_loss = ((flag >> 4) & 0x1) != 0;
|
||||
if (time(nullptr) - m_auto_recovery_detection.detect_hold_start > HOLD_TIME_3SEC) {
|
||||
m_auto_recovery_detection.current_detect_value = ((flag >> 4) & 0x1) != 0;
|
||||
}
|
||||
}
|
||||
|
||||
void DevPrintOptions::parse_allow_prompt_sound_status(int flag)
|
||||
{
|
||||
if (time(nullptr) - xcam_prompt_sound_hold_start > HOLD_TIME_3SEC) {
|
||||
xcam_allow_prompt_sound = ((flag >> 17) & 0x1) != 0;
|
||||
if (time(nullptr) - m_allow_prompt_sound_detection.detect_hold_start > HOLD_TIME_3SEC) {
|
||||
m_allow_prompt_sound_detection.current_detect_value = ((flag >> 17) & 0x1) != 0;
|
||||
}
|
||||
}
|
||||
|
||||
void DevPrintOptions::parse_filament_tangle_detect_status(int flag)
|
||||
{
|
||||
if (time(nullptr) - xcam_filament_tangle_detect_hold_start > HOLD_TIME_3SEC) {
|
||||
xcam_filament_tangle_detect = ((flag >> 20) & 0x1) != 0;
|
||||
if (time(nullptr) - m_filament_tangle_detection.detect_hold_start > HOLD_TIME_3SEC) {
|
||||
m_filament_tangle_detection.current_detect_value = ((flag >> 20) & 0x1) != 0;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -289,11 +358,75 @@ int DevPrintOptions::command_set_filament_tangle_detect(bool filament_tangle_det
|
||||
return obj->publish_json(j);
|
||||
}
|
||||
|
||||
int DevPrintOptions::command_xcam_control_build_plate_align_detector(bool on_off)
|
||||
{
|
||||
m_buildplate_align_detection.current_detect_value = on_off;
|
||||
m_buildplate_align_detection.detect_hold_start = time(nullptr);
|
||||
return command_xcam_control("plate_offset_switch", on_off, m_obj);
|
||||
}
|
||||
|
||||
int DevPrintOptions::command_xcam_control_fod_check(bool on_off)
|
||||
{
|
||||
m_fod_check_detection.current_detect_value = on_off;
|
||||
m_fod_check_detection.detect_hold_start = time(nullptr);
|
||||
return command_xcam_control("fod_check", on_off, m_obj);
|
||||
}
|
||||
|
||||
int DevPrintOptions::command_xcam_control_displacement_detection(bool on_off)
|
||||
{
|
||||
m_displacement_detection.current_detect_value = on_off;
|
||||
m_displacement_detection.detect_hold_start = time(nullptr);
|
||||
return command_xcam_control("model_movement_check", on_off, m_obj);
|
||||
}
|
||||
|
||||
int DevPrintOptions::command_xcam_control_purify_air_at_print_end(int on_off)
|
||||
{
|
||||
m_purify_air_at_print_end.current_detect_value = on_off;
|
||||
m_purify_air_at_print_end.detect_hold_start = time(nullptr);
|
||||
return command_set_purify_air_at_print_end((PurifyAirAtPrintEndState) on_off, m_obj);
|
||||
}
|
||||
|
||||
int DevPrintOptions::command_set_purify_air_at_print_end(PurifyAirAtPrintEndState state, MachineObject *obj)
|
||||
{
|
||||
json j;
|
||||
j["print"]["command"] = "print_option";
|
||||
j["print"]["sequence_id"] = std::to_string(MachineObject::m_sequence_id++);
|
||||
switch (state)
|
||||
{
|
||||
case PurifyAirAtPrintEndState::PurifyAirDisable: j["print"]["air_purification"] = 0; break;
|
||||
case PurifyAirAtPrintEndState::PurifyAirByInside: j["print"]["air_purification"] = 1; break;
|
||||
case PurifyAirAtPrintEndState::PurifyAirByOutside: j["print"]["air_purification"] = 2; break;
|
||||
default: assert(0);
|
||||
}
|
||||
return obj->publish_json(j);
|
||||
}
|
||||
|
||||
int DevPrintOptions::command_snapshot_control(int on_off)
|
||||
{
|
||||
m_snapshot_detection.current_detect_value = on_off;
|
||||
m_snapshot_detection.detect_hold_start = time(nullptr);
|
||||
return command_set_snapshot_control(on_off, m_obj);
|
||||
}
|
||||
|
||||
int DevPrintOptions::command_set_snapshot_control(int on_off, MachineObject *obj)
|
||||
{
|
||||
json j;
|
||||
j["camera"]["command"] = "ipcam_cap_pic_set";
|
||||
j["camera"]["sequence_id"] = std::to_string(MachineObject::m_sequence_id++);
|
||||
j["camera"]["control"] = on_off ? "enable" : "disable";
|
||||
return obj->publish_json(j);
|
||||
}
|
||||
|
||||
int DevPrintOptions::command_smart_nozzle_blob_detect_mode(int mode)
|
||||
{
|
||||
json j;
|
||||
j["print"]["command"] = "print_option";
|
||||
j["print"]["sequence_id"] = std::to_string(MachineObject::m_sequence_id++);
|
||||
j["print"]["nozzle_blob_detect_v2"] = mode; // int: 0=off, 1=on, 2=auto
|
||||
m_smart_nozzle_blob_detection.current_detect_value = mode;
|
||||
m_smart_nozzle_blob_detection.detect_hold_start = time(nullptr);
|
||||
return m_obj->publish_json(j);
|
||||
}
|
||||
|
||||
}
|
||||
// namespace Slic3r
|
||||
// namespace Slic3r
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
#pragma once
|
||||
#include <map>
|
||||
#include <nlohmann/json.hpp>
|
||||
#include "slic3r/Utils/json_diff.hpp"
|
||||
#include <wx/string.h>
|
||||
@@ -9,17 +10,53 @@ namespace Slic3r {
|
||||
|
||||
class MachineObject;
|
||||
|
||||
// Identifies a print-detection option tracked in DevPrintOptions::m_detection_list.
|
||||
enum class PrintOptionEnum
|
||||
{
|
||||
AI_Monitoring,
|
||||
First_Layer_Detection,
|
||||
Buildplate_Mark_Detection,
|
||||
Buildplate_Align_Detection,
|
||||
Auto_Recovery_Detection,
|
||||
Allow_Prompt_Sound_Detection,
|
||||
Filament_Tangle_Detection,
|
||||
Idle_Heating_Protect_Detection,
|
||||
Purify_Air_At_Print_End,
|
||||
Snapshot_Detection,
|
||||
FOD_Check_Detection,
|
||||
Displacement_Detection,
|
||||
Smart_Nozzle_Blob_Detection,
|
||||
};
|
||||
|
||||
// State of a single print-detection option.
|
||||
struct PrintOptionData
|
||||
{
|
||||
bool is_support_detect{false}; // some detections have no supporting field
|
||||
int current_detect_value{-1}; // -1 until parsed; otherwise the reported value
|
||||
std::string current_detect_sensitivity_value;
|
||||
time_t detect_hold_start{0}; // suppresses parsed overwrites for a short hold
|
||||
};
|
||||
|
||||
class DevPrintOptions
|
||||
{
|
||||
friend class DevPrintOptionsParser;
|
||||
public:
|
||||
DevPrintOptions(MachineObject* obj): m_obj(obj) {}
|
||||
DevPrintOptions(MachineObject* obj);
|
||||
|
||||
// Tri-state for the purify-air-at-print-end firmware option (matches the MQTT air_purification value).
|
||||
enum class PurifyAirAtPrintEndState : int {
|
||||
PurifyAirDisable = 0,
|
||||
PurifyAirByInside = 1,
|
||||
PurifyAirByOutside = 2,
|
||||
};
|
||||
|
||||
public:
|
||||
void SetPrintingSpeedLevel(DevPrintingSpeedLevel speed_level);
|
||||
DevPrintingSpeedLevel GetPrintingSpeedLevel() const { return m_speed_level;}
|
||||
|
||||
// Returns the holder for a tracked detection option, or nullptr if not tracked.
|
||||
PrintOptionData* GetDetectionOption(PrintOptionEnum print_option);
|
||||
|
||||
// detect options
|
||||
int command_xcam_control_ai_monitoring(bool on_off, std::string lvl);
|
||||
int command_xcam_control_first_layer_inspector(bool on_off, bool print_halt);
|
||||
@@ -29,6 +66,13 @@ public:
|
||||
int command_xcam_control_filament_tangle_detect(bool on_off);
|
||||
int command_xcam_control_idelheatingprotect_detector(bool on_off);
|
||||
|
||||
// Firmware print-option toggles (each gated on a fun2 capability bit).
|
||||
int command_xcam_control_build_plate_align_detector(bool on_off);
|
||||
int command_xcam_control_fod_check(bool on_off);
|
||||
int command_xcam_control_displacement_detection(bool on_off);
|
||||
int command_xcam_control_purify_air_at_print_end(int on_off);
|
||||
int command_smart_nozzle_blob_detect_mode(int mode); // 0=off, 1=on, 2=auto
|
||||
int command_snapshot_control(int on_off);
|
||||
|
||||
int command_xcam_control(std::string module_name, bool on_off, MachineObject *obj ,std::string lvl = "");
|
||||
// set print option
|
||||
@@ -39,42 +83,44 @@ public:
|
||||
int command_set_filament_tangle_detect(bool fliament_tangle_detect, MachineObject *obj);
|
||||
|
||||
int command_set_against_continued_heating_mode(bool on_off);
|
||||
int command_set_purify_air_at_print_end(PurifyAirAtPrintEndState state, MachineObject *obj);
|
||||
int command_set_snapshot_control(int on_off, MachineObject *obj);
|
||||
|
||||
void parse_auto_recovery_step_loss_status(int flag);
|
||||
void parse_allow_prompt_sound_status(int flag);
|
||||
void parse_filament_tangle_detect_status(int flag);
|
||||
|
||||
bool GetAiMonitoring() const { return xcam_ai_monitoring; }
|
||||
bool GetFirstLayerInspector() const{ return xcam_first_layer_inspector; }
|
||||
bool GetBuildplateMarkerDetector() const { return xcam_buildplate_marker_detector; }
|
||||
bool GetAutoRecoveryStepLoss() const { return xcam_auto_recovery_step_loss; }
|
||||
bool GetAllowPromptSound() const { return xcam_allow_prompt_sound; }
|
||||
bool GetFilamentTangleDetect() const { return xcam_filament_tangle_detect; }
|
||||
int GetIdelHeatingProtectEenabled() const { return idel_heating_protect_enabled; }
|
||||
string GetAiMonitoringSensitivity() const { return xcam_ai_monitoring_sensitivity; }
|
||||
// Thin wrappers preserved for existing callers; read from the detection-option map.
|
||||
bool GetAiMonitoring() const { return m_ai_monitoring_detection.current_detect_value == 1; }
|
||||
bool GetFirstLayerInspector() const{ return m_first_layer_detection.current_detect_value == 1; }
|
||||
bool GetBuildplateMarkerDetector() const { return m_buildplate_mark_detection.current_detect_value == 1; }
|
||||
bool GetAutoRecoveryStepLoss() const { return m_auto_recovery_detection.current_detect_value == 1; }
|
||||
bool GetAllowPromptSound() const { return m_allow_prompt_sound_detection.current_detect_value == 1; }
|
||||
bool GetFilamentTangleDetect() const { return m_filament_tangle_detection.current_detect_value == 1; }
|
||||
int GetIdelHeatingProtectEenabled() const { return m_idel_heating_protect_detection.current_detect_value; }
|
||||
std::string GetAiMonitoringSensitivity() const { return m_ai_monitoring_detection.current_detect_sensitivity_value; }
|
||||
|
||||
|
||||
private:
|
||||
// print option
|
||||
DevPrintingSpeedLevel m_speed_level = SPEED_LEVEL_INVALID;
|
||||
|
||||
// detect options
|
||||
bool xcam_ai_monitoring{false};
|
||||
// detection options
|
||||
PrintOptionData m_ai_monitoring_detection;
|
||||
PrintOptionData m_first_layer_detection;
|
||||
PrintOptionData m_buildplate_mark_detection;
|
||||
PrintOptionData m_buildplate_align_detection;
|
||||
PrintOptionData m_auto_recovery_detection;
|
||||
PrintOptionData m_allow_prompt_sound_detection;
|
||||
PrintOptionData m_filament_tangle_detection;
|
||||
PrintOptionData m_idel_heating_protect_detection;
|
||||
PrintOptionData m_purify_air_at_print_end;
|
||||
PrintOptionData m_snapshot_detection;
|
||||
PrintOptionData m_fod_check_detection;
|
||||
PrintOptionData m_displacement_detection;
|
||||
PrintOptionData m_smart_nozzle_blob_detection;
|
||||
|
||||
std::string xcam_ai_monitoring_sensitivity;
|
||||
bool xcam_buildplate_marker_detector{false};
|
||||
bool xcam_first_layer_inspector{false};
|
||||
bool xcam_auto_recovery_step_loss{false};
|
||||
bool xcam_allow_prompt_sound{false};
|
||||
bool xcam_filament_tangle_detect{false};
|
||||
int idel_heating_protect_enabled = -1;
|
||||
time_t xcam_ai_monitoring_hold_start = 0;
|
||||
time_t xcam_buildplate_marker_hold_start = 0;
|
||||
time_t xcam_first_layer_hold_start = 0;
|
||||
time_t xcam_auto_recovery_hold_start = 0;
|
||||
time_t xcam_prompt_sound_hold_start = 0;
|
||||
time_t xcam_filament_tangle_detect_hold_start = 0;
|
||||
time_t idel_heating_protect_hold_strat = 0;
|
||||
std::map<PrintOptionEnum, PrintOptionData*> m_detection_list;
|
||||
|
||||
MachineObject* m_obj;/*owner*/
|
||||
};
|
||||
@@ -93,4 +139,4 @@ public:
|
||||
static void ParseDetectionV2_1(DevPrintOptions *opts, std::string cfg);
|
||||
};
|
||||
|
||||
} // namespace Slic3r
|
||||
} // namespace Slic3r
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
#include "DevStatus.h"
|
||||
#include "slic3r/GUI/DeviceManager.hpp"
|
||||
#include <boost/log/trivial.hpp>
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
void DevStatus::ParseStatus(const nlohmann::json& print_jj)
|
||||
{
|
||||
try {
|
||||
if (print_jj.contains("job")) {
|
||||
const nlohmann::json& job_jj = print_jj.at("job");
|
||||
if (job_jj.contains("job_state")) {
|
||||
auto new_state = (DevJobState)job_jj.at("job_state").get<int>();
|
||||
if (m_job_state != new_state) {
|
||||
m_job_state = new_state;
|
||||
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << ": job_state-> " << (int)new_state;
|
||||
}
|
||||
|
||||
if (m_job_state == DevJobState::JobStateFinishing) {
|
||||
m_job_state = DevJobState::JobStateFinish;
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (const std::exception& e) {
|
||||
#if BBL_RELEASE_TO_PUBLIC
|
||||
BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << ": get exception";
|
||||
#else
|
||||
BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << ": get exception=" << e.what();
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace Slic3r
|
||||
@@ -0,0 +1,43 @@
|
||||
#pragma once
|
||||
#include <optional>
|
||||
#include <nlohmann/json.hpp>
|
||||
#include "slic3r/Utils/json_diff.hpp"
|
||||
|
||||
#include "DevDefs.h"
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
enum DevJobState
|
||||
{
|
||||
JobStateIdle = 0,
|
||||
JobStateSlicing = 1,
|
||||
JobStatePrepare = 2,
|
||||
JobStateStarting = 3,
|
||||
JobStateRunning = 4,
|
||||
JobStatePause = 5,
|
||||
JobStatePausing = 6,
|
||||
JobStateResuming = 7,
|
||||
JobStateFinish = 8,
|
||||
JobStateFailed = 9,
|
||||
JobStateFinishing = 10,
|
||||
JobStateStoppping = 11,
|
||||
};
|
||||
|
||||
class MachineObject;
|
||||
class DevStatus
|
||||
{
|
||||
public:
|
||||
DevStatus(MachineObject* owner) : m_owner(owner) {};
|
||||
|
||||
public:
|
||||
std::optional<DevJobState> GetJobState() const { return m_job_state; }
|
||||
|
||||
// Parse
|
||||
void ParseStatus(const nlohmann::json& print_jj);
|
||||
|
||||
private:
|
||||
MachineObject *m_owner = nullptr;
|
||||
std::optional<DevJobState> m_job_state; // could be nullopt for some old firmware
|
||||
};
|
||||
|
||||
} // namespace Slic3r
|
||||
@@ -19,16 +19,42 @@ DevStorage::SdcardState Slic3r::DevStorage::set_sdcard_state(int state)
|
||||
{
|
||||
if (system)
|
||||
{
|
||||
if (print_json.contains("sdcard")) {
|
||||
if (print_json["sdcard"].get<bool>())
|
||||
system->m_sdcard_state = DevStorage::SdcardState::HAS_SDCARD_NORMAL;
|
||||
else
|
||||
try {
|
||||
if (print_json.contains("sdcard")) {
|
||||
if (print_json["sdcard"].get<bool>())
|
||||
system->m_sdcard_state = DevStorage::SdcardState::HAS_SDCARD_NORMAL;
|
||||
else
|
||||
system->m_sdcard_state = DevStorage::SdcardState::NO_SDCARD;
|
||||
} else {
|
||||
system->m_sdcard_state = DevStorage::SdcardState::NO_SDCARD;
|
||||
} else {
|
||||
system->m_sdcard_state = DevStorage::SdcardState::NO_SDCARD;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// parse timelapse storage space from cam push data
|
||||
if (print_json.contains("device") && print_json["device"].is_object()) {
|
||||
const auto& device_json = print_json["device"];
|
||||
if (device_json.contains("cam") && device_json["cam"].is_object()) {
|
||||
const auto& cam_data = device_json["cam"];
|
||||
if (cam_data.contains("tl_internal_free_kb"))
|
||||
system->tl_internal_free_kb = cam_data["tl_internal_free_kb"].get<int>();
|
||||
if (cam_data.contains("tl_internal_total_kb"))
|
||||
system->tl_internal_total_kb = cam_data["tl_internal_total_kb"].get<int>();
|
||||
if (cam_data.contains("tl_external_free_kb"))
|
||||
system->tl_external_free_kb = cam_data["tl_external_free_kb"].get<int>();
|
||||
if (cam_data.contains("tl_external_total_kb"))
|
||||
system->tl_external_total_kb = cam_data["tl_external_total_kb"].get<int>();
|
||||
}
|
||||
}
|
||||
} catch (...) {}
|
||||
}
|
||||
}
|
||||
|
||||
bool DevStorage::is_timelapse_storage_low(const std::string& storage) const
|
||||
{
|
||||
const int THRESHOLD_KB = 20480; // Orca: 20MB (20480 KB; corrected mislabeled comment)
|
||||
if (storage == "internal")
|
||||
return tl_internal_free_kb >= 0 && tl_internal_free_kb < THRESHOLD_KB;
|
||||
else
|
||||
return tl_external_free_kb >= 0 && tl_external_free_kb < THRESHOLD_KB;
|
||||
}
|
||||
|
||||
} // namespace Slic3r
|
||||
@@ -26,11 +26,21 @@ public:
|
||||
SdcardState get_sdcard_state() const { return m_sdcard_state; };
|
||||
SdcardState set_sdcard_state(int state);
|
||||
|
||||
static void ParseV1_0(const json &print_json, DevStorage *system);
|
||||
static void ParseV1_0(const json &print_json, DevStorage *system);
|
||||
|
||||
bool is_timelapse_storage_low(const std::string& storage) const;
|
||||
|
||||
private:
|
||||
MachineObject *m_owner;
|
||||
SdcardState m_sdcard_state { NO_SDCARD };
|
||||
// timelapse storage space info (from device push cam data)
|
||||
int tl_internal_free_kb{-1};
|
||||
int tl_internal_total_kb{-1};
|
||||
int tl_external_free_kb{-1};
|
||||
int tl_external_total_kb{-1};
|
||||
|
||||
|
||||
|
||||
};
|
||||
|
||||
} // namespace Slic3r
|
||||
@@ -0,0 +1,130 @@
|
||||
#include "DevUpgrade.h"
|
||||
#include "DevUtil.h"
|
||||
|
||||
#include "slic3r/GUI/DeviceManager.hpp"
|
||||
|
||||
#include "slic3r/GUI/GUI_App.hpp"
|
||||
#include "slic3r/GUI/I18N.hpp"
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
int DevUpgrade::GetUpgradeProgressInt() const
|
||||
{
|
||||
try {
|
||||
return std::stoi(m_upgrade_progress);
|
||||
} catch (const std::exception &e) {
|
||||
BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << ": " << e.what();
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void DevUpgrade::ParseUpgrade_V1_0(const json &print_jj)
|
||||
{
|
||||
if (print_jj.contains("upgrade_state")) {
|
||||
const auto &upgrade_jj = print_jj["upgrade_state"];
|
||||
|
||||
// status and steps
|
||||
DevJsonValParser::ParseVal(upgrade_jj, "status", m_upgrade_status);
|
||||
DevJsonValParser::ParseVal(upgrade_jj, "progress", m_upgrade_progress);
|
||||
DevJsonValParser::ParseVal(upgrade_jj, "message", m_upgrade_message);
|
||||
DevJsonValParser::ParseVal(upgrade_jj, "module", m_upgrade_module);
|
||||
DevJsonValParser::ParseVal(upgrade_jj, "err_code", m_upgrade_err_code);
|
||||
|
||||
// version requested
|
||||
DevJsonValParser::ParseVal(upgrade_jj, "new_version_state", m_upgrade_new_version_state);
|
||||
DevJsonValParser::ParseVal(upgrade_jj, "consistency_request", m_upgrade_consistency_request);
|
||||
DevJsonValParser::ParseVal(upgrade_jj, "force_upgrade", m_upgrade_force_upgrade);
|
||||
|
||||
// version info
|
||||
DevJsonValParser::ParseVal(upgrade_jj, "ota_new_version_number", m_ota_new_version_number);
|
||||
|
||||
// version list
|
||||
if (upgrade_jj.contains("new_ver_list")) {
|
||||
m_new_ver_list.clear();
|
||||
for (auto ver_item = upgrade_jj["new_ver_list"].begin(); ver_item != upgrade_jj["new_ver_list"].end(); ver_item++) {
|
||||
DevFirmwareVersionInfo ver_info;
|
||||
DevJsonValParser::ParseVal(*ver_item, "name", ver_info.name);
|
||||
DevJsonValParser::ParseVal(*ver_item, "cur_ver", ver_info.sw_ver);
|
||||
DevJsonValParser::ParseVal(*ver_item, "new_ver", ver_info.sw_new_ver);
|
||||
m_new_ver_list[ver_info.name] = ver_info;
|
||||
if (ver_info.name == "ota" && m_ota_new_version_number.empty()) { m_ota_new_version_number = ver_info.sw_new_ver; }
|
||||
}
|
||||
} else {
|
||||
m_new_ver_list.clear();
|
||||
}
|
||||
|
||||
// try to parse display state
|
||||
ParseUpgradeDisplayState(upgrade_jj);
|
||||
}
|
||||
|
||||
if (print_jj.contains("cfg")) {
|
||||
const std::string &cfg = print_jj["cfg"].get<std::string>();
|
||||
if (!cfg.empty()) { m_upgrade_force_upgrade = DevUtil::get_flag_bits(cfg, 2); }
|
||||
}
|
||||
}
|
||||
|
||||
void DevUpgrade::ParseUpgradeDisplayState(const json &upgrade_state_jj)
|
||||
{
|
||||
if (upgrade_state_jj.contains("dis_state")) {
|
||||
if ((int) m_upgrade_display_state != upgrade_state_jj["dis_state"].get<int>()) {
|
||||
DevJsonValParser::ParseVal(upgrade_state_jj, "dis_state", m_upgrade_display_state);
|
||||
|
||||
// update the version after upgrade finished
|
||||
if (m_upgrade_display_state == DevFirmwareUpgradeState::UpgradingFinished) {
|
||||
Slic3r::GUI::wxGetApp().CallAfter([this] {
|
||||
m_owner->command_get_version();
|
||||
});
|
||||
}
|
||||
|
||||
// lan mode printer, hide upgrade avaliable state
|
||||
if (m_upgrade_display_state == DevFirmwareUpgradeState::UpgradingAvaliable &&
|
||||
m_owner->is_lan_mode_printer()) {
|
||||
m_upgrade_display_state = DevFirmwareUpgradeState::UpgradingUnavaliable;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// BBS compatibility with old version
|
||||
if (m_upgrade_status == "DOWNLOADING" ||
|
||||
m_upgrade_status == "FLASHING" ||
|
||||
m_upgrade_status == "UPGRADE_REQUEST" ||
|
||||
m_upgrade_status == "PRE_FLASH_START" ||
|
||||
m_upgrade_status == "PRE_FLASH_SUCCESS") {
|
||||
m_upgrade_display_state = DevFirmwareUpgradeState::UpgradingInProgress;
|
||||
} else if (m_upgrade_status == "UPGRADE_SUCCESS" ||
|
||||
m_upgrade_status == "DOWNLOAD_FAIL" ||
|
||||
m_upgrade_status == "FLASH_FAIL" ||
|
||||
m_upgrade_status == "PRE_FLASH_FAIL" ||
|
||||
m_upgrade_status == "UPGRADE_FAIL") {
|
||||
m_upgrade_display_state = DevFirmwareUpgradeState::UpgradingFinished;
|
||||
} else {
|
||||
if (HasNewVersion()) {
|
||||
m_upgrade_display_state = DevFirmwareUpgradeState::UpgradingAvaliable;
|
||||
} else {
|
||||
m_upgrade_display_state = DevFirmwareUpgradeState::UpgradingUnavaliable;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#define UpgradeNoError 0
|
||||
#define UpgradeDownloadFailed -1
|
||||
#define UpgradeVerfifyFailed -2
|
||||
#define UpgradeFlashFailed -3
|
||||
#define UpgradePrinting -4
|
||||
|
||||
wxString DevUpgrade::GetUpgradeErrCodeStr() const
|
||||
{
|
||||
switch (m_upgrade_err_code) {
|
||||
case UpgradeNoError: return _L("Update successful.");
|
||||
case UpgradeDownloadFailed: return _L("Downloading failed.");
|
||||
case UpgradeVerfifyFailed: return _L("Verification failed.");
|
||||
case UpgradeFlashFailed: return _L("Update failed.");
|
||||
case UpgradePrinting: return _L("Update failed.");
|
||||
default: return _L("Update failed.");
|
||||
}
|
||||
|
||||
return "";
|
||||
}
|
||||
|
||||
} // namespace Slic3r
|
||||
@@ -0,0 +1,108 @@
|
||||
#pragma once
|
||||
#include <nlohmann/json.hpp>
|
||||
#include "slic3r/Utils/json_diff.hpp"
|
||||
|
||||
#include "DevDefs.h"
|
||||
#include "DevFirmware.h"
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
class MachineObject;
|
||||
class DevUpgrade
|
||||
{
|
||||
public:
|
||||
static std::shared_ptr<DevUpgrade> Create(MachineObject *owner) { return std::shared_ptr<DevUpgrade>(new DevUpgrade(owner)); }
|
||||
|
||||
public:
|
||||
// upgrade status
|
||||
bool IsUpgrading() const { return m_upgrade_display_state == DevFirmwareUpgradeState::UpgradingInProgress; }
|
||||
bool IsUpgradeAvaliable() const { return m_upgrade_display_state == DevFirmwareUpgradeState::UpgradingAvaliable; }
|
||||
DevFirmwareUpgradeState GetUpgradeState() const { return m_upgrade_display_state; }
|
||||
std::string GetUpgradeStatusStr() const { return m_upgrade_status; }
|
||||
|
||||
// upgrade request
|
||||
bool IsUpgradeConsistencyRequest() const { return m_upgrade_consistency_request; }
|
||||
bool IsUpgradeForceUpgrade() const { return m_upgrade_force_upgrade; }
|
||||
|
||||
bool HasNewVersion() const { return m_upgrade_new_version_state == 1; }
|
||||
std::string GetOtaNewVersion() const { return m_ota_new_version_number; }
|
||||
std::map<std::string, DevFirmwareVersionInfo> GetNewVersionList() const { return m_new_ver_list; }
|
||||
|
||||
// upgrading
|
||||
std::string GetUpgradeModuleStr() const { return m_upgrade_module; }
|
||||
std::string GetUpgradeProgressStr() const { return m_upgrade_progress; }
|
||||
int GetUpgradeProgressInt() const;
|
||||
std::string GetUpgradeMessageStr() const { return m_upgrade_message; }
|
||||
wxString GetUpgradeErrCodeStr() const;
|
||||
|
||||
public:
|
||||
// ctrls
|
||||
int CtrlUpgradeConfirm();
|
||||
int CtrlUpgradeConsistencyConfirm();
|
||||
int CtrlUpgradeFirmware(FirmwareInfo info);
|
||||
int CtrlUpgradeModule(std::string url, std::string module_type, std::string version);
|
||||
|
||||
// parser
|
||||
void ParseUpgrade_V1_0(const json &print_jj);
|
||||
void ParseUpgradeDisplayState(const json &upgrade_state_jj);
|
||||
|
||||
protected:
|
||||
DevUpgrade(MachineObject *owner) : m_owner(owner) {}
|
||||
|
||||
private:
|
||||
MachineObject *m_owner = nullptr;
|
||||
|
||||
int m_upgrade_new_version_state = 0; // 0: invalid version, 1: new version available, 2: no new version
|
||||
bool m_upgrade_consistency_request = false;
|
||||
bool m_upgrade_force_upgrade = false;
|
||||
|
||||
std::string m_ota_new_version_number;
|
||||
std::map<std::string, DevFirmwareVersionInfo> m_new_ver_list; // some protcols have version list
|
||||
DevFirmwareUpgradeState m_upgrade_display_state = DevFirmwareUpgradeState::DC;
|
||||
|
||||
int m_upgrade_err_code = 0;
|
||||
std::string m_upgrade_status;
|
||||
std::string m_upgrade_progress;
|
||||
std::string m_upgrade_message;
|
||||
std::string m_upgrade_module;
|
||||
};
|
||||
|
||||
#if 0 /*TODO*/
|
||||
class DevUpgradeGetVersionInfo
|
||||
{
|
||||
public:
|
||||
DevUpgradeGetVersionInfo(MachineObject* obj) : m_owner(obj) {} ;
|
||||
|
||||
public:
|
||||
bool IsEmpty() const { m_module_version_map.empty();}
|
||||
|
||||
DevFirmwareVersionInfo GetAirPumpVersionInfo() const { return m_air_pump_version_info; }
|
||||
DevFirmwareVersionInfo GetLaserVersionInfo() const { return m_laser_version_info; }
|
||||
DevFirmwareVersionInfo GetCuttingModuleVersionInfo() const { return m_cutting_module_version_info; }
|
||||
DevFirmwareVersionInfo GetExtinguishVersionInfo() const { return m_extinguish_version_info; }
|
||||
DevFirmwareVersionInfo GetRotaryVersionInfo() const { return m_rotary_version_info; }
|
||||
std::map<std::string, DevFirmwareVersionInfo> GetModuleVersionInfoMap() const { return m_module_version_map; }
|
||||
|
||||
public:
|
||||
int CtrlGetVersion(bool with_retry = true);
|
||||
|
||||
void ParseGetVersion(const json &print_jj);
|
||||
|
||||
private:
|
||||
MachineObject *m_owner;
|
||||
|
||||
// some version info
|
||||
DevFirmwareVersionInfo m_air_pump_version_info;
|
||||
DevFirmwareVersionInfo m_laser_version_info;
|
||||
DevFirmwareVersionInfo m_cutting_module_version_info;
|
||||
DevFirmwareVersionInfo m_extinguish_version_info;
|
||||
DevFirmwareVersionInfo m_rotary_version_info;
|
||||
std::map<std::string, DevFirmwareVersionInfo> m_module_version_map;
|
||||
|
||||
// ctrl retry
|
||||
int m_retry_count = 0;
|
||||
int m_max_retry = 3;
|
||||
};
|
||||
#endif
|
||||
|
||||
} // namespace Slic3r
|
||||
@@ -0,0 +1,56 @@
|
||||
#include "DevUpgrade.h"
|
||||
#include "DevUtil.h"
|
||||
|
||||
#include "slic3r/GUI/DeviceManager.hpp"
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
int DevUpgrade::CtrlUpgradeConfirm()
|
||||
{
|
||||
json j;
|
||||
j["upgrade"]["command"] = "upgrade_confirm";
|
||||
j["upgrade"]["sequence_id"] = std::to_string(MachineObject::m_sequence_id++);
|
||||
j["upgrade"]["src_id"] = 1; // 1 for slicer
|
||||
return m_owner->publish_json(j);
|
||||
}
|
||||
|
||||
int DevUpgrade::CtrlUpgradeConsistencyConfirm()
|
||||
{
|
||||
json j;
|
||||
j["upgrade"]["command"] = "consistency_confirm";
|
||||
j["upgrade"]["sequence_id"] = std::to_string(MachineObject::m_sequence_id++);
|
||||
j["upgrade"]["src_id"] = 1; // 1 for slicer
|
||||
return m_owner->publish_json(j);
|
||||
}
|
||||
|
||||
int DevUpgrade::CtrlUpgradeFirmware(FirmwareInfo info)
|
||||
{
|
||||
std::string version = info.version;
|
||||
std::string dst_url = info.url;
|
||||
std::string module_name = info.module_type;
|
||||
|
||||
json j;
|
||||
j["upgrade"]["command"] = "start";
|
||||
j["upgrade"]["sequence_id"] = std::to_string(MachineObject::m_sequence_id++);
|
||||
j["upgrade"]["url"] = info.url;
|
||||
j["upgrade"]["module"] = info.module_type;
|
||||
j["upgrade"]["version"] = info.version;
|
||||
j["upgrade"]["src_id"] = 1;
|
||||
|
||||
return m_owner->publish_json(j);
|
||||
}
|
||||
|
||||
int DevUpgrade::CtrlUpgradeModule(std::string url, std::string module_type, std::string version)
|
||||
{
|
||||
json j;
|
||||
j["upgrade"]["command"] = "start";
|
||||
j["upgrade"]["sequence_id"] = std::to_string(MachineObject::m_sequence_id++);
|
||||
j["upgrade"]["url"] = url;
|
||||
j["upgrade"]["module"] = module_type;
|
||||
j["upgrade"]["version"] = version;
|
||||
j["upgrade"]["src_id"] = 1;
|
||||
|
||||
return m_owner->publish_json(j);
|
||||
}
|
||||
|
||||
} // namespace Slic3r
|
||||
@@ -24,6 +24,79 @@ int DevUtil::get_flag_bits(std::string str, int start, int count)
|
||||
|
||||
return 0;
|
||||
}
|
||||
uint32_t DevUtil::get_flag_bits_no_border(std::string str, int start_idx, int count)
|
||||
{
|
||||
if (start_idx < 0 || count <= 0) return 0;
|
||||
|
||||
try {
|
||||
// --- 1) trim ---
|
||||
auto ltrim = [](std::string &s) { s.erase(s.begin(), std::find_if(s.begin(), s.end(), [](unsigned char ch) { return !std::isspace(ch); })); };
|
||||
auto rtrim = [](std::string &s) { s.erase(std::find_if(s.rbegin(), s.rend(), [](unsigned char ch) { return !std::isspace(ch); }).base(), s.end()); };
|
||||
ltrim(str);
|
||||
rtrim(str);
|
||||
|
||||
// --- 2) remove 0x/0X prefix ---
|
||||
if (str.size() >= 2 && str[0] == '0' && (str[1] == 'x' || str[1] == 'X')) { str.erase(0, 2); }
|
||||
|
||||
// --- 3) keep only hex digits ---
|
||||
std::string hex;
|
||||
hex.reserve(str.size());
|
||||
for (char c : str) {
|
||||
if (std::isxdigit(static_cast<unsigned char>(c))) hex.push_back(c);
|
||||
}
|
||||
if (hex.empty()) return 0;
|
||||
|
||||
// --- 4) use size_t for all index/bit math ---
|
||||
const size_t total_bits = hex.size() * 4ULL;
|
||||
|
||||
const size_t ustart = static_cast<size_t>(start_idx);
|
||||
if (ustart >= total_bits) return 0;
|
||||
|
||||
const int int_bits = std::numeric_limits<uint32_t>::digits; // typically 32
|
||||
const size_t need_bits = static_cast<size_t>(std::min(count, int_bits));
|
||||
|
||||
// [first_bit, last_bit]
|
||||
const size_t first_bit = ustart;
|
||||
const size_t last_bit = std::min(ustart + need_bits, total_bits) - 1ULL;
|
||||
if (last_bit < first_bit) return 0;
|
||||
|
||||
const size_t right_index = hex.size() - 1ULL;
|
||||
|
||||
const size_t first_nibble = first_bit / 4ULL;
|
||||
const size_t last_nibble = last_bit / 4ULL;
|
||||
|
||||
const size_t start_idx = right_index - last_nibble;
|
||||
const size_t end_idx = right_index - first_nibble;
|
||||
if (end_idx < start_idx) return 0;
|
||||
|
||||
const size_t sub_len = end_idx - start_idx + 1ULL;
|
||||
if (end_idx >= hex.size()) return 0;
|
||||
|
||||
const std::string sub_hex = hex.substr(start_idx, sub_len);
|
||||
|
||||
unsigned long long chunk = std::stoull(sub_hex, nullptr, 16);
|
||||
|
||||
const unsigned nibble_offset = static_cast<unsigned>(first_bit % 4ULL);
|
||||
const unsigned long long shifted = (nibble_offset == 0U) ? chunk : (chunk >> nibble_offset);
|
||||
|
||||
uint32_t mask;
|
||||
if (need_bits >= static_cast<size_t>(std::numeric_limits<uint32_t>::digits)) {
|
||||
mask = std::numeric_limits<uint32_t>::max();
|
||||
} else {
|
||||
mask = static_cast<uint32_t>((1ULL << need_bits) - 1ULL);
|
||||
}
|
||||
|
||||
const uint32_t val = static_cast<uint32_t>(shifted & mask);
|
||||
return val;
|
||||
} catch (const std::invalid_argument &) {
|
||||
return 0;
|
||||
} catch (const std::out_of_range &) {
|
||||
return 0;
|
||||
} catch (...) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
int DevUtil::get_flag_bits(int num, int start, int count, int base)
|
||||
{
|
||||
|
||||
@@ -12,8 +12,15 @@
|
||||
#include <stdexcept>
|
||||
|
||||
#include <boost/log/trivial.hpp>
|
||||
#include <boost/lexical_cast.hpp>
|
||||
|
||||
#include "nlohmann/json.hpp"
|
||||
|
||||
/* Sequence Id*/
|
||||
#define STUDIO_START_SEQ_ID 20000
|
||||
#define STUDIO_END_SEQ_ID 30000
|
||||
#define CLOUD_SEQ_ID 0
|
||||
|
||||
namespace Slic3r
|
||||
{
|
||||
|
||||
@@ -27,14 +34,18 @@ public:
|
||||
public:
|
||||
static int get_flag_bits(std::string str, int start, int count = 1);
|
||||
static int get_flag_bits(int num, int start, int count = 1, int base = 10);
|
||||
static uint32_t get_flag_bits_no_border(std::string str, int start_idx, int count = 1);
|
||||
|
||||
// Extract the hex digit at position `pos` (0 = least-significant).
|
||||
// eg. get_hex_bits(16, 1, 10) = 1
|
||||
static int get_hex_bits(int num, int pos, int input_num_base = 10) { return get_flag_bits(num, pos * 4, 4, input_num_base); }
|
||||
static int get_hex_bits(int num, int pos, int input_num_base = 10) { return get_flag_bits(num, pos * 4, 4, input_num_base);};
|
||||
|
||||
static float string_to_float(const std::string& str_value);
|
||||
|
||||
static std::string convertToIp(long long ip);
|
||||
|
||||
// sequence id check
|
||||
static bool is_studio_cmd(int seq) { return seq >= STUDIO_START_SEQ_ID && seq < STUDIO_END_SEQ_ID;};
|
||||
static bool is_cloud_cmd(int seq) { return seq == CLOUD_SEQ_ID;};
|
||||
};
|
||||
|
||||
|
||||
@@ -108,4 +119,94 @@ struct NumericStrCompare
|
||||
}
|
||||
};
|
||||
|
||||
enum class DirtyMode{
|
||||
COUNTER,
|
||||
TIMER
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
class DevDirtyHandler{
|
||||
public:
|
||||
DevDirtyHandler(T init_value, int setting_threshold, DirtyMode mode): m_value(init_value), m_setting_threshold(setting_threshold), m_mode(mode)
|
||||
{
|
||||
m_threshold = setting_threshold;
|
||||
}
|
||||
~DevDirtyHandler(){};
|
||||
|
||||
T GetValue() const { return m_value; };
|
||||
|
||||
void SetOptimisticValue(const T& data)
|
||||
{
|
||||
m_value = data;
|
||||
m_start_time = time(nullptr);
|
||||
m_threshold = m_setting_threshold;
|
||||
}
|
||||
|
||||
void UpdateValue(const T& data)
|
||||
{
|
||||
if (m_mode == DirtyMode::COUNTER)
|
||||
{
|
||||
if (m_threshold > 0)
|
||||
m_threshold--;
|
||||
else
|
||||
m_value = data;
|
||||
}
|
||||
else if (m_mode == DirtyMode::TIMER)
|
||||
{
|
||||
if (time(nullptr) - m_start_time > m_threshold)
|
||||
m_value = data;
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
T m_value;
|
||||
DirtyMode m_mode;
|
||||
int m_setting_threshold{0};
|
||||
|
||||
int m_start_time{0};
|
||||
int m_threshold{0};
|
||||
};
|
||||
|
||||
|
||||
static std::string s_get_diameter_str(float diameter)
|
||||
{
|
||||
return (boost::format("%.2f") % diameter).str();
|
||||
}
|
||||
|
||||
static std::string s_get_diameter_str(const std::string& diameter)
|
||||
{
|
||||
try {
|
||||
float dia = boost::lexical_cast<float>(diameter);
|
||||
return s_get_diameter_str(dia);
|
||||
} catch (...) {
|
||||
BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << " failed to boost::lexical_cast: " << diameter;
|
||||
return diameter;
|
||||
}
|
||||
|
||||
try {
|
||||
float dia = std::stof(diameter);
|
||||
return s_get_diameter_str(dia);
|
||||
} catch (...) {
|
||||
BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << " std::stof: " << diameter;
|
||||
return diameter;
|
||||
}
|
||||
}
|
||||
|
||||
static float s_get_diameter(const std::string& diameter)
|
||||
{
|
||||
try {
|
||||
return boost::lexical_cast<float>(diameter);
|
||||
} catch (...) {
|
||||
BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << " failed to boost::lexical_cast: " << diameter;
|
||||
}
|
||||
|
||||
try {
|
||||
return std::stof(diameter);
|
||||
} catch (...) {
|
||||
BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << " std::stof: " << diameter;
|
||||
}
|
||||
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
}; // namespace Slic3r
|
||||
@@ -2,15 +2,28 @@
|
||||
|
||||
#include "slic3r/GUI/BackgroundSlicingProcess.hpp"
|
||||
|
||||
#include "slic3r/GUI/PartPlate.hpp"
|
||||
#include "slic3r/GUI/Plater.hpp"
|
||||
#include "slic3r/GUI/GUI_App.hpp"
|
||||
|
||||
#include <boost/log/trivial.hpp>
|
||||
#include <boost/lexical_cast.hpp>
|
||||
#include <wx/string.h>
|
||||
|
||||
namespace Slic3r
|
||||
{
|
||||
|
||||
std::shared_ptr<MultiNozzleUtils::NozzleGroupResultBase> DevUtilBackend::GetNozzleGroupResult(Slic3r::GUI::Plater* plater)
|
||||
|
||||
Slic3r::MultiNozzleUtils::NozzleInfo DevUtilBackend::GetNozzleInfo(const DevNozzle& dev_nozzle)
|
||||
{
|
||||
MultiNozzleUtils::NozzleInfo info;
|
||||
info.diameter = dev_nozzle.GetNozzleDiameterStr().ToStdString();
|
||||
info.volume_type = DevNozzle::ToNozzleVolumeType(dev_nozzle.GetNozzleFlowType());
|
||||
info.extruder_id = dev_nozzle.GetLogicExtruderId();
|
||||
|
||||
return info;
|
||||
}
|
||||
|
||||
std::shared_ptr<Slic3r::MultiNozzleUtils::NozzleGroupResultBase> DevUtilBackend::GetNozzleGroupResult(Slic3r::GUI::Plater *plater)
|
||||
{
|
||||
if (plater && plater->background_process().get_current_gcode_result()) {
|
||||
return plater->background_process().get_current_gcode_result()->nozzle_group_result;
|
||||
@@ -19,6 +32,29 @@ std::shared_ptr<MultiNozzleUtils::NozzleGroupResultBase> DevUtilBackend::GetNozz
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::unordered_map<NozzleDef, int> DevUtilBackend::CollectNozzleInfo(MultiNozzleUtils::NozzleGroupResultBase *nozzle_group_res, int logic_ext_id)
|
||||
{
|
||||
std::unordered_map<NozzleDef, int> need_nozzle_map;
|
||||
if (!nozzle_group_res) {
|
||||
return need_nozzle_map;
|
||||
}
|
||||
|
||||
const std::vector<Slic3r::MultiNozzleUtils::NozzleInfo>& nozzle_vec = nozzle_group_res->get_used_nozzles_in_extruder(logic_ext_id);
|
||||
for (auto slicing_nozzle : nozzle_vec) {
|
||||
try {
|
||||
NozzleDef data;
|
||||
data.nozzle_diameter = boost::lexical_cast<float>(slicing_nozzle.diameter);
|
||||
data.nozzle_flow_type = DevNozzle::ToNozzleFlowType(slicing_nozzle.volume_type);
|
||||
need_nozzle_map[data]++;
|
||||
} catch (const std::exception& e) {
|
||||
assert(0);
|
||||
BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << "exception: " << e.what();
|
||||
}
|
||||
}
|
||||
|
||||
return need_nozzle_map;
|
||||
}
|
||||
|
||||
static std::unordered_map<std::string, DevAmsType> s_ams_type_map = {
|
||||
{"0", DevAmsType::N3F},
|
||||
{"1", DevAmsType::N3S},
|
||||
@@ -41,6 +77,7 @@ std::optional<Slic3r::DevFilamentDryingPreset> DevUtilBackend::GetFilamentDrying
|
||||
std::vector<std::string> types = config.option<ConfigOptionStrings>("filament_dev_ams_drying_ams_limitations")->values;
|
||||
for (auto type : types) {
|
||||
if (s_ams_type_map.count(type) == 0) {
|
||||
// assert(0);
|
||||
continue;
|
||||
}
|
||||
info.ams_limitations.insert(s_ams_type_map[type]);
|
||||
@@ -62,10 +99,10 @@ std::optional<Slic3r::DevFilamentDryingPreset> DevUtilBackend::GetFilamentDrying
|
||||
}
|
||||
|
||||
if (config.has("filament_dev_drying_softening_temperature")) {
|
||||
info.filament_dev_drying_softening_temperature = config.option<ConfigOptionFloats>("filament_dev_drying_softening_temperature")->get_at(0);
|
||||
info.filament_dev_drying_softening_temperature = config.option<ConfigOptionFloats>("filament_dev_drying_softening_temperature")->get_at(0);
|
||||
}
|
||||
|
||||
if (config.has("filament_dev_ams_drying_heat_distortion_temperature")) {
|
||||
if (config.has("filament_dev_ams_drying_heat_distortion_temperature")){
|
||||
info.filament_dev_ams_drying_heat_distortion_temperature = config.option<ConfigOptionFloats>("filament_dev_ams_drying_heat_distortion_temperature")->get_at(0);
|
||||
}
|
||||
|
||||
@@ -83,4 +120,4 @@ std::optional<Slic3r::DevFilamentDryingPreset> DevUtilBackend::GetFilamentDrying
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
}; // namespace Slic3r
|
||||
};// namespace Slic3r
|
||||
@@ -1,19 +1,32 @@
|
||||
/**
|
||||
* @file DevUtilBackend.h
|
||||
* @brief Provides common static utility methods for backend (preset/slicing).
|
||||
*
|
||||
* This class offers a collection of static helper functions such as string manipulation,
|
||||
* file operations, and other frequently used utilities.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include "DevDefs.h"
|
||||
#include "DevNozzleSystem.h"
|
||||
#include "DevFilaSystem.h"
|
||||
|
||||
#include "libslic3r/MultiNozzleUtils.hpp"
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
|
||||
// Forward declarations
|
||||
namespace Slic3r
|
||||
{
|
||||
class MachineObject;
|
||||
|
||||
namespace GUI
|
||||
{
|
||||
class Plater;
|
||||
}
|
||||
}; // namespace Slic3r::GUI
|
||||
|
||||
namespace Slic3r
|
||||
{
|
||||
namespace GUI { class Plater; }
|
||||
|
||||
class DevUtilBackend
|
||||
{
|
||||
@@ -21,14 +34,14 @@ public:
|
||||
DevUtilBackend() = delete;
|
||||
|
||||
public:
|
||||
static MultiNozzleUtils::NozzleInfo GetNozzleInfo(const DevNozzle& dev_nozzle);
|
||||
|
||||
// for rack: the slicer's per-filament -> logical-nozzle grouping for the current plate, read off
|
||||
// the post-slice GCodeProcessorResult (plater->background_process().get_current_gcode_result()).
|
||||
// Returns nullptr when there is no plater / no current result.
|
||||
static std::shared_ptr<MultiNozzleUtils::NozzleGroupResultBase> GetNozzleGroupResult(Slic3r::GUI::Plater* plater);
|
||||
// for rack
|
||||
static std::shared_ptr<MultiNozzleUtils::NozzleGroupResultBase> GetNozzleGroupResult(Slic3r::GUI::Plater *plater);
|
||||
static std::unordered_map<NozzleDef, int> CollectNozzleInfo(MultiNozzleUtils::NozzleGroupResultBase *nozzle_group_res, int logic_ext_id);
|
||||
|
||||
// for filament preset
|
||||
static std::optional<DevFilamentDryingPreset> GetFilamentDryingPreset(const std::string& fila_id);
|
||||
};
|
||||
|
||||
}; // namespace Slic3r
|
||||
}; // namespace Slic3r
|
||||
@@ -5,6 +5,10 @@
|
||||
#include "GUI_App.hpp"
|
||||
#include "MainFrame.hpp"
|
||||
#include "ReleaseNote.hpp"
|
||||
#include "wxExtensions.hpp"
|
||||
|
||||
#include <wx/mstream.h>
|
||||
#include <wx/dcmemory.h>
|
||||
|
||||
namespace Slic3r {
|
||||
namespace GUI
|
||||
@@ -88,10 +92,22 @@ DeviceErrorDialog::DeviceErrorDialog(MachineObject* obj, wxWindow* parent, wxWin
|
||||
if (m_obj) { m_obj->command_clean_print_error_uiop(m_obj->print_error); }
|
||||
e.Skip();
|
||||
});
|
||||
|
||||
m_request_timer = new wxTimer(this);
|
||||
Bind(wxEVT_TIMER, &DeviceErrorDialog::on_request_timeout, this, m_request_timer->GetId());
|
||||
}
|
||||
|
||||
DeviceErrorDialog::~DeviceErrorDialog()
|
||||
{
|
||||
// Orca: invalidate any in-flight cloud snapshot callback (the request has no cancel handle)
|
||||
*m_alive = false;
|
||||
|
||||
if (m_request_timer) {
|
||||
m_request_timer->Stop();
|
||||
delete m_request_timer;
|
||||
m_request_timer = nullptr;
|
||||
}
|
||||
|
||||
if (web_request.IsOk() && web_request.GetState() == wxWebRequest::State_Active)
|
||||
{
|
||||
BOOST_LOG_TRIVIAL(info) << "web_request: cancelled";
|
||||
@@ -103,6 +119,11 @@ DeviceErrorDialog::~DeviceErrorDialog()
|
||||
void DeviceErrorDialog::on_webrequest_state(wxWebRequestEvent& evt)
|
||||
{
|
||||
BOOST_LOG_TRIVIAL(trace) << "monitor: monitor_panel web request state = " << evt.GetState();
|
||||
|
||||
// A local/HTTP illustration resolved (or the cloud path fell back to it): stop the watchdog.
|
||||
m_request_cancelled.store(true);
|
||||
clear_request_timer();
|
||||
|
||||
switch (evt.GetState())
|
||||
{
|
||||
case wxWebRequest::State_Completed:
|
||||
@@ -120,7 +141,9 @@ void DeviceErrorDialog::on_webrequest_state(wxWebRequestEvent& evt)
|
||||
case wxWebRequest::State_Cancelled:
|
||||
case wxWebRequest::State_Unauthorized:
|
||||
{
|
||||
m_error_picture->SetBitmap(wxBitmap());
|
||||
m_error_picture->SetBitmap(make_placeholder_image(_L("Network unavailable")));
|
||||
Layout();
|
||||
Fit();
|
||||
break;
|
||||
}
|
||||
case wxWebRequest::State_Active:
|
||||
@@ -129,6 +152,123 @@ void DeviceErrorDialog::on_webrequest_state(wxWebRequestEvent& evt)
|
||||
}
|
||||
}
|
||||
|
||||
void DeviceErrorDialog::on_request_timeout(wxTimerEvent& event)
|
||||
{
|
||||
if (m_request_cancelled.load()) { return; }
|
||||
// Same fallback order as the cloud callback: local/HTTP illustration first, placeholder last.
|
||||
if (!get_fail_snapshot_from_local(m_local_img_url)) {
|
||||
m_error_picture->SetBitmap(make_placeholder_image(_L("Network unavailable")));
|
||||
}
|
||||
Layout();
|
||||
Fit();
|
||||
}
|
||||
|
||||
void DeviceErrorDialog::clear_request_timer()
|
||||
{
|
||||
if (m_request_timer && m_request_timer->IsRunning()) {
|
||||
m_request_timer->Stop();
|
||||
}
|
||||
}
|
||||
|
||||
wxBitmap DeviceErrorDialog::make_placeholder_image(const wxString& text)
|
||||
{
|
||||
const int w = FromDIP(320);
|
||||
const int h = FromDIP(180);
|
||||
|
||||
wxBitmap bmp(wxSize(w, h));
|
||||
wxMemoryDC dc(bmp);
|
||||
dc.SetBackground(wxBrush(wxColour(238, 238, 238)));
|
||||
dc.Clear();
|
||||
|
||||
ScalableBitmap icon = ScalableBitmap(this, "dev_hms_diag_loading", 80);
|
||||
wxBitmap icon_bmp = icon.bmp();
|
||||
if (icon_bmp.IsOk()) {
|
||||
int ix = (w - icon_bmp.GetWidth()) / 2;
|
||||
int iy = (h - icon_bmp.GetHeight()) / 3;
|
||||
dc.DrawBitmap(icon_bmp, ix, iy, true);
|
||||
}
|
||||
|
||||
dc.SetTextForeground(wxColour(158, 158, 158));
|
||||
dc.SetFont(Label::Body_14);
|
||||
wxSize txtSize = dc.GetTextExtent(text);
|
||||
int tx = (w - txtSize.GetWidth()) / 2;
|
||||
int ty = h - txtSize.GetHeight() - FromDIP(45);
|
||||
dc.DrawText(text, tx, ty);
|
||||
|
||||
dc.SelectObject(wxNullBitmap);
|
||||
return bmp;
|
||||
}
|
||||
|
||||
bool DeviceErrorDialog::get_fail_snapshot_from_cloud()
|
||||
{
|
||||
if (!m_obj || m_obj->m_print_error_img_id.empty()) { return false; }
|
||||
|
||||
NetworkAgent* agent = wxGetApp().getAgent();
|
||||
if (!agent) { return false; }
|
||||
|
||||
// Orca: the cloud request has no cancel handle, so guard the callback against dialog
|
||||
// destruction with a liveness token (m_alive, cleared in the dtor) and marshal onto the UI
|
||||
// thread via the always-valid app handler instead of this->CallAfter. The raw bytes are
|
||||
// decoded on the UI thread so no wxImage (non-atomic refcount) is shared across threads.
|
||||
// m_request_seq guards against a callback from a previous error painting over the
|
||||
// current one when the dialog is reused by a later show_error_code().
|
||||
auto alive = m_alive;
|
||||
const int seq = m_request_seq;
|
||||
int ret = agent->get_hms_snapshot(m_obj->get_dev_id(), m_obj->m_print_error_img_id,
|
||||
[this, alive, seq](std::string body, int status) {
|
||||
if (!alive->load()) { return; }
|
||||
wxGetApp().CallAfter([this, alive, seq, body = std::move(body), status]() {
|
||||
if (!alive->load()) { return; }
|
||||
if (seq != m_request_seq) { return; }
|
||||
m_request_cancelled.store(true);
|
||||
clear_request_timer();
|
||||
|
||||
wxImage image;
|
||||
bool ok = false;
|
||||
if (status == 200) {
|
||||
wxMemoryInputStream stream(body.data(), body.size());
|
||||
ok = image.LoadFile(stream, wxBITMAP_TYPE_ANY);
|
||||
if (!ok) { BOOST_LOG_TRIVIAL(error) << "get_fail_snapshot_from_cloud: failed to resolve stream"; }
|
||||
} else {
|
||||
BOOST_LOG_TRIVIAL(error) << "get_fail_snapshot_from_cloud: status = " << status;
|
||||
}
|
||||
|
||||
if (ok) {
|
||||
wxImage resize_img = image.Scale(FromDIP(320), FromDIP(180), wxIMAGE_QUALITY_HIGH);
|
||||
m_error_picture->SetBitmap(wxBitmap(resize_img));
|
||||
} else if (!get_fail_snapshot_from_local(m_local_img_url)) {
|
||||
m_error_picture->SetBitmap(make_placeholder_image(_L("Network unavailable")));
|
||||
}
|
||||
Layout();
|
||||
Fit();
|
||||
});
|
||||
});
|
||||
|
||||
return ret == 0; // dispatched; the frame arrives later via the callback
|
||||
}
|
||||
|
||||
bool DeviceErrorDialog::get_fail_snapshot_from_local(const wxString& image_url)
|
||||
{
|
||||
if (image_url.empty()) { return false; }
|
||||
|
||||
const wxImage& img = wxGetApp().get_hms_query()->query_image_from_local(image_url);
|
||||
if (!img.IsOk() && image_url.Contains("http"))
|
||||
{
|
||||
web_request = wxWebSession::GetDefault().CreateRequest(this, image_url);
|
||||
BOOST_LOG_TRIVIAL(trace) << "monitor: create new webrequest, state = " << web_request.GetState();
|
||||
if (web_request.GetState() == wxWebRequest::State_Idle) web_request.Start();
|
||||
BOOST_LOG_TRIVIAL(trace) << "monitor: start new webrequest, state = " << web_request.GetState();
|
||||
}
|
||||
else
|
||||
{
|
||||
m_request_cancelled.store(true);
|
||||
clear_request_timer();
|
||||
const wxImage& resize_img = img.Scale(FromDIP(320), FromDIP(180), wxIMAGE_QUALITY_HIGH);
|
||||
m_error_picture->SetBitmap(wxBitmap(resize_img));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void DeviceErrorDialog::init_button(ActionButton style, wxString buton_text)
|
||||
{
|
||||
if (btn_bg_white.count() == 0)
|
||||
@@ -170,6 +310,7 @@ void DeviceErrorDialog::init_button_list()
|
||||
init_button(FILAMENT_LOAD_RESUME, _L("Filament Loaded, Resume"));
|
||||
init_button(JUMP_TO_LIVEVIEW, _L("View Liveview"));
|
||||
init_button(NO_REMINDER_NEXT_TIME, _L("No Reminder Next Time"));
|
||||
init_button(REFRESH_NOZZLE, _L("Recheck"));
|
||||
init_button(IGNORE_NO_REMINDER_NEXT_TIME, _L("Ignore. Don't Remind Next Time"));
|
||||
init_button(IGNORE_RESUME, _L("Ignore this and Resume"));
|
||||
init_button(PROBLEM_SOLVED_RESUME, _L("Problem Solved and Resume"));
|
||||
@@ -178,6 +319,10 @@ void DeviceErrorDialog::init_button_list()
|
||||
init_button(CANCEL, _L("Cancel"));
|
||||
init_button(STOP_DRYING, _L("Stop Drying"));
|
||||
init_button(PROCEED, _L("Proceed"));
|
||||
init_button(OK_JUMP_RACK, _L("OK"));
|
||||
init_button(ABORT, _L("Abort"));
|
||||
init_button(DISABLE_PURIFICATION, _L("Disable Purification for This Print"));
|
||||
init_button(DONT_REMIND_NEXT_TIME, _L("Don't Remind Me"));
|
||||
init_button(DBL_CHECK_CANCEL, _L("Cancel"));
|
||||
init_button(DBL_CHECK_DONE, _L("Done"));
|
||||
init_button(DBL_CHECK_RETRY, _L("Retry"));
|
||||
@@ -241,6 +386,7 @@ wxString DeviceErrorDialog::show_error_code(int error_code)
|
||||
Raise();
|
||||
|
||||
#ifndef __linux__
|
||||
// Orca: skip RequestUserAttention on Linux/Wayland (urgency-hint deadlock).
|
||||
// On Linux (especially Wayland) RequestUserAttention(wxUSER_ATTENTION_ERROR) maps to
|
||||
// gtk_window_set_urgency_hint(TRUE) which can leave the window in an urgent-but-unfocused
|
||||
// state — clicks no longer reach any widget in the app and the user has to kill the
|
||||
@@ -305,22 +451,23 @@ void DeviceErrorDialog::update_contents(const wxString& title, const wxString& t
|
||||
}
|
||||
|
||||
/* image */
|
||||
if (!image_url.empty())
|
||||
// Orca: tiered image slot — prefer the printer's captured camera frame of the failure
|
||||
// (cloud, keyed by m_print_error_img_id), else the local/HTTP HMS illustration, else hide.
|
||||
// Reuses m_error_picture + on_webrequest_state (single widget, no second request). The
|
||||
// loading placeholder and 10s watchdog are armed only for the async cloud fetch; the local
|
||||
// path keeps its existing behavior.
|
||||
m_local_img_url = image_url;
|
||||
m_request_cancelled.store(false);
|
||||
++m_request_seq; // invalidate any snapshot fetch still in flight for the previous error
|
||||
clear_request_timer();
|
||||
if (get_fail_snapshot_from_cloud())
|
||||
{
|
||||
m_error_picture->SetBitmap(make_placeholder_image(_L("Loading ...")));
|
||||
m_request_timer->StartOnce(10000);
|
||||
m_error_picture->Show();
|
||||
}
|
||||
else if (get_fail_snapshot_from_local(image_url))
|
||||
{
|
||||
const wxImage& img = wxGetApp().get_hms_query()->query_image_from_local(image_url);
|
||||
if (!img.IsOk() && image_url.Contains("http"))
|
||||
{
|
||||
web_request = wxWebSession::GetDefault().CreateRequest(this, image_url);
|
||||
BOOST_LOG_TRIVIAL(trace) << "monitor: create new webrequest, state = " << web_request.GetState();
|
||||
if (web_request.GetState() == wxWebRequest::State_Idle) web_request.Start();
|
||||
BOOST_LOG_TRIVIAL(trace) << "monitor: start new webrequest, state = " << web_request.GetState();
|
||||
}
|
||||
else
|
||||
{
|
||||
const wxImage& resize_img = img.Scale(FromDIP(320), FromDIP(180), wxIMAGE_QUALITY_HIGH);
|
||||
m_error_picture->SetBitmap(wxBitmap(resize_img));
|
||||
}
|
||||
|
||||
m_error_picture->Show();
|
||||
}
|
||||
else
|
||||
@@ -349,7 +496,10 @@ void DeviceErrorDialog::update_contents(const wxString& title, const wxString& t
|
||||
auto text_size = m_error_msg_label->GetBestSize();
|
||||
if (text_size.y < FromDIP(360))
|
||||
{
|
||||
if (!image_url.empty())
|
||||
// Orca: reserve the image area exactly when an image is coming (cloud snapshot,
|
||||
// local illustration, or the loading placeholder) - m_error_picture's visibility
|
||||
// was decided above, so no blank slot is reserved when neither source is usable.
|
||||
if (m_error_picture->IsShown())
|
||||
{
|
||||
m_scroll_area->SetMinSize(wxSize(FromDIP(320), text_size.y + FromDIP(220)));
|
||||
}
|
||||
@@ -462,6 +612,29 @@ void DeviceErrorDialog::on_button_click(ActionButton btn_id)
|
||||
}
|
||||
break;
|
||||
}
|
||||
case DeviceErrorDialog::REFRESH_NOZZLE: {
|
||||
m_obj->command_refresh_nozzle();
|
||||
break;
|
||||
}
|
||||
case DeviceErrorDialog::OK_JUMP_RACK: {
|
||||
Slic3r::GUI::wxGetApp().mainframe->jump_to_monitor();
|
||||
Slic3r::GUI::wxGetApp().mainframe->m_monitor->jump_to_Rack();
|
||||
break;
|
||||
}
|
||||
case DeviceErrorDialog::ABORT: {
|
||||
m_obj->command_ams_control("abort");
|
||||
break;
|
||||
}
|
||||
case DeviceErrorDialog::DISABLE_PURIFICATION: {
|
||||
m_obj->command_purification_disable();
|
||||
break;
|
||||
}
|
||||
case DeviceErrorDialog::DONT_REMIND_NEXT_TIME: {
|
||||
if (!m_action_json.is_null()) {
|
||||
m_obj->command_dont_remind_next_time(m_action_json);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case DeviceErrorDialog::ERROR_BUTTON_COUNT: break;
|
||||
|
||||
case DeviceErrorDialog::DBL_CHECK_CANCEL: {
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
#pragma once
|
||||
|
||||
#include <unordered_set>
|
||||
#include <atomic>
|
||||
#include <memory>
|
||||
#include <wx/statbmp.h>
|
||||
#include <wx/timer.h>
|
||||
#include <wx/webrequest.h>
|
||||
|
||||
#include "GUI_Utils.hpp"
|
||||
@@ -35,6 +38,7 @@ public:
|
||||
JUMP_TO_LIVEVIEW,
|
||||
|
||||
NO_REMINDER_NEXT_TIME = 23,
|
||||
REFRESH_NOZZLE = 24,
|
||||
IGNORE_NO_REMINDER_NEXT_TIME = 25,
|
||||
//LOAD_FILAMENT = 26*/
|
||||
IGNORE_RESUME = 27,
|
||||
@@ -46,6 +50,10 @@ public:
|
||||
CANCEL = 37,
|
||||
REMOVE_CLOSE_BTN = 39, // special case, do not show close button
|
||||
PROCEED = 41,
|
||||
OK_JUMP_RACK = 49,
|
||||
ABORT = 51,
|
||||
DISABLE_PURIFICATION = 54,
|
||||
DONT_REMIND_NEXT_TIME = 57,
|
||||
|
||||
ERROR_BUTTON_COUNT,
|
||||
|
||||
@@ -84,6 +92,11 @@ protected:
|
||||
|
||||
void on_button_click(ActionButton btn_id);
|
||||
void on_webrequest_state(wxWebRequestEvent& evt);
|
||||
void on_request_timeout(wxTimerEvent& event);
|
||||
void clear_request_timer();
|
||||
wxBitmap make_placeholder_image(const wxString& text);
|
||||
bool get_fail_snapshot_from_cloud();
|
||||
bool get_fail_snapshot_from_local(const wxString& image_url);
|
||||
void on_dpi_changed(const wxRect& suggested_rect);
|
||||
|
||||
private:
|
||||
@@ -93,6 +106,14 @@ private:
|
||||
std::unordered_set<Button*> m_used_button;
|
||||
|
||||
wxWebRequest web_request;
|
||||
wxTimer* m_request_timer{ nullptr };
|
||||
std::atomic<bool> m_request_cancelled{ false };
|
||||
// UI-thread-only generation counter: bumped on every update_contents() so a cloud
|
||||
// snapshot callback from a previous error cannot paint over the current one.
|
||||
int m_request_seq{ 0 };
|
||||
wxString m_local_img_url;
|
||||
// Orca: liveness token for the async cloud snapshot callback (the request has no cancel handle)
|
||||
std::shared_ptr<std::atomic_bool> m_alive{ std::make_shared<std::atomic_bool>(true) };
|
||||
wxStaticBitmap* m_error_picture;
|
||||
Label* m_error_msg_label{ nullptr };
|
||||
Label* m_error_code_label{ nullptr };
|
||||
|
||||
@@ -44,6 +44,12 @@
|
||||
#include "DeviceCore/DevManager.h"
|
||||
#include "DeviceCore/DevUtil.h"
|
||||
|
||||
// Orca: adopt DeviceCore split — axis/calib/chamber/status/upgrade modules
|
||||
#include "DeviceCore/DevAxis.h"
|
||||
#include "DeviceCore/DevChamber.h"
|
||||
#include "DeviceCore/DevStatus.h"
|
||||
#include "DeviceCore/DevUpgrade.h"
|
||||
|
||||
|
||||
#define CALI_DEBUG
|
||||
#define MINUTE_30 1800000 //ms
|
||||
@@ -68,6 +74,44 @@ int get_tray_id_by_ams_id_and_slot_id(int ams_id, int slot_id)
|
||||
}
|
||||
}
|
||||
|
||||
namespace {
|
||||
|
||||
// Stringing-prone filament IDs per nozzle-diameter bucket.
|
||||
// Mirrors the printer firmware tables (see g_leak_pron_idx_for_0_4 / _0_6_0_8).
|
||||
// Keep these in sync with firmware when new stringing-prone filaments are added.
|
||||
const std::unordered_set<std::string> g_stringing_prone_for_0_4 = {
|
||||
"GFA11", // PLA Aero
|
||||
"GFU90", // TPU 90A
|
||||
"GFU00", // TPU 95A HF
|
||||
"GFU02", // Generic TPU for AMS
|
||||
"GFU98", // TPU for AMS
|
||||
};
|
||||
|
||||
const std::unordered_set<std::string> g_stringing_prone_for_0_6_0_8 = {
|
||||
"GFA11", // PLA Aero
|
||||
"GFU00", // TPU 95A HF
|
||||
};
|
||||
|
||||
// Pick the right table for the given nozzle diameter; returns nullptr if the
|
||||
// nozzle bucket has no entries (e.g. 0.2 mm) or the diameter is invalid.
|
||||
const std::unordered_set<std::string>* pick_stringing_set(float nozzle_diameter)
|
||||
{
|
||||
if (!(nozzle_diameter > 0.f)) return nullptr;
|
||||
if (nozzle_diameter < 0.3f) return nullptr; // 0.2 nozzle: empty
|
||||
if (nozzle_diameter < 0.5f) return &g_stringing_prone_for_0_4;
|
||||
return &g_stringing_prone_for_0_6_0_8; // 0.6 / 0.8 nozzles
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
bool Slic3r::is_stringing_prone_filament(const std::string& filament_id, float nozzle_diameter)
|
||||
{
|
||||
if (filament_id.empty()) return false;
|
||||
const auto* set = pick_stringing_set(nozzle_diameter);
|
||||
if (!set) return false;
|
||||
return set->count(filament_id) > 0;
|
||||
}
|
||||
|
||||
wxString Slic3r::get_stage_string(int stage)
|
||||
{
|
||||
switch(stage) {
|
||||
@@ -553,6 +597,7 @@ MachineObject::MachineObject(DeviceManager* manager, NetworkAgent* agent, std::s
|
||||
mc_print_sub_stage = 0;
|
||||
mc_left_time = 0;
|
||||
hw_switch_state = 0;
|
||||
m_print_error_img_id = "";
|
||||
|
||||
has_ipcam = true; // default true
|
||||
|
||||
@@ -577,6 +622,13 @@ MachineObject::MachineObject(DeviceManager* manager, NetworkAgent* agent, std::s
|
||||
m_print_options = new DevPrintOptions(this);
|
||||
|
||||
m_nozzle_mapping_ptr = std::make_shared<DevNozzleMappingCtrl>(this);
|
||||
|
||||
// Orca: adopt DeviceCore split — axis/calib/chamber/status/upgrade modules
|
||||
m_axis = DevAxis::Create(this);
|
||||
m_chamber = DevChamber::Create(this);
|
||||
m_upgrade = DevUpgrade::Create(this);
|
||||
m_status = new DevStatus(this);
|
||||
m_calib = new DevCalib(this);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -629,6 +681,13 @@ MachineObject::~MachineObject()
|
||||
|
||||
delete m_print_options;
|
||||
m_print_options = nullptr;
|
||||
|
||||
// Orca: adopt DeviceCore split
|
||||
delete m_calib;
|
||||
m_calib = nullptr;
|
||||
|
||||
delete m_status;
|
||||
m_status = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -698,6 +757,33 @@ std::string MachineObject::get_filament_display_type(const std::string& ams_id,
|
||||
return this->get_tray(ams_id, tray_id).get_display_filament_type();
|
||||
}
|
||||
|
||||
bool MachineObject::any_loaded_filament_is_stringing_prone() const
|
||||
{
|
||||
if (print_job_filament_mapping.empty()) return false;
|
||||
|
||||
std::vector<float> nozzle_diameters;
|
||||
if (m_extder_system) {
|
||||
for (const auto& ext : m_extder_system->GetExtruders()) {
|
||||
const float d = ext.GetNozzleDiameter();
|
||||
if (d > 0.f) nozzle_diameters.push_back(d);
|
||||
}
|
||||
}
|
||||
if (nozzle_diameters.empty()) return false;
|
||||
|
||||
for (uint16_t v : print_job_filament_mapping) {
|
||||
if (v == 0xFFFF) continue;
|
||||
const int ams_id = (v >> 8) & 0xFF;
|
||||
const int slot_id = v & 0xFF;
|
||||
const std::string fid = this->get_filament_id(std::to_string(ams_id), std::to_string(slot_id));
|
||||
if (fid.empty()) continue;
|
||||
for (float d : nozzle_diameters) {
|
||||
if (Slic3r::is_stringing_prone_filament(fid, d))
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void MachineObject::_parse_ams_status(int ams_status)
|
||||
{
|
||||
ams_status_sub = ams_status & 0xFF;
|
||||
@@ -716,6 +802,8 @@ void MachineObject::_parse_ams_status(int ams_status)
|
||||
ams_status_main = AmsStatusMain::AMS_STATUS_MAIN_SELF_CHECK;
|
||||
} else if (ams_status_main_int == (int) AmsStatusMain::AMS_STATUS_MAIN_DEBUG) {
|
||||
ams_status_main = AmsStatusMain::AMS_STATUS_MAIN_DEBUG;
|
||||
} else if (ams_status_main_int == (int) AmsStatusMain::AMS_STATUS_MAIN_COLD_PULL) {
|
||||
ams_status_main = AmsStatusMain::AMS_STATUS_MAIN_COLD_PULL;
|
||||
} else {
|
||||
ams_status_main = AmsStatusMain::AMS_STATUS_MAIN_UNKNOWN;
|
||||
}
|
||||
@@ -867,6 +955,9 @@ void MachineObject::clear_version_info()
|
||||
laser_version_info = DevFirmwareVersionInfo();
|
||||
cutting_module_version_info = DevFirmwareVersionInfo();
|
||||
extinguish_version_info = DevFirmwareVersionInfo();
|
||||
rotary_version_info = DevFirmwareVersionInfo();
|
||||
exhaustfan_version_info = DevFirmwareVersionInfo();
|
||||
amshub_version_info = DevFirmwareVersionInfo();
|
||||
filatrack_version_info = DevFirmwareVersionInfo();
|
||||
module_vers.clear();
|
||||
// Drop cached rack-hotend (WTM) firmware alongside the module list.
|
||||
@@ -884,11 +975,17 @@ void MachineObject::store_version_info(const DevFirmwareVersionInfo& info)
|
||||
cutting_module_version_info = info;
|
||||
} else if (info.isExtinguishSystem()) {
|
||||
extinguish_version_info = info;
|
||||
} else if (info.isRotary()) {
|
||||
rotary_version_info = info;
|
||||
} else if (info.isWTM()) {
|
||||
// Route rack-hotend / extruder-nozzle firmware into the nozzle system so
|
||||
// the rack upgrade UI can read per-nozzle versions. isWTM() is false for every non-rack
|
||||
// printer's modules, so this branch never fires outside H2C.
|
||||
m_nozzle_system->AddFirmwareInfoWTM(info);
|
||||
} else if (info.isExhaustFan()) {
|
||||
exhaustfan_version_info = info;
|
||||
} else if (info.isHmshub()) {
|
||||
amshub_version_info = info;
|
||||
} else if (info.isFilaTrackSwitch()) {
|
||||
filatrack_version_info = info;
|
||||
}
|
||||
@@ -1207,7 +1304,7 @@ bool MachineObject::canEnableTimelapse(wxString &error_message) const
|
||||
return true;
|
||||
}
|
||||
|
||||
if (m_storage->get_sdcard_state() != DevStorage::SdcardState::HAS_SDCARD_NORMAL) {
|
||||
if (m_storage->get_sdcard_state() != DevStorage::SdcardState::HAS_SDCARD_NORMAL && !m_has_timelapse_kit) {
|
||||
if (m_storage->get_sdcard_state() == DevStorage::SdcardState::NO_SDCARD) {
|
||||
error_message = _L("Timelapse is not supported while the storage does not exist.");
|
||||
} else if (m_storage->get_sdcard_state() == DevStorage::SdcardState::HAS_SDCARD_ABNORMAL) {
|
||||
@@ -1857,10 +1954,17 @@ int MachineObject::command_axis_control(std::string axis, double unit, double in
|
||||
{
|
||||
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"] = input_val > 0 ? 1 : -1;
|
||||
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);
|
||||
@@ -2199,6 +2303,14 @@ int MachineObject::command_ack_proceed(json& proceed) {
|
||||
} else {
|
||||
proceed["err_ignored"] = std::vector<int>{proceed["err_index"]};
|
||||
}
|
||||
|
||||
for (auto& item : proceed["err_ignored"]) {
|
||||
json error_item;
|
||||
error_item["idx"] = item.get<int>();
|
||||
error_item["mode"] = 0;
|
||||
proceed["rm_idx"].push_back(error_item);
|
||||
}
|
||||
|
||||
proceed["sequence_id"] = std::to_string(MachineObject::m_sequence_id++);
|
||||
|
||||
json j;
|
||||
@@ -2206,6 +2318,78 @@ int MachineObject::command_ack_proceed(json& proceed) {
|
||||
return this->publish_json(j);
|
||||
}
|
||||
|
||||
bool MachineObject::is_timelapse_storage_low(const std::string& storage) const
|
||||
{
|
||||
return m_storage && m_storage->is_timelapse_storage_low(storage);
|
||||
}
|
||||
|
||||
int MachineObject::command_ipcam_check_timelapse_storage(const std::string& storage, int total_layer)
|
||||
{
|
||||
json j;
|
||||
j["camera"]["sequence_id"] = std::to_string(MachineObject::m_sequence_id++);
|
||||
j["camera"]["command"] = "ipcam_get_media_info";
|
||||
j["camera"]["sub_command"] = "is_timelapse_storage_enough";
|
||||
j["camera"]["storage"] = storage;
|
||||
j["camera"]["total_layer"] = total_layer;
|
||||
return this->publish_json(j);
|
||||
}
|
||||
|
||||
int MachineObject::command_ipcam_delete_oldest_timelapse(const std::string& storage, int total_layer)
|
||||
{
|
||||
json j;
|
||||
j["camera"]["sequence_id"] = std::to_string(MachineObject::m_sequence_id++);
|
||||
j["camera"]["command"] = "ipcam_delete_oldest_timelapse";
|
||||
j["camera"]["storage"] = storage;
|
||||
j["camera"]["total_layer"] = total_layer;
|
||||
return this->publish_json(j);
|
||||
}
|
||||
|
||||
int MachineObject::command_purification_disable()
|
||||
{
|
||||
json j;
|
||||
j["print"]["command"] = "close_air_filt";
|
||||
j["print"]["sequence_id"] = std::to_string(MachineObject::m_sequence_id++);
|
||||
|
||||
return this->publish_json(j, 1);
|
||||
}
|
||||
|
||||
int MachineObject::command_dont_remind_next_time(json& mqtt_guard_json)
|
||||
{
|
||||
if (!mqtt_guard_json.contains("command") ||
|
||||
!mqtt_guard_json.contains("err_index") ||
|
||||
mqtt_guard_json["err_index"].empty()) return -1;
|
||||
|
||||
json j;
|
||||
j["print"]["sequence_id"] = std::to_string(MachineObject::m_sequence_id++);
|
||||
|
||||
try {
|
||||
j["print"]["command"] = mqtt_guard_json["command"].get<std::string>();
|
||||
int err_index = mqtt_guard_json["err_index"].get<int>();
|
||||
|
||||
if (mqtt_guard_json.contains("err_ignored") &&
|
||||
mqtt_guard_json["err_ignored"].is_array()) {
|
||||
j["print"]["err_ignored"] = mqtt_guard_json["err_ignored"];
|
||||
j["print"]["err_ignored"].push_back(err_index);
|
||||
} else {
|
||||
j["print"]["err_ignored"] = std::vector<int>{err_index};
|
||||
}
|
||||
|
||||
for (auto& item : j["print"]["err_ignored"]) {
|
||||
if (!item.is_number_integer()) continue;
|
||||
|
||||
json item_json;
|
||||
item_json["idx"] = item.get<int>();
|
||||
item_json["mode"] = 1; // 1-next time ignore, 2-always ignore
|
||||
j["print"]["rm_idx"].push_back(item_json);
|
||||
}
|
||||
} catch (const json::exception& e) {
|
||||
BOOST_LOG_TRIVIAL(error) << "JSON parsing error in command_dont_remind_next_time: " << e.what();
|
||||
return -1;
|
||||
}
|
||||
|
||||
return this->publish_json(j, 1);
|
||||
}
|
||||
|
||||
int MachineObject::command_xcam_control_ai_monitoring(bool on_off, std::string lvl)
|
||||
{
|
||||
bool print_halt = (lvl == "never_halt") ? false:true;
|
||||
@@ -2915,6 +3099,7 @@ int MachineObject::parse_json(std::string tunnel, std::string payload, bool key_
|
||||
|
||||
//supported function
|
||||
m_config->ParseConfig(jj);
|
||||
m_status->ParseStatus(jj); // Orca: adopt DeviceCore split — populate DevStatus module
|
||||
|
||||
if (jj.contains("support_build_plate_marker_detect")) {
|
||||
if (jj["support_build_plate_marker_detect"].is_boolean()) {
|
||||
@@ -3142,6 +3327,17 @@ int MachineObject::parse_json(std::string tunnel, std::string payload, bool key_
|
||||
if (jj["print_error"].is_number())
|
||||
print_error = jj["print_error"].get<int>();
|
||||
}
|
||||
// Orca: keep the failure-snapshot id only while an error is active, so a stale
|
||||
// id can't leak into a later unrelated error dialog once the failure clears.
|
||||
if (print_error <= 0) {
|
||||
m_print_error_img_id.clear();
|
||||
}
|
||||
else if (jj.contains("err2") && jj["err2"].is_object()) {
|
||||
json err2 = jj["err2"];
|
||||
if (err2.contains("img_id") && err2["img_id"].is_string()) {
|
||||
m_print_error_img_id = err2["img_id"].get<std::string>();
|
||||
}
|
||||
}
|
||||
|
||||
DevStorage::ParseV1_0(jj, m_storage);
|
||||
|
||||
@@ -3163,6 +3359,9 @@ int MachineObject::parse_json(std::string tunnel, std::string payload, bool key_
|
||||
int flag3 = jj["flag3"].get<int>();
|
||||
is_support_filament_setting_inprinting = get_flag_bits(flag3, 3);
|
||||
is_enable_ams_np = get_flag_bits(flag3, 9);
|
||||
is_support_fila_change_abort = get_flag_bits(flag3, 13); // filament-change Stop button
|
||||
is_support_ext_change_assist_old = get_flag_bits(flag3, 16); // A/P-series multi-color external change assist
|
||||
is_support_filament_32_colors = get_flag_bits(flag3, 17);
|
||||
}
|
||||
}
|
||||
if (!key_field_only) {
|
||||
@@ -3328,6 +3527,16 @@ int MachineObject::parse_json(std::string tunnel, std::string payload, bool key_
|
||||
if (!key_field_only) {
|
||||
/* temperature */
|
||||
|
||||
// Orca: adopt DeviceCore split — populate DevAxis/DevChamber modules
|
||||
// alongside the inline handling (side-effect-free; inline stays authoritative).
|
||||
// Contained locally so a malformed field cannot abort the whole status parse.
|
||||
try {
|
||||
m_axis->ParseAxis(jj);
|
||||
m_chamber->ParseChamber(jj);
|
||||
} catch (...) {
|
||||
BOOST_LOG_TRIVIAL(warning) << "parse_json: DevAxis/DevChamber parse failed";
|
||||
}
|
||||
|
||||
DevBed::ParseV1_0(jj,m_bed);
|
||||
|
||||
if (jj.contains("frame_temper")) {
|
||||
@@ -4336,6 +4545,17 @@ int MachineObject::parse_json(std::string tunnel, std::string payload, bool key_
|
||||
this->camera_resolution = j["camera"]["resolution"].get<std::string>();
|
||||
BOOST_LOG_TRIVIAL(info) << "ack of resolution = " << camera_resolution;
|
||||
}
|
||||
} else if (j["camera"]["command"].get<std::string>() == "ipcam_get_media_info") {
|
||||
if (j["camera"].contains("sub_command") &&
|
||||
j["camera"]["sub_command"].get<std::string>() == "is_timelapse_storage_enough") {
|
||||
timelapse_storage_check_result = j["camera"].value("result", -1);
|
||||
timelapse_storage_is_enough = j["camera"].value("is_enough", true);
|
||||
timelapse_storage_file_count = j["camera"].value("file_count", 0);
|
||||
timelapse_storage_check_done = true;
|
||||
BOOST_LOG_TRIVIAL(info) << "timelapse storage check: result=" << timelapse_storage_check_result
|
||||
<< " is_enough=" << timelapse_storage_is_enough
|
||||
<< " file_count=" << timelapse_storage_file_count;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -4940,6 +5160,15 @@ bool MachineObject::check_enable_np(const json& print) const
|
||||
return false;
|
||||
}
|
||||
|
||||
// Max filament color count for the send gate; 0 = no explicit upper bound.
|
||||
int MachineObject::get_max_filament_color_count() const
|
||||
{
|
||||
if (is_support_filament_32_colors) return 32;
|
||||
if (is_enable_ams_np && !is_series_x()) return 20;
|
||||
if (!is_series_x() && !is_series_o()) return 16;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void MachineObject::parse_new_info(json print)
|
||||
{
|
||||
is_enable_np = check_enable_np(print);
|
||||
@@ -5093,8 +5322,23 @@ void MachineObject::parse_new_info(json print)
|
||||
// fun2 may have infinite length, use get_flag_bits_no_border
|
||||
if (!fun2.empty()) {
|
||||
is_support_print_with_emmc = get_flag_bits_no_border(fun2, 0) == 1;
|
||||
is_support_pa_mode = (get_flag_bits_no_border(fun2, 3) == 1);
|
||||
is_support_remote_dry = (get_flag_bits_no_border(fun2, 5) == 1);
|
||||
is_support_check_track_switch_match_slice_printer = get_flag_bits_no_border(fun2, 19) == 1;
|
||||
|
||||
if (DevPrinterConfigUtil::support_print_check_firmware_for_tpu_left(printer_type)) {
|
||||
m_firmware_support_print_tpu_left = get_flag_bits_no_border(fun2, 7) == 1;
|
||||
}
|
||||
}
|
||||
|
||||
/* Per-filament-index AMS slot mapping reported by the printer (task-level state) */
|
||||
if (print.contains("mapping") && print["mapping"].is_array()) {
|
||||
std::vector<uint16_t> new_mapping;
|
||||
new_mapping.reserve(print["mapping"].size());
|
||||
for (const auto& v : print["mapping"]) {
|
||||
new_mapping.push_back(static_cast<uint16_t>(v.get<unsigned>()));
|
||||
}
|
||||
print_job_filament_mapping = std::move(new_mapping);
|
||||
}
|
||||
|
||||
/*aux*/
|
||||
@@ -5104,6 +5348,7 @@ void MachineObject::parse_new_info(json print)
|
||||
|
||||
if (!aux.empty()) {
|
||||
m_storage->set_sdcard_state(get_flag_bits(aux, 12, 2));
|
||||
m_has_timelapse_kit = (get_flag_bits(aux, 26, 1) == 1);
|
||||
}
|
||||
|
||||
/*stat*/
|
||||
@@ -5564,7 +5809,9 @@ wxString MachineObject::get_nozzle_replace_url() const
|
||||
return link_map["en"].get<wxString>();
|
||||
}/*retry with en*/
|
||||
|
||||
return "https://wiki.bambulab.com/en/h2/maintenance/replace-hotend";
|
||||
// Orca: no neutral wiki equivalent for this fallback — return empty so the caller hides the link
|
||||
// (PrinterPartsDialog::OnWikiClicked reports "No wiki link available" instead of opening a browser)
|
||||
return wxEmptyString;
|
||||
}
|
||||
|
||||
std::string MachineObject::get_error_code_str(int error_code)
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
|
||||
#include <map>
|
||||
#include <mutex>
|
||||
#include <atomic>
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <memory>
|
||||
@@ -21,6 +22,7 @@
|
||||
#include "DeviceCore/DevDefs.h"
|
||||
#include "DeviceCore/DevConfigUtil.h"
|
||||
#include "DeviceCore/DevFirmware.h"
|
||||
#include "DeviceCore/DevCalib.h" // Orca: adopt DeviceCore split (defines DevCalib, CalibStatus, ManualPaCaliMethod)
|
||||
#include "DeviceErrorDialog.hpp"
|
||||
|
||||
#include <wx/object.h>
|
||||
@@ -62,11 +64,7 @@ class DeviceErrorDialog; // Previous definitions
|
||||
}
|
||||
|
||||
class NetworkAgent;
|
||||
enum ManualPaCaliMethod {
|
||||
PA_LINE = 0,
|
||||
PA_PATTERN,
|
||||
};
|
||||
|
||||
// Orca: ManualPaCaliMethod now provided by DeviceCore/DevCalib.h (enum class)
|
||||
|
||||
#define UpgradeNoError 0
|
||||
#define UpgradeDownloadFailed -1
|
||||
@@ -77,7 +75,9 @@ enum ManualPaCaliMethod {
|
||||
// Previous definitions
|
||||
class DevAms;
|
||||
class DevAmsTray;
|
||||
class DevAxis; // Orca: adopt DeviceCore split
|
||||
class DevBed;
|
||||
class DevChamber; // Orca: adopt DeviceCore split
|
||||
class DevConfig;
|
||||
class DevCtrl;
|
||||
class DevExtensionTool;
|
||||
@@ -91,9 +91,15 @@ class DevLamp;
|
||||
class DevNozzleSystem;
|
||||
class DevNozzleMappingCtrl;
|
||||
class DeviceManager;
|
||||
class DevStatus; // Orca: adopt DeviceCore split
|
||||
class DevStorage;
|
||||
class DevUpgrade; // Orca: adopt DeviceCore split
|
||||
struct DevPrintTaskRatingInfo;
|
||||
|
||||
// Returns true when filament_id (e.g. "GFA11", "GFU00") is on the stringing-prone list for the
|
||||
// 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
|
||||
{
|
||||
@@ -123,6 +129,15 @@ private:
|
||||
DevBed * m_bed;
|
||||
DevStorage* m_storage;
|
||||
|
||||
/* Orca: adopt DeviceCore split — axis/calib/chamber/status/upgrade modules alongside
|
||||
MachineObject's inline handling of these concerns, which stays authoritative here (Orca keeps
|
||||
the inline model permanently; the split modules coexist with it via accessors). */
|
||||
std::shared_ptr<DevAxis> m_axis;
|
||||
std::shared_ptr<DevChamber> m_chamber;
|
||||
DevCalib* m_calib{ nullptr };
|
||||
DevStatus* m_status{ nullptr };
|
||||
std::shared_ptr<DevUpgrade> m_upgrade;
|
||||
|
||||
/*Ctrl*/
|
||||
DevCtrl* m_ctrl;
|
||||
|
||||
@@ -145,6 +160,18 @@ public:
|
||||
~MachineObject();
|
||||
|
||||
void set_agent(NetworkAgent* agent) { m_agent = agent; }
|
||||
NetworkAgent* get_agent() const { return m_agent; } // Orca: needed by DeviceCore modules (DevAxisCtrl)
|
||||
|
||||
// Orca: these DeviceCore module accessors are unwired on the read side — axis/chamber/status
|
||||
// are fed every MQTT push but no GUI consumer reads them yet, and for calib/upgrade the inline
|
||||
// parse in DeviceManager.cpp remains authoritative. Do not wire DevUpgrade naively: its
|
||||
// ParseUpgradeDisplayState would duplicate the inline block's dis_state==3 ->
|
||||
// command_get_version CallAfter side effect.
|
||||
std::shared_ptr<DevAxis> GetAxis() const { return m_axis; }
|
||||
std::shared_ptr<DevChamber> GetChamber() const { return m_chamber; }
|
||||
DevCalib* GetCalib() const { return m_calib; }
|
||||
DevStatus* GetStatus() const { return m_status; }
|
||||
std::weak_ptr<DevUpgrade> GetUpgrade() const { return m_upgrade; }
|
||||
|
||||
public:
|
||||
enum ActiveState {
|
||||
@@ -379,6 +406,9 @@ public:
|
||||
DevFirmwareVersionInfo laser_version_info;
|
||||
DevFirmwareVersionInfo cutting_module_version_info;
|
||||
DevFirmwareVersionInfo extinguish_version_info;
|
||||
DevFirmwareVersionInfo rotary_version_info;
|
||||
DevFirmwareVersionInfo exhaustfan_version_info;
|
||||
DevFirmwareVersionInfo amshub_version_info;
|
||||
DevFirmwareVersionInfo filatrack_version_info;
|
||||
std::map<std::string, DevFirmwareVersionInfo> module_vers;
|
||||
std::map<std::string, DevFirmwareVersionInfo> new_ver_list;
|
||||
@@ -417,6 +447,7 @@ public:
|
||||
bool is_system_printing();
|
||||
|
||||
int print_error;
|
||||
std::string m_print_error_img_id;
|
||||
static std::string get_error_code_str(int error_code);
|
||||
std::string get_print_error_str() const { return MachineObject::get_error_code_str(this->print_error); }
|
||||
|
||||
@@ -557,6 +588,7 @@ public:
|
||||
|
||||
bool file_model_download{false};
|
||||
bool virtual_camera{false};
|
||||
bool m_has_timelapse_kit{false};
|
||||
|
||||
bool xcam_ai_monitoring{ false };
|
||||
bool xcam_disable_ai_detection_display{false};
|
||||
@@ -633,10 +665,18 @@ public:
|
||||
bool is_support_airprinting_detection{false};
|
||||
bool is_support_idelheadingprotect_detection{false};
|
||||
|
||||
// timelapse storage check result (temp state from MQTT response)
|
||||
std::atomic<bool> timelapse_storage_check_done { false };
|
||||
int timelapse_storage_check_result { -1 };
|
||||
bool timelapse_storage_is_enough { true };
|
||||
int timelapse_storage_file_count { 0 };
|
||||
|
||||
// fun2
|
||||
bool is_support_print_with_emmc{false};
|
||||
bool is_support_remote_dry = false;
|
||||
bool is_support_check_track_switch_match_slice_printer{false};
|
||||
bool is_support_pa_mode{false};
|
||||
std::optional<bool> m_firmware_support_print_tpu_left;
|
||||
|
||||
bool installed_upgrade_kit{false};
|
||||
int bed_temperature_limit = -1;
|
||||
@@ -666,6 +706,11 @@ public:
|
||||
boost::thread* get_slice_info_thread { nullptr };
|
||||
boost::thread* get_model_task_thread { nullptr };
|
||||
|
||||
// Per-filament-index AMS slot mapping reported by the printer in print.mapping. Up to
|
||||
// 32 entries, each value packs (ams_id << 8) | slot_id; 0xFFFF means unused.
|
||||
std::vector<uint16_t> print_job_filament_mapping;
|
||||
bool any_loaded_filament_is_stringing_prone() const;
|
||||
|
||||
/* job attr */
|
||||
int jobState_ = 0;
|
||||
|
||||
@@ -703,6 +748,7 @@ public:
|
||||
|
||||
/* quick check*/
|
||||
bool canEnableTimelapse(wxString& error_message) const;
|
||||
bool is_timelapse_storage_low(const std::string& storage) const;
|
||||
|
||||
/* command commands */
|
||||
int command_get_version(bool with_retry = true);
|
||||
@@ -714,6 +760,8 @@ public:
|
||||
int command_set_printer_nozzle2(int id, std::string nozzle_type, float diameter);
|
||||
int command_get_access_code();
|
||||
int command_ack_proceed(json& proceed);
|
||||
int command_purification_disable();
|
||||
int command_dont_remind_next_time(json& mqtt_guard_json);
|
||||
|
||||
/* command upgrade */
|
||||
int command_upgrade_confirm();
|
||||
@@ -805,6 +853,8 @@ public:
|
||||
int command_ipcam_record(bool on_off);
|
||||
int command_ipcam_timelapse(bool on_off);
|
||||
int command_ipcam_resolution_set(std::string resolution);
|
||||
int command_ipcam_check_timelapse_storage(const std::string& storage, int total_layer);
|
||||
int command_ipcam_delete_oldest_timelapse(const std::string& storage, int total_layer);
|
||||
int command_xcam_control(std::string module_name, bool on_off, std::string lvl = "");
|
||||
|
||||
//refine printer
|
||||
@@ -878,6 +928,12 @@ public:
|
||||
/*for more extruder*/
|
||||
bool is_enable_np{ false };
|
||||
bool is_enable_ams_np{ false };
|
||||
bool is_support_filament_32_colors{ false };
|
||||
bool is_support_fila_change_abort{ false }; // filament-change Stop button
|
||||
bool is_support_ext_change_assist_old{ false }; // A/P-series multi-color external change assist
|
||||
|
||||
// Max filament color count for the send gate; returns 0 when there is no explicit upper bound.
|
||||
int get_max_filament_color_count() const;
|
||||
|
||||
/**
|
||||
* Virtual Tray (vt_slot) - External/manual filament loading slots.
|
||||
|
||||
@@ -2,10 +2,10 @@
|
||||
list(APPEND SLIC3R_GUI_SOURCES
|
||||
GUI/DeviceTab/uiAmsHumidityPopup.h
|
||||
GUI/DeviceTab/uiAmsHumidityPopup.cpp
|
||||
GUI/DeviceTab/uiAMSBestPositionPopup.hpp
|
||||
GUI/DeviceTab/uiAMSBestPositionPopup.cpp
|
||||
GUI/DeviceTab/uiDeviceUpdateVersion.h
|
||||
GUI/DeviceTab/uiDeviceUpdateVersion.cpp
|
||||
GUI/DeviceTab/wgtDeviceNozzleRackNozzleItem.h
|
||||
GUI/DeviceTab/wgtDeviceNozzleRackNozzleItem.cpp
|
||||
GUI/DeviceTab/wgtDeviceNozzleRack.h
|
||||
GUI/DeviceTab/wgtDeviceNozzleRack.cpp
|
||||
GUI/DeviceTab/wgtDeviceNozzleRackUpdate.h
|
||||
@@ -14,5 +14,7 @@ list(APPEND SLIC3R_GUI_SOURCES
|
||||
GUI/DeviceTab/wgtDeviceNozzleSelect.cpp
|
||||
GUI/DeviceTab/wgtMsgBox.h
|
||||
GUI/DeviceTab/wgtMsgBox.cpp
|
||||
GUI/DeviceTab/wgtMsgPanel.h
|
||||
GUI/DeviceTab/wgtMsgPanel.cpp
|
||||
)
|
||||
set(SLIC3R_GUI_SOURCES ${SLIC3R_GUI_SOURCES} PARENT_SCOPE)
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user