mirror of
https://github.com/OrcaSlicer/OrcaSlicer.git
synced 2026-09-29 03:41:31 +00:00
Merge branch 'main' into feature/filament_id
Brings in 560 commits (merge base 2026-07-04 .. origin/main af9fd10d7a).
19 conflicts resolved; the other 138 touched files auto-merged.
Conflict resolutions
- Snapmaker/Polymaker (7): main normalised JSON key order in #15039 while this
branch inserted filament_id/filament_vendor. Took main's key order and kept
this branch's values, inserting a single filament_id key rather than letting
git's line merge leave duplicate "from"/"instantiation" keys.
- re3D rPETG @0.8/@1.75 nozzle (2): main renamed these from "re3D Greengate
rPETG @*" and re-vendored GreenGate3D -> re3D (#15169). Git's rename-aware
merge produced a file with two filament_vendor keys; took main's version
wholesale instead. The id is reconciled by the tooling, not by hand.
- re3D rPP (1): kept main's inherits change (fdm_filament_pet -> fdm_filament_pp,
local filament_type override dropped) and its widened compatible_printers.
The flattened type is still PP, so the product triple and id OFfHGM1D are
unchanged.
- re3D Greengate rPETG.json, Afinia {ABS+,ABS,PLA,TPU,Value ABS,Value PLA}.json
(7 modify/delete): accepted main's deletions. The Afinia ids are not orphaned
- the surviving @HS presets resolve the same triple and already carry the
same ids, so nothing is retired.
- .github/workflows/check_profiles.yml: kept both main's new "validate slice"
step and this branch's tree-wide filament-subtype check.
- tests/libslic3r/CMakeLists.txt: kept both test_filament_id_succession.cpp and
main's test_preset_diff.cpp.
Verified
- No conflict markers; all 12795 profile JSONs parse with no duplicate keys.
- All branch artifacts (tooling, snapshot, ledger, doc, tests) intact and
unmodified by the merge.
- The succession-ledger C++ call sites survived main's refactors; audited
Preset.{cpp,hpp}, PresetBundle.cpp, DeviceManager.cpp, PresetComboBoxes.cpp,
CaliHistoryDialog.cpp, MoonrakerPrinterAgent.cpp against base/ours/theirs.
- scripts/tests: 115/116 pass.
Known follow-up: assign_filament_ids.py --check reports 243 errors, all from
filament data main added in the last month (BBL/addnorth, Qidi X5/Plus 5,
Snapmaker U1, re3D). The single failing unit test is the live-tree conformance
test asserting that count is zero. Reconciliation lands separately.
This commit is contained in:
+108
-18
@@ -70,6 +70,9 @@ float FullTransparentModdifiedToFixAlpha = 0.3f;
|
||||
// value like 0.18f could not because in C++ (int)(0.18f * 255) == 45 however in OpenGL it renders this as 46
|
||||
// which breaks the `SelectMachineDialog::record_edge_pixels_data()` function!
|
||||
float FULL_BLACK_THRESHOLD = 0.2f;
|
||||
// Keep depth_tex away from texture unit 0 to avoid sampler-type aliasing with
|
||||
// shadow/environment samplers when realistic view is disabled.
|
||||
static constexpr int OUTLINE_DEPTH_TEX_UNIT = 5;
|
||||
|
||||
Slic3r::ColorRGBA adjust_color_for_rendering(const Slic3r::ColorRGBA &colors)
|
||||
{
|
||||
@@ -495,7 +498,60 @@ void GLVolume::render_with_outline(const GUI::Size& cnv_size)
|
||||
simple_render(shader, model_objects, colors);
|
||||
return;
|
||||
}
|
||||
|
||||
// 0th. render pass, render the model using stencil buffer
|
||||
glsafe(::glEnable(GL_STENCIL_TEST));
|
||||
glsafe(::glStencilMask(0xFF));
|
||||
glsafe(::glStencilOp(GL_KEEP, GL_REPLACE, GL_REPLACE));
|
||||
glsafe(::glClearStencil(0));
|
||||
glsafe(::glClear(GL_STENCIL_BUFFER_BIT));
|
||||
glsafe(::glStencilFunc(GL_ALWAYS, 0xFF, 0xFF));
|
||||
if (tverts_range == std::make_pair<size_t, size_t>(0, -1))
|
||||
model.render(shader);
|
||||
else
|
||||
model.render(this->tverts_range, shader);
|
||||
glsafe(::glStencilFunc(GL_NOTEQUAL, 0xFF, 0xFF));
|
||||
glsafe(::glStencilMask(0x00));
|
||||
shader->set_uniform("is_outline", true);
|
||||
shader->set_uniform("screen_size", Vec2f{cnv_size.get_width(), cnv_size.get_height()});
|
||||
if (tverts_range == std::make_pair<size_t, size_t>(0, -1))
|
||||
model.render(shader);
|
||||
else
|
||||
model.render(this->tverts_range, shader);
|
||||
shader->set_uniform("is_outline", false);
|
||||
glsafe(::glStencilMask(0xFF));
|
||||
glsafe(::glDisable(GL_STENCIL_TEST));
|
||||
// render the outline using depth buffer and discard the pixels that are not on the outline
|
||||
// The silhouette is resolved per sample in the shader (see DetectSilho in gouraud.fs/phong.fs).
|
||||
// That needs the GL 3.2 entry points and a shader that declares depth_tex as sampler2DMS, which
|
||||
// only the 140 ones do and only under GL_ARB_texture_multisample - so ask the compiled program
|
||||
// rather than the GL version, or a sampler2D ends up bound to a multisample texture.
|
||||
// Only the Arb branch below allocates a multisample texture, so keep the target consistent with it.
|
||||
const bool use_msaa_outline = framebuffers_type == GUI::OpenGLManager::EFramebufferType::Arb &&
|
||||
GUI::wxGetApp().is_gl_version_greater_or_equal_to(3, 2) &&
|
||||
shader->get_uniform_location("msaa_samples") >= 0;
|
||||
const GLenum depth_tex_target = use_msaa_outline ? GL_TEXTURE_2D_MULTISAMPLE : GL_TEXTURE_2D;
|
||||
// Keep the depth texture off image unit 0. The object shaders leave shadow_map (and
|
||||
// environment_tex) at the default sampler value 0 whenever the shadow pass is skipped - which is
|
||||
// the case with realistic view off - and GL forbids two sampler types referring to the same image
|
||||
// unit. A sampler2DMS on unit 0 then makes every draw fail with INVALID_OPERATION on drivers that
|
||||
// enforce it (Mesa), i.e. the model disappears entirely. Unit 5 is unused (shadow_map takes 4).
|
||||
const int depth_tex_unit = OUTLINE_DEPTH_TEX_UNIT;
|
||||
int aa_samples = 1;
|
||||
if (use_msaa_outline) {
|
||||
if (const AppConfig* app_config = GUI::wxGetApp().app_config; app_config != nullptr) {
|
||||
const std::string value = app_config->get(SETTING_OPENGL_AA_SAMPLES);
|
||||
if (value == "2" || value == "4" || value == "8" || value == "16")
|
||||
aa_samples = ::atoi(value.c_str());
|
||||
}
|
||||
// Never request more samples than the driver supports for depth textures (a 1-sample texture
|
||||
// is used when MSAA is disabled, keeping a single code path for the sampler2DMS shader).
|
||||
GLint max_samples = 1;
|
||||
glsafe(::glGetIntegerv(GL_MAX_DEPTH_TEXTURE_SAMPLES, &max_samples));
|
||||
if (aa_samples > max_samples)
|
||||
aa_samples = max_samples < 1 ? 1 : max_samples;
|
||||
if (aa_samples < 1)
|
||||
aa_samples = 1;
|
||||
}
|
||||
// 1st. render pass, render the model into a separate render target that has only depth buffer
|
||||
GLuint depth_fbo = 0;
|
||||
GLuint depth_tex = 0;
|
||||
@@ -503,21 +559,26 @@ void GLVolume::render_with_outline(const GUI::Size& cnv_size)
|
||||
glsafe(::glGenFramebuffers(1, &depth_fbo));
|
||||
glsafe(::glBindFramebuffer(GL_FRAMEBUFFER, depth_fbo));
|
||||
|
||||
glActiveTexture(GL_TEXTURE0);
|
||||
glsafe(::glActiveTexture(GL_TEXTURE0 + depth_tex_unit));
|
||||
glsafe(::glGenTextures(1, &depth_tex));
|
||||
glsafe(::glBindTexture(GL_TEXTURE_2D, depth_tex));
|
||||
glsafe(::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE));
|
||||
glsafe(::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE));
|
||||
glsafe(::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR));
|
||||
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(::glBindTexture(depth_tex_target, depth_tex));
|
||||
if (use_msaa_outline) {
|
||||
// Multisample textures do not take filter/wrap parameters.
|
||||
glsafe(::glTexImage2DMultisample(GL_TEXTURE_2D_MULTISAMPLE, aa_samples, GL_DEPTH_COMPONENT32F, cnv_size.get_width(), cnv_size.get_height(), GL_TRUE));
|
||||
} else {
|
||||
glsafe(::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE));
|
||||
glsafe(::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE));
|
||||
glsafe(::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR));
|
||||
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, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, depth_tex, 0));
|
||||
glsafe(::glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, depth_tex_target, depth_tex, 0));
|
||||
} else {
|
||||
glsafe(::glGenFramebuffersEXT(1, &depth_fbo));
|
||||
glsafe(::glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, depth_fbo));
|
||||
|
||||
glActiveTexture(GL_TEXTURE0);
|
||||
glsafe(::glActiveTexture(GL_TEXTURE0 + depth_tex_unit));
|
||||
glsafe(::glGenTextures(1, &depth_tex));
|
||||
glsafe(::glBindTexture(GL_TEXTURE_2D, depth_tex));
|
||||
glsafe(::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE));
|
||||
@@ -526,14 +587,17 @@ 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));
|
||||
}
|
||||
// Unbind before drawing: the texture is this framebuffer's depth attachment, so leaving it bound
|
||||
// to a sampled unit would be a feedback loop.
|
||||
glsafe(::glBindTexture(depth_tex_target, 0));
|
||||
glsafe(::glActiveTexture(GL_TEXTURE0));
|
||||
glsafe(::glClear(GL_DEPTH_BUFFER_BIT));
|
||||
if (tverts_range == std::make_pair<size_t, size_t>(0, -1))
|
||||
model.render(shader);
|
||||
else
|
||||
model.render(this->tverts_range, shader);
|
||||
glsafe(::glBindTexture(GL_TEXTURE_2D, 0));
|
||||
|
||||
// 2nd. render pass, just a normal render with the depth buffer passed as a texture
|
||||
if (framebuffers_type == GUI::OpenGLManager::EFramebufferType::Arb) {
|
||||
@@ -543,13 +607,17 @@ void GLVolume::render_with_outline(const GUI::Size& cnv_size)
|
||||
}
|
||||
shader->set_uniform("is_outline", true);
|
||||
shader->set_uniform("screen_size", Vec2f{cnv_size.get_width(), cnv_size.get_height()});
|
||||
glActiveTexture(GL_TEXTURE0);
|
||||
glsafe(::glBindTexture(GL_TEXTURE_2D, depth_tex));
|
||||
shader->set_uniform("depth_tex", 0);
|
||||
shader->set_uniform("msaa_samples", aa_samples);
|
||||
glsafe(::glActiveTexture(GL_TEXTURE0 + depth_tex_unit));
|
||||
glsafe(::glBindTexture(depth_tex_target, depth_tex));
|
||||
glsafe(::glActiveTexture(GL_TEXTURE0));
|
||||
shader->set_uniform("depth_tex", depth_tex_unit);
|
||||
simple_render(shader, model_objects, colors);
|
||||
|
||||
// Some clean up to do
|
||||
glsafe(::glBindTexture(GL_TEXTURE_2D, 0));
|
||||
glsafe(::glActiveTexture(GL_TEXTURE0 + depth_tex_unit));
|
||||
glsafe(::glBindTexture(depth_tex_target, 0));
|
||||
glsafe(::glActiveTexture(GL_TEXTURE0));
|
||||
shader->set_uniform("is_outline", false);
|
||||
if (framebuffers_type == GUI::OpenGLManager::EFramebufferType::Arb) {
|
||||
glsafe(::glBindFramebuffer(GL_FRAMEBUFFER, 0));
|
||||
@@ -1024,7 +1092,8 @@ void GLVolumeCollection::render(GLVolumeCollection::ERenderType type,
|
||||
const Transform3d& projection_matrix,
|
||||
const GUI::Size& cnv_size,
|
||||
std::function<bool(const GLVolume &)> filter_func,
|
||||
bool partly_inside_enable) const
|
||||
bool partly_inside_enable,
|
||||
std::vector<double> * printable_heights) const
|
||||
{
|
||||
GLVolumeWithIdAndZList to_render = volumes_to_render(volumes, type, view_matrix, filter_func);
|
||||
if (to_render.empty())
|
||||
@@ -1052,6 +1121,10 @@ void GLVolumeCollection::render(GLVolumeCollection::ERenderType type,
|
||||
|
||||
const float support_normal_z = get_selection_support_normal_z();
|
||||
|
||||
// Prime depth_tex on every frame so non-outline draws do not keep the
|
||||
// default sampler unit 0, which can conflict with other sampler types.
|
||||
shader->set_uniform("depth_tex", OUTLINE_DEPTH_TEX_UNIT);
|
||||
|
||||
for (GLVolumeWithIdAndZ& volume : to_render) {
|
||||
#if ENABLE_MODIFIERS_ALWAYS_TRANSPARENT
|
||||
if (type == ERenderType::Transparent) {
|
||||
@@ -1108,7 +1181,24 @@ void GLVolumeCollection::render(GLVolumeCollection::ERenderType type,
|
||||
//use -1 ad a invalid type
|
||||
shader->set_uniform("print_volume.type", -1);
|
||||
}
|
||||
|
||||
|
||||
// Per-extruder printable-height shading. The flag is set to
|
||||
// 2.0 only for multi-extruder printers (two per-extruder heights); otherwise it is forced to 0.0
|
||||
// on every render so no stale flag survives a multi->single-extruder plate switch, keeping the
|
||||
// shared gouraud shader pixel-identical for single-extruder printers. When active the height
|
||||
// branch reads print_volume.xy_data (the bed rect), so set it explicitly here.
|
||||
std::array<float, 3> extruder_printable_heights = {0.0f, 0.0f, 0.0f};
|
||||
if (printable_heights != nullptr && printable_heights->size() > 1) {
|
||||
extruder_printable_heights[0] = 2.0f;
|
||||
extruder_printable_heights[1] = static_cast<float>((*printable_heights)[0]);
|
||||
extruder_printable_heights[2] = static_cast<float>((*printable_heights)[1]);
|
||||
shader->set_uniform("extruder_printable_heights", extruder_printable_heights);
|
||||
shader->set_uniform("print_volume.xy_data", m_print_volume.data);
|
||||
}
|
||||
else {
|
||||
shader->set_uniform("extruder_printable_heights", extruder_printable_heights);
|
||||
}
|
||||
|
||||
shader->set_uniform("volume_world_matrix", volume.first->world_matrix());
|
||||
shader->set_uniform("slope.actived", m_slope.isGlobalActive && !volume.first->is_modifier && !volume.first->is_wipe_tower);
|
||||
shader->set_uniform("slope.volume_world_normal_matrix", static_cast<Matrix3f>(volume.first->world_matrix().matrix().block(0, 0, 3, 3).inverse().transpose().cast<float>()));
|
||||
|
||||
@@ -497,7 +497,10 @@ public:
|
||||
const Transform3d& projection_matrix,
|
||||
const GUI::Size& cnv_size,
|
||||
std::function<bool(const GLVolume &)> filter_func = std::function<bool(const GLVolume &)>(),
|
||||
bool partly_inside_enable =true
|
||||
bool partly_inside_enable =true,
|
||||
// Per-extruder printable heights (extruder_printable_height); null / size<=1
|
||||
// leaves the shader's extruder_printable_heights flag at 0.0 (single-extruder = inert).
|
||||
std::vector<double> * printable_heights = nullptr
|
||||
) const;
|
||||
|
||||
// Clear the geometry
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,253 @@
|
||||
#pragma once
|
||||
#include "GUI_ObjectLayers.hpp"
|
||||
#include "slic3r/GUI/Widgets/AMSItem.hpp"
|
||||
#include "slic3r/GUI/Widgets/Label.hpp"
|
||||
#include "slic3r/GUI/Widgets/PopupWindow.hpp"
|
||||
|
||||
#include "slic3r/GUI/wxExtensions.hpp"
|
||||
#include "slic3r/GUI/DeviceCore/DevFilaSystem.h"
|
||||
#include "slic3r/GUI/I18N.hpp" // Orca: explicit _L() catalog include
|
||||
|
||||
#include <chrono>
|
||||
#include <optional>
|
||||
|
||||
|
||||
//Previous defintions
|
||||
class wxGrid;
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
namespace GUI {
|
||||
|
||||
|
||||
enum class DryCtrState {
|
||||
IDLE,
|
||||
DRY_WHEN_PRINT,
|
||||
UNKNOWN
|
||||
};
|
||||
|
||||
enum class DryCtrDev {
|
||||
N3S,
|
||||
N3F,
|
||||
UNKNOWN
|
||||
};
|
||||
|
||||
struct DryingPreset {
|
||||
DryCtrState state;
|
||||
DryCtrDev dev;
|
||||
int dry_temp;
|
||||
int dry_time;
|
||||
};
|
||||
|
||||
class FilamentItemPanel : public wxPanel
|
||||
{
|
||||
public:
|
||||
FilamentItemPanel(wxWindow* parent, const wxString& text, const std::string& icon_name = "",
|
||||
wxWindowID id = wxID_ANY);
|
||||
|
||||
void SetText(const wxString& text);
|
||||
void SetIcon(const std::string& icon_name);
|
||||
void msw_rescale();
|
||||
|
||||
private:
|
||||
void OnPaint(wxPaintEvent& event);
|
||||
void OnSize(wxSizeEvent& event);
|
||||
|
||||
Label* m_text_label;
|
||||
wxStaticBitmap* m_icon_bitmap;
|
||||
int m_target_size;
|
||||
std::string m_icon_name;
|
||||
ScalableBitmap m_icon;
|
||||
};
|
||||
|
||||
class AMSFilamentPanel : public wxPanel
|
||||
{
|
||||
wxBoxSizer* m_filament_sizer;
|
||||
Label* m_ams_name_label;
|
||||
int m_border_radius;
|
||||
wxPanel* m_filament_container{nullptr};
|
||||
std::vector<FilamentItemPanel*> m_filament_items;
|
||||
|
||||
public:
|
||||
AMSFilamentPanel(wxWindow* parent, const wxString& ams_name, wxWindowID id = wxID_ANY);
|
||||
|
||||
void AddFilamentItem(const wxString& text, const std::string& icon_name);
|
||||
void AddFilamentItem(FilamentItemPanel* panel);
|
||||
void SetAmsName(const wxString& ams_name);
|
||||
void Clear();
|
||||
void msw_rescale();
|
||||
private:
|
||||
void OnPaint(wxPaintEvent& event);
|
||||
};
|
||||
|
||||
|
||||
class AMSDryCtrWin : public DPIDialog
|
||||
{
|
||||
public:
|
||||
AMSDryCtrWin(wxWindow *parent);
|
||||
~AMSDryCtrWin();
|
||||
|
||||
void msw_rescale();
|
||||
void update(std::shared_ptr<DevFilaSystem> fila_system, MachineObject* obj);
|
||||
void set_ams_id(const std::string& ams_id);
|
||||
|
||||
protected:
|
||||
void on_dpi_changed(const wxRect &suggested_rect) override;
|
||||
|
||||
private:
|
||||
wxSimplebook* m_main_simplebook{nullptr};
|
||||
wxPanel* m_original_page{nullptr};
|
||||
|
||||
wxWindow* m_amswin{nullptr};
|
||||
wxBoxSizer* m_sizer_ams_items{nullptr};
|
||||
wxScrolledWindow* m_panel_prv_left {nullptr};
|
||||
wxScrolledWindow* m_panel_prv_right{nullptr};
|
||||
wxBoxSizer* m_sizer_prv_left{nullptr};
|
||||
wxBoxSizer* m_sizer_prv_right{nullptr};
|
||||
|
||||
// left panel related members
|
||||
ScalableBitmap m_humidity_image;
|
||||
wxStaticBitmap* m_humidity_img{nullptr};
|
||||
Label* m_image_description{nullptr};
|
||||
wxStaticBitmap* m_image_description_icon{nullptr};
|
||||
ScalableBitmap m_description_icon_bitmap;
|
||||
|
||||
Label* m_humidity_data_label = nullptr;
|
||||
Label* m_temperature_data_label = nullptr;
|
||||
Label* m_time_data_label = nullptr;
|
||||
wxBoxSizer* m_time_descrition_container = nullptr;
|
||||
|
||||
// right panel related members
|
||||
wxBoxSizer* m_normal_state_sizer{nullptr};
|
||||
wxBoxSizer* m_cannot_dry_sizer{nullptr};
|
||||
wxBoxSizer* m_dry_error_sizer{nullptr};
|
||||
Label* m_cannot_dry_description_label = nullptr;
|
||||
|
||||
// right panel normal state
|
||||
ComboBox* m_trays_combo;
|
||||
std::vector<FilamentBaseInfo> m_tray_ids;
|
||||
wxTextCtrl* m_temperature_input;
|
||||
wxTextCtrl* m_time_input;
|
||||
Label* m_normal_description;
|
||||
Button* m_start_button{nullptr};
|
||||
Button* m_next_button{nullptr};
|
||||
Button* m_stop_button{nullptr};
|
||||
Button* m_back_button{nullptr};
|
||||
Button* m_unload_button{nullptr};
|
||||
|
||||
// guide page description
|
||||
Label* m_guide_title_label{nullptr};
|
||||
Label* m_guide_description_label{nullptr};
|
||||
|
||||
wxCheckBox* m_rotate_spool_toggle{nullptr};
|
||||
|
||||
wxPanel* m_progress_page;
|
||||
ProgressBar* m_progress_gauge;
|
||||
wxTimer* m_progress_timer;
|
||||
|
||||
std::optional<std::chrono::steady_clock::time_point> m_stop_button_restore_deadline;
|
||||
std::optional<std::chrono::steady_clock::time_point> m_unload_button_restore_deadline;
|
||||
|
||||
Label* m_progress_title;
|
||||
int m_progress_value;
|
||||
int m_progress_message_index;
|
||||
std::vector<wxString> m_progress_text = {
|
||||
_L("Starting: Checking adapter connection"),
|
||||
_L("Starting: Checking filament status"),
|
||||
_L("Starting: Checking drying presets"),
|
||||
_L("Starting: Checking filament location"),
|
||||
_L("Starting: Checking air intake"),
|
||||
_L("Starting: Checking air vent")
|
||||
};
|
||||
|
||||
// Guide page
|
||||
wxPanel* m_guide_page{nullptr};
|
||||
AMSFilamentPanel* m_ams_filament_panel{nullptr};
|
||||
std::map<std::string, FilamentItemPanel*> m_filament_items;
|
||||
wxStaticBitmap* m_image_placeholder{nullptr};
|
||||
ScalableBitmap m_guide_image;
|
||||
|
||||
|
||||
bool m_is_ams_changed = false;
|
||||
std::weak_ptr<DevFilaSystem> m_fila_system;
|
||||
struct {
|
||||
std::string m_ams_id;
|
||||
DevAmsType m_model = DevAmsType::EXT_SPOOL;
|
||||
DevAms::DryStatus m_dry_status = DevAms::DryStatus::Off;
|
||||
DevAms::DrySubStatus m_dry_sub_status = DevAms::DrySubStatus::Off;
|
||||
int m_humidity_percent;
|
||||
int m_temperature;
|
||||
int m_left_dry_time;
|
||||
float m_recommand_dry_temp;
|
||||
} m_ams_info;
|
||||
|
||||
struct {
|
||||
std::unordered_map<std::string, std::string> m_filament_names;
|
||||
std::unordered_map<std::string, std::string> m_filament_type;
|
||||
std::unordered_map<std::string, int> m_dry_temp;
|
||||
std::unordered_map<std::string, int> m_dry_time;
|
||||
} m_dry_setting;
|
||||
|
||||
struct {
|
||||
bool m_is_printing = false;
|
||||
} m_printer_status;
|
||||
|
||||
private:
|
||||
void create();
|
||||
wxBoxSizer* create_guide_page_sizer(wxPanel* parent);
|
||||
wxBoxSizer* create_main_content_section(wxPanel* parent);
|
||||
wxBoxSizer* create_guide_info_filament(wxPanel* parent);
|
||||
wxBoxSizer* create_guide_info_section(wxPanel* parent);
|
||||
wxBoxSizer* create_guide_right_section(wxPanel* parent);
|
||||
wxBoxSizer* create_main_page_sizer(wxPanel* parent);
|
||||
wxBoxSizer* create_left_panel(wxPanel* parent);
|
||||
wxBoxSizer* create_humidity_status_section(wxPanel* parent);
|
||||
wxBoxSizer* create_description_item(wxPanel* parent, const wxString& title, Label*& dataLabel);
|
||||
wxBoxSizer* create_status_descriptions_section(wxPanel* parent);
|
||||
|
||||
wxBoxSizer* create_right_panel(wxPanel* parent);
|
||||
wxBoxSizer* create_normal_state_panel(wxPanel* parent);
|
||||
wxBoxSizer* create_cannot_dry_panel(wxPanel* parent);
|
||||
wxBoxSizer* create_drying_error_panel(wxPanel* parent);
|
||||
Button* create_button(wxPanel* parent, const wxString& title,
|
||||
const wxColour& background_color, const wxColour& border_color, const wxColour& text_color);
|
||||
|
||||
wxBoxSizer* create_progress_page_sizer(wxPanel* parent);
|
||||
void OnProgressTimer(wxTimerEvent& event);
|
||||
void OnClose(wxCloseEvent& event);
|
||||
void OnShow(wxShowEvent& event);
|
||||
|
||||
void OnFilamentSelectionChanged(wxCommandEvent& event);
|
||||
|
||||
|
||||
wxScrolledWindow* create_preview_scrolled_window(wxWindow* parent);
|
||||
|
||||
bool check_values_changed(DevAms* dev_ams);
|
||||
int update_image(DevAmsType type, DevAms::DryStatus status, DevAms::DrySubStatus sub_status, int humidity_percent);
|
||||
void update_img_description(DevAms::DryStatus status, DevAms::DrySubStatus sub_status);
|
||||
void update_normal_description(DevAms* dev_ams);
|
||||
int update_state(DevAms* dev_ams);
|
||||
int update_dryness_status(DevAms* dev_ams);
|
||||
int update_ams_change(DevAms* dev_ams);
|
||||
int update_filament_list(DevAms* dev_ams, MachineObject* obj);
|
||||
void update_filament_guide_info(DevAms* dev_ams);
|
||||
void update_normal_state(DevAms* dev_ams);
|
||||
void update_printer_state(MachineObject* obj);
|
||||
|
||||
std::shared_ptr<DevFilaSystem> get_fila_system() const;
|
||||
void start_sending_drying_command();
|
||||
void restore_stop_button_if_deadline_passed();
|
||||
void restore_unload_button_if_deadline_passed();
|
||||
void update_button_size(Button* button);
|
||||
|
||||
bool is_dry_status_changed(DevAms* dev_ams);
|
||||
bool is_dry_ctr_idle(DevAms* dev_ams);
|
||||
bool is_ams_changed(DevAms* dev_ams);
|
||||
bool is_dry_ctr_idle();
|
||||
bool is_tray_changed(DevAms* dev_ams);
|
||||
bool is_dry_ctr_err(DevAms* dev_ams);
|
||||
|
||||
};
|
||||
|
||||
} // GUI
|
||||
} // Slic3r
|
||||
@@ -16,6 +16,9 @@
|
||||
#include "DeviceCore/DevExtruderSystem.h"
|
||||
#include "DeviceCore/DevFilaBlackList.h"
|
||||
#include "DeviceCore/DevFilaSystem.h"
|
||||
#include "DeviceCore/DevFilaSwitch.h"
|
||||
#include "DeviceCore/DevNozzleSystem.h" // DevNozzle / GetNozzleByPosId / GetNozzleRack
|
||||
#include "DeviceCore/DevNozzleRack.h" // DevNozzleRack::IsSupported (rack gating)
|
||||
|
||||
#define FILAMENT_MAX_TEMP 300
|
||||
#define FILAMENT_MIN_TEMP 120
|
||||
@@ -293,12 +296,9 @@ 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);
|
||||
m_wiki_ctrl = new HyperLink(parent, "Wiki Guide", link_url);
|
||||
// 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);
|
||||
|
||||
@@ -446,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)
|
||||
@@ -531,56 +541,123 @@ void AMSMaterialsSetting::on_select_reset(wxCommandEvent& event) {
|
||||
Close();
|
||||
}
|
||||
|
||||
// Nozzle-context builder for the AMS-edit blacklist check. Factors the blacklist evaluation out of
|
||||
// on_select_ok and threads rack-gated per-nozzle context (extruder id + nozzle flow + nozzle diameter)
|
||||
// into CheckFilamentInfo, returning the accumulate-all CheckResult. As a side effect it resolves
|
||||
// ams_filament_id/ams_setting_id from the matched preset. fila_name is kept as the preset name (not
|
||||
// the short alias) so the name / name_suffix rules match on the non-rack fleet (no unintended
|
||||
// activation of e.g. the PLA Glow rule).
|
||||
static DevFilaBlacklist::CheckResult
|
||||
sCheckFilamentInfo(PresetBundle* preset_bundle,
|
||||
MachineObject* obj,
|
||||
int ams_id,
|
||||
int slot_id,
|
||||
const std::string& filament_id,
|
||||
std::string& ams_filament_id,
|
||||
std::string& ams_setting_id)
|
||||
{
|
||||
DevFilaBlacklist::CheckResult result;
|
||||
if (!preset_bundle || !obj)
|
||||
return result;
|
||||
|
||||
// Orca: there is no lookup struct that also carries setting_id, so resolve the (root) preset by
|
||||
// scanning the filaments collection for a matching filament_id.
|
||||
Preset* fila_preset = nullptr;
|
||||
for (auto it = preset_bundle->filaments.begin(); it != preset_bundle->filaments.end(); ++it) {
|
||||
if (it->filament_id == filament_id) {
|
||||
fila_preset = &(*it);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!fila_preset)
|
||||
return result;
|
||||
|
||||
if (wxGetApp().app_config->get("skip_ams_blacklist_check") != "true") {
|
||||
std::string filamnt_type;
|
||||
fila_preset->get_filament_type(filamnt_type);
|
||||
|
||||
DevFilaBlacklist::CheckFilamentInfo check_info;
|
||||
check_info.dev_id = obj->get_dev_id();
|
||||
check_info.model_id = obj->printer_type;
|
||||
check_info.fila_id = fila_preset->filament_id;
|
||||
check_info.fila_type = filamnt_type;
|
||||
check_info.fila_name = fila_preset->name;
|
||||
check_info.has_filament_switch = obj->GetFilaSwitch()->IsInstalled();
|
||||
check_info.ams_id = ams_id;
|
||||
check_info.slot_id = slot_id;
|
||||
|
||||
if (auto vendor = dynamic_cast<ConfigOptionStrings*>(fila_preset->config.option("filament_vendor"));
|
||||
vendor && !vendor->values.empty()) {
|
||||
check_info.fila_vendor = vendor->values[0];
|
||||
}
|
||||
|
||||
check_info.extruder_id = obj->GetFilaSystem()->GetExtruderIdByAmsId(std::to_string(ams_id));
|
||||
|
||||
// Rack-gated per-nozzle context. For a multi-nozzle main extruder on a rack printer (H2C) the
|
||||
// specific nozzle is resolved later by the print-dispatch rack mapping, so leave
|
||||
// nozzle_flow/nozzle_diameter unset here to keep the high-flow nozzle rules dormant. Every
|
||||
// non-rack printer takes the else branch, threading the reported nozzle flow + diameter and
|
||||
// thereby activating the high-flow blacklist warnings in the AMS-edit dialog for H2D/H2S/X2D/P2S
|
||||
// (and the E3D-kit X1C/P1x).
|
||||
DevNozzleSystem* nozzle_system = obj->GetNozzleSystem();
|
||||
const bool rack_supported = nozzle_system && nozzle_system->GetNozzleRack() &&
|
||||
nozzle_system->GetNozzleRack()->IsSupported();
|
||||
if (check_info.extruder_id == MAIN_EXTRUDER_ID && rack_supported) {
|
||||
; // multi-nozzle main extruder on a rack printer — nozzle resolved later by print dispatch
|
||||
} else {
|
||||
check_info.nozzle_flow = obj->GetFilaSystem()->GetNozzleFlowStringByAmsId(std::to_string(ams_id));
|
||||
if (nozzle_system) {
|
||||
DevNozzle nozzle = nozzle_system->GetNozzleByPosId(check_info.extruder_id.value_or(-1));
|
||||
if (!nozzle.IsEmpty())
|
||||
check_info.nozzle_diameter = nozzle.GetNozzleDiameter();
|
||||
}
|
||||
}
|
||||
|
||||
result = DevFilaBlacklist::check_filaments_in_blacklist(check_info);
|
||||
}
|
||||
|
||||
ams_filament_id = fila_preset->filament_id;
|
||||
ams_setting_id = fila_preset->setting_id;
|
||||
return result;
|
||||
}
|
||||
|
||||
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 = "";
|
||||
|
||||
// the combobox item
|
||||
auto filament_item = map_filament_items[m_comboBox_filament->GetValue().ToStdString()];
|
||||
|
||||
// check filament info (rack-gated per-nozzle blacklist evaluation)
|
||||
PresetBundle* preset_bundle = wxGetApp().preset_bundle;
|
||||
if (preset_bundle) {
|
||||
for (auto it = preset_bundle->filaments.begin(); it != preset_bundle->filaments.end(); it++) {
|
||||
const auto& fila_check_res = sCheckFilamentInfo(preset_bundle, obj, ams_id, slot_id,
|
||||
filament_item.filament_id, ams_filament_id, ams_setting_id);
|
||||
|
||||
auto filament_item = map_filament_items[m_comboBox_filament->GetValue().ToStdString()];
|
||||
std::string filament_id = filament_item.filament_id;
|
||||
if (it->filament_id.compare(filament_id) == 0) {
|
||||
if (const auto& prohibit_items = fila_check_res.get_items_by_action("prohibition"); !prohibit_items.empty()) {
|
||||
wxString info_msg;
|
||||
for (const auto& item : prohibit_items) { info_msg += item.info_msg + "\n"; }
|
||||
MessageDialog msg_wingow(nullptr, info_msg, _L("Error"), wxICON_WARNING | wxOK);
|
||||
msg_wingow.ShowModal();
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
//check is it in the filament blacklist
|
||||
if (wxGetApp().app_config->get("skip_ams_blacklist_check") != "true") {
|
||||
bool in_blacklist = false;
|
||||
std::string action;
|
||||
wxString info;
|
||||
std::string filamnt_type;
|
||||
std::string filamnt_name;
|
||||
it->get_filament_type(filamnt_type);
|
||||
|
||||
auto vendor = dynamic_cast<ConfigOptionStrings *>(it->config.option("filament_vendor"));
|
||||
|
||||
if (vendor && (vendor->values.size() > 0)) {
|
||||
std::string vendor_name = vendor->values[0];
|
||||
DevFilaBlacklist::check_filaments_in_blacklist(obj->printer_type, vendor_name, filamnt_type, it->filament_id, ams_id, slot_id, it->name, in_blacklist, action, info);
|
||||
}
|
||||
|
||||
if (in_blacklist) {
|
||||
if (action == "prohibition") {
|
||||
MessageDialog msg_wingow(nullptr, info, _L("Error"), wxICON_WARNING | wxOK);
|
||||
msg_wingow.ShowModal();
|
||||
//m_comboBox_filament->SetSelection(m_filament_selection);
|
||||
return;
|
||||
}
|
||||
else if (action == "warning") {
|
||||
MessageDialog msg_wingow(nullptr, info, _L("Warning"), wxICON_INFORMATION | wxOK);
|
||||
msg_wingow.ShowModal();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ams_filament_id = it->filament_id;
|
||||
ams_setting_id = it->setting_id;
|
||||
break;
|
||||
}
|
||||
if (const auto& warning_items = fila_check_res.get_items_by_action("warning"); !warning_items.empty()) {
|
||||
std::vector<FilamentWarningInfo> infos;
|
||||
for (const auto& item : warning_items) {
|
||||
FilamentWarningInfo winfo;
|
||||
winfo.info_msg = item.info_msg;
|
||||
winfo.wiki_url = item.wiki_url;
|
||||
infos.emplace_back(winfo);
|
||||
}
|
||||
FilamentWarningDialog msg_window(nullptr, _L("Warning"), infos);
|
||||
msg_window.ShowModal();
|
||||
}
|
||||
|
||||
wxString nozzle_temp_min = m_input_nozzle_min->GetTextCtrl()->GetValue();
|
||||
@@ -762,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
|
||||
{
|
||||
|
||||
@@ -324,10 +324,12 @@ AboutDialog::AboutDialog()
|
||||
m_html->SetFonts(font.GetFaceName(), font.GetFaceName(), size);
|
||||
m_html->SetMinSize(wxSize(FromDIP(-1), FromDIP(16)));
|
||||
m_html->SetBorders(2);
|
||||
wxColour bgr_clr = GetBackgroundColour();
|
||||
const auto bgr_clr_str = encode_color(ColorRGB(bgr_clr.Red(), bgr_clr.Green(), bgr_clr.Blue()));
|
||||
const auto text = from_u8(
|
||||
(boost::format(
|
||||
"<html>"
|
||||
"<body>"
|
||||
"<body bgcolor= \"" + bgr_clr_str + "\" >"
|
||||
"<p style=\"text-align:left\"><a style=\"color:#009789\" href=\"https://www.orcaslicer.com\">https://www.orcaslicer.com</ a></p>"
|
||||
"</body>"
|
||||
"</html>")
|
||||
|
||||
@@ -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
|
||||
+734
-784
File diff suppressed because it is too large
Load Diff
@@ -34,7 +34,7 @@
|
||||
#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>
|
||||
@@ -43,7 +43,19 @@
|
||||
|
||||
#define MAPPING_ITEM_INVALID_REMAIN -1
|
||||
|
||||
namespace Slic3r { namespace GUI {
|
||||
// Previous definitions
|
||||
namespace Slic3r
|
||||
{
|
||||
class DevNozzleRack;
|
||||
namespace GUI
|
||||
{
|
||||
class wgtDeviceNozzleRackSelect;
|
||||
class wgtMsgPanel;
|
||||
};
|
||||
};
|
||||
|
||||
namespace Slic3r {
|
||||
namespace GUI {
|
||||
|
||||
|
||||
#define AMS_TOTAL_COUNT 4
|
||||
@@ -57,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
|
||||
@@ -78,23 +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;
|
||||
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;
|
||||
@@ -107,34 +156,24 @@ public:
|
||||
ScalableBitmap m_filament_wheel_transparent;
|
||||
ScalableBitmap m_ams_wheel_mitem;
|
||||
ScalableBitmap m_ams_not_match;
|
||||
ScalableBitmap m_rack_nozzle_bitmap;
|
||||
|
||||
bool m_selected {false};
|
||||
bool m_warning{false};
|
||||
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();
|
||||
|
||||
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;
|
||||
@@ -166,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; };
|
||||
|
||||
@@ -207,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() {};
|
||||
@@ -230,7 +294,10 @@ public:
|
||||
int m_current_filament_id;
|
||||
ShowType m_show_type{ShowType::RIGHT};
|
||||
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};
|
||||
@@ -244,28 +311,34 @@ 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};
|
||||
|
||||
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;};
|
||||
void update_materials_list(std::vector<std::string> list);
|
||||
void set_tag_texture(std::string texture);
|
||||
void update(MachineObject* obj, const std::vector<FilamentInfo>& ams_mapping_result);
|
||||
void update_title(MachineObject* obj);
|
||||
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_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);
|
||||
@@ -279,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();
|
||||
@@ -295,9 +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
@@ -30,6 +30,19 @@ BaseTransparentDPIFrame::BaseTransparentDPIFrame(
|
||||
// SetBackgroundStyle(wxBackgroundStyle::wxBG_STYLE_TRANSPARENT);
|
||||
SetTransparent(m_init_transparent);
|
||||
SetBackgroundColour(wxColour(23, 25, 22, 128));
|
||||
|
||||
// ORCA add border
|
||||
Bind(wxEVT_PAINT, [this](wxPaintEvent& evt) {
|
||||
wxPaintDC dc(this);
|
||||
dc.SetPen(wxPen(StateColor::darkModeColorFor(wxColour("#009688")), FromDIP(2)));
|
||||
dc.SetBrush(*wxTRANSPARENT_BRUSH);
|
||||
dc.DrawRoundedRectangle(0, 0, GetSize().x, GetSize().y, 0);
|
||||
});
|
||||
|
||||
int window_padding = 15;
|
||||
auto imgsize = 32;
|
||||
auto imgright = 10;
|
||||
|
||||
//Adaptive Frame Width
|
||||
wxClientDC dc(parent);
|
||||
wxSize msg_sz = dc.GetMultiLineTextExtent(ok_text);
|
||||
@@ -44,21 +57,16 @@ BaseTransparentDPIFrame::BaseTransparentDPIFrame(
|
||||
|
||||
m_sizer_main = new wxBoxSizer(wxVERTICAL);
|
||||
wxBoxSizer *text_sizer = new wxBoxSizer(wxHORIZONTAL);
|
||||
text_sizer->AddSpacer(FromDIP(20));
|
||||
auto image_sizer = new wxBoxSizer(wxVERTICAL);
|
||||
auto imgsize = FromDIP(25);
|
||||
auto completedimg = new wxStaticBitmap(this, wxID_ANY, create_scaled_bitmap("completed", this, 25), wxDefaultPosition, wxSize(imgsize, imgsize), 0);
|
||||
image_sizer->Add(completedimg, 0, wxALIGN_LEFT | wxALIGN_CENTER_VERTICAL | wxALL, FromDIP(0));
|
||||
image_sizer->AddStretchSpacer();
|
||||
text_sizer->Add(image_sizer);
|
||||
text_sizer->AddSpacer(FromDIP(5));
|
||||
|
||||
auto completedimg = new wxStaticBitmap(this, wxID_ANY, create_scaled_bitmap("completed", this, imgsize), wxDefaultPosition, FromDIP(wxSize(imgsize, imgsize)), 0);
|
||||
|
||||
text_sizer->Add(completedimg, 0, wxALIGN_CENTER_VERTICAL | wxRIGHT, FromDIP(imgright));
|
||||
m_finish_text = new Label(this, win_text, LB_AUTO_WRAP);
|
||||
m_finish_text->SetMinSize(wxSize(FromDIP(win_width - 64), -1));
|
||||
m_finish_text->SetMaxSize(wxSize(FromDIP(win_width - 64), -1));
|
||||
m_finish_text->SetMinSize(wxSize(FromDIP(win_width - (window_padding * 2 + imgright + imgsize)), -1));
|
||||
m_finish_text->SetMaxSize(wxSize(FromDIP(win_width - (window_padding * 2 + imgright + imgsize)), -1));
|
||||
m_finish_text->SetForegroundColour(wxColour(255, 255, 255, 255));
|
||||
text_sizer->Add(m_finish_text, 0, wxALIGN_LEFT | wxALIGN_CENTER_VERTICAL | wxALL, 0);
|
||||
text_sizer->AddSpacer(FromDIP(20));
|
||||
m_sizer_main->Add(text_sizer, FromDIP(0), wxALIGN_LEFT | wxALIGN_CENTER_VERTICAL | wxTOP, FromDIP(15));
|
||||
text_sizer->Add(m_finish_text, 0, wxALIGN_CENTER_VERTICAL);
|
||||
m_sizer_main->Add(text_sizer, 0, wxALL, FromDIP(15));
|
||||
|
||||
wxBoxSizer *bSizer_button = new wxBoxSizer(wxHORIZONTAL);
|
||||
bSizer_button->SetMinSize(wxSize(FromDIP(100), -1));
|
||||
@@ -66,22 +74,18 @@ BaseTransparentDPIFrame::BaseTransparentDPIFrame(
|
||||
bSizer_button->Add(m_checkbox, 0, wxALIGN_LEFT);*/
|
||||
bSizer_button->AddStretchSpacer(1);
|
||||
m_button_ok = new Button(this, ok_text);
|
||||
m_button_ok->SetStyle(ButtonStyle::Confirm, ButtonType::Window);
|
||||
m_button_ok->SetSize(wxSize(FromDIP(60), FromDIP(30)));
|
||||
m_button_ok->SetMinSize(wxSize(FromDIP(90), FromDIP(30)));
|
||||
bSizer_button->Add(m_button_ok, 0, wxALIGN_RIGHT | wxLEFT | wxTOP, FromDIP(10));
|
||||
m_button_ok->SetStyle(ButtonStyle::Confirm, ButtonType::Choice);
|
||||
bSizer_button->Add(m_button_ok, 0, wxALIGN_RIGHT | wxLEFT, FromDIP(10));
|
||||
|
||||
m_button_ok->Bind(wxEVT_COMMAND_BUTTON_CLICKED, [this](wxCommandEvent &e) { deal_ok(); });
|
||||
|
||||
m_button_cancel = new Button(this, cancel_text);
|
||||
m_button_cancel->SetStyle(ButtonStyle::Regular, ButtonType::Window);
|
||||
m_button_cancel->SetSize(wxSize(FromDIP(65), FromDIP(30)));
|
||||
m_button_cancel->SetMinSize(wxSize(FromDIP(65), FromDIP(30)));
|
||||
bSizer_button->Add(m_button_cancel, 0, wxALIGN_RIGHT | wxLEFT | wxTOP, FromDIP(10));
|
||||
m_button_cancel->SetStyle(ButtonStyle::Regular, ButtonType::Choice);
|
||||
bSizer_button->Add(m_button_cancel, 0, wxALIGN_RIGHT | wxLEFT, FromDIP(10));
|
||||
|
||||
m_button_cancel->Bind(wxEVT_COMMAND_BUTTON_CLICKED, [this](wxCommandEvent &e) { deal_cancel(); });
|
||||
|
||||
m_sizer_main->Add(bSizer_button, 1, wxEXPAND | wxLEFT | wxRIGHT | wxBOTTOM, FromDIP(20));
|
||||
m_sizer_main->Add(bSizer_button, 1, wxEXPAND | wxLEFT | wxRIGHT | wxBOTTOM, FromDIP(window_padding));
|
||||
|
||||
Bind(wxEVT_CLOSE_WINDOW, [this](auto &e) {
|
||||
on_hide();
|
||||
|
||||
@@ -192,7 +192,7 @@ PingCodeBindDialog::PingCodeBindDialog(Plater* plater /*= nullptr*/)
|
||||
|
||||
|
||||
|
||||
SetSizer(sizer_main);
|
||||
SetSizerAndFit(sizer_main);
|
||||
Layout();
|
||||
Fit();
|
||||
|
||||
@@ -590,7 +590,7 @@ PingCodeBindDialog::~PingCodeBindDialog() {
|
||||
sizer_error_code->Add(m_st_txt_error_code, 0, wxALL, 0);
|
||||
|
||||
|
||||
auto st_title_error_desc = new wxStaticText(m_sw_bind_failed_info, wxID_ANY, wxT("Error desc"));
|
||||
auto st_title_error_desc = new wxStaticText(m_sw_bind_failed_info, wxID_ANY, _L("Error desc"));
|
||||
auto st_title_error_desc_doc = new wxStaticText(m_sw_bind_failed_info, wxID_ANY, ": ");
|
||||
m_st_txt_error_desc = new Label(m_sw_bind_failed_info, wxEmptyString);
|
||||
st_title_error_desc->SetForegroundColour(0x909090);
|
||||
@@ -607,7 +607,7 @@ PingCodeBindDialog::~PingCodeBindDialog() {
|
||||
sizer_error_desc->Add(st_title_error_desc_doc, 0, wxALL, 0);
|
||||
sizer_error_desc->Add(m_st_txt_error_desc, 0, wxALL, 0);
|
||||
|
||||
auto st_title_extra_info = new wxStaticText(m_sw_bind_failed_info, wxID_ANY, wxT("Extra info"));
|
||||
auto st_title_extra_info = new wxStaticText(m_sw_bind_failed_info, wxID_ANY, _L("Extra info"));
|
||||
auto st_title_extra_info_doc = new wxStaticText(m_sw_bind_failed_info, wxID_ANY, ": ");
|
||||
m_st_txt_extra_info = new Label(m_sw_bind_failed_info, wxEmptyString);
|
||||
st_title_extra_info->SetForegroundColour(0x909090);
|
||||
@@ -670,7 +670,7 @@ PingCodeBindDialog::~PingCodeBindDialog() {
|
||||
m_sizer_main->Add(m_sw_bind_failed_info, 0, wxALIGN_CENTER, 0);
|
||||
m_sizer_main->Add(m_simplebook, 0, wxALIGN_RIGHT | wxRIGHT | wxBOTTOM, ButtonProps::ChoiceButtonGap());
|
||||
|
||||
SetSizer(m_sizer_main);
|
||||
SetSizerAndFit(m_sizer_main);
|
||||
Layout();
|
||||
Fit();
|
||||
Centre(wxBOTH);
|
||||
@@ -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);
|
||||
@@ -992,7 +992,7 @@ UnBindMachineDialog::UnBindMachineDialog(Plater *plater /*= nullptr*/)
|
||||
m_sizer_main->Add(m_sizer_button, 0, wxALIGN_RIGHT | wxRIGHT, ButtonProps::ChoiceButtonGap());
|
||||
m_sizer_main->Add(0, 0, 0, wxTOP, FromDIP(20));
|
||||
|
||||
SetSizer(m_sizer_main);
|
||||
SetSizerAndFit(m_sizer_main);
|
||||
Layout();
|
||||
Fit();
|
||||
Centre(wxBOTH);
|
||||
|
||||
@@ -8,7 +8,6 @@
|
||||
#include <boost/nowide/convert.hpp>
|
||||
|
||||
#include <wx/sizer.h>
|
||||
#include <wx/button.h>
|
||||
#include <wx/listctrl.h>
|
||||
#include <wx/stattext.h>
|
||||
#include <wx/timer.h>
|
||||
@@ -20,6 +19,8 @@
|
||||
#include "slic3r/GUI/format.hpp"
|
||||
#include "slic3r/Utils/Bonjour.hpp"
|
||||
|
||||
#include "slic3r/GUI/Widgets/DialogButtons.hpp"
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
|
||||
@@ -63,30 +64,30 @@ BonjourDialog::BonjourDialog(wxWindow *parent, Slic3r::PrinterTechnology tech)
|
||||
, timer_state(0)
|
||||
, tech(tech)
|
||||
{
|
||||
SetBackgroundColour(*wxWHITE);
|
||||
|
||||
const int em = GUI::wxGetApp().em_unit();
|
||||
list->SetMinSize(wxSize(80 * em, 30 * em));
|
||||
|
||||
wxBoxSizer *vsizer = new wxBoxSizer(wxVERTICAL);
|
||||
|
||||
label->SetFont(Label::Body_14);
|
||||
vsizer->Add(label, 0, wxEXPAND | wxTOP | wxLEFT | wxRIGHT, em);
|
||||
|
||||
list->SetSingleStyle(wxLC_SINGLE_SEL);
|
||||
list->SetSingleStyle(wxLC_SORT_DESCENDING);
|
||||
list->AppendColumn(_(L("Address")), wxLIST_FORMAT_LEFT, 5 * em);
|
||||
list->AppendColumn(_(L("Address")), wxLIST_FORMAT_LEFT, 20 * em);
|
||||
list->AppendColumn(_(L("Hostname")), wxLIST_FORMAT_LEFT, 10 * em);
|
||||
list->AppendColumn(_(L("Service name")), wxLIST_FORMAT_LEFT, 20 * em);
|
||||
if (tech == ptFFF) {
|
||||
list->AppendColumn(_(L("OctoPrint version")), wxLIST_FORMAT_LEFT, 5 * em);
|
||||
list->AppendColumn(_(L("OctoPrint version")), wxLIST_FORMAT_LEFT, 15 * em);
|
||||
}
|
||||
|
||||
vsizer->Add(list, 1, wxEXPAND | wxALL, em);
|
||||
|
||||
wxBoxSizer *button_sizer = new wxBoxSizer(wxHORIZONTAL);
|
||||
button_sizer->Add(new wxButton(this, wxID_OK, _L("OK")), 0, wxALL, em);
|
||||
button_sizer->Add(new wxButton(this, wxID_CANCEL, _L("Cancel")), 0, wxALL, em);
|
||||
// ^ Note: The Ok/Cancel labels are translated by wxWidgets
|
||||
auto dlg_btns = new GUI::DialogButtons(this, {"OK", "Cancel"});
|
||||
|
||||
vsizer->Add(button_sizer, 0, wxALIGN_CENTER);
|
||||
vsizer->Add(dlg_btns, 0, wxEXPAND);
|
||||
SetSizerAndFit(vsizer);
|
||||
|
||||
Bind(EVT_BONJOUR_REPLY, &BonjourDialog::on_reply, this);
|
||||
@@ -241,12 +242,15 @@ IPListDialog::IPListDialog(wxWindow* parent, const wxString& hostname, const std
|
||||
, m_list(new wxListView(this, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxLC_REPORT | wxSIMPLE_BORDER))
|
||||
, m_selected_index (selected_index)
|
||||
{
|
||||
SetBackgroundColour(*wxWHITE);
|
||||
|
||||
const int em = GUI::wxGetApp().em_unit();
|
||||
m_list->SetMinSize(wxSize(40 * em, 30 * em));
|
||||
|
||||
wxBoxSizer* vsizer = new wxBoxSizer(wxVERTICAL);
|
||||
|
||||
auto* label = new wxStaticText(this, wxID_ANY, GUI::format_wxstr(_L("There are several IP addresses resolving to hostname %1%.\nPlease select one that should be used."), hostname));
|
||||
label->SetFont(Label::Body_14);
|
||||
vsizer->Add(label, 0, wxEXPAND | wxTOP | wxLEFT | wxRIGHT, em);
|
||||
|
||||
m_list->SetSingleStyle(wxLC_SINGLE_SEL);
|
||||
@@ -259,11 +263,9 @@ IPListDialog::IPListDialog(wxWindow* parent, const wxString& hostname, const std
|
||||
|
||||
vsizer->Add(m_list, 1, wxEXPAND | wxALL, em);
|
||||
|
||||
wxBoxSizer* button_sizer = new wxBoxSizer(wxHORIZONTAL);
|
||||
button_sizer->Add(new wxButton(this, wxID_OK, _L("OK")), 0, wxALL, em);
|
||||
button_sizer->Add(new wxButton(this, wxID_CANCEL, _L("Cancel")), 0, wxALL, em);
|
||||
auto dlg_btns = new GUI::DialogButtons(this, {"OK", "Cancel"});
|
||||
|
||||
vsizer->Add(button_sizer, 0, wxALIGN_CENTER);
|
||||
vsizer->Add(dlg_btns, 0, wxEXPAND);
|
||||
SetSizerAndFit(vsizer);
|
||||
|
||||
GUI::wxGetApp().UpdateDlgDarkUI(this);
|
||||
|
||||
@@ -13,6 +13,9 @@
|
||||
#include "Plater.hpp"
|
||||
#include "DeviceCore/DevExtruderSystem.h"
|
||||
#include "DeviceCore/DevManager.h"
|
||||
#include "DeviceCore/DevConfigUtil.h"
|
||||
#include "DeviceCore/DevNozzleSystem.h"
|
||||
#include "DeviceCore/DevNozzleRack.h"
|
||||
|
||||
namespace Slic3r {
|
||||
namespace GUI {
|
||||
@@ -26,6 +29,7 @@ namespace GUI {
|
||||
enum CaliColumnType : int {
|
||||
Cali_Name = 0,
|
||||
Cali_Filament,
|
||||
Cali_Nozzle_ID,
|
||||
Cali_Nozzle,
|
||||
Cali_K_Value,
|
||||
Cali_Delete,
|
||||
@@ -33,6 +37,9 @@ enum CaliColumnType : int {
|
||||
Cali_Type_Count
|
||||
};
|
||||
|
||||
// Orca: derive nozzle-volume support from the machine preset's nozzle_volume array
|
||||
// rather than a live device-capability query, to avoid flipping the Nozzle-Flow column
|
||||
// visibility on shipping printers (H2D/X1/P1/A1).
|
||||
bool support_nozzle_volume(const MachineObject* obj)
|
||||
{
|
||||
if (!obj)
|
||||
@@ -49,14 +56,17 @@ bool support_nozzle_volume(const MachineObject* obj)
|
||||
return false;
|
||||
}
|
||||
|
||||
int get_colume_idx(CaliColumnType type, MachineObject* obj)
|
||||
// Rack hotend position code -> label: 0 = fixed ("R"); >=0x10 = rack slot number; else N/A.
|
||||
static wxString nozzle_id_code_to_string(int code)
|
||||
{
|
||||
if (!support_nozzle_volume(obj)
|
||||
&& (type > CaliColumnType::Cali_Nozzle)) {
|
||||
return type - 1;
|
||||
if (code == 0) {
|
||||
return "R";
|
||||
} else if (code >= 0x10) {
|
||||
return wxString::Format("%d", code - 0x10 + 1);
|
||||
} else {
|
||||
BOOST_LOG_TRIVIAL(warning) << __FUNCTION__ << "Nozzle position id is -1 or invalid.";
|
||||
return "N/A";
|
||||
}
|
||||
|
||||
return type;
|
||||
}
|
||||
|
||||
static wxString get_preset_name_by_filament_id(std::string filament_id, bool follow_succession = true)
|
||||
@@ -250,6 +260,10 @@ void HistoryWindow::on_device_connected(MachineObject* obj)
|
||||
}
|
||||
m_comboBox_nozzle_dia->SetSelection(selection);
|
||||
|
||||
m_extruder_switch_btn->SetLabels(
|
||||
_L(DevPrinterConfigUtil::get_toolhead_display_name(obj->printer_type, DEPUTY_EXTRUDER_ID, ToolHeadComponent::Nozzle, ToolHeadNameCase::TitleCase)),
|
||||
_L(DevPrinterConfigUtil::get_toolhead_display_name(obj->printer_type, MAIN_EXTRUDER_ID, ToolHeadComponent::Nozzle, ToolHeadNameCase::TitleCase)));
|
||||
|
||||
if (obj->is_multi_extruders())
|
||||
m_extruder_switch_btn->Show();
|
||||
else
|
||||
@@ -326,7 +340,21 @@ void HistoryWindow::enbale_action_buttons(bool enable) {
|
||||
}
|
||||
}
|
||||
|
||||
// The main extruder can carry a nozzle rack (H2C); the Nozzle-ID column only shows for it.
|
||||
bool HistoryWindow::support_nozzle_id_column()
|
||||
{
|
||||
if (!curr_obj || get_extruder_id() != MAIN_EXTRUDER_ID)
|
||||
return false;
|
||||
auto ns = curr_obj->GetNozzleSystem();
|
||||
if (!ns)
|
||||
return false;
|
||||
auto rack = ns->GetNozzleRack();
|
||||
return rack && rack->IsSupported();
|
||||
}
|
||||
|
||||
void HistoryWindow::sync_history_data() {
|
||||
int column_idx = 0;
|
||||
|
||||
Freeze();
|
||||
m_history_data_panel->DestroyChildren();
|
||||
m_history_data_panel->Enable();
|
||||
@@ -337,24 +365,35 @@ void HistoryWindow::sync_history_data() {
|
||||
|
||||
m_history_data_panel->SetSizer(gbSizer, true);
|
||||
|
||||
const bool has_nozzle_id = support_nozzle_id_column();
|
||||
|
||||
auto title_name = new Label(m_history_data_panel, _L("Name"));
|
||||
title_name->SetFont(Label::Head_14);
|
||||
gbSizer->Add(title_name, {0, get_colume_idx(CaliColumnType::Cali_Name, curr_obj) }, {1, 1}, wxBOTTOM, FromDIP(15));
|
||||
BOOST_LOG_TRIVIAL(info) << "=====================" << title_name->GetLabelText().ToStdString();
|
||||
gbSizer->Add(title_name, {0, column_idx++ }, {1, 1}, wxBOTTOM, FromDIP(15));
|
||||
|
||||
auto title_preset_name = new Label(m_history_data_panel, _L("Filament"));
|
||||
title_preset_name->SetFont(Label::Head_14);
|
||||
gbSizer->Add(title_preset_name, { 0, get_colume_idx(CaliColumnType::Cali_Filament, curr_obj) }, { 1, 1 }, wxBOTTOM, FromDIP(15));
|
||||
gbSizer->Add(title_preset_name, { 0, column_idx++ }, { 1, 1 }, wxBOTTOM, FromDIP(15));
|
||||
|
||||
if (has_nozzle_id) {
|
||||
auto nozzle_id_name = new Label(m_history_data_panel, _L("Nozzle ID"));
|
||||
nozzle_id_name->SetFont(Label::Head_14);
|
||||
{
|
||||
wxString chd_ext_name = _L(DevPrinterConfigUtil::get_toolhead_display_name(curr_obj->printer_type, MAIN_EXTRUDER_ID, ToolHeadComponent::Extruder, ToolHeadNameCase::LowerCase));
|
||||
nozzle_id_name->SetToolTip(wxString::Format(_L("Note: The hotend number on the %s is tied to the holder. When the hotend is moved to a new holder, its number will update automatically."), chd_ext_name));
|
||||
}
|
||||
gbSizer->Add(nozzle_id_name, {0, column_idx++}, {1, 1}, wxBOTTOM, FromDIP(15));
|
||||
}
|
||||
|
||||
if (support_nozzle_volume(curr_obj)) {
|
||||
auto nozzle_name = new Label(m_history_data_panel, _L("Nozzle Flow"));
|
||||
nozzle_name->SetFont(Label::Head_14);
|
||||
gbSizer->Add(nozzle_name, {0, get_colume_idx(CaliColumnType::Cali_Nozzle, curr_obj)}, {1, 1}, wxBOTTOM, FromDIP(15));
|
||||
gbSizer->Add(nozzle_name, {0, column_idx++}, {1, 1}, wxBOTTOM, FromDIP(15));
|
||||
}
|
||||
|
||||
auto title_k = new Label(m_history_data_panel, _L("Factor K"));
|
||||
title_k->SetFont(Label::Head_14);
|
||||
gbSizer->Add(title_k, { 0, get_colume_idx(CaliColumnType::Cali_K_Value,curr_obj) }, { 1, 1 }, wxBOTTOM, FromDIP(15));
|
||||
gbSizer->Add(title_k, { 0, column_idx++ }, { 1, 1 }, wxBOTTOM, FromDIP(15));
|
||||
|
||||
// Hide
|
||||
//auto title_n = new Label(m_history_data_panel, wxID_ANY, _L("N"));
|
||||
@@ -363,7 +402,12 @@ void HistoryWindow::sync_history_data() {
|
||||
|
||||
auto title_action = new Label(m_history_data_panel, _L("Action"));
|
||||
title_action->SetFont(Label::Head_14);
|
||||
gbSizer->Add(title_action, {0, get_colume_idx(CaliColumnType::Cali_Delete, curr_obj)}, {1, 1});
|
||||
gbSizer->Add(title_action, {0, column_idx++}, {1, 1});
|
||||
|
||||
// grid item num in row (Delete + Edit share one "Action" header)
|
||||
const int column_count = column_idx + 1;
|
||||
// reset column_idx for the data rows
|
||||
column_idx = 0;
|
||||
|
||||
int i = 1;
|
||||
for (auto& result : m_calib_results_history) {
|
||||
@@ -379,13 +423,13 @@ void HistoryWindow::sync_history_data() {
|
||||
n_value->Hide();
|
||||
auto delete_button = new Button(m_history_data_panel, _L("Delete"));
|
||||
delete_button->SetStyle(ButtonStyle::Alert, ButtonType::Window);
|
||||
delete_button->Bind(wxEVT_BUTTON, [this, gbSizer, i, &result](auto& e) {
|
||||
delete_button->Bind(wxEVT_BUTTON, [this, gbSizer, i, &result, column_count](auto& e) {
|
||||
if (m_ui_op_lock) {
|
||||
return;
|
||||
} else {
|
||||
m_ui_op_lock = true;
|
||||
}
|
||||
for (int j = 0; j < HISTORY_WINDOW_ITEMS_COUNT; j++) {
|
||||
for (int j = 0; j < column_count; j++) {
|
||||
auto item = gbSizer->FindItemAtPosition({ i, j });
|
||||
if (item && item->GetWindow())
|
||||
item->GetWindow()->Hide();
|
||||
@@ -398,6 +442,8 @@ void HistoryWindow::sync_history_data() {
|
||||
cali_info.cali_idx = result.cali_idx;
|
||||
cali_info.nozzle_diameter = result.nozzle_diameter;
|
||||
cali_info.filament_id = result.filament_id;
|
||||
cali_info.nozzle_pos_id = result.nozzle_pos_id;
|
||||
cali_info.nozzle_sn = result.nozzle_sn;
|
||||
CalibUtils::delete_PA_calib_result(cali_info);
|
||||
});
|
||||
|
||||
@@ -424,17 +470,23 @@ void HistoryWindow::sync_history_data() {
|
||||
}
|
||||
});
|
||||
|
||||
gbSizer->Add(name_value, {i, get_colume_idx(CaliColumnType::Cali_Name, curr_obj)}, {1, 1}, wxBOTTOM, FromDIP(15));
|
||||
gbSizer->Add(preset_name_value, {i, get_colume_idx(CaliColumnType::Cali_Filament, curr_obj)}, {1, 1}, wxBOTTOM, FromDIP(15));
|
||||
gbSizer->Add(name_value, {i, column_idx++}, {1, 1}, wxBOTTOM, FromDIP(15));
|
||||
gbSizer->Add(preset_name_value, {i, column_idx++}, {1, 1}, wxBOTTOM, FromDIP(15));
|
||||
if (has_nozzle_id) {
|
||||
wxString nozzle_id = nozzle_id_code_to_string(result.nozzle_pos_id);
|
||||
auto nozzle_id_label = new Label(m_history_data_panel, nozzle_id);
|
||||
gbSizer->Add(nozzle_id_label, {i, column_idx++}, {1, 1}, wxBOTTOM, FromDIP(15));
|
||||
}
|
||||
if (support_nozzle_volume(curr_obj)) {
|
||||
wxString nozzle_name = get_nozzle_volume_type_name(result.nozzle_volume_type);
|
||||
auto nozzle_name_label = new Label(m_history_data_panel, nozzle_name);
|
||||
gbSizer->Add(nozzle_name_label, {i, get_colume_idx(CaliColumnType::Cali_Nozzle, curr_obj)}, {1, 1}, wxBOTTOM, FromDIP(15));
|
||||
gbSizer->Add(nozzle_name_label, {i, column_idx++}, {1, 1}, wxBOTTOM, FromDIP(15));
|
||||
}
|
||||
gbSizer->Add(k_value, {i, get_colume_idx(CaliColumnType::Cali_K_Value, curr_obj)}, {1, 1}, wxBOTTOM, FromDIP(15));
|
||||
gbSizer->Add(k_value, {i, column_idx++}, {1, 1}, wxBOTTOM, FromDIP(15));
|
||||
//gbSizer->Add(n_value, { i, 3 }, { 1, 1 }, wxBOTTOM, FromDIP(15));
|
||||
gbSizer->Add(delete_button, {i, get_colume_idx(CaliColumnType::Cali_Delete, curr_obj)}, {1, 1}, wxBOTTOM, FromDIP(15));
|
||||
gbSizer->Add(edit_button, {i, get_colume_idx(CaliColumnType::Cali_Edit, curr_obj)}, {1, 1}, wxBOTTOM, FromDIP(15));
|
||||
gbSizer->Add(delete_button, {i, column_idx++}, {1, 1}, wxBOTTOM, FromDIP(15));
|
||||
gbSizer->Add(edit_button, {i, column_idx++}, {1, 1}, wxBOTTOM, FromDIP(15));
|
||||
column_idx = 0;
|
||||
i++;
|
||||
m_ui_op_lock = false;
|
||||
}
|
||||
@@ -529,13 +581,22 @@ EditCalibrationHistoryDialog::EditCalibrationHistoryDialog(wxWindow
|
||||
if (obj && obj->is_multi_extruders()) {
|
||||
|
||||
Label *extruder_name_title = new Label(top_panel, _L("Extruder"));
|
||||
int extruder_index = obj->is_main_extruder_on_left() ? result.extruder_id : 1 - result.extruder_id;
|
||||
wxString extruder_name = extruder_index == 0 ? _L("Left") : _L("Right");
|
||||
wxString extruder_name = _L(DevPrinterConfigUtil::get_toolhead_display_name(
|
||||
obj->printer_type, result.extruder_id, ToolHeadComponent::Extruder, ToolHeadNameCase::TitleCase, true));
|
||||
Label *extruder_name_value = new Label(top_panel, extruder_name);
|
||||
flex_sizer->Add(extruder_name_title);
|
||||
flex_sizer->Add(extruder_name_value);
|
||||
}
|
||||
|
||||
if (obj && obj->GetNozzleSystem() && obj->GetNozzleSystem()->GetNozzleRack()
|
||||
&& obj->GetNozzleSystem()->GetNozzleRack()->IsSupported() && result.extruder_id == MAIN_EXTRUDER_ID) {
|
||||
Label* nozzle_id_title = new Label(top_panel, _L("Nozzle ID"));
|
||||
wxString nozzle_id = nozzle_id_code_to_string(result.nozzle_pos_id);
|
||||
Label* nozzle_id_value = new Label(top_panel, nozzle_id);
|
||||
flex_sizer->Add(nozzle_id_title);
|
||||
flex_sizer->Add(nozzle_id_value);
|
||||
}
|
||||
|
||||
if (support_nozzle_volume(curr_obj)) {
|
||||
Label *nozzle_name_title = new Label(top_panel, _L("Nozzle"));
|
||||
wxString nozzle_name;
|
||||
@@ -577,9 +638,8 @@ EditCalibrationHistoryDialog::EditCalibrationHistoryDialog(wxWindow
|
||||
|
||||
main_sizer->Add(top_panel, 1, wxEXPAND | wxALL, FromDIP(20));
|
||||
|
||||
SetSizer(main_sizer);
|
||||
SetSizerAndFit(main_sizer);
|
||||
Layout();
|
||||
Fit();
|
||||
CenterOnParent();
|
||||
|
||||
wxGetApp().UpdateDlgDarkUI(this);
|
||||
@@ -774,14 +834,40 @@ NewCalibrationHistoryDialog::NewCalibrationHistoryDialog(wxWindow *parent, const
|
||||
Label *extruder_name_title = new Label(top_panel, _L("Extruder"));
|
||||
m_comboBox_extruder = new ::ComboBox(top_panel, wxID_ANY, wxEmptyString, wxDefaultPosition, NEW_HISTORY_DIALOG_INPUT_SIZE, 0, nullptr, wxCB_READONLY);
|
||||
wxArrayString extruder_items;
|
||||
extruder_items.push_back(_L("Left"));
|
||||
extruder_items.push_back(_L("Right"));
|
||||
extruder_items.push_back(_L(DevPrinterConfigUtil::get_toolhead_display_name(
|
||||
curr_obj->printer_type, DEPUTY_EXTRUDER_ID, ToolHeadComponent::Extruder, ToolHeadNameCase::TitleCase, true)));
|
||||
extruder_items.push_back(_L(DevPrinterConfigUtil::get_toolhead_display_name(
|
||||
curr_obj->printer_type, MAIN_EXTRUDER_ID, ToolHeadComponent::Extruder, ToolHeadNameCase::TitleCase, true)));
|
||||
m_comboBox_extruder->Set(extruder_items);
|
||||
m_comboBox_extruder->SetSelection(-1);
|
||||
flex_sizer->Add(extruder_name_title);
|
||||
flex_sizer->Add(m_comboBox_extruder);
|
||||
}
|
||||
|
||||
// Nozzle ID (rack printers only): choose the hotend position this manual record belongs to.
|
||||
if (curr_obj->GetNozzleSystem() && curr_obj->GetNozzleSystem()->GetNozzleRack()
|
||||
&& curr_obj->GetNozzleSystem()->GetNozzleRack()->IsSupported()) {
|
||||
auto rack = curr_obj->GetNozzleSystem()->GetNozzleRack();
|
||||
Label *nozzle_id_title = new Label(top_panel, _L("Nozzle ID"));
|
||||
m_comboBox_nozzle_id = new ::ComboBox(top_panel, wxID_ANY, wxEmptyString, wxDefaultPosition, NEW_HISTORY_DIALOG_INPUT_SIZE, 0, nullptr, wxCB_READONLY);
|
||||
m_comboBox_nozzle_id->Bind(wxEVT_COMMAND_COMBOBOX_SELECTED, &NewCalibrationHistoryDialog::on_select_nozzle_pos, this);
|
||||
|
||||
auto main_extder = curr_obj->GetExtderSystem()->GetExtderById(MAIN_EXTRUDER_ID);
|
||||
int r_nozzle_id = main_extder ? main_extder->GetNozzleId() : 0;
|
||||
auto r_nozzle = curr_obj->GetNozzleSystem()->GetExtNozzle(r_nozzle_id);
|
||||
if (r_nozzle.IsNormal()) {
|
||||
m_comboBox_nozzle_id->Append("R", wxNullBitmap, new int{0});
|
||||
}
|
||||
for (auto nozzle_item : rack->GetRackNozzles()) {
|
||||
if (nozzle_item.second.IsNormal()) {
|
||||
m_comboBox_nozzle_id->Append(wxString::Format("%d", nozzle_item.first + 1), wxNullBitmap, new int{0x10 | nozzle_item.first});
|
||||
}
|
||||
}
|
||||
m_comboBox_nozzle_id->SetSelection(-1);
|
||||
flex_sizer->Add(nozzle_id_title);
|
||||
flex_sizer->Add(m_comboBox_nozzle_id);
|
||||
}
|
||||
|
||||
if (support_nozzle_volume(curr_obj)) {
|
||||
Label *nozzle_name_title = new Label(top_panel, _L("Nozzle"));
|
||||
m_comboBox_nozzle_type = new ::ComboBox(top_panel, wxID_ANY, wxEmptyString, wxDefaultPosition, NEW_HISTORY_DIALOG_INPUT_SIZE, 0, nullptr, wxCB_READONLY);
|
||||
@@ -829,9 +915,8 @@ NewCalibrationHistoryDialog::NewCalibrationHistoryDialog(wxWindow *parent, const
|
||||
|
||||
main_sizer->Add(top_panel, 1, wxEXPAND | wxALL, FromDIP(20));
|
||||
|
||||
SetSizer(main_sizer);
|
||||
SetSizerAndFit(main_sizer);
|
||||
Layout();
|
||||
Fit();
|
||||
CenterOnParent();
|
||||
|
||||
wxGetApp().UpdateDlgDarkUI(this);
|
||||
@@ -845,6 +930,35 @@ int NewCalibrationHistoryDialog::get_extruder_id(int extruder_index)
|
||||
return 0;
|
||||
}
|
||||
|
||||
int NewCalibrationHistoryDialog::get_nozzle_combo_id_code() const
|
||||
{
|
||||
if (!m_comboBox_nozzle_id)
|
||||
return -1;
|
||||
|
||||
auto sel = m_comboBox_nozzle_id->GetSelection();
|
||||
if (sel != wxNOT_FOUND && m_comboBox_nozzle_id->GetClientData(sel))
|
||||
return *(reinterpret_cast<int*>(m_comboBox_nozzle_id->GetClientData(sel)));
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
void NewCalibrationHistoryDialog::on_select_nozzle_pos(wxCommandEvent &event)
|
||||
{
|
||||
// Mirror the picked hotend's flow onto the (Orca index-based) nozzle-type combo.
|
||||
if (!curr_obj || !m_comboBox_nozzle_id || !m_comboBox_nozzle_type || !curr_obj->GetNozzleSystem())
|
||||
return;
|
||||
|
||||
int pos = get_nozzle_combo_id_code();
|
||||
if (pos < 0)
|
||||
return;
|
||||
|
||||
DevNozzle nozzle = curr_obj->GetNozzleSystem()->GetNozzleByPosId(pos);
|
||||
if (nozzle.IsNormal()) {
|
||||
NozzleVolumeType volume_type = DevNozzle::ToNozzleVolumeType(nozzle.GetNozzleFlowType());
|
||||
m_comboBox_nozzle_type->SetSelection(static_cast<int>(volume_type));
|
||||
}
|
||||
}
|
||||
|
||||
void NewCalibrationHistoryDialog::on_ok(wxCommandEvent &event)
|
||||
{
|
||||
wxString name = m_name_value->GetTextCtrl()->GetValue();
|
||||
@@ -903,6 +1017,7 @@ void NewCalibrationHistoryDialog::on_ok(wxCommandEvent &event)
|
||||
m_new_result.nozzle_diameter = nozzle_value;
|
||||
m_new_result.filament_id = filament_id;
|
||||
m_new_result.setting_id = setting_id;
|
||||
m_new_result.nozzle_pos_id = get_nozzle_combo_id_code();
|
||||
|
||||
// Check for duplicate names from history
|
||||
{
|
||||
|
||||
@@ -27,6 +27,7 @@ protected:
|
||||
void enbale_action_buttons(bool enable);
|
||||
float get_nozzle_value();
|
||||
int get_extruder_id();
|
||||
bool support_nozzle_id_column();
|
||||
|
||||
void on_click_new_button(wxCommandEvent &event);
|
||||
|
||||
@@ -76,10 +77,12 @@ public:
|
||||
protected:
|
||||
virtual void on_ok(wxCommandEvent &event);
|
||||
virtual void on_cancel(wxCommandEvent &event);
|
||||
void on_select_nozzle_pos(wxCommandEvent &event);
|
||||
|
||||
|
||||
wxArrayString get_all_filaments(const MachineObject *obj);
|
||||
int get_extruder_id(int extruder_index); // extruder_index 0 : left, 1 : right
|
||||
int get_nozzle_combo_id_code() const; // rack hotend position code, -1 if none
|
||||
|
||||
protected:
|
||||
PACalibResult m_new_result;
|
||||
@@ -93,6 +96,7 @@ protected:
|
||||
ComboBox *m_comboBox_filament;
|
||||
ComboBox *m_comboBox_extruder;
|
||||
ComboBox *m_comboBox_nozzle_type;
|
||||
ComboBox *m_comboBox_nozzle_id{nullptr};
|
||||
|
||||
struct FilamentInfos
|
||||
{
|
||||
|
||||
@@ -162,7 +162,7 @@ CalibrationDialog::CalibrationDialog(Plater *plater)
|
||||
body_panel->Layout();
|
||||
|
||||
m_sizer_main->Add(body_panel, 0, wxEXPAND | wxALL, FromDIP(25));
|
||||
SetSizer(m_sizer_main);
|
||||
SetSizerAndFit(m_sizer_main);
|
||||
Layout();
|
||||
Fit();
|
||||
|
||||
|
||||
@@ -9,7 +9,9 @@
|
||||
#include "DeviceCore/DevExtruderSystem.h"
|
||||
#include "DeviceCore/DevFilaBlackList.h"
|
||||
#include "DeviceCore/DevFilaSystem.h"
|
||||
#include "DeviceCore/DevFilaSwitch.h"
|
||||
#include "DeviceCore/DevManager.h"
|
||||
#include "DeviceCore/DevNozzleSystem.h"
|
||||
#include "DeviceCore/DevStorage.h"
|
||||
|
||||
#define CALIBRATION_LABEL_SIZE wxSize(FromDIP(150), FromDIP(24))
|
||||
@@ -1457,31 +1459,40 @@ bool CalibrationPresetPage::is_filament_in_blacklist(int tray_id, Preset* preset
|
||||
get_tray_ams_and_slot_id(curr_obj, tray_id, ams_id, slot_id, out_tray_id);
|
||||
|
||||
if (wxGetApp().app_config->get("skip_ams_blacklist_check") != "true") {
|
||||
bool in_blacklist = false;
|
||||
std::string action;
|
||||
wxString info;
|
||||
std::string filamnt_type;
|
||||
preset->get_filament_type(filamnt_type);
|
||||
DevFilaBlacklist::CheckFilamentInfo check_info;
|
||||
check_info.dev_id = curr_obj->get_dev_id();
|
||||
check_info.model_id = curr_obj->printer_type;
|
||||
check_info.fila_id = preset->filament_id;
|
||||
preset->get_filament_type(check_info.fila_type);
|
||||
check_info.ams_id = ams_id;
|
||||
check_info.slot_id = slot_id;
|
||||
check_info.has_filament_switch = curr_obj->GetFilaSwitch()->IsInstalled();
|
||||
// fila_name intentionally left empty: the engine recovers it from the selected AMS slot,
|
||||
// preserving the name-match behavior.
|
||||
|
||||
auto vendor = dynamic_cast<ConfigOptionStrings*> (preset->config.option("filament_vendor"));
|
||||
if (vendor && (vendor->values.size() > 0)) {
|
||||
std::string vendor_name = vendor->values[0];
|
||||
DevFilaBlacklist::check_filaments_in_blacklist(curr_obj->printer_type, vendor_name, filamnt_type, preset->filament_id, ams_id, slot_id, "", in_blacklist, action, info);
|
||||
check_info.fila_vendor = vendor->values[0];
|
||||
}
|
||||
|
||||
if (in_blacklist) {
|
||||
error_tips = info.ToUTF8().data();
|
||||
if (action == "prohibition") {
|
||||
return false;
|
||||
}
|
||||
else if (action == "warning") {
|
||||
return true;
|
||||
}
|
||||
const auto &result = DevFilaBlacklist::check_filaments_in_blacklist(check_info);
|
||||
|
||||
if (const auto &prohibition_items = result.get_items_by_action("prohibition"); !prohibition_items.empty()) {
|
||||
wxString combined_msg;
|
||||
for (const auto &item : prohibition_items) { combined_msg += item.info_msg + "\n"; }
|
||||
error_tips = combined_msg.ToUTF8().data();
|
||||
return false;
|
||||
}
|
||||
else {
|
||||
error_tips = "";
|
||||
|
||||
if (const auto &warning_items = result.get_items_by_action("warning"); !warning_items.empty()) {
|
||||
wxString combined_msg;
|
||||
for (const auto &item : warning_items) { combined_msg += item.info_msg + "\n"; }
|
||||
error_tips = combined_msg.ToUTF8().data();
|
||||
return true;
|
||||
}
|
||||
|
||||
error_tips = "";
|
||||
return true;
|
||||
}
|
||||
if (devPrinterUtil::IsVirtualSlot(ams_id)) {
|
||||
if (m_cali_mode == CalibMode::Calib_PA_Line && (m_cali_method == CalibrationMethod::CALI_METHOD_AUTO || m_cali_method == CalibrationMethod::CALI_METHOD_NEW_AUTO)) {
|
||||
@@ -1663,7 +1674,9 @@ bool CalibrationPresetPage::is_nozzle_info_synced() const
|
||||
if (curr_obj->is_nozzle_flow_type_supported()) {
|
||||
if (extruder.GetNozzleFlowType() == NozzleFlowType::NONE_FLOWTYPE)
|
||||
return false;
|
||||
if (int(extruder.GetNozzleFlowType()) - 1 != int(get_nozzle_volume_type(extruder_id)))
|
||||
// Map device flow -> volume type via DevNozzle::ToNozzleVolumeType so U_FLOW resolves to
|
||||
// nvtTPUHighFlow(3); the naive flow-1 would yield nvtHybrid(2). Identical to flow-1 for S/H flow.
|
||||
if (int(DevNozzle::ToNozzleVolumeType(extruder.GetNozzleFlowType())) != int(get_nozzle_volume_type(extruder_id)))
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -2124,7 +2137,7 @@ void CalibrationPresetPage::init_with_machine(MachineObject* obj)
|
||||
}
|
||||
|
||||
if (obj->GetExtderSystem()->GetNozzleFlowType(i) != NozzleFlowType::NONE_FLOWTYPE) {
|
||||
m_left_comboBox_nozzle_volume->SetSelection(obj->GetExtderSystem()->GetNozzleFlowType(i) - 1);
|
||||
m_left_comboBox_nozzle_volume->SetSelection(int(DevNozzle::ToNozzleVolumeType(obj->GetExtderSystem()->GetNozzleFlowType(i))));
|
||||
} else {
|
||||
m_left_comboBox_nozzle_volume->SetSelection(0);
|
||||
}
|
||||
@@ -2144,7 +2157,7 @@ void CalibrationPresetPage::init_with_machine(MachineObject* obj)
|
||||
}
|
||||
|
||||
if (obj->GetExtderSystem()->GetNozzleFlowType(i) != NozzleFlowType::NONE_FLOWTYPE) {
|
||||
m_right_comboBox_nozzle_volume->SetSelection(obj->GetExtderSystem()->GetNozzleFlowType(i) - 1);
|
||||
m_right_comboBox_nozzle_volume->SetSelection(int(DevNozzle::ToNozzleVolumeType(obj->GetExtderSystem()->GetNozzleFlowType(i))));
|
||||
} else {
|
||||
m_right_comboBox_nozzle_volume->SetSelection(0);
|
||||
}
|
||||
@@ -2182,7 +2195,7 @@ void CalibrationPresetPage::init_with_machine(MachineObject* obj)
|
||||
else {
|
||||
if ((obj->GetExtderSystem()->GetTotalExtderCount() > 0) && (obj->GetExtderSystem()->GetNozzleFlowType(0) != NozzleFlowType::NONE_FLOWTYPE))
|
||||
{
|
||||
m_comboBox_nozzle_volume->SetSelection(obj->GetExtderSystem()->GetNozzleFlowType(0) - 1);
|
||||
m_comboBox_nozzle_volume->SetSelection(int(DevNozzle::ToNozzleVolumeType(obj->GetExtderSystem()->GetNozzleFlowType(0))));
|
||||
} else {
|
||||
m_comboBox_nozzle_volume->SetSelection(0);
|
||||
}
|
||||
|
||||
@@ -2,6 +2,9 @@
|
||||
#include "I18N.hpp"
|
||||
#include "Widgets/Label.hpp"
|
||||
#include "MsgDialog.hpp"
|
||||
#include "DeviceCore/DevConfigUtil.h"
|
||||
#include "DeviceCore/DevNozzleSystem.h"
|
||||
#include "DeviceCore/DevNozzleRack.h"
|
||||
|
||||
|
||||
namespace Slic3r { namespace GUI {
|
||||
@@ -95,35 +98,43 @@ enum class GridTextInputType {
|
||||
class GridTextInput : public TextInput
|
||||
{
|
||||
public:
|
||||
GridTextInput(wxWindow* parent, wxString text, wxString label, wxSize size, int col_idx, GridTextInputType type);
|
||||
GridTextInput(wxWindow* parent, wxString text, wxString label, wxSize size, int col_idx, GridTextInputType type, int extruder_id = MAIN_EXTRUDER_ID);
|
||||
int get_col_idx() { return m_col_idx; }
|
||||
void set_col_idx(int idx) { m_col_idx = idx; }
|
||||
int get_extruder_id() { return m_extruder_id; }
|
||||
void set_extruder_id(int id) { m_extruder_id = id; }
|
||||
GridTextInputType get_type() { return m_type; }
|
||||
void set_type(GridTextInputType type) { m_type = type; }
|
||||
private:
|
||||
int m_col_idx;
|
||||
int m_extruder_id{MAIN_EXTRUDER_ID};
|
||||
GridTextInputType m_type;
|
||||
};
|
||||
|
||||
GridTextInput::GridTextInput(wxWindow* parent, wxString text, wxString label, wxSize size, int col_idx, GridTextInputType type)
|
||||
GridTextInput::GridTextInput(wxWindow* parent, wxString text, wxString label, wxSize size, int col_idx, GridTextInputType type, int extruder_id)
|
||||
: TextInput(parent, text, label, "", wxDefaultPosition, size, wxTE_PROCESS_ENTER)
|
||||
, m_col_idx(col_idx)
|
||||
, m_extruder_id(extruder_id)
|
||||
, m_type(type)
|
||||
{
|
||||
}
|
||||
|
||||
class GridComboBox : public ComboBox {
|
||||
public:
|
||||
GridComboBox(wxWindow* parent, wxSize size, int col_idx);
|
||||
GridComboBox(wxWindow* parent, wxSize size, int col_idx, int extruder_id = MAIN_EXTRUDER_ID);
|
||||
int get_col_idx() { return m_col_idx; }
|
||||
void set_col_idx(int idx) { m_col_idx = idx; }
|
||||
int get_extruder_id() { return m_extruder_id; }
|
||||
void set_extruder_id(int id) { m_extruder_id = id; }
|
||||
private:
|
||||
int m_col_idx;
|
||||
int m_extruder_id{MAIN_EXTRUDER_ID};
|
||||
};
|
||||
|
||||
GridComboBox::GridComboBox(wxWindow* parent, wxSize size, int col_idx)
|
||||
GridComboBox::GridComboBox(wxWindow* parent, wxSize size, int col_idx, int extruder_id)
|
||||
: ComboBox(parent, wxID_ANY, "", wxDefaultPosition, size, 0, nullptr)
|
||||
, m_col_idx(col_idx)
|
||||
, m_extruder_id(extruder_id)
|
||||
{
|
||||
|
||||
}
|
||||
@@ -227,7 +238,7 @@ void CaliPASaveAutoPanel::sync_cali_result(const std::vector<PACalibResult>& cal
|
||||
m_calib_results.clear();
|
||||
for (auto& item : cali_result) {
|
||||
if (item.confidence == 0)
|
||||
m_calib_results[item.tray_id] = item;
|
||||
m_calib_results.push_back(item);
|
||||
}
|
||||
m_grid_panel->DestroyChildren();
|
||||
auto grid_sizer = new wxBoxSizer(wxHORIZONTAL);
|
||||
@@ -281,14 +292,14 @@ void CaliPASaveAutoPanel::sync_cali_result(const std::vector<PACalibResult>& cal
|
||||
wxString tray_name = get_tray_name_by_tray_id(item.tray_id);
|
||||
tray_title->SetLabel(tray_name);
|
||||
|
||||
auto k_value = new GridTextInput(m_grid_panel, "", "", CALIBRATION_SAVE_INPUT_SIZE, item.tray_id, GridTextInputType::K);
|
||||
auto n_value = new GridTextInput(m_grid_panel, "", "", CALIBRATION_SAVE_INPUT_SIZE, item.tray_id, GridTextInputType::N);
|
||||
auto k_value = new GridTextInput(m_grid_panel, "", "", CALIBRATION_SAVE_INPUT_SIZE, item.tray_id, GridTextInputType::K, item.extruder_id);
|
||||
auto n_value = new GridTextInput(m_grid_panel, "", "", CALIBRATION_SAVE_INPUT_SIZE, item.tray_id, GridTextInputType::N, item.extruder_id);
|
||||
k_value->GetTextCtrl()->SetValidator(wxTextValidator(wxFILTER_NUMERIC));
|
||||
n_value->GetTextCtrl()->SetValidator(wxTextValidator(wxFILTER_NUMERIC));
|
||||
auto k_value_failed = new Label(m_grid_panel, _L("Failed"));
|
||||
auto n_value_failed = new Label(m_grid_panel, _L("Failed"));
|
||||
|
||||
auto comboBox_tray_name = new GridComboBox(m_grid_panel, CALIBRATION_SAVE_INPUT_SIZE, item.tray_id);
|
||||
auto comboBox_tray_name = new GridComboBox(m_grid_panel, CALIBRATION_SAVE_INPUT_SIZE, item.tray_id, item.extruder_id);
|
||||
auto tray_name_failed = new Label(m_grid_panel, " - ");
|
||||
wxArrayString selections;
|
||||
static std::vector<PACalibResult> filtered_results;
|
||||
@@ -395,17 +406,26 @@ void CaliPASaveAutoPanel::save_to_result_from_widgets(wxWindow* window, bool* ou
|
||||
return;
|
||||
|
||||
//operate
|
||||
// The grid can hold two rows with the same tray_id under different extruders
|
||||
// (rack / dual-address configs), so results must be keyed by (tray_id, extruder_id).
|
||||
auto find_result = [this](int tray_id, int extruder_id) -> PACalibResult* {
|
||||
auto it = std::find_if(m_calib_results.begin(), m_calib_results.end(), [tray_id, extruder_id](const PACalibResult& r) {
|
||||
return r.tray_id == tray_id && r.extruder_id == extruder_id;
|
||||
});
|
||||
return it != m_calib_results.end() ? &(*it) : nullptr;
|
||||
};
|
||||
auto input = dynamic_cast<GridTextInput*>(window);
|
||||
if (input && input->IsShown()) {
|
||||
int tray_id = input->get_col_idx();
|
||||
int tray_id = input->get_col_idx();
|
||||
int extruder_id = input->get_extruder_id();
|
||||
if (input->get_type() == GridTextInputType::K) {
|
||||
float k = 0.0f;
|
||||
if (!CalibUtils::validate_input_k_value(input->GetTextCtrl()->GetValue(), &k)) {
|
||||
*out_msg = wxString::Format(_L("Please input a valid value (K in %.1f~%.1f)"), MIN_PA_K_VALUE, MAX_PA_K_VALUE);
|
||||
*out_is_valid = false;
|
||||
}
|
||||
else
|
||||
m_calib_results[tray_id].k_value = k;
|
||||
else if (auto* result = find_result(tray_id, extruder_id))
|
||||
result->k_value = k;
|
||||
}
|
||||
else if (input->get_type() == GridTextInputType::N) {
|
||||
}
|
||||
@@ -413,7 +433,8 @@ void CaliPASaveAutoPanel::save_to_result_from_widgets(wxWindow* window, bool* ou
|
||||
|
||||
auto comboBox = dynamic_cast<GridComboBox*>(window);
|
||||
if (comboBox && comboBox->IsShown()) {
|
||||
int tray_id = comboBox->get_col_idx();
|
||||
int tray_id = comboBox->get_col_idx();
|
||||
int extruder_id = comboBox->get_extruder_id();
|
||||
wxString name = comboBox->GetTextCtrl()->GetValue().ToStdString();
|
||||
if (name.IsEmpty()) {
|
||||
*out_msg = _L("Please enter the name you want to save to printer.");
|
||||
@@ -423,7 +444,8 @@ void CaliPASaveAutoPanel::save_to_result_from_widgets(wxWindow* window, bool* ou
|
||||
*out_msg = _L("The name cannot exceed 40 characters.");
|
||||
*out_is_valid = false;
|
||||
}
|
||||
m_calib_results[tray_id].name = into_u8(name);
|
||||
if (auto* result = find_result(tray_id, extruder_id))
|
||||
result->name = into_u8(name);
|
||||
}
|
||||
|
||||
auto childern = window->GetChildren();
|
||||
@@ -441,52 +463,7 @@ bool CaliPASaveAutoPanel::get_result(std::vector<PACalibResult>& out_result) {
|
||||
else
|
||||
save_to_result_from_widgets(m_grid_panel, &is_valid, &err_msg);
|
||||
if (is_valid) {
|
||||
/*
|
||||
std::vector<PACalibResult> to_save_result;
|
||||
for (auto &result : m_calib_results) {
|
||||
auto iter = std::find_if(to_save_result.begin(), to_save_result.end(), [this, &result](const PACalibResult &item) {
|
||||
bool has_same_name = (item.name == result.second.name && item.filament_id == result.second.filament_id);
|
||||
if (m_obj && m_obj->is_multi_extruders()) {
|
||||
has_same_name &= (item.extruder_id == result.second.extruder_id && item.nozzle_volume_type == result.second.nozzle_volume_type);
|
||||
}
|
||||
return has_same_name;
|
||||
});
|
||||
|
||||
if (iter != to_save_result.end()) {
|
||||
MessageDialog msg_dlg(nullptr, _L("Only one of the results with the same name will be saved. Are you sure you want to overwrite the other results?"),
|
||||
wxEmptyString, wxICON_WARNING | wxYES_NO);
|
||||
if (msg_dlg.ShowModal() != wxID_YES) {
|
||||
return false;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (auto &result : m_history_results) {
|
||||
auto iter = std::find_if(m_history_results.begin(), m_history_results.end(), [this, &result](const PACalibResult &item) {
|
||||
bool has_same_name = (item.name == result.name && item.filament_id == result.filament_id);
|
||||
if (m_obj && m_obj->is_multi_extruders()) {
|
||||
has_same_name &= (item.extruder_id == result.extruder_id && item.nozzle_volume_type == result.nozzle_volume_type);
|
||||
}
|
||||
return has_same_name;
|
||||
});
|
||||
|
||||
if (iter != m_history_results.end()) {
|
||||
MessageDialog msg_dlg(nullptr,
|
||||
wxString::Format(_L("There is already a historical calibration result with the same name: %s. Are you sure you want to override the historical result?"),
|
||||
result.name),
|
||||
wxEmptyString, wxICON_WARNING | wxYES_NO);
|
||||
if (msg_dlg.ShowModal() != wxID_YES) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
for (auto& result : m_calib_results) {
|
||||
out_result.push_back(result.second);
|
||||
}
|
||||
out_result = m_calib_results;
|
||||
return true;
|
||||
}
|
||||
else {
|
||||
@@ -517,11 +494,12 @@ void CaliPASaveAutoPanel::sync_cali_result_for_multi_extruder(const std::vector<
|
||||
m_calib_results.clear();
|
||||
for (auto &item : cali_result) {
|
||||
if (item.confidence == 0) {
|
||||
int tray_id = 4 * item.ams_id + item.slot_id;
|
||||
PACalibResult result = item;
|
||||
result.tray_id = 4 * item.ams_id + item.slot_id;
|
||||
if (item.ams_id == VIRTUAL_TRAY_MAIN_ID || item.ams_id == VIRTUAL_TRAY_DEPUTY_ID) {
|
||||
tray_id = item.ams_id;
|
||||
result.tray_id = item.ams_id;
|
||||
}
|
||||
m_calib_results[tray_id] = item;
|
||||
m_calib_results.push_back(result);
|
||||
}
|
||||
}
|
||||
m_multi_extruder_grid_panel->DestroyChildren();
|
||||
@@ -532,14 +510,21 @@ void CaliPASaveAutoPanel::sync_cali_result_for_multi_extruder(const std::vector<
|
||||
m_multi_extruder_grid_panel->SetSizer(grid_sizer, true);
|
||||
m_multi_extruder_grid_panel->Bind(wxEVT_LEFT_DOWN, [this](auto &e) { SetFocusIgnoringChildren(); });
|
||||
|
||||
wxStaticBoxSizer *left_sizer = new wxStaticBoxSizer(wxVERTICAL, m_multi_extruder_grid_panel, _L("Left extruder"));
|
||||
wxStaticBoxSizer *right_sizer = new wxStaticBoxSizer(wxVERTICAL, m_multi_extruder_grid_panel, _L("Right extruder"));
|
||||
const std::string& cwsp_pt = m_obj->printer_type;
|
||||
wxStaticBoxSizer *left_sizer = new wxStaticBoxSizer(wxVERTICAL, m_multi_extruder_grid_panel, _L(DevPrinterConfigUtil::get_toolhead_display_name(cwsp_pt, DEPUTY_EXTRUDER_ID, ToolHeadComponent::Extruder, ToolHeadNameCase::SentenceCase)));
|
||||
wxStaticBoxSizer *right_sizer = new wxStaticBoxSizer(wxVERTICAL, m_multi_extruder_grid_panel, _L(DevPrinterConfigUtil::get_toolhead_display_name(cwsp_pt, MAIN_EXTRUDER_ID, ToolHeadComponent::Extruder, ToolHeadNameCase::SentenceCase)));
|
||||
grid_sizer->Add(left_sizer);
|
||||
grid_sizer->AddSpacer(COLUMN_GAP);
|
||||
grid_sizer->Add(right_sizer);
|
||||
|
||||
wxFlexGridSizer *left_grid_sizer = new wxFlexGridSizer(3, COLUMN_GAP, ROW_GAP);
|
||||
wxFlexGridSizer *right_grid_sizer = new wxFlexGridSizer(3, COLUMN_GAP, ROW_GAP);
|
||||
|
||||
// The main extruder can carry a nozzle rack (H2C); non-rack printers keep the 3-column layout.
|
||||
auto ns = m_obj->GetNozzleSystem();
|
||||
auto rack = ns ? ns->GetNozzleRack() : nullptr;
|
||||
bool has_rack = rack && rack->IsSupported();
|
||||
|
||||
wxFlexGridSizer *right_grid_sizer = new wxFlexGridSizer(has_rack ? 4 : 3, COLUMN_GAP, ROW_GAP);
|
||||
left_sizer->Add(left_grid_sizer);
|
||||
right_sizer->Add(right_grid_sizer);
|
||||
|
||||
@@ -567,29 +552,46 @@ void CaliPASaveAutoPanel::sync_cali_result_for_multi_extruder(const std::vector<
|
||||
auto k_title = new Label(m_multi_extruder_grid_panel, _L("Factor K"), 0, CALIBRATION_SAVE_NUMBER_INPUT_SIZE);
|
||||
k_title->SetFont(Label::Head_14);
|
||||
right_grid_sizer->Add(k_title, 1, wxALIGN_CENTER);
|
||||
|
||||
if (has_rack) {
|
||||
auto nozzle_title = new Label(m_multi_extruder_grid_panel, _L("Nozzle ID"), 0, CALIBRATION_SAVE_NUMBER_INPUT_SIZE);
|
||||
nozzle_title->SetFont(Label::Head_14);
|
||||
right_grid_sizer->Add(nozzle_title, 1, wxALIGN_CENTER);
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<std::pair<int, std::string>> preset_names;
|
||||
// key: (extruder_id, tray_id)
|
||||
std::vector<std::pair<std::pair<int, int>, std::string>> preset_names;
|
||||
int i = 1;
|
||||
std::unordered_set<std::string> set;
|
||||
for (auto &info : m_obj->selected_cali_preset) {
|
||||
std::string default_name;
|
||||
// extruder _id
|
||||
// Derive extruder_id from the tray so the extruder/tray key stays consistent
|
||||
// with the calibration result's extruder_id.
|
||||
int extruder_id = 0;
|
||||
if (info.tray_id == VIRTUAL_TRAY_MAIN_ID) {
|
||||
extruder_id = 0;
|
||||
} else if (info.tray_id == VIRTUAL_TRAY_DEPUTY_ID) {
|
||||
extruder_id = 1;
|
||||
} else {
|
||||
int ams_id = info.tray_id / 4;
|
||||
extruder_id = m_obj->get_extruder_id_by_ams_id(std::to_string(ams_id));
|
||||
}
|
||||
{
|
||||
int extruder_id = 0;
|
||||
if (info.tray_id == VIRTUAL_TRAY_MAIN_ID) {
|
||||
extruder_id = 0;
|
||||
} else if (info.tray_id == VIRTUAL_TRAY_DEPUTY_ID) {
|
||||
extruder_id = 1;
|
||||
} else {
|
||||
int ams_id = info.tray_id / 4;
|
||||
extruder_id = m_obj->get_extruder_id_by_ams_id(std::to_string(ams_id));
|
||||
}
|
||||
|
||||
if (extruder_id == 0) {
|
||||
default_name += L("Right Nozzle");
|
||||
} else if (extruder_id == 1){
|
||||
default_name += L("Left Nozzle");
|
||||
if (extruder_id == MAIN_EXTRUDER_ID) {
|
||||
default_name += DevPrinterConfigUtil::get_toolhead_display_name(cwsp_pt, MAIN_EXTRUDER_ID, ToolHeadComponent::Nozzle, ToolHeadNameCase::TitleCase);
|
||||
if (has_rack) {
|
||||
default_name += "_";
|
||||
if (info.nozzle_pos_id == 0) {
|
||||
default_name += "R";
|
||||
} else if (info.nozzle_pos_id >= 0x10) {
|
||||
default_name += std::to_string((info.nozzle_pos_id & 0x0f) + 1);
|
||||
} else {
|
||||
default_name += "N/A";
|
||||
}
|
||||
}
|
||||
} else if (extruder_id == DEPUTY_EXTRUDER_ID) {
|
||||
default_name += DevPrinterConfigUtil::get_toolhead_display_name(cwsp_pt, DEPUTY_EXTRUDER_ID, ToolHeadComponent::Nozzle, ToolHeadNameCase::TitleCase);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -614,7 +616,7 @@ void CaliPASaveAutoPanel::sync_cali_result_for_multi_extruder(const std::vector<
|
||||
}
|
||||
}
|
||||
|
||||
preset_names.push_back({info.tray_id, default_name});
|
||||
preset_names.push_back({{extruder_id, info.tray_id}, default_name});
|
||||
}
|
||||
|
||||
bool left_first_add_item = true;
|
||||
@@ -648,14 +650,19 @@ void CaliPASaveAutoPanel::sync_cali_result_for_multi_extruder(const std::vector<
|
||||
tray_title->SetBitmap(*get_extruder_color_icon(full_filament_ams_list[item.tray_id].opt_string("filament_colour", 0u), tray_name.ToStdString(), FromDIP(20), FromDIP(20)));
|
||||
tray_title->SetToolTip("");
|
||||
|
||||
auto k_value = new GridTextInput(m_multi_extruder_grid_panel, "", "", CALIBRATION_SAVE_NUMBER_INPUT_SIZE, item.tray_id, GridTextInputType::K);
|
||||
auto n_value = new GridTextInput(m_multi_extruder_grid_panel, "", "", CALIBRATION_SAVE_NUMBER_INPUT_SIZE, item.tray_id, GridTextInputType::N);
|
||||
auto k_value = new GridTextInput(m_multi_extruder_grid_panel, "", "", CALIBRATION_SAVE_NUMBER_INPUT_SIZE, item.tray_id, GridTextInputType::K, item.extruder_id);
|
||||
auto n_value = new GridTextInput(m_multi_extruder_grid_panel, "", "", CALIBRATION_SAVE_NUMBER_INPUT_SIZE, item.tray_id, GridTextInputType::N, item.extruder_id);
|
||||
k_value->GetTextCtrl()->SetValidator(wxTextValidator(wxFILTER_NUMERIC));
|
||||
n_value->GetTextCtrl()->SetValidator(wxTextValidator(wxFILTER_NUMERIC));
|
||||
auto k_value_failed = new Label(m_multi_extruder_grid_panel, _L("Failed"));
|
||||
auto n_value_failed = new Label(m_multi_extruder_grid_panel, _L("Failed"));
|
||||
|
||||
auto comboBox_tray_name = new GridComboBox(m_multi_extruder_grid_panel, CALIBRATION_SAVE_INPUT_SIZE, item.tray_id);
|
||||
auto nozzle_id_value = new Label(m_multi_extruder_grid_panel, wxEmptyString);
|
||||
nozzle_id_value->SetMinSize(wxSize(FromDIP(180), FromDIP(24)));
|
||||
nozzle_id_value->Wrap(-1);
|
||||
auto nozzle_id_failed = new Label(m_multi_extruder_grid_panel, _L("Failed"));
|
||||
|
||||
auto comboBox_tray_name = new GridComboBox(m_multi_extruder_grid_panel, CALIBRATION_SAVE_INPUT_SIZE, item.tray_id, item.extruder_id);
|
||||
auto tray_name_failed = new Label(m_multi_extruder_grid_panel, " - ");
|
||||
wxArrayString selections;
|
||||
static std::vector<PACalibResult> filtered_results;
|
||||
@@ -671,22 +678,26 @@ void CaliPASaveAutoPanel::sync_cali_result_for_multi_extruder(const std::vector<
|
||||
}
|
||||
comboBox_tray_name->Set(selections);
|
||||
|
||||
auto set_edit_mode = [this, k_value, n_value, k_value_failed, n_value_failed, comboBox_tray_name, tray_name_failed](std::string str) {
|
||||
auto set_edit_mode = [this, k_value, n_value, k_value_failed, n_value_failed, nozzle_id_value, nozzle_id_failed, comboBox_tray_name, tray_name_failed](std::string str, bool display_nozzle) {
|
||||
if (str == "normal") {
|
||||
comboBox_tray_name->Show();
|
||||
tray_name_failed->Show(false);
|
||||
k_value->Show();
|
||||
n_value->Show();
|
||||
nozzle_id_value->Show(display_nozzle);
|
||||
k_value_failed->Show(false);
|
||||
n_value_failed->Show(false);
|
||||
nozzle_id_failed->Show(false);
|
||||
}
|
||||
if (str == "failed") {
|
||||
comboBox_tray_name->Show(false);
|
||||
tray_name_failed->Show();
|
||||
k_value->Show(false);
|
||||
n_value->Show(false);
|
||||
nozzle_id_value->Show(false);
|
||||
k_value_failed->Show();
|
||||
n_value_failed->Show();
|
||||
nozzle_id_failed->Show(display_nozzle);
|
||||
}
|
||||
|
||||
// hide n value
|
||||
@@ -698,15 +709,39 @@ void CaliPASaveAutoPanel::sync_cali_result_for_multi_extruder(const std::vector<
|
||||
};
|
||||
|
||||
if (!result_failed) {
|
||||
set_edit_mode("normal");
|
||||
set_edit_mode("normal", has_rack && item.extruder_id == MAIN_EXTRUDER_ID);
|
||||
|
||||
auto k_str = wxString::Format("%.3f", item.k_value);
|
||||
auto n_str = wxString::Format("%.3f", item.n_coef);
|
||||
k_value->GetTextCtrl()->SetValue(k_str);
|
||||
n_value->GetTextCtrl()->SetValue(n_str);
|
||||
|
||||
if (has_rack && item.extruder_id == MAIN_EXTRUDER_ID) {
|
||||
wxString nozzle_id_str;
|
||||
if (item.nozzle_pos_id == 0) {
|
||||
nozzle_id_str += "R | ";
|
||||
} else if (item.nozzle_pos_id >= 0x10) {
|
||||
nozzle_id_str += wxString::Format("%d | ", (item.nozzle_pos_id & 0x0f) + 1);
|
||||
} else {
|
||||
nozzle_id_str += "N/A | ";
|
||||
BOOST_LOG_TRIVIAL(warning) << __FUNCTION__ << "Nozzle position id is -1 or invalid.";
|
||||
}
|
||||
nozzle_id_str += wxString::Format("%.1f mm ", item.nozzle_diameter);
|
||||
switch (item.nozzle_volume_type) {
|
||||
case NozzleVolumeType::nvtStandard: nozzle_id_str += _L("Standard Flow"); break;
|
||||
case NozzleVolumeType::nvtHighFlow: nozzle_id_str += _L("High Flow"); break;
|
||||
case NozzleVolumeType::nvtTPUHighFlow: nozzle_id_str += _L("TPU High Flow"); break;
|
||||
default: break;
|
||||
}
|
||||
nozzle_id_value->SetLabel(nozzle_id_str);
|
||||
}
|
||||
|
||||
for (auto &name : preset_names) {
|
||||
if (item.tray_id == name.first) { comboBox_tray_name->SetValue(from_u8(name.second)); }
|
||||
int extruder_id = name.first.first;
|
||||
int tray_id = name.first.second;
|
||||
if (item.tray_id == tray_id && item.extruder_id == extruder_id) {
|
||||
comboBox_tray_name->SetValue(from_u8(name.second));
|
||||
}
|
||||
}
|
||||
|
||||
comboBox_tray_name->Bind(wxEVT_COMBOBOX, [this, comboBox_tray_name, k_value, n_value](auto &e) {
|
||||
@@ -714,7 +749,7 @@ void CaliPASaveAutoPanel::sync_cali_result_for_multi_extruder(const std::vector<
|
||||
auto history = filtered_results[selection];
|
||||
});
|
||||
} else {
|
||||
set_edit_mode("failed");
|
||||
set_edit_mode("failed", has_rack && item.extruder_id == MAIN_EXTRUDER_ID);
|
||||
}
|
||||
|
||||
if ((m_obj->is_main_extruder_on_left() && item.extruder_id == 0)
|
||||
@@ -762,6 +797,14 @@ void CaliPASaveAutoPanel::sync_cali_result_for_multi_extruder(const std::vector<
|
||||
} else {
|
||||
right_grid_sizer->Add(k_value_failed, 1, wxEXPAND);
|
||||
}
|
||||
|
||||
if (has_rack) {
|
||||
if (nozzle_id_value->IsShown()) {
|
||||
right_grid_sizer->Add(nozzle_id_value, 1, wxEXPAND);
|
||||
} else {
|
||||
right_grid_sizer->Add(nozzle_id_failed, 1, wxEXPAND);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -107,7 +107,10 @@ protected:
|
||||
wxPanel* m_part_failed_panel;
|
||||
wxPanel* m_grid_panel{ nullptr };
|
||||
wxPanel* m_multi_extruder_grid_panel{ nullptr };
|
||||
std::map<int, PACalibResult> m_calib_results;// map<tray_id, PACalibResult>
|
||||
// keyed by (tray_id, extruder_id): the same tray_id can appear under two
|
||||
// extruders (rack / dual-address configs), so a tray_id-only map would
|
||||
// silently overwrite one extruder's K/N.
|
||||
std::vector<PACalibResult> m_calib_results;
|
||||
std::vector<PACalibResult> m_history_results;
|
||||
bool m_is_all_failed{ true };
|
||||
MachineObject* m_obj{ nullptr };
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -112,9 +112,8 @@ CloneDialog::CloneDialog(wxWindow *parent)
|
||||
|
||||
v_sizer->Add(bottom_sizer, 0, wxEXPAND);
|
||||
|
||||
this->SetSizer(v_sizer);
|
||||
this->SetSizerAndFit(v_sizer);
|
||||
this->Layout();
|
||||
v_sizer->Fit(this);
|
||||
|
||||
wxGetApp().UpdateDlgDarkUI(this);
|
||||
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
#include "libslic3r/GCode/AdaptivePAProcessor.hpp"
|
||||
#include "Plater.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
#include <sstream>
|
||||
#include <wx/msgdlg.h>
|
||||
|
||||
@@ -70,6 +71,12 @@ void ConfigManipulation::toggle_line(const std::string& opt_key, const bool togg
|
||||
cb_toggle_line(opt_key, toggle, opt_index);
|
||||
}
|
||||
|
||||
void ConfigManipulation::set_option_label(const std::string& opt_key, const wxString& label, int opt_index)
|
||||
{
|
||||
if (cb_set_option_label)
|
||||
cb_set_option_label(opt_key, label, opt_index);
|
||||
}
|
||||
|
||||
void ConfigManipulation::check_nozzle_recommended_temperature_range(DynamicPrintConfig *config) {
|
||||
if (is_msg_dlg_already_exist)
|
||||
return;
|
||||
@@ -244,6 +251,59 @@ void ConfigManipulation::check_chamber_minimal_temperature(DynamicPrintConfig* c
|
||||
}
|
||||
}
|
||||
|
||||
void ConfigManipulation::layer_height_limits(double& min_layer_height, double& max_layer_height) const
|
||||
{
|
||||
const DynamicPrintConfig& printer_config = GUI::wxGetApp().preset_bundle->printers.get_edited_preset().config;
|
||||
const std::vector<double>& min_limits = printer_config.option<ConfigOptionFloats>("min_layer_height")->values;
|
||||
const std::vector<double>& max_limits = printer_config.option<ConfigOptionFloats>("max_layer_height")->values;
|
||||
min_layer_height = *std::min_element(min_limits.begin(), min_limits.end());
|
||||
max_layer_height = *std::max_element(max_limits.begin(), max_limits.end());
|
||||
}
|
||||
|
||||
bool ConfigManipulation::check_layer_height(DynamicPrintConfig* config)
|
||||
{
|
||||
double min_layer_height = 0., max_layer_height = 0.;
|
||||
layer_height_limits(min_layer_height, max_layer_height);
|
||||
const double layer_height = config->opt_float("layer_height");
|
||||
|
||||
if (min_layer_height > EPSILON && layer_height < EPSILON) {
|
||||
const wxString msg_text = wxString::Format(_L("Layer height is too small. It will be set to the minimum (%g mm)."), min_layer_height);
|
||||
MessageDialog dialog(wxGetApp().plater(), msg_text, "", wxICON_WARNING | wxOK);
|
||||
dialog.SetButtonLabel(wxID_OK, _L("OK"));
|
||||
is_msg_dlg_already_exist = true;
|
||||
dialog.ShowModal();
|
||||
is_msg_dlg_already_exist = false;
|
||||
DynamicPrintConfig new_conf = *config;
|
||||
new_conf.set_key_value("layer_height", new ConfigOptionFloat(min_layer_height));
|
||||
apply(config, &new_conf);
|
||||
return true;
|
||||
}
|
||||
if (max_layer_height > EPSILON && layer_height > max_layer_height + EPSILON)
|
||||
return layer_height_out_of_range_dialog(config, max_layer_height);
|
||||
if (min_layer_height > EPSILON && layer_height < min_layer_height - EPSILON)
|
||||
return layer_height_out_of_range_dialog(config, min_layer_height);
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ConfigManipulation::layer_height_out_of_range_dialog(DynamicPrintConfig* config, double clamp_to)
|
||||
{
|
||||
wxString msg_text = _(L("Layer height is outside the limits set in Printer Settings -> Extruder -> Layer height limits, "
|
||||
"this may cause printing quality issues."));
|
||||
msg_text += "\n\n" + wxString::Format(_L("Adjust it to the limit (%g mm) automatically?"), clamp_to);
|
||||
MessageDialog dialog(wxGetApp().plater(), msg_text, "", wxICON_WARNING | wxYES | wxNO);
|
||||
dialog.SetButtonLabel(wxID_YES, _L("Adjust"));
|
||||
dialog.SetButtonLabel(wxID_NO, _L("Ignore"));
|
||||
is_msg_dlg_already_exist = true;
|
||||
const bool adjust = dialog.ShowModal() == wxID_YES;
|
||||
if (adjust) {
|
||||
DynamicPrintConfig new_conf = *config;
|
||||
new_conf.set_key_value("layer_height", new ConfigOptionFloat(clamp_to));
|
||||
apply(config, &new_conf);
|
||||
}
|
||||
is_msg_dlg_already_exist = false;
|
||||
return adjust;
|
||||
}
|
||||
|
||||
void ConfigManipulation::update_print_fff_config(DynamicPrintConfig* config, const bool is_global_config, const bool is_plate_config)
|
||||
{
|
||||
// #ys_FIXME_to_delete
|
||||
@@ -258,7 +318,6 @@ void ConfigManipulation::update_print_fff_config(DynamicPrintConfig* config, con
|
||||
|
||||
// layer_height shouldn't be equal to zero
|
||||
auto layer_height = config->opt_float("layer_height");
|
||||
auto gpreset = GUI::wxGetApp().preset_bundle->printers.get_edited_preset();
|
||||
if (layer_height < EPSILON)
|
||||
{
|
||||
const wxString msg_text = _(L("Layer height too small\nIt has been reset to 0.2"));
|
||||
@@ -271,20 +330,6 @@ void ConfigManipulation::update_print_fff_config(DynamicPrintConfig* config, con
|
||||
is_msg_dlg_already_exist = false;
|
||||
}
|
||||
|
||||
//BBS: limite the max layer_herght
|
||||
auto max_lh = gpreset.config.opt_float("max_layer_height",0);
|
||||
if (max_lh > 0.2 && layer_height > max_lh+ EPSILON)
|
||||
{
|
||||
const wxString msg_text = wxString::Format(L"Too large layer height.\nReset to %0.3f.", max_lh);
|
||||
MessageDialog dialog(nullptr, msg_text, "", wxICON_WARNING | wxOK);
|
||||
DynamicPrintConfig new_conf = *config;
|
||||
is_msg_dlg_already_exist = true;
|
||||
dialog.ShowModal();
|
||||
new_conf.set_key_value("layer_height", new ConfigOptionFloat(max_lh));
|
||||
apply(config, &new_conf);
|
||||
is_msg_dlg_already_exist = false;
|
||||
}
|
||||
|
||||
//BBS: ironing_spacing shouldn't be too small or equal to zero
|
||||
if (config->opt_float("ironing_spacing") < 0.05)
|
||||
{
|
||||
@@ -703,16 +748,48 @@ void ConfigManipulation::toggle_print_fff_options(DynamicPrintConfig *config, in
|
||||
toggle_line("spiral_mode_max_xy_smoothing", has_spiral_vase && config->opt_bool("spiral_mode_smooth"));
|
||||
toggle_line("spiral_starting_flow_ratio", has_spiral_vase);
|
||||
toggle_line("spiral_finishing_flow_ratio", has_spiral_vase);
|
||||
bool has_top_shell = config->opt_int("top_shell_layers") > 0 || (has_spiral_vase && config->opt_int("bottom_shell_layers") > 1);
|
||||
bool has_top_shell_layers = config->opt_int("top_shell_layers") > 0 || (has_spiral_vase && config->opt_int("bottom_shell_layers") > 1);
|
||||
bool has_top_shell = has_top_shell_layers && config->option<ConfigOptionPercent>("top_surface_density")->value > 0;
|
||||
bool has_bottom_shell = config->opt_int("bottom_shell_layers") > 0;
|
||||
bool has_solid_infill = has_top_shell || has_bottom_shell;
|
||||
bool has_solid_infill = has_top_shell_layers || has_bottom_shell;
|
||||
toggle_line("sparse_infill_smooth_factor", pattern == ipHilbertCurve);
|
||||
toggle_field("top_surface_pattern", has_top_shell);
|
||||
toggle_field("bottom_surface_pattern", has_bottom_shell);
|
||||
toggle_field("top_surface_density", has_top_shell);
|
||||
toggle_field("top_surface_density", has_top_shell_layers);
|
||||
toggle_field("bottom_surface_density", has_bottom_shell);
|
||||
toggle_field("top_layer_direction", has_top_shell);
|
||||
toggle_field("bottom_layer_direction", has_bottom_shell);
|
||||
|
||||
toggle_line("top_surface_expansion", has_top_shell);
|
||||
toggle_line("top_surface_expansion_margin", has_top_shell);
|
||||
bool has_top_surface_expansion = config->opt_float("top_surface_expansion") > 0;
|
||||
toggle_field("top_surface_expansion_margin", has_top_surface_expansion);
|
||||
toggle_line("top_surface_expansion_direction", has_top_shell);
|
||||
toggle_field("top_surface_expansion_direction", has_top_surface_expansion);
|
||||
|
||||
// Orca: Archimedean Chords and Octagram Spiral are the centered surface patterns that the
|
||||
// pattern-centering feature acts on.
|
||||
auto is_centered_pattern = [](InfillPattern p) {
|
||||
return p == InfillPattern::ipArchimedeanChords || p == InfillPattern::ipOctagramSpiral;
|
||||
};
|
||||
bool is_top_centered = is_centered_pattern(config->option<ConfigOptionEnum<InfillPattern>>("top_surface_pattern")->value);
|
||||
bool is_bottom_centered = is_centered_pattern(config->option<ConfigOptionEnum<InfillPattern>>("bottom_surface_pattern")->value);
|
||||
bool has_centered_surface = (has_top_shell && is_top_centered) || (has_bottom_shell && is_bottom_centered);
|
||||
|
||||
// Orca: center of surface pattern
|
||||
toggle_line("center_of_surface_pattern", has_centered_surface);
|
||||
|
||||
// Orca: separate infills
|
||||
bool is_internal_infill_separable = is_separable_infill_pattern(config->option<ConfigOptionEnum<InfillPattern>>("sparse_infill_pattern")->value) ||
|
||||
config->opt_string("sparse_infill_rotate_template") != "" ||
|
||||
config->opt_string("solid_infill_rotate_template") != "";
|
||||
toggle_line("separated_infills", is_internal_infill_separable);
|
||||
|
||||
// Fill order is only meaningful for the center-based surface fill patterns; hide it otherwise.
|
||||
auto is_centered_fill = [](InfillPattern p) { return p == ipConcentric || p == ipArchimedeanChords || p == ipOctagramSpiral; };
|
||||
toggle_line("top_surface_fill_order", has_top_shell && is_centered_fill(config->opt_enum<InfillPattern>("top_surface_pattern")));
|
||||
toggle_line("bottom_surface_fill_order", has_bottom_shell && is_centered_fill(config->opt_enum<InfillPattern>("bottom_surface_pattern")));
|
||||
|
||||
for (auto el : { "infill_direction", "sparse_infill_line_width", "gap_fill_target","filter_out_gap_fill","infill_wall_overlap",
|
||||
"bridge_angle", "internal_bridge_angle", "relative_bridge_angle",
|
||||
"solid_infill_direction", "solid_infill_rotate_template", "internal_solid_infill_pattern", "internal_solid_filament_id", "top_surface_filament_id", "bottom_surface_filament_id",
|
||||
@@ -721,7 +798,7 @@ void ConfigManipulation::toggle_print_fff_options(DynamicPrintConfig *config, in
|
||||
for (auto el : { "sparse_infill_speed", "bridge_speed", "internal_bridge_speed"})
|
||||
toggle_field(el, have_infill || has_solid_infill, variant_index);
|
||||
|
||||
toggle_field("top_shell_thickness", ! has_spiral_vase && has_top_shell);
|
||||
toggle_field("top_shell_thickness", ! has_spiral_vase && has_top_shell_layers);
|
||||
toggle_field("bottom_shell_thickness", ! has_spiral_vase && has_bottom_shell);
|
||||
|
||||
// Gap fill is newly allowed in between perimeter lines even for empty infill (see GH #1476).
|
||||
@@ -776,14 +853,19 @@ void ConfigManipulation::toggle_print_fff_options(DynamicPrintConfig *config, in
|
||||
toggle_field("outer_wall_filament_id", have_perimeters || have_brim);
|
||||
toggle_field("inner_wall_filament_id", have_perimeters || have_brim);
|
||||
|
||||
bool have_brim_ear = (config->opt_enum<BrimType>("brim_type") == btEar);
|
||||
const BrimType brim_type = config->opt_enum<BrimType>("brim_type");
|
||||
const bool have_auto_brim_ear = brim_type == btEar;
|
||||
const bool have_painted_brim_ear = brim_type == btPainted;
|
||||
set_option_label("brim_width", have_auto_brim_ear ? _L("Brim ear radius") : _L("Brim width"));
|
||||
const auto brim_width = config->opt_float("brim_width");
|
||||
// disable brim_ears_max_angle and brim_ears_detection_length if brim_width is 0
|
||||
// Automatic brim ear settings require a non-zero brim width.
|
||||
toggle_field("brim_ears_max_angle", brim_width > 0.0f);
|
||||
toggle_field("brim_ears_detection_length", brim_width > 0.0f);
|
||||
// hide brim_ears_max_angle and brim_ears_detection_length if brim_ear is not selected
|
||||
toggle_line("brim_ears_max_angle", have_brim_ear);
|
||||
toggle_line("brim_ears_detection_length", have_brim_ear);
|
||||
// Painted ears carry their own radius and do not depend on brim_width.
|
||||
toggle_field("brim_ears_outer_only", have_painted_brim_ear || brim_width > 0.0f);
|
||||
toggle_line("brim_ears_max_angle", have_auto_brim_ear);
|
||||
toggle_line("brim_ears_detection_length", have_auto_brim_ear);
|
||||
toggle_line("brim_ears_outer_only", have_auto_brim_ear || have_painted_brim_ear);
|
||||
|
||||
// Hide Elephant foot compensation layers if elefant_foot_compensation is not enabled
|
||||
toggle_line("elefant_foot_compensation_layers", config->opt_float("elefant_foot_compensation") > 0 || config->option<ConfigOptionPercent>("elefant_foot_layers_density")->get_abs_value(1.0f) < 1.0f);
|
||||
@@ -916,7 +998,7 @@ void ConfigManipulation::toggle_print_fff_options(DynamicPrintConfig *config, in
|
||||
toggle_line("wipe_tower_extra_rib_length", have_rib_wall);
|
||||
toggle_line("wipe_tower_rib_width", have_rib_wall);
|
||||
toggle_line("wipe_tower_fillet_wall", have_rib_wall);
|
||||
toggle_field("prime_tower_width", have_prime_tower && (supports_wipe_tower_2 || have_rib_wall));
|
||||
toggle_field("prime_tower_width", have_prime_tower && !have_rib_wall);
|
||||
|
||||
toggle_line("single_extruder_multi_material_priming", !bSEMM && have_prime_tower && supports_wipe_tower_2);
|
||||
|
||||
@@ -978,9 +1060,13 @@ void ConfigManipulation::toggle_print_fff_options(DynamicPrintConfig *config, in
|
||||
toggle_line("make_overhang_printable_angle", have_make_overhang_printable);
|
||||
toggle_line("make_overhang_printable_hole_size", have_make_overhang_printable);
|
||||
|
||||
toggle_line("min_width_top_surface", config->opt_bool("only_one_wall_top") || ((config->opt_float("min_length_factor") > 0.5f) && have_arachne)); // 0.5 is default value
|
||||
// Orca: the one-wall options act on top/bottom surfaces, which exist only with a shell. An unfilled surface
|
||||
// (0% surface density) is still a surface, so these are gated on the layer counts alone.
|
||||
toggle_line("only_one_wall_first_layer", has_bottom_shell);
|
||||
toggle_line("only_one_wall_top", has_top_shell_layers);
|
||||
toggle_line("min_width_top_surface", (has_top_shell_layers && config->opt_bool("only_one_wall_top")) || ((config->opt_float("min_length_factor") > 0.5f) && have_arachne)); // 0.5 is default value
|
||||
|
||||
for (auto el : { "hole_to_polyhole_threshold", "hole_to_polyhole_twisted" })
|
||||
for (auto el : { "hole_to_polyhole_threshold", "hole_to_polyhole_twisted", "hole_to_polyhole_max_edges" })
|
||||
toggle_line(el, config->opt_bool("hole_to_polyhole"));
|
||||
|
||||
bool has_detect_overhang_wall = config->opt_bool("detect_overhang_wall");
|
||||
|
||||
@@ -29,6 +29,7 @@ class ConfigManipulation
|
||||
std::function<void()> load_config = nullptr;
|
||||
std::function<void (const std::string&, bool toggle, int opt_index)> cb_toggle_field = nullptr;
|
||||
std::function<void(const std::string &, bool toggle, int opt_index)> cb_toggle_line = nullptr;
|
||||
std::function<void(const std::string &, const wxString &, int opt_index)> cb_set_option_label = nullptr;
|
||||
// callback to propagation of changed value, if needed
|
||||
std::function<void(const std::string&, const boost::any&)> cb_value_change = nullptr;
|
||||
//BBS: change local config to const DynamicPrintConfig
|
||||
@@ -45,10 +46,12 @@ public:
|
||||
std::function<void(const std::string&, const boost::any&)> cb_value_change,
|
||||
//BBS: change local config to DynamicPrintConfig
|
||||
const DynamicPrintConfig* local_config = nullptr,
|
||||
wxWindow* msg_dlg_parent = nullptr) :
|
||||
wxWindow* msg_dlg_parent = nullptr,
|
||||
std::function<void(const std::string &, const wxString &, int opt_index)> cb_set_option_label = nullptr) :
|
||||
load_config(load_config),
|
||||
cb_toggle_field(cb_toggle_field),
|
||||
cb_toggle_line(cb_toggle_line),
|
||||
cb_set_option_label(cb_set_option_label),
|
||||
cb_value_change(cb_value_change),
|
||||
m_msg_dlg_parent(msg_dlg_parent),
|
||||
local_config(local_config) {}
|
||||
@@ -58,6 +61,7 @@ public:
|
||||
load_config = nullptr;
|
||||
cb_toggle_field = nullptr;
|
||||
cb_toggle_line = nullptr;
|
||||
cb_set_option_label = nullptr;
|
||||
cb_value_change = nullptr;
|
||||
}
|
||||
|
||||
@@ -67,6 +71,7 @@ public:
|
||||
t_config_option_keys const &applying_keys() const;
|
||||
void toggle_field(const std::string& field_key, const bool toggle, int opt_index = -1);
|
||||
void toggle_line(const std::string& field_key, const bool toggle, int opt_index = -1);
|
||||
void set_option_label(const std::string& field_key, const wxString& label, int opt_index = -1);
|
||||
|
||||
// FFF print
|
||||
void update_print_fff_config(DynamicPrintConfig* config, const bool is_global_config = false, const bool is_plate_config = false);
|
||||
@@ -81,6 +86,9 @@ public:
|
||||
void check_filament_max_volumetric_speed(DynamicPrintConfig *config);
|
||||
void check_chamber_temperature(DynamicPrintConfig* config);
|
||||
void check_chamber_minimal_temperature(DynamicPrintConfig* config);
|
||||
bool check_layer_height(DynamicPrintConfig* config);
|
||||
bool layer_height_out_of_range_dialog(DynamicPrintConfig* config, double clamp_to);
|
||||
void layer_height_limits(double& min_layer_height, double& max_layer_height) const;
|
||||
void set_is_BBL_Printer(bool is_bbl_printer) { is_BBL_Printer = is_bbl_printer; };
|
||||
bool get_is_BBL_Printer() { return is_BBL_Printer; };
|
||||
// SLA print
|
||||
|
||||
@@ -1440,7 +1440,7 @@ PageTemperatures::PageTemperatures(ConfigWizard *parent)
|
||||
spin_bed->SetValue(default_bed != nullptr && default_bed->size() > 0 ? default_bed->get_at(0) : 0);
|
||||
|
||||
append_text(_L("Enter the nozzle_temperature needed for extruding your filament."));
|
||||
append_text(_L("A rule of thumb is 160 to 230 °C for PLA, and 215 to 250 °C for ABS."));
|
||||
append_text(_L("A rule of thumb is 160 to 230℃ for PLA, and 215 to 250℃ for ABS."));
|
||||
#endif
|
||||
|
||||
auto *sizer_extr = new wxFlexGridSizer(3, 5, 5);
|
||||
@@ -1455,7 +1455,7 @@ PageTemperatures::PageTemperatures(ConfigWizard *parent)
|
||||
append_spacer(VERTICAL_SPACING);
|
||||
|
||||
append_text(_L("Enter the bed temperature needed for getting your filament to stick to your heated bed."));
|
||||
append_text(_L("A rule of thumb is 60 °C for PLA and 110 °C for ABS. Leave zero if you have no heated bed."));
|
||||
append_text(_L("A rule of thumb is 60℃ for PLA and 110℃ for ABS. Leave zero if you have no heated bed."));
|
||||
|
||||
auto *sizer_bed = new wxFlexGridSizer(3, 5, 5);
|
||||
auto *text_bed = new wxStaticText(this, wxID_ANY, _L("Bed Temperature:"));
|
||||
|
||||
@@ -80,9 +80,8 @@ ConnectPrinterDialog::ConnectPrinterDialog(wxWindow *parent, wxWindowID id, cons
|
||||
|
||||
main_sizer->Add(sizer_top);
|
||||
|
||||
this->SetSizer(main_sizer);
|
||||
this->SetSizerAndFit(main_sizer);
|
||||
this->Layout();
|
||||
this->Fit();
|
||||
CentreOnParent();
|
||||
|
||||
m_textCtrl_code->Bind(wxEVT_TEXT, &ConnectPrinterDialog::on_input_enter, this);
|
||||
|
||||
@@ -28,6 +28,8 @@ list(APPEND SLIC3R_GUI_SOURCES
|
||||
GUI/DeviceCore/DevFilaSystem.h
|
||||
GUI/DeviceCore/DevFilaSystem.cpp
|
||||
GUI/DeviceCore/DevFilaSystemCtrl.cpp
|
||||
GUI/DeviceCore/DevFilaSwitch.h
|
||||
GUI/DeviceCore/DevFilaSwitch.cpp
|
||||
GUI/DeviceCore/DevFirmware.h
|
||||
GUI/DeviceCore/DevFirmware.cpp
|
||||
GUI/DeviceCore/DevPrintOptions.h
|
||||
@@ -47,10 +49,30 @@ list(APPEND SLIC3R_GUI_SOURCES
|
||||
GUI/DeviceCore/DevManager.cpp
|
||||
GUI/DeviceCore/DevMapping.h
|
||||
GUI/DeviceCore/DevMapping.cpp
|
||||
GUI/DeviceCore/DevMappingNozzle.h
|
||||
GUI/DeviceCore/DevMappingNozzle.cpp
|
||||
GUI/DeviceCore/DevNozzleSystem.h
|
||||
GUI/DeviceCore/DevNozzleSystem.cpp
|
||||
GUI/DeviceCore/DevNozzleRack.h
|
||||
GUI/DeviceCore/DevNozzleRack.cpp
|
||||
GUI/DeviceCore/DevNozzleRackCtrl.cpp
|
||||
GUI/DeviceCore/DevUtil.h
|
||||
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 = "";
|
||||
|
||||
|
||||
@@ -87,9 +112,30 @@ std::string DevPrinterConfigUtil::get_printer_ext_img(const std::string& type_st
|
||||
return (vec.size() > pos) ? vec[pos] : std::string();
|
||||
};
|
||||
|
||||
std::string DevPrinterConfigUtil::get_filament_load_img(const std::string &type_str, int ext_id, bool has_nozzle_rack)
|
||||
{
|
||||
if (has_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())
|
||||
{
|
||||
return rack_vec[ext_id];
|
||||
}
|
||||
return rack_vec[0];
|
||||
}
|
||||
}
|
||||
const auto &vec = get_value_from_config<std::vector<std::string>>(type_str, "filament_load_image");
|
||||
if (ext_id >= 0 && static_cast<size_t>(ext_id) < vec.size())
|
||||
{
|
||||
return vec[ext_id];
|
||||
}
|
||||
return vec.empty() ? std::string() : vec[0];
|
||||
}
|
||||
|
||||
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
|
||||
@@ -112,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;
|
||||
@@ -159,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;
|
||||
@@ -262,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" :
|
||||
@@ -285,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,13 +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,
|
||||
@@ -89,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"); }
|
||||
@@ -102,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>
|
||||
@@ -125,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();
|
||||
};
|
||||
|
||||
@@ -177,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();
|
||||
}
|
||||
|
||||
@@ -186,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,11 +10,21 @@
|
||||
|
||||
#pragma once
|
||||
#include <string>
|
||||
#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
|
||||
@@ -35,11 +45,43 @@ 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,
|
||||
};
|
||||
|
||||
enum DevAmsType : int
|
||||
{
|
||||
EXT_SPOOL = 0, // EXT
|
||||
AMS = 1, // AMS1
|
||||
AMS_LITE = 2, // AMS-Lite
|
||||
N3F = 3, // N3F, AMS 2PRO
|
||||
N3S = 4, // N3S, AMS HT
|
||||
AMS_LITE_MIXED = 5, // AMS-Lite for N9
|
||||
};
|
||||
|
||||
enum DevFilamentStep
|
||||
{
|
||||
STEP_IDLE = 0x00,
|
||||
STEP_PAUSE = 0x01,
|
||||
STEP_HEAT_NOZZLE = 0x02,
|
||||
STEP_CUT_FILAMENT = 0x03,
|
||||
STEP_PULL_CURR_FILAMENT = 0x04,
|
||||
STEP_PUSH_NEW_FILAMENT = 0x05,
|
||||
STEP_GRAB_NEW_FILAMENT = 0x06,
|
||||
STEP_PURGE_OLD_FILAMENT = 0x07,
|
||||
STEP_CHECK_POSITION = 0x08,
|
||||
STEP_SWITCH_EXTRUDER = 0x09,
|
||||
STEP_SWITCH_HOTEND = 0x0A,
|
||||
STEP_AMS_FILA_COOLING = 0x0B,
|
||||
STEP_PUSH_SWITCHER_FILA = 0x0C,
|
||||
STEP_PULL_SWITCHER_FILA = 0x0D,
|
||||
STEP_SWITCHER_SWITCH = 0x0E,
|
||||
STEP_CONFIRM_EXTRUDED = 0x08,
|
||||
STEP_COUNT,
|
||||
};
|
||||
|
||||
// Slots and Tray
|
||||
#define VIRTUAL_TRAY_MAIN_ID 255
|
||||
#define VIRTUAL_TRAY_DEPUTY_ID 254
|
||||
@@ -47,6 +89,9 @@ enum AmsStatusMain
|
||||
#define VIRTUAL_AMS_MAIN_ID_STR "255"
|
||||
#define VIRTUAL_AMS_DEPUTY_ID_STR "254"
|
||||
|
||||
// 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()
|
||||
|
||||
/* Extruder*/
|
||||
@@ -55,13 +100,31 @@ enum AmsStatusMain
|
||||
#define UNIQUE_EXTRUDER_ID MAIN_EXTRUDER_ID
|
||||
#define INVALID_EXTRUDER_ID -1
|
||||
|
||||
// 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
|
||||
H_FLOW,
|
||||
U_FLOW, // TPU 1.75 High Flow
|
||||
E_FLOW, // E3D High Flow
|
||||
};
|
||||
/* 0.2mm 0.4mm 0.6mm 0.8mm */
|
||||
enum NozzleDiameterType : int
|
||||
{
|
||||
NONE_DIAMETER_TYPE,
|
||||
NOZZLE_DIAMETER_0_2,
|
||||
NOZZLE_DIAMETER_0_4,
|
||||
NOZZLE_DIAMETER_0_6,
|
||||
NOZZLE_DIAMETER_0_8
|
||||
};
|
||||
|
||||
/*Print speed*/
|
||||
@@ -76,7 +139,7 @@ enum DevPrintingSpeedLevel
|
||||
};
|
||||
|
||||
/*Upgrade*/
|
||||
enum class DevFirmwareUpgradingState : int
|
||||
enum class DevFirmwareUpgradeState : int
|
||||
{
|
||||
DC = -1,
|
||||
UpgradingUnavaliable = 0,
|
||||
@@ -85,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:
|
||||
@@ -96,4 +163,37 @@ public:
|
||||
static bool IsVirtualSlot(const std::string& ams_id) { return (ams_id == VIRTUAL_AMS_MAIN_ID_STR || ams_id == VIRTUAL_AMS_DEPUTY_ID_STR); }
|
||||
};
|
||||
|
||||
};// namespace Slic3r
|
||||
namespace GUI
|
||||
{
|
||||
enum PrintFromType
|
||||
{
|
||||
FROM_NORMAL,
|
||||
FROM_SDCARD_VIEW,
|
||||
};
|
||||
}
|
||||
|
||||
};// namespace Slic3r
|
||||
|
||||
struct NozzleDef
|
||||
{
|
||||
float nozzle_diameter;
|
||||
Slic3r::NozzleFlowType nozzle_flow_type;
|
||||
|
||||
bool operator==(const NozzleDef& other) const
|
||||
{
|
||||
return nozzle_diameter == other.nozzle_diameter && nozzle_flow_type == other.nozzle_flow_type;
|
||||
}
|
||||
};
|
||||
|
||||
template<> struct std::hash<NozzleDef>
|
||||
{
|
||||
std::size_t operator()(const NozzleDef& v) const noexcept
|
||||
{
|
||||
size_t h1 = std::hash<int>{}(v.nozzle_diameter * 1000);
|
||||
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>>;
|
||||
@@ -1,6 +1,7 @@
|
||||
#include <nlohmann/json.hpp>
|
||||
#include "DevExtruderSystem.h"
|
||||
#include "DevNozzleSystem.h"
|
||||
#include "DevFilaSystem.h" // complete type for GetFilaSystem()->GetTrayIndexMap() in GetBackupAmsSlotInGroup
|
||||
|
||||
// TODO: remove this include
|
||||
#include "slic3r/GUI/DeviceManager.hpp"
|
||||
@@ -48,17 +49,43 @@ namespace Slic3r
|
||||
|
||||
NozzleType DevExtder::GetNozzleType() const
|
||||
{
|
||||
return system->Owner()->GetNozzleSystem()->GetNozzle(m_current_nozzle_id).m_nozzle_type;
|
||||
return system->Owner()->GetNozzleSystem()->GetExtNozzle(m_current_nozzle_id).m_nozzle_type;
|
||||
}
|
||||
|
||||
NozzleFlowType DevExtder::GetNozzleFlowType() const
|
||||
{
|
||||
return system->Owner()->GetNozzleSystem()->GetNozzle(m_current_nozzle_id).m_nozzle_flow;
|
||||
return system->Owner()->GetNozzleSystem()->GetExtNozzle(m_current_nozzle_id).m_nozzle_flow;
|
||||
}
|
||||
|
||||
float DevExtder::GetNozzleDiameter() const
|
||||
{
|
||||
return system->Owner()->GetNozzleSystem()->GetNozzle(m_current_nozzle_id).m_diameter;
|
||||
return system->Owner()->GetNozzleSystem()->GetExtNozzle(m_current_nozzle_id).m_diameter;
|
||||
}
|
||||
|
||||
std::unordered_map<int, bool> DevExtder::GetBackupStatus(unsigned int fila_back_group)
|
||||
{
|
||||
std::unordered_map<int, bool> trayid_group;
|
||||
for (int i = 0; i < 16; i++)
|
||||
{
|
||||
if (fila_back_group & (1 << i))
|
||||
{
|
||||
trayid_group[i] = true;
|
||||
}
|
||||
}
|
||||
|
||||
for (int j = 16; j <= 23; j++)/* single ams is from 128*/
|
||||
{
|
||||
if (fila_back_group & (1 << j)) {
|
||||
trayid_group[128 + j - 16] = true;
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 24; i <= 27; i++) /* ams lite for N9*/
|
||||
{
|
||||
if (fila_back_group & (1 << i)) { trayid_group[i] = true; }
|
||||
}
|
||||
|
||||
return trayid_group;
|
||||
}
|
||||
|
||||
DevExtderSystem::DevExtderSystem(MachineObject* obj)
|
||||
@@ -143,6 +170,24 @@ namespace Slic3r
|
||||
return false;
|
||||
}
|
||||
|
||||
std::vector<DevAmsSlotId> DevExtderSystem::GetBackupAmsSlotInGroup(const DevAmsSlotId& ams_slot_id)
|
||||
{
|
||||
std::map<int, DevAmsSlotId> tray_map = Owner()->GetFilaSystem()->GetTrayIndexMap();
|
||||
|
||||
std::vector<DevAmsSlotId> backup_group;
|
||||
for (const auto& extruder : m_extders) {
|
||||
for (int fila_backup : extruder.GetFilamBackup()) {
|
||||
for (auto [tray_id, is_valid] : DevExtder::GetBackupStatus(fila_backup)) {
|
||||
if (is_valid && tray_map.find(tray_id) != tray_map.end() && tray_map[tray_id] != ams_slot_id) {
|
||||
backup_group.emplace_back(tray_map[tray_id]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return backup_group;
|
||||
}
|
||||
|
||||
void ExtderSystemParser::ParseV1_0(const nlohmann::json& print_json, DevExtderSystem* system)
|
||||
{
|
||||
if (system->GetTotalExtderCount() != 1)
|
||||
@@ -226,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;
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#pragma once
|
||||
#include <optional>
|
||||
#include <unordered_map>
|
||||
#include "libslic3r/CommonDefs.hpp"
|
||||
|
||||
#include "slic3r/Utils/json_diff.hpp"
|
||||
@@ -21,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;}
|
||||
};
|
||||
|
||||
@@ -66,6 +68,10 @@ public:
|
||||
bool HasFilamBackup() const { return !m_filam_bak.empty(); }
|
||||
std::vector<int> GetFilamBackup() const { return m_filam_bak; }
|
||||
|
||||
// Decode a filament-backup bit group into { tray_id -> valid }. Bits 0-15 map directly to tray ids,
|
||||
// bits 16-23 to single-AMS tray ids 128+, bits 24-27 to AMS-Lite-mixed (N9) tray ids 24-27.
|
||||
static std::unordered_map<int, bool> GetBackupStatus(unsigned int fila_back_group);
|
||||
|
||||
// ams binding on current extruder
|
||||
const DevAmsSlotInfo& GetSlotPre() const { return m_spre; }
|
||||
const DevAmsSlotInfo& GetSlotNow() const { return m_snow; }
|
||||
@@ -168,6 +174,11 @@ public:
|
||||
bool HasFilamentBackup() const;
|
||||
bool HasFilamentInExt(int exter_id) { return GetExtderById(exter_id) ? GetExtderById(exter_id)->HasFilamentInExt() : false; }
|
||||
|
||||
// List the other AMS slots that back up the given slot within the same filament-backup group,
|
||||
// across all extruders. Uses DevFilaSystem::GetTrayIndexMap() to translate backup bits (incl. the
|
||||
// A2L/N9 AMS-Lite-mixed trays 24-27) into ams/slot ids.
|
||||
std::vector<DevAmsSlotId> GetBackupAmsSlotInGroup(const DevAmsSlotId& ams_slot_id);
|
||||
|
||||
protected:
|
||||
void AddExtder(const DevExtder& ext) { m_extders[ext.GetExtId()] = ext; };
|
||||
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
@@ -1,8 +1,12 @@
|
||||
#include <nlohmann/json.hpp>
|
||||
|
||||
#include <algorithm>
|
||||
#include <set>
|
||||
|
||||
#include "DevFilaBlackList.h"
|
||||
#include "DevFilaSystem.h"
|
||||
#include "DevManager.h"
|
||||
#include "DevConfigUtil.h"
|
||||
|
||||
#include "libslic3r/Utils.hpp"
|
||||
|
||||
@@ -73,64 +77,97 @@ static std::string _get_filament_name_from_ams(int ams_id, int slot_id)
|
||||
}
|
||||
|
||||
// moved from tao.wang and zhimin.zeng
|
||||
void check_filaments(std::string model_id,
|
||||
std::string tag_vendor,
|
||||
std::string tag_type,
|
||||
int ams_id,
|
||||
int slot_id,
|
||||
std::string tag_name,
|
||||
bool& in_blacklist,
|
||||
std::string& ac,
|
||||
wxString& info,
|
||||
wxString& wiki_url)
|
||||
// Struct-in / struct-out, accumulate-all parser: every matching rule is pushed into
|
||||
// result.action_items[action] and scanning continues (rather than stopping at the first match).
|
||||
void check_filaments(const DevFilaBlacklist::CheckFilamentInfo& check_info, DevFilaBlacklist::CheckResult& result)
|
||||
{
|
||||
std::string tag_type = check_info.fila_type;
|
||||
std::string tag_name = check_info.fila_name;
|
||||
std::string tag_vendor = check_info.fila_vendor;
|
||||
std::string tag_calib_mode = check_info.calib_mode;
|
||||
|
||||
// Orca: the print-send and calibration consumers do not populate fila_name; recover it from the
|
||||
// selected AMS slot so name / name_suffix rules keep matching.
|
||||
if (tag_name.empty())
|
||||
{
|
||||
tag_name = _get_filament_name_from_ams(ams_id, slot_id);
|
||||
tag_name = _get_filament_name_from_ams(check_info.ams_id, check_info.slot_id);
|
||||
}
|
||||
|
||||
in_blacklist = false;
|
||||
std::transform(tag_vendor.begin(), tag_vendor.end(), tag_vendor.begin(), ::tolower);
|
||||
std::transform(tag_type.begin(), tag_type.end(), tag_type.begin(), ::tolower);
|
||||
std::transform(tag_name.begin(), tag_name.end(), tag_name.begin(), ::tolower);
|
||||
std::transform(tag_calib_mode.begin(), tag_calib_mode.end(), tag_calib_mode.begin(), ::tolower);
|
||||
|
||||
DevFilaBlacklist::load_filaments_blacklist_config();
|
||||
if (DevFilaBlacklist::filaments_blacklist.contains("blacklist"))
|
||||
{
|
||||
for (auto filament_item : DevFilaBlacklist::filaments_blacklist["blacklist"])
|
||||
{
|
||||
|
||||
std::string vendor = filament_item.contains("vendor") ? filament_item["vendor"].get<std::string>() : "";
|
||||
std::string type = filament_item.contains("type") ? filament_item["type"].get<std::string>() : "";
|
||||
std::string type_suffix = filament_item.contains("type_suffix") ? filament_item["type_suffix"].get<std::string>() : "";
|
||||
std::string name = filament_item.contains("name") ? filament_item["name"].get<std::string>() : "";
|
||||
std::string slot = filament_item.contains("slot") ? filament_item["slot"].get<std::string>() : "";
|
||||
std::vector<std::string> model_ids = filament_item.contains("model_id") ? filament_item["model_id"].get<std::vector<std::string>>() : std::vector<std::string>();
|
||||
std::string action = filament_item.contains("action") ? filament_item["action"].get<std::string>() : "";
|
||||
std::string description = filament_item.contains("description") ? filament_item["description"].get<std::string>() : "";
|
||||
|
||||
// blacklist items
|
||||
std::string vendor = filament_item.contains("vendor") ? filament_item["vendor"].get<std::string>() : "";
|
||||
std::string type = filament_item.contains("type") ? filament_item["type"].get<std::string>() : "";
|
||||
std::vector<std::string> types = filament_item.contains("types") ? filament_item["types"].get<std::vector<std::string>>() : std::vector<std::string>();
|
||||
std::string type_suffix = filament_item.contains("type_suffix") ? filament_item["type_suffix"].get<std::string>() : "";
|
||||
std::string name_suffix = filament_item.contains("name_suffix") ? filament_item["name_suffix"].get<std::string>() : "";
|
||||
std::string name = filament_item.contains("name") ? filament_item["name"].get<std::string>() : "";
|
||||
std::string slot = filament_item.contains("slot") ? filament_item["slot"].get<std::string>() : "";
|
||||
std::optional<bool> used_for_print_support = filament_item.contains("used_for_print_support") ? filament_item["used_for_print_support"].get<bool>() : std::optional<bool>();
|
||||
std::optional<bool> used_for_print_object = filament_item.contains("used_for_print_object") ? filament_item["used_for_print_object"].get<bool>() : std::optional<bool>();
|
||||
std::vector<std::string> model_ids = filament_item.contains("model_id") ? filament_item["model_id"].get<std::vector<std::string>>() : std::vector<std::string>();
|
||||
std::vector<int> extruder_ids = filament_item.contains("extruder_id") ? filament_item["extruder_id"].get<std::vector<int>>() : std::vector<int>();
|
||||
std::vector<std::string> nozzle_flows = filament_item.contains("nozzle_flows") ? filament_item["nozzle_flows"].get<std::vector<std::string>>() : std::vector<std::string>();
|
||||
std::string calib_mode = filament_item.contains("calib_mode") ? filament_item["calib_mode"].get<std::string>() : "";
|
||||
|
||||
// check model id
|
||||
if (!model_ids.empty() && std::find(model_ids.begin(), model_ids.end(), model_id) == model_ids.end()) { continue; }
|
||||
if (!model_ids.empty() && std::find(model_ids.begin(), model_ids.end(), check_info.model_id) == model_ids.end()) { continue; }
|
||||
|
||||
// A rule keyed on nozzle_flows/nozzle_diameters only matches when check_info carries real
|
||||
// per-nozzle context (threaded by the print-send consumer). Consumers that leave
|
||||
// nozzle_flow/nozzle_diameter unset (AMS edit, calibration, and the no-nozzle-context
|
||||
// fallback) never satisfy the non-empty match below, so those rules stay dormant there.
|
||||
|
||||
// check nozzle flows
|
||||
if (!nozzle_flows.empty() && std::find(nozzle_flows.begin(), nozzle_flows.end(), check_info.nozzle_flow.value_or("")) == nozzle_flows.end()) { continue; }
|
||||
|
||||
// check nozzle diameter
|
||||
const std::vector<float> nozzle_diameters = filament_item.contains("nozzle_diameters") ? filament_item["nozzle_diameters"].get<std::vector<float>>() : std::vector<float>();
|
||||
if (!nozzle_diameters.empty() && check_info.nozzle_diameter.has_value() &&
|
||||
std::find(nozzle_diameters.begin(), nozzle_diameters.end(), check_info.nozzle_diameter.value()) == nozzle_diameters.end()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// check vendor
|
||||
std::transform(vendor.begin(), vendor.end(), vendor.begin(), ::tolower);
|
||||
if (!vendor.empty())
|
||||
{
|
||||
if ((vendor == "bambu lab" && (tag_vendor == vendor)) ||
|
||||
(vendor == "third party" && (tag_vendor != "bambu lab")))
|
||||
{
|
||||
// Do nothing
|
||||
}
|
||||
else
|
||||
if (!((vendor == "bambu lab" && tag_vendor == "bambu lab") ||
|
||||
(vendor == "third party" && tag_vendor != "bambu lab")))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// check calib mode
|
||||
std::transform(calib_mode.begin(), calib_mode.end(), calib_mode.begin(), ::tolower);
|
||||
if (!calib_mode.empty() && calib_mode != tag_calib_mode) { continue; }
|
||||
|
||||
// check type
|
||||
std::transform(type.begin(), type.end(), type.begin(), ::tolower);
|
||||
if (!type.empty() && (type != tag_type)) { continue; }
|
||||
|
||||
// check types (plural): item matches if tag_type is any of the listed types.
|
||||
if (!types.empty())
|
||||
{
|
||||
auto it = std::find_if(types.begin(), types.end(), [&tag_type](std::string ttype) {
|
||||
std::transform(ttype.begin(), ttype.end(), ttype.begin(), ::tolower);
|
||||
return ttype == tag_type;
|
||||
});
|
||||
if (it == types.end()) { continue; }
|
||||
}
|
||||
|
||||
// check type suffix
|
||||
std::transform(type_suffix.begin(), type_suffix.end(), type_suffix.begin(), ::tolower);
|
||||
if (!type_suffix.empty())
|
||||
@@ -143,10 +180,31 @@ void check_filaments(std::string model_id,
|
||||
std::transform(name.begin(), name.end(), name.begin(), ::tolower);
|
||||
if (!name.empty() && (name != tag_name)) { continue; }
|
||||
|
||||
// check name suffix
|
||||
std::transform(name_suffix.begin(), name_suffix.end(), name_suffix.begin(), ::tolower);
|
||||
if (!name_suffix.empty())
|
||||
{
|
||||
if (tag_name.length() < name_suffix.length()) { continue; }
|
||||
if ((tag_name.substr(tag_name.length() - name_suffix.length()) != name_suffix)) { continue; }
|
||||
}
|
||||
|
||||
// check filament used for print support
|
||||
if (used_for_print_support.has_value() && used_for_print_support != check_info.used_for_print_support) { continue; }
|
||||
|
||||
// check filament used for print object
|
||||
if (used_for_print_object.has_value() && used_for_print_object != check_info.used_for_print_object) { continue; }
|
||||
|
||||
// check filament switch
|
||||
if (filament_item.contains("has_filament_switch"))
|
||||
{
|
||||
bool has_filament_switch = filament_item["has_filament_switch"].get<bool>();
|
||||
if (check_info.has_filament_switch != has_filament_switch) { continue; }
|
||||
}
|
||||
|
||||
// check loc
|
||||
if (!slot.empty())
|
||||
{
|
||||
bool is_virtual_slot = devPrinterUtil::IsVirtualSlot(ams_id);
|
||||
bool is_virtual_slot = devPrinterUtil::IsVirtualSlot(check_info.ams_id);
|
||||
bool check_virtual_slot = (slot == "ext");
|
||||
bool check_ams_slot = (slot == "ams");
|
||||
if (is_virtual_slot && !check_virtual_slot)
|
||||
@@ -159,71 +217,115 @@ void check_filaments(std::string model_id,
|
||||
}
|
||||
}
|
||||
|
||||
if (GUI::wxGetApp().app_config->get("skip_ams_blacklist_check") == "true") {
|
||||
action = "warning";
|
||||
// check extruder id
|
||||
if (!extruder_ids.empty() && check_info.extruder_id.has_value() &&
|
||||
std::find(extruder_ids.begin(), extruder_ids.end(), check_info.extruder_id.value()) == extruder_ids.end()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
in_blacklist = true;
|
||||
ac = action;
|
||||
info = _L(description);
|
||||
wiki_url = filament_item.contains("wiki") ? filament_item["wiki"].get<std::string>() : "";
|
||||
return;
|
||||
// white items: a matched rule is suppressed when the filament is whitelisted
|
||||
{
|
||||
// contains match
|
||||
std::set<std::string> white_names = filament_item.contains("white_names") ? filament_item["white_names"].get<std::set<std::string>>() : std::set<std::string>();
|
||||
if (!white_names.empty() && !tag_name.empty())
|
||||
{
|
||||
auto it = std::find_if(white_names.begin(), white_names.end(), [&tag_name](std::string white_name) {
|
||||
std::transform(white_name.begin(), white_name.end(), white_name.begin(), ::tolower);
|
||||
return tag_name.find(white_name) != std::string::npos;
|
||||
});
|
||||
if (it != white_names.end()) { continue; }
|
||||
}
|
||||
}
|
||||
{
|
||||
// equal match
|
||||
std::set<std::string> white_fila_ids = filament_item.contains("white_fila_ids") ? filament_item["white_fila_ids"].get<std::set<std::string>>() : std::set<std::string>();
|
||||
if (!white_fila_ids.empty() && !check_info.fila_id.empty())
|
||||
{
|
||||
auto it = std::find_if(white_fila_ids.begin(), white_fila_ids.end(), [&check_info](const std::string& white_fila_id) {
|
||||
return white_fila_id == check_info.fila_id;
|
||||
});
|
||||
if (it != white_fila_ids.end()) { continue; }
|
||||
}
|
||||
}
|
||||
|
||||
// the item is matched: accumulate it (do not stop scanning)
|
||||
DevFilaBlacklist::CheckResultItem result_item;
|
||||
result_item.action = action;
|
||||
result_item.wiki_url = filament_item.contains("wiki") ? filament_item["wiki"].get<std::string>() : "";
|
||||
|
||||
if (description == "%s has a risk of nozzle clogging when using 0.4mm high-flow nozzles. Use with caution.") {
|
||||
result_item.info_msg = wxString::Format(_L(description), check_info.fila_name);
|
||||
} else if (description == "%s filaments are hard and brittle and could break in AMS, and there is also a risk of nozzle clogging when using 0.4mm high-flow nozzles. Use with caution.") {
|
||||
result_item.info_msg = wxString::Format(_L(description), check_info.fila_type);
|
||||
} else if (description == "%s has a risk of nozzle clogging when using 0.4, 0.6, 0.8mm high-flow nozzles. Use with caution.") {
|
||||
result_item.info_msg = wxString::Format(_L(description), check_info.fila_name);
|
||||
} else if (description == "%s may fail to load or unload due to the Filament Track Switch. If you wish to continue.") {
|
||||
result_item.info_msg = wxString::Format(_L(description), check_info.fila_name);
|
||||
} else {
|
||||
result_item.info_msg = _L(description);
|
||||
}
|
||||
|
||||
result.action_items[result_item.action].push_back(result_item);
|
||||
continue;
|
||||
|
||||
// Error in description
|
||||
L("TPU is not supported by AMS.");
|
||||
L("AMS does not support 'Bambu Lab PET-CF'.");
|
||||
L("The current filament doesn't support the E3D high-flow nozzle and can't be used.");
|
||||
L("The current filament doesn't support the TPU high-flow nozzle and can't be used.");
|
||||
L("Auto dynamic flow calibration is not supported for TPU filament.");
|
||||
L("Bambu TPU 85A is not supported for printing with 0.4 mm Standard or High Flow nozzles.");
|
||||
|
||||
// Warning in description
|
||||
L("How to feed TPU filament.");
|
||||
L("How to feed TPU filament on X2D.");
|
||||
L("Please cold pull before printing TPU to avoid clogging. You may use cold pull maintenance on the printer.");
|
||||
L("Damp PVA will become flexible and get stuck inside AMS, please take care to dry it before use.");
|
||||
L("Damp PVA is flexible and may get stuck in extruder. Dry it before use.");
|
||||
L("The rough surface of PLA Glow can accelerate wear on the AMS system, particularly on the internal components of the AMS Lite.");
|
||||
L("PLA Glow may wear the AMS first stage feeder. Use an external spool instead.");
|
||||
L("CF/GF filaments are hard and brittle, it's easy to break or get stuck in AMS, please use with caution.");
|
||||
L("PPS-CF is brittle and could break in bended PTFE tube above Toolhead.");
|
||||
L("PPA-CF is brittle and could break in bended PTFE tube above Toolhead.");
|
||||
L("Default settings may affect print quality. Adjust as needed for best results.");
|
||||
L("%s has a risk of nozzle clogging when using 0.4mm high-flow nozzles. Use with caution.");
|
||||
L("%s filaments are hard and brittle and could break in AMS, and there is also a risk of nozzle clogging when using 0.4mm high-flow nozzles. Use with caution.");
|
||||
L("%s has a risk of nozzle clogging when using 0.4, 0.6, 0.8mm high-flow nozzles. Use with caution.");
|
||||
L("%s may fail to load or unload due to the Filament Track Switch. If you wish to continue.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
void DevFilaBlacklist::check_filaments_in_blacklist(std::string model_id,
|
||||
std::string tag_vendor,
|
||||
std::string tag_type,
|
||||
const std::string& filament_id,
|
||||
int ams_id,
|
||||
int slot_id,
|
||||
std::string tag_name,
|
||||
bool& in_blacklist,
|
||||
std::string& ac,
|
||||
wxString& info)
|
||||
// Extends the prohibition-only bit check with a 4-level filament_extruder_compatibility model
|
||||
// (0 printable / 1 error / 2 critical warning / 3 warning), bowden-extruder message variants and
|
||||
// toolhead display names for the "%s extruder" name.
|
||||
//
|
||||
// Orca: several deliberate divergences preserve H2D dual-extruder behaviour when the compatibility
|
||||
// key is absent (default 0 = printable):
|
||||
// - The extruder is resolved from the ams_id rather than a threaded check_info.extruder_id with an
|
||||
// early-return: extruder_id is only threaded on the print-send consumer, so falling back on
|
||||
// ams_id keeps the bit-check firing on the AMS-edit / calibration paths.
|
||||
// - The prohibition bit-check keeps check_info.fila_type for its "%s", preserving the exact H2D
|
||||
// AMS-edit message that ships today. The new 4-level messages use the fila_name fallback since
|
||||
// they stay inert until a filament declares the compatibility key (only X2D ships it).
|
||||
// - is_bowden_extruder is assigned correctly here so the Bowden message variants actually fire.
|
||||
void check_filaments_printable(const DevFilaBlacklist::CheckFilamentInfo& check_info, DevFilaBlacklist::CheckResult& result)
|
||||
{
|
||||
wxString wiki_url;
|
||||
check_filaments_in_blacklist_url(model_id, tag_vendor, tag_type, filament_id, ams_id, slot_id, tag_name, in_blacklist, ac, info, wiki_url);
|
||||
}
|
||||
DeviceManager* dev = Slic3r::GUI::wxGetApp().getDeviceManager();
|
||||
if (!dev) { return; }
|
||||
|
||||
MachineObject* obj = dev->get_selected_machine();
|
||||
if (obj == nullptr || !obj->is_multi_extruders()) { return; }
|
||||
|
||||
bool check_filaments_printable(const std::string &tag_vendor, const std::string &tag_type, const std::string& filament_id, int ams_id, bool &in_blacklist, std::string &ac, wxString &info)
|
||||
{
|
||||
DeviceManager *dev = Slic3r::GUI::wxGetApp().getDeviceManager();
|
||||
if (!dev) {
|
||||
return true;
|
||||
}
|
||||
Preset* printer_preset = GUI::get_printer_preset(obj);
|
||||
if (!printer_preset) { return; }
|
||||
|
||||
MachineObject *obj = dev->get_selected_machine();
|
||||
if (obj == nullptr || !obj->is_multi_extruders()) {
|
||||
return true;
|
||||
}
|
||||
ConfigOptionInts* physical_extruder_map_op = dynamic_cast<ConfigOptionInts*>(printer_preset->config.option("physical_extruder_map"));
|
||||
if (!physical_extruder_map_op) { return; }
|
||||
|
||||
Preset *printer_preset = GUI::get_printer_preset(obj);
|
||||
if (!printer_preset)
|
||||
return true;
|
||||
|
||||
ConfigOptionInts *physical_extruder_map_op = dynamic_cast<ConfigOptionInts *>(printer_preset->config.option("physical_extruder_map"));
|
||||
if (!physical_extruder_map_op)
|
||||
return true;
|
||||
std::vector<int> physical_extruder_maps = physical_extruder_map_op->values;
|
||||
int obj_extruder_id = obj->get_extruder_id_by_ams_id(std::to_string(ams_id));
|
||||
int obj_extruder_id = obj->get_extruder_id_by_ams_id(std::to_string(check_info.ams_id));
|
||||
int extruder_idx = obj_extruder_id;
|
||||
for (int index = 0; index < physical_extruder_maps.size(); ++index) {
|
||||
if (physical_extruder_maps[index] == obj_extruder_id) {
|
||||
@@ -232,32 +334,85 @@ bool check_filaments_printable(const std::string &tag_vendor, const std::string
|
||||
}
|
||||
}
|
||||
|
||||
PresetBundle *preset_bundle = GUI::wxGetApp().preset_bundle;
|
||||
std::optional<FilamentBaseInfo> filament_info = preset_bundle->get_filament_by_filament_id(filament_id, printer_preset->name);
|
||||
if (filament_info.has_value() && !(filament_info->filament_printable >> extruder_idx & 1)) {
|
||||
wxString extruder_name = extruder_idx == 0 ? _L("left") : _L("right");
|
||||
ac = "prohibition";
|
||||
info = wxString::Format(_L("%s is not supported by %s extruder."), tag_type, extruder_name);
|
||||
in_blacklist = true;
|
||||
return false;
|
||||
PresetBundle* preset_bundle = GUI::wxGetApp().preset_bundle;
|
||||
std::optional<FilamentBaseInfo> filament_info = preset_bundle->get_filament_by_filament_id(check_info.fila_id, printer_preset->name);
|
||||
if (!filament_info.has_value()) { return; }
|
||||
|
||||
// Physical extruder 0 -> deputy (left), 1 -> main (right); the toolhead display name replaces the
|
||||
// hard-coded left/right labels (falls back to "left"/"right" for printers without an override).
|
||||
int bl_ext_id = (extruder_idx == 0) ? DEPUTY_EXTRUDER_ID : MAIN_EXTRUDER_ID;
|
||||
wxString extruder_name = _L(DevPrinterConfigUtil::get_toolhead_display_name(
|
||||
obj->printer_type, bl_ext_id, ToolHeadComponent::Extruder, ToolHeadNameCase::LowerCase, true));
|
||||
|
||||
// Prohibition-only bit check; keeps fila_type in the "%s" to preserve current behaviour.
|
||||
if (!(filament_info->filament_printable >> extruder_idx & 1)) {
|
||||
DevFilaBlacklist::CheckResultItem item;
|
||||
item.action = "prohibition";
|
||||
item.info_msg = wxString::Format(_L("%s is not supported by %s extruder."), check_info.fila_type, extruder_name);
|
||||
result.action_items[item.action].push_back(item);
|
||||
return;
|
||||
}
|
||||
|
||||
return true;
|
||||
// Is this a bowden extruder? (drives the message variant for compatibility warnings.)
|
||||
bool is_bowden_extruder = false;
|
||||
auto extruder_type_opt = dynamic_cast<const ConfigOptionEnumsGeneric*>(printer_preset->config.option("extruder_type"));
|
||||
if (extruder_type_opt && (int) extruder_type_opt->values.size() > extruder_idx) {
|
||||
ExtruderType extruder_type = (ExtruderType) extruder_type_opt->values[extruder_idx];
|
||||
is_bowden_extruder = (extruder_type == ExtruderType::etBowden);
|
||||
}
|
||||
|
||||
// Compatibility levels: 0 = printable, 1 = error, 2 = critical warning, 3 = warning (4-7 reserved).
|
||||
std::string fila_name = check_info.fila_name.empty() ? check_info.fila_type : check_info.fila_name;
|
||||
int compatible_val = filament_info->get_extruder_compatibility(extruder_idx);
|
||||
if (compatible_val == 0) {
|
||||
// printable, nothing to report
|
||||
} else if (compatible_val == 1) {
|
||||
DevFilaBlacklist::CheckResultItem item;
|
||||
item.action = "prohibition";
|
||||
item.info_msg = wxString::Format(_L("%s is not supported by %s extruder."), fila_name, extruder_name);
|
||||
result.action_items[item.action].push_back(item);
|
||||
} else if (compatible_val == 2) {
|
||||
DevFilaBlacklist::CheckResultItem item;
|
||||
item.action = "warning";
|
||||
item.info_msg = is_bowden_extruder
|
||||
? wxString::Format(_L("There may be critical print quality issues when printing '%s' with %s Bowden extruder. Use with caution!"), fila_name, extruder_name)
|
||||
: wxString::Format(_L("There may be critical print quality issues when printing '%s' with %s extruder. Use with caution!"), fila_name, extruder_name);
|
||||
result.action_items[item.action].push_back(item);
|
||||
} else if (compatible_val == 3) {
|
||||
DevFilaBlacklist::CheckResultItem item;
|
||||
item.action = "warning";
|
||||
item.info_msg = is_bowden_extruder
|
||||
? wxString::Format(_L("There may be print quality issues when printing '%s' with %s Bowden extruder. Use with caution."), fila_name, extruder_name)
|
||||
: wxString::Format(_L("There may be print quality issues when printing '%s' with %s extruder. Use with caution."), fila_name, extruder_name);
|
||||
result.action_items[item.action].push_back(item);
|
||||
}
|
||||
}
|
||||
|
||||
void DevFilaBlacklist::check_filaments_in_blacklist_url(std::string model_id, std::string tag_vendor, std::string tag_type, const std::string& filament_id, int ams_id, int slot_id, std::string tag_name, bool& in_blacklist, std::string& ac, wxString& info, wxString& wiki_url)
|
||||
DevFilaBlacklist::CheckResult DevFilaBlacklist::check_filaments_in_blacklist(const CheckFilamentInfo& info)
|
||||
{
|
||||
if (ams_id < 0 || slot_id < 0)
|
||||
CheckResult result;
|
||||
if (info.ams_id < 0 || info.slot_id < 0)
|
||||
{
|
||||
return;
|
||||
return result;
|
||||
}
|
||||
|
||||
if (!check_filaments_printable(tag_vendor, tag_type, filament_id, ams_id, in_blacklist, ac, info))
|
||||
{
|
||||
return;
|
||||
// check the filaments in preset
|
||||
check_filaments_printable(info, result);
|
||||
|
||||
// check the filaments in blacklist file
|
||||
check_filaments(info, result);
|
||||
|
||||
// If skip_ams_blacklist_check is true, demote every prohibition to a warning (aggregate: demote
|
||||
// all at once rather than per-item).
|
||||
if (GUI::wxGetApp().app_config->get("skip_ams_blacklist_check") == "true") {
|
||||
const auto& prohibit_items = result.get_items_by_action("prohibition");
|
||||
if (!prohibit_items.empty()) {
|
||||
for (const auto& prohibit_item : prohibit_items) { result.action_items["warning"].push_back(prohibit_item); }
|
||||
result.action_items.erase("prohibition");
|
||||
}
|
||||
}
|
||||
|
||||
check_filaments(model_id, tag_vendor, tag_type, ams_id, slot_id, tag_name, in_blacklist, ac, info, wiki_url);
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,18 +1,70 @@
|
||||
#pragma once
|
||||
|
||||
#include <optional>
|
||||
#include <wx/string.h>
|
||||
#include "slic3r/Utils/json_diff.hpp"
|
||||
|
||||
namespace Slic3r
|
||||
{
|
||||
class MachineObject;
|
||||
class DevFilaBlacklist
|
||||
{
|
||||
public:
|
||||
// Struct-in / struct-out check API that accumulates all matching rules,
|
||||
// replacing the earlier out-param, first-match overloads.
|
||||
// The per-nozzle dimensions (extruder_id/nozzle_flow/nozzle_diameter), calib_mode and
|
||||
// has_filament_switch are currently left unset, so the engine behaves exactly as the
|
||||
// old first-match parser for the shipping rule set.
|
||||
struct CheckFilamentInfo
|
||||
{
|
||||
std::string dev_id;
|
||||
std::string model_id;
|
||||
|
||||
std::string fila_id;
|
||||
std::string fila_type;
|
||||
std::string fila_name;
|
||||
std::string fila_vendor;
|
||||
|
||||
std::string calib_mode;
|
||||
bool has_filament_switch = false;
|
||||
|
||||
std::optional<bool> used_for_print_support;// optional
|
||||
std::optional<bool> used_for_print_object;// optional
|
||||
|
||||
int ams_id;
|
||||
int slot_id;
|
||||
|
||||
std::optional<int> extruder_id;// optional
|
||||
std::optional<std::string> nozzle_flow;// optional
|
||||
std::optional<float> nozzle_diameter;// optional
|
||||
};
|
||||
|
||||
struct CheckResultItem
|
||||
{
|
||||
std::string action;// warning/prohibition
|
||||
wxString info_msg;
|
||||
wxString wiki_url;
|
||||
};
|
||||
|
||||
struct CheckResult
|
||||
{
|
||||
std::map<std::string, std::vector<CheckResultItem>> action_items;
|
||||
std::vector<CheckResultItem> get_items_by_action(const std::string& action) const
|
||||
{
|
||||
auto it = action_items.find(action);
|
||||
if (it != action_items.end()) {
|
||||
return it->second;
|
||||
}
|
||||
return std::vector<CheckResultItem>();
|
||||
}
|
||||
};
|
||||
|
||||
public:
|
||||
static bool load_filaments_blacklist_config();
|
||||
static void check_filaments_in_blacklist(std::string model_id, std::string tag_vendor, std::string tag_type, const std::string& filament_id, int ams_id, int slot_id, std::string tag_name, bool& in_blacklist, std::string& ac, wxString& info);
|
||||
static void check_filaments_in_blacklist_url(std::string model_id, std::string tag_vendor, std::string tag_type, const std::string& filament_id, int ams_id, int slot_id, std::string tag_name, bool& in_blacklist, std::string& ac, wxString& info, wxString& wiki_url);
|
||||
static CheckResult check_filaments_in_blacklist(const CheckFilamentInfo& info);
|
||||
|
||||
public:
|
||||
static json filaments_blacklist;
|
||||
};// class DevFilaBlacklist
|
||||
|
||||
}// namespace Slic3r
|
||||
}// namespace Slic3r
|
||||
|
||||
@@ -0,0 +1,136 @@
|
||||
#include "DevExtruderSystem.h"
|
||||
#include "DevFilaSystem.h"
|
||||
#include "DevFilaSwitch.h"
|
||||
#include "DevUtil.h"
|
||||
|
||||
#include "slic3r/GUI/DeviceManager.hpp"
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
DevFilaSwitch::DevFilaSwitch(MachineObject *owner) { m_owner = owner; }
|
||||
|
||||
void DevFilaSwitch::Reset()
|
||||
{
|
||||
m_is_installed = false;
|
||||
m_in_a_has_filament.reset();
|
||||
m_in_b_has_filament.reset();
|
||||
m_in_a_slot.reset();
|
||||
m_in_b_slot.reset();
|
||||
m_out_a_extruder_id.reset();
|
||||
m_out_b_extruder_id.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();
|
||||
if (m_owner && ams_id_opt.has_value()) { return m_owner->get_tray(std::to_string(ams_id_opt->first), std::to_string(ams_id_opt->first)); }
|
||||
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
std::optional<DevAmsTray> DevFilaSwitch::GetInB_Slot() const
|
||||
{
|
||||
auto ams_id_opt = GetInB_SlotId();
|
||||
if (m_owner && ams_id_opt.has_value()) { return m_owner->get_tray(std::to_string(ams_id_opt->first), std::to_string(ams_id_opt->first)); }
|
||||
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
std::optional<DevExtder> DevFilaSwitch::GetOutA_Extruder() const
|
||||
{
|
||||
auto ext_id_opt = GetOutA_ExtruderId();
|
||||
if (m_owner && ext_id_opt.has_value()) { return m_owner->GetExtderSystem()->GetExtderById(ext_id_opt.value()); }
|
||||
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
std::optional<DevExtder> DevFilaSwitch::GetOutB_Extruder() const
|
||||
{
|
||||
auto ext_id_opt = GetOutB_ExtruderId();
|
||||
if (m_owner && ext_id_opt.has_value()) { return m_owner->GetExtderSystem()->GetExtderById(ext_id_opt.value()); }
|
||||
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
void DevFilaSwitch::ParseFilaSwitchInfo(const nlohmann::json &print_jj)
|
||||
{
|
||||
if (print_jj.contains("aux")) {
|
||||
const auto &info_bits = DevJsonValParser::GetVal<std::string>(print_jj, "aux");
|
||||
if (m_is_installed != (DevUtil::get_flag_bits(info_bits, 29, 1) == 1)) {
|
||||
m_is_installed = (DevUtil::get_flag_bits(info_bits, 29, 1) == 1);
|
||||
if (!m_is_installed) { Reset(); }
|
||||
};
|
||||
}
|
||||
|
||||
if (print_jj.contains("device") && print_jj["device"].contains("fila_switch")) {
|
||||
const auto &fila_switch_jj = print_jj["device"]["fila_switch"];
|
||||
if (fila_switch_jj.contains("in")) {
|
||||
const auto &in_vec = DevJsonValParser::GetVal<std::vector<int>>(fila_switch_jj, "in");
|
||||
if (in_vec.size() == 2) {
|
||||
if (in_vec[0] != -1) {
|
||||
DevAmsSlotId slot_id;
|
||||
slot_id.first = DevUtil::get_flag_bits(in_vec[0], 8, 8);
|
||||
slot_id.second = DevUtil::get_flag_bits(in_vec[0], 0, 8);
|
||||
m_in_b_slot = slot_id;
|
||||
} else {
|
||||
m_in_b_slot = std::nullopt;
|
||||
};
|
||||
|
||||
if (in_vec[1] != -1) {
|
||||
DevAmsSlotId slot_id;
|
||||
slot_id.first = DevUtil::get_flag_bits(in_vec[1], 8, 8);
|
||||
slot_id.second = DevUtil::get_flag_bits(in_vec[1], 0, 8);
|
||||
m_in_a_slot = slot_id;
|
||||
} else {
|
||||
m_in_a_slot.reset();
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
if (fila_switch_jj.contains("out")) {
|
||||
const auto &out_vec = DevJsonValParser::GetVal<std::vector<int>>(fila_switch_jj, "out");
|
||||
if (out_vec.size() == 2) {
|
||||
if (out_vec[0] != 0xE) {
|
||||
m_out_b_extruder_id = out_vec[0];
|
||||
} else {
|
||||
m_out_b_extruder_id.reset();
|
||||
}
|
||||
|
||||
if (out_vec[1] != 0xE) {
|
||||
m_out_a_extruder_id = out_vec[1];
|
||||
} else {
|
||||
m_out_a_extruder_id.reset();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (fila_switch_jj.contains("stat")) { m_cali_status = DevJsonValParser::GetVal<CaliStatus>(fila_switch_jj, "stat"); }
|
||||
|
||||
if (fila_switch_jj.contains("info")) {
|
||||
const auto &info_bits = DevJsonValParser::GetVal<int>(fila_switch_jj, "info");
|
||||
m_in_b_has_filament = DevUtil::get_flag_bits(info_bits, 0, 1);
|
||||
m_in_a_has_filament = DevUtil::get_flag_bits(info_bits, 0, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}; // namespace Slic3r
|
||||
@@ -0,0 +1,81 @@
|
||||
#pragma once
|
||||
#include "DevDefs.h"
|
||||
#include <optional>
|
||||
|
||||
namespace Slic3r
|
||||
{
|
||||
// Previous definitions
|
||||
class MachineObject;
|
||||
class DevExtder;
|
||||
class DevAmsTray;
|
||||
|
||||
class DevFilaSwitch
|
||||
{
|
||||
public:
|
||||
enum class CaliStatus : int
|
||||
{
|
||||
CALI_IDLE = 0,
|
||||
CALI_STEPING = 1,
|
||||
};
|
||||
|
||||
enum class CaliStep : int
|
||||
{
|
||||
CALI_IDLE = 0,
|
||||
CALI_SWITCHING = 1,
|
||||
CALI_SWITCH_CHECK = 2,
|
||||
CALI_FILA_CHECK = 3,
|
||||
CALI_FILA_TO_AMS = 4,
|
||||
CALI_FILA_TO_SWITCH = 5,
|
||||
CALI_FILA_BACK = 9,
|
||||
CALI_FINISHED = 14,
|
||||
};
|
||||
|
||||
enum SwitchPos : int
|
||||
{
|
||||
POS_IN_B = 0,
|
||||
POS_IN_A = 1,
|
||||
};
|
||||
|
||||
public:
|
||||
DevFilaSwitch(MachineObject* owner);
|
||||
virtual ~DevFilaSwitch() = default;
|
||||
|
||||
public:
|
||||
bool IsInstalled() const { return m_is_installed; };
|
||||
bool IsReady() const;
|
||||
|
||||
std::optional<bool> IsInA_HasFilament() const { return m_in_a_has_filament; };
|
||||
std::optional<bool> IsInB_HasFilament() const { return m_in_b_has_filament; };
|
||||
|
||||
std::optional<DevAmsTray> GetInA_Slot() const;
|
||||
std::optional<DevAmsTray> GetInB_Slot() const;
|
||||
std::optional<DevAmsSlotId> GetInA_SlotId() const { return m_in_a_slot; };
|
||||
std::optional<DevAmsSlotId> GetInB_SlotId() const { return m_in_b_slot; };
|
||||
|
||||
std::optional<DevExtder> GetOutA_Extruder() const;
|
||||
std::optional<DevExtder> GetOutB_Extruder() const;
|
||||
std::optional<int> GetOutA_ExtruderId() const { return m_out_a_extruder_id; };
|
||||
std::optional<int> GetOutB_ExtruderId() const { return m_out_b_extruder_id; };
|
||||
|
||||
CaliStatus GetCaliStatus() const { return m_cali_status; };
|
||||
|
||||
void Reset();
|
||||
void ParseFilaSwitchInfo(const nlohmann::json& print_jj);
|
||||
|
||||
private:
|
||||
MachineObject* m_owner;
|
||||
|
||||
bool m_is_installed = false;
|
||||
|
||||
std::optional<bool> m_in_a_has_filament;
|
||||
std::optional<bool> m_in_b_has_filament;
|
||||
|
||||
std::optional<DevAmsSlotId> m_in_a_slot;
|
||||
std::optional<DevAmsSlotId> m_in_b_slot;
|
||||
|
||||
std::optional<int> m_out_a_extruder_id;
|
||||
std::optional<int> m_out_b_extruder_id;
|
||||
|
||||
CaliStatus m_cali_status = CaliStatus::CALI_IDLE;
|
||||
};
|
||||
};
|
||||
@@ -1,11 +1,13 @@
|
||||
#include <nlohmann/json.hpp>
|
||||
#include "DevFilaSystem.h"
|
||||
#include "DevNozzleSystem.h" // DevNozzle / DevNozzleSystem for GetNozzleFlowStringByAmsId
|
||||
|
||||
// TODO: remove this include
|
||||
#include "slic3r/GUI/DeviceManager.hpp"
|
||||
#include "slic3r/GUI/I18N.hpp"
|
||||
|
||||
#include "DevUtil.h"
|
||||
#include "DevUtilBackend.h"
|
||||
|
||||
using namespace nlohmann;
|
||||
|
||||
@@ -98,6 +100,11 @@ std::string DevAmsTray::get_filament_type()
|
||||
return m_fila_type;
|
||||
}
|
||||
|
||||
std::optional<Slic3r::DevFilamentDryingPreset> DevAmsTray::get_ams_drying_preset() const
|
||||
{
|
||||
return DevUtilBackend::GetFilamentDryingPreset(setting_id);
|
||||
}
|
||||
|
||||
|
||||
DevAms::DevAms(const std::string& ams_id, int extruder_id, AmsType type)
|
||||
{
|
||||
@@ -111,7 +118,7 @@ DevAms::DevAms(const std::string& ams_id, int nozzle_id, int type)
|
||||
m_ams_id = ams_id;
|
||||
m_ext_id = nozzle_id;
|
||||
m_ams_type = (AmsType)type;
|
||||
assert(DUMMY < type && m_ams_type <= N3S);
|
||||
assert(EXT_SPOOL < type && m_ams_type <= AMS_LITE_MIXED);
|
||||
}
|
||||
|
||||
DevAms::~DevAms()
|
||||
@@ -137,7 +144,8 @@ static unordered_map<int, wxString> s_ams_display_formats = {
|
||||
wxString DevAms::GetDisplayName() const
|
||||
{
|
||||
wxString ams_display_format;
|
||||
auto iter = s_ams_display_formats.find(m_ams_type);
|
||||
// GetAmsType() maps AMS_LITE_MIXED -> AMS_LITE so N9 shows the AMS-Lite name.
|
||||
auto iter = s_ams_display_formats.find(GetAmsType());
|
||||
if (iter != s_ams_display_formats.end())
|
||||
{
|
||||
ams_display_format = iter->second;
|
||||
@@ -166,11 +174,13 @@ wxString DevAms::GetDisplayName() const
|
||||
|
||||
int DevAms::GetSlotCount() const
|
||||
{
|
||||
if (m_ams_type == AMS || m_ams_type == AMS_LITE || m_ams_type == N3F)
|
||||
// GetAmsType() maps AMS_LITE_MIXED -> AMS_LITE, so N9 reports 4 slots like AMS-Lite.
|
||||
auto ams_type = GetAmsType();
|
||||
if (ams_type == AMS || ams_type == AMS_LITE || ams_type == N3F)
|
||||
{
|
||||
return 4;
|
||||
}
|
||||
else if (m_ams_type == N3S)
|
||||
else if (ams_type == N3S)
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
@@ -189,6 +199,27 @@ DevAmsTray* DevAms::GetTray(const std::string& tray_id) const
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool DevAms::IsSupportRemoteDry(const MachineObject* obj) const
|
||||
{
|
||||
if (obj && obj->is_support_remote_dry) {
|
||||
return SupportDrying();
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool DevAms::AmsIsDrying()
|
||||
{
|
||||
if (!GetDryStatus().has_value()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return GetDryStatus().value() == DevAms::DryStatus::Checking
|
||||
|| GetDryStatus().value() == DevAms::DryStatus::Drying
|
||||
|| GetDryStatus().value() == DevAms::DryStatus::Error
|
||||
|| GetDryStatus().value() == DevAms::DryStatus::CannotStopHeatOutofControl;
|
||||
}
|
||||
|
||||
DevFilaSystem::~DevFilaSystem()
|
||||
{
|
||||
for (auto it = amsList.begin(); it != amsList.end(); it++)
|
||||
@@ -258,6 +289,44 @@ int DevFilaSystem::GetExtruderIdByAmsId(const std::string& ams_id) const
|
||||
return 0; // not found
|
||||
}
|
||||
|
||||
std::string DevFilaSystem::GetNozzleFlowStringByAmsId(const std::string& ams_id) const
|
||||
{
|
||||
auto extruder_id = GetExtruderIdByAmsId(ams_id);
|
||||
auto nozzle = GetOwner()->GetNozzleSystem()->GetExtNozzle(extruder_id);
|
||||
return DevNozzle::GetNozzleFlowTypeString(nozzle.GetNozzleFlowType());
|
||||
}
|
||||
|
||||
std::map<int, DevAmsSlotId> DevFilaSystem::GetTrayIndexMap()
|
||||
{
|
||||
std::map<int, DevAmsSlotId> tray_id_map;
|
||||
tray_id_map[VIRTUAL_TRAY_MAIN_ID] = DevAmsSlotId{VIRTUAL_TRAY_MAIN_ID, 0};
|
||||
tray_id_map[VIRTUAL_TRAY_DEPUTY_ID] = DevAmsSlotId{VIRTUAL_TRAY_DEPUTY_ID, 0};
|
||||
|
||||
for (auto& [ams_id, ams_item] : GetAmsList()) {
|
||||
for (auto &[slot_id, slot_item] : ams_item->GetTrays()) {
|
||||
if (ams_item && slot_item) {
|
||||
try {
|
||||
int ams_id_int = stoi(ams_id);
|
||||
int slot_id_int = stoi(slot_id);
|
||||
int tray_index = -1;
|
||||
if (ams_item->GetAmsType() == DevAms::N3S) {
|
||||
tray_index = ams_id_int;
|
||||
} else if(ams_item->GetAmsType() == DevAms::AMS_LITE && ams_item->IsAmsLiteMixed()) {
|
||||
tray_index = 24 + slot_id_int;
|
||||
} else {
|
||||
tray_index = (ams_id_int * 4 + slot_id_int);
|
||||
}
|
||||
tray_id_map[tray_index] = {ams_id_int, slot_id_int};
|
||||
} catch(...) {
|
||||
BOOST_LOG_TRIVIAL(warning) << __FUNCTION__ << "invalid ams id: " << ams_id << " or slot id: " << slot_id;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return tray_id_map;
|
||||
}
|
||||
|
||||
bool DevFilaSystem::IsAmsSettingUp() const
|
||||
{
|
||||
int setting_up_stat = DevUtil::get_flag_bits(m_ams_cali_stat, 0, 8);
|
||||
@@ -304,6 +373,15 @@ void DevFilaSystemParser::ParseV1_0(const json& jj, MachineObject* obj, DevFilaS
|
||||
|
||||
if (!key_field_only)
|
||||
{
|
||||
// Newer firmware sends the exact filament-change step sequence in ams.cfs, so the
|
||||
// client no longer hardcodes the steps per model. Absent on current firmware, which
|
||||
// keeps the list empty and the legacy hardcoded/ams_status_sub step path in effect.
|
||||
if (jj["ams"].contains("cfs")) {
|
||||
system->m_filament_change_steps = DevJsonValParser::GetVal<std::vector<DevFilamentStep>>(jj["ams"], "cfs");
|
||||
} else {
|
||||
system->m_filament_change_steps.clear();
|
||||
}
|
||||
|
||||
if (jj["ams"].contains("tray_read_done_bits"))
|
||||
{
|
||||
obj->tray_read_done_bits = stol(jj["ams"]["tray_read_done_bits"].get<std::string>(), nullptr, 16);
|
||||
@@ -361,17 +439,43 @@ void DevFilaSystemParser::ParseV1_0(const json& jj, MachineObject* obj, DevFilaS
|
||||
int type_id = 1; // 0:dummy 1:ams 2:ams-lite 3:n3f 4:n3s
|
||||
|
||||
/*ams info*/
|
||||
std::set<int> binded_extruder_set;
|
||||
std::optional<DevFilaSwitch::SwitchPos> binded_switcher_pos;
|
||||
if (it->contains("info")) {
|
||||
const std::string& info = (*it)["info"].get<std::string>();
|
||||
type_id = DevUtil::get_flag_bits(info, 0, 4);
|
||||
extuder_id = DevUtil::get_flag_bits(info, 8, 4);
|
||||
if (extuder_id == 0xE && obj->GetFilaSwitch()->IsInstalled()) {
|
||||
int bind_switch_in = DevUtil::get_flag_bits(info, 24, 4);
|
||||
if (bind_switch_in == 0 || bind_switch_in == 1) {
|
||||
binded_extruder_set = { MAIN_EXTRUDER_ID, DEPUTY_EXTRUDER_ID };
|
||||
}
|
||||
|
||||
if (bind_switch_in == 0) {
|
||||
binded_switcher_pos = DevFilaSwitch::SwitchPos::POS_IN_B;
|
||||
} else if (bind_switch_in == 1) {
|
||||
binded_switcher_pos = DevFilaSwitch::SwitchPos::POS_IN_A;
|
||||
}
|
||||
|
||||
// Orca: the switch feeds every AMS to both extruders; pin a deterministic
|
||||
// single-extruder id so legacy GetExtruderId() consumers keep a valid value
|
||||
// while switch-aware code reads the binding set instead.
|
||||
extuder_id = MAIN_EXTRUDER_ID;
|
||||
} else if (extuder_id != 0xE) {
|
||||
binded_extruder_set = { extuder_id };
|
||||
}
|
||||
} else {
|
||||
if (!obj->is_enable_ams_np && obj->get_printer_ams_type() == "f1") {
|
||||
type_id = DevAms::AMS_LITE;
|
||||
}
|
||||
binded_extruder_set = { MAIN_EXTRUDER_ID };
|
||||
}
|
||||
|
||||
/*AMS without initialization*/
|
||||
// Orca: an AMS reporting extruder 0xE without a Filament Track Switch installed has
|
||||
// no usable extruder binding; drop it, preserving the existing display for
|
||||
// half-initialized printers. With the switch installed the 0xE case is remapped
|
||||
// above to both extruders and falls through to normal handling.
|
||||
if (extuder_id == 0xE)
|
||||
{
|
||||
ams_id_set.erase(ams_id);
|
||||
@@ -403,6 +507,13 @@ void DevFilaSystemParser::ParseV1_0(const json& jj, MachineObject* obj, DevFilaS
|
||||
/*set ams type flag*/
|
||||
curr_ams->SetAmsType(type_id);
|
||||
|
||||
// Refresh the switch-aware extruder binding on every push (both create and
|
||||
// update paths) so it can't go stale after an AMS is re-homed to another
|
||||
// extruder. Without the switch the set holds the single bound extruder and
|
||||
// the track position stays empty.
|
||||
curr_ams->m_binded_extruder_set = binded_extruder_set;
|
||||
curr_ams->m_binded_switcher_pos = binded_switcher_pos;
|
||||
|
||||
|
||||
/*set ams exist flag*/
|
||||
try
|
||||
@@ -415,6 +526,11 @@ void DevFilaSystemParser::ParseV1_0(const json& jj, MachineObject* obj, DevFilaS
|
||||
{
|
||||
curr_ams->m_exist = (obj->ams_exist_bits & (1 << ams_id_int)) != 0 ? true : false;
|
||||
}
|
||||
else if (type_id == DevAms::AMS_LITE_MIXED)
|
||||
{
|
||||
// Mixed AMS-Lite (A2L / N9) exist flag lives at bit 12.
|
||||
curr_ams->m_exist = DevUtil::get_flag_bits(obj->ams_exist_bits, 12);
|
||||
}
|
||||
else
|
||||
{
|
||||
curr_ams->m_exist = DevUtil::get_flag_bits(obj->ams_exist_bits, 4 + (ams_id_int - 128));
|
||||
@@ -426,9 +542,18 @@ void DevFilaSystemParser::ParseV1_0(const json& jj, MachineObject* obj, DevFilaS
|
||||
;
|
||||
}
|
||||
|
||||
if (it->contains("dry_time") && (*it)["dry_time"].is_number())
|
||||
|
||||
if (it->contains("temp"))
|
||||
{
|
||||
curr_ams->m_left_dry_time = (*it)["dry_time"].get<int>();
|
||||
std::string temp = (*it)["temp"].get<std::string>();
|
||||
try
|
||||
{
|
||||
curr_ams->m_current_temperature = DevUtil::string_to_float(temp);
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
curr_ams->m_current_temperature = INVALID_AMS_TEMPERATURE;
|
||||
}
|
||||
}
|
||||
|
||||
if (it->contains("humidity"))
|
||||
@@ -457,17 +582,32 @@ void DevFilaSystemParser::ParseV1_0(const json& jj, MachineObject* obj, DevFilaS
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (it->contains("temp"))
|
||||
if (it->contains("dry_time") && (*it)["dry_time"].is_number())
|
||||
{
|
||||
std::string temp = (*it)["temp"].get<std::string>();
|
||||
try
|
||||
{
|
||||
curr_ams->m_current_temperature = DevUtil::string_to_float(temp);
|
||||
curr_ams->m_left_dry_time = (*it)["dry_time"].get<int>();
|
||||
}
|
||||
|
||||
// Drying status — only parse if printer supports remote drying
|
||||
if (obj->is_support_remote_dry) {
|
||||
if (it->contains("info")) {
|
||||
const std::string& info = (*it)["info"].get<std::string>();
|
||||
curr_ams->m_dry_status = (DevAms::DryStatus)DevUtil::get_flag_bits(info, 4, 4);
|
||||
curr_ams->m_dry_fan1_status = (DevAms::DryFanStatus)DevUtil::get_flag_bits(info, 18, 2);
|
||||
curr_ams->m_dry_fan2_status = (DevAms::DryFanStatus)DevUtil::get_flag_bits(info, 20, 2);
|
||||
curr_ams->m_dry_sub_status = (DevAms::DrySubStatus)DevUtil::get_flag_bits(info, 22, 2);
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
curr_ams->m_current_temperature = INVALID_AMS_TEMPERATURE;
|
||||
|
||||
if (it->contains("dry_setting")) {
|
||||
const auto& j_dry_settings = (*it)["dry_setting"];
|
||||
DevAms::DrySettings dry_settings;
|
||||
DevJsonValParser::ParseVal(j_dry_settings, "dry_filament", dry_settings.dry_filament);
|
||||
DevJsonValParser::ParseVal(j_dry_settings, "dry_temperature", dry_settings.dry_temp);
|
||||
DevJsonValParser::ParseVal(j_dry_settings, "dry_duration", dry_settings.dry_hour);
|
||||
curr_ams->m_dry_settings = dry_settings;
|
||||
}
|
||||
|
||||
if (it->contains("dry_sf_reason")) {
|
||||
curr_ams->m_dry_cannot_reasons = DevJsonValParser::GetVal<std::vector<DevAms::CannotDryReason>>((*it), "dry_sf_reason");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -628,6 +768,11 @@ void DevFilaSystemParser::ParseV1_0(const json& jj, MachineObject* obj, DevFilaS
|
||||
{
|
||||
curr_tray->is_exists = (obj->tray_exist_bits & (1 << (ams_id_int * 4 + tray_id_int))) != 0 ? true : false;
|
||||
}
|
||||
else if (type_id == DevAms::AMS_LITE_MIXED)
|
||||
{
|
||||
// Mixed AMS-Lite (A2L / N9) trays occupy tray-exist bits 24..27.
|
||||
curr_tray->is_exists = DevUtil::get_flag_bits(obj->tray_exist_bits, AMS_LITE_MIXED_TRAY_INDEX_OFFSET + tray_id_int);
|
||||
}
|
||||
else
|
||||
{
|
||||
curr_tray->is_exists = DevUtil::get_flag_bits(obj->tray_exist_bits, 16 + (ams_id_int - 128));
|
||||
@@ -694,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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -4,11 +4,17 @@
|
||||
|
||||
#include "DevDefs.h"
|
||||
#include "DevFilaAmsSetting.h"
|
||||
#include "DevFilaSwitch.h"
|
||||
#include "DevUtil.h"
|
||||
|
||||
#include <map>
|
||||
#include <set>
|
||||
#include <vector>
|
||||
#include <optional>
|
||||
#include <memory>
|
||||
#include <unordered_map>
|
||||
#include <unordered_set>
|
||||
#include <vector>
|
||||
#include <wx/string.h>
|
||||
#include <wx/colour.h>
|
||||
|
||||
@@ -17,6 +23,7 @@
|
||||
namespace Slic3r
|
||||
{
|
||||
class MachineObject;
|
||||
struct DevFilamentDryingPreset;
|
||||
|
||||
/**
|
||||
* DevAmsTray - Represents a single filament tray/slot in an AMS unit or virtual tray.
|
||||
@@ -103,6 +110,8 @@ public:
|
||||
|
||||
// static
|
||||
static wxColour decode_color(const std::string& color);
|
||||
|
||||
std::optional<DevFilamentDryingPreset> get_ams_drying_preset() const;
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -130,13 +139,67 @@ class DevAms
|
||||
{
|
||||
friend class DevFilaSystemParser;
|
||||
public:
|
||||
enum AmsType : int
|
||||
using AmsType = DevAmsType;
|
||||
static constexpr AmsType EXT_SPOOL = DevAmsType::EXT_SPOOL;
|
||||
static constexpr AmsType AMS = DevAmsType::AMS;
|
||||
static constexpr AmsType AMS_LITE = DevAmsType::AMS_LITE;
|
||||
static constexpr AmsType N3F = DevAmsType::N3F;
|
||||
static constexpr AmsType N3S = DevAmsType::N3S;
|
||||
static constexpr AmsType AMS_LITE_MIXED = DevAmsType::AMS_LITE_MIXED;
|
||||
|
||||
public:
|
||||
|
||||
enum class DryCtrlMode : int
|
||||
{
|
||||
DUMMY = 0,
|
||||
AMS = 1, // AMS
|
||||
AMS_LITE = 2, // AMS-Lite
|
||||
N3F = 3, // N3F
|
||||
N3S = 4, // N3S
|
||||
Off = 0,
|
||||
OnTime = 1,
|
||||
OnHumidity = 2,
|
||||
};
|
||||
|
||||
enum class DryStatus : char
|
||||
{
|
||||
Off = 0,
|
||||
Checking = 1,
|
||||
Drying = 2,
|
||||
Cooling = 3,
|
||||
Stopping = 4,
|
||||
Error = 5,
|
||||
CannotStopHeatOutofControl = 6,
|
||||
PrdTesting = 7,
|
||||
};
|
||||
|
||||
enum class DrySubStatus
|
||||
{
|
||||
Off = 0,
|
||||
Heating = 1,
|
||||
Dehumidify = 2,
|
||||
};
|
||||
|
||||
enum class DryFanStatus : char
|
||||
{
|
||||
Off = 0,
|
||||
On = 1,
|
||||
};
|
||||
|
||||
enum class CannotDryReason : int
|
||||
{
|
||||
TaskOccupied = 0,
|
||||
InsufficientPower = 1,
|
||||
AmsBusy = 2,
|
||||
ConsumableAtAmsOutlet = 3,
|
||||
InitiatingAmsDrying = 4,
|
||||
NotSupportedIn2dMode = 5,
|
||||
DryingInProgress = 6,
|
||||
Upgrading = 7,
|
||||
InsufficientPowerNeedPluginPower = 8,
|
||||
FilamentAtAmsOutletManualUnload = 10,
|
||||
};
|
||||
|
||||
struct DrySettings
|
||||
{
|
||||
std::string dry_filament;
|
||||
int dry_temp = -1; // -1 means invalid
|
||||
int dry_hour = -1; // -1 means invalid, hours
|
||||
};
|
||||
|
||||
public:
|
||||
@@ -150,11 +213,16 @@ public:
|
||||
|
||||
void SetAmsType(int type) { m_ams_type = (AmsType)type; }
|
||||
void SetAmsType(AmsType type) { m_ams_type = type; }
|
||||
AmsType GetAmsType() const { return m_ams_type; }
|
||||
// AMS-Lite-mixed (N9) is a variant of AMS-Lite: report AMS_LITE to generic callers so all
|
||||
// existing AMS_LITE handling applies. Mixed-specific code uses IsAmsLiteMixed().
|
||||
AmsType GetAmsType() const { return m_ams_type == AMS_LITE_MIXED ? AMS_LITE : m_ams_type; }
|
||||
|
||||
// exist or not
|
||||
bool IsExist() const { return m_exist; }
|
||||
|
||||
// AMS-Lite-mixed for N9 (A2L)
|
||||
bool IsAmsLiteMixed() const { return m_ams_type == AMS_LITE_MIXED; }
|
||||
|
||||
// slots
|
||||
int GetSlotCount() const;
|
||||
DevAmsTray* GetTray(const std::string& tray_id) const;
|
||||
@@ -163,6 +231,20 @@ public:
|
||||
// installed on the extruder
|
||||
int GetExtruderId() const { return m_ext_id; }
|
||||
|
||||
// Extruders this AMS can currently feed. Without a Filament Track Switch installed this
|
||||
// is the single {m_ext_id}; with the switch installed the device binds it to both extruders.
|
||||
const std::set<int>& GetBindedExtruderSet() const { return m_binded_extruder_set; }
|
||||
|
||||
// The bound extruder when exactly one is bound, empty otherwise. Used to attribute an AMS
|
||||
// to a single extruder on printers without the switch.
|
||||
std::optional<int> GetUniqueBindedExtruderId() const
|
||||
{
|
||||
return m_binded_extruder_set.size() == 1 ? std::optional<int>(*m_binded_extruder_set.begin()) : std::nullopt;
|
||||
}
|
||||
|
||||
// Which switch input track (A/B) feeds this AMS, set only when a Filament Track Switch is installed.
|
||||
std::optional<DevFilaSwitch::SwitchPos> GetSwitcherPos() const { return m_binded_switcher_pos; }
|
||||
|
||||
// temperature and humidity
|
||||
float GetCurrentTemperature() const { return m_current_temperature; }
|
||||
|
||||
@@ -170,13 +252,30 @@ public:
|
||||
int GetHumidityLevel() const { return m_humidity_level; }
|
||||
int GetHumidityPercent() const { return m_humidity_percent; }
|
||||
|
||||
bool SupportDrying() const { return m_ams_type > AMS_LITE; }
|
||||
bool SupportDrying() const { return m_ams_type == DevAmsType::N3F || m_ams_type == DevAmsType::N3S; }
|
||||
int GetLeftDryTime() const { return m_left_dry_time; }
|
||||
|
||||
// remote drying control
|
||||
bool IsSupportRemoteDry(const MachineObject* obj) const;
|
||||
std::optional<DryStatus> GetDryStatus() const { return m_dry_status; };
|
||||
std::optional<DrySubStatus> GetDrySubStatus() const { return m_dry_sub_status; }
|
||||
std::optional<DryFanStatus> GetFan1Status() const { return m_dry_fan1_status; }
|
||||
std::optional<DryFanStatus> GetFan2Status() const { return m_dry_fan2_status; }
|
||||
std::optional<std::vector<CannotDryReason>> GetCannotDryReason() const { return m_dry_cannot_reasons; }
|
||||
std::optional<DrySettings> GetDrySettings() const { return m_dry_settings; };
|
||||
|
||||
bool AmsIsDrying();
|
||||
|
||||
private:
|
||||
AmsType m_ams_type = AmsType::AMS;
|
||||
std::string m_ams_id;
|
||||
int m_ext_id;//extruder id
|
||||
// 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;
|
||||
|
||||
// slots and trays
|
||||
@@ -187,6 +286,14 @@ private:
|
||||
int m_humidity_level = 5; // AmsType::AMS
|
||||
int m_humidity_percent = -1; // N3F N3S, the percentage, -1 means invalid. eg. 100 means 100%
|
||||
int m_left_dry_time = 0;
|
||||
|
||||
// see is_support_remote_dry
|
||||
std::optional<DryStatus> m_dry_status;
|
||||
std::optional<DrySubStatus> m_dry_sub_status;
|
||||
std::optional<DryFanStatus> m_dry_fan1_status;
|
||||
std::optional<DryFanStatus> m_dry_fan2_status;
|
||||
std::optional<std::vector<CannotDryReason>> m_dry_cannot_reasons;
|
||||
std::optional<DrySettings> m_dry_settings;
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -241,9 +348,16 @@ public:
|
||||
DevAmsTray* GetAmsTray(const std::string& ams_id, const std::string& tray_id) const;
|
||||
void CollectAmsColors(std::vector<wxColour>& ams_colors) const;
|
||||
|
||||
// Map a linear tray index -> {ams_id, slot_id}. Includes the two virtual (external-spool) trays,
|
||||
// N3S single-slot units, and the A2L/N9 AMS-Lite-mixed layout (trays 24-27).
|
||||
std::map<int, DevAmsSlotId> GetTrayIndexMap();
|
||||
|
||||
// extruder
|
||||
int GetExtruderIdByAmsId(const std::string& ams_id) const;
|
||||
|
||||
// nozzle: untranslated flow-type string of the extruder bound to this ams (for blacklist matching)
|
||||
std::string GetNozzleFlowStringByAmsId(const std::string& ams_id) const;
|
||||
|
||||
/* AMS settings*/
|
||||
DevAmsSystemSetting& GetAmsSystemSetting() { return m_ams_system_setting; }
|
||||
std::optional<bool> IsDetectOnInsertEnabled() const { return m_ams_system_setting.IsDetectOnInsertEnabled(); };
|
||||
@@ -253,10 +367,19 @@ public:
|
||||
|
||||
std::weak_ptr<DevAmsSystemFirmwareSwitch> GetAmsFirmwareSwitch() const { return m_ams_firmware_switch;}
|
||||
|
||||
// filament change steps
|
||||
// The exact change-step sequence reported by the AMS (ams.cfs). Empty on current firmware,
|
||||
// which leaves the legacy hardcoded/ams_status_sub step handling in effect.
|
||||
const std::vector<DevFilamentStep>& GetFilamentChangeSteps() const { return m_filament_change_steps; }
|
||||
|
||||
public:
|
||||
// ctrls
|
||||
int CtrlAmsReset() const;
|
||||
|
||||
|
||||
// crtl
|
||||
int CtrlAmsStartDryingHour(int ams_id, std::string filament_type, int tag_temp, int tag_duration_hour, bool rotate_tray, int cooling_temp, bool close_power_conflict = false) const;
|
||||
int CtrlAmsStopDrying(int ams_id) const;
|
||||
|
||||
public:
|
||||
static bool IsBBL_Filament(std::string tag_uid);
|
||||
|
||||
@@ -266,6 +389,9 @@ private:
|
||||
/* ams properties */
|
||||
int m_ams_cali_stat = 0;
|
||||
|
||||
// Change-step sequence reported by the AMS (ams.cfs); empty when firmware doesn't send it.
|
||||
std::vector<DevFilamentStep> m_filament_change_steps;
|
||||
|
||||
std::map<std::string, DevAms*, NumericStrCompare> amsList;// key: ams[id], start with 0
|
||||
|
||||
DevAmsSystemSetting m_ams_system_setting{ this };
|
||||
@@ -289,6 +415,22 @@ 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
|
||||
{
|
||||
std::string filament_id;
|
||||
|
||||
std::unordered_set<DevAmsType> ams_limitations; // only use ams types in the set
|
||||
std::unordered_map<DevAmsType, float> filament_dev_ams_drying_time_on_idle; // hour
|
||||
std::unordered_map<DevAmsType, float> filament_dev_ams_drying_temperature_on_idle;
|
||||
std::unordered_map<DevAmsType, float> filament_dev_ams_drying_time_on_print; // hour
|
||||
std::unordered_map<DevAmsType, float> filament_dev_ams_drying_temperature_on_print;
|
||||
float filament_dev_drying_cooling_temperature = 0.0f;
|
||||
float filament_dev_drying_softening_temperature = 0.0f;
|
||||
float filament_dev_ams_drying_heat_distortion_temperature = 0.0f;
|
||||
};
|
||||
|
||||
}// namespace Slic3r
|
||||
@@ -16,4 +16,44 @@ int DevFilaSystem::CtrlAmsReset() const
|
||||
return m_owner->publish_json(jj_command);
|
||||
}
|
||||
|
||||
int DevFilaSystem::CtrlAmsStartDryingHour(int ams_id,
|
||||
std::string filament_type,
|
||||
int tag_temp,
|
||||
int tag_duration_hour,
|
||||
bool rotate_tray,
|
||||
int cooling_temp,
|
||||
bool close_power_conflict) const
|
||||
{
|
||||
json jj_command;
|
||||
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); // 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;
|
||||
jj_command["print"]["rotate_tray"] = rotate_tray;
|
||||
jj_command["print"]["cooling_temp"] = cooling_temp;
|
||||
jj_command["print"]["close_power_conflict"] = close_power_conflict;
|
||||
return m_owner->publish_json(jj_command);
|
||||
}
|
||||
|
||||
int DevFilaSystem::CtrlAmsStopDrying(int ams_id) const
|
||||
{
|
||||
json jj_command;
|
||||
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); // 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;
|
||||
jj_command["print"]["rotate_tray"] = false;
|
||||
jj_command["print"]["cooling_temp"] = 0;
|
||||
jj_command["print"]["close_power_conflict"] = false;
|
||||
return m_owner->publish_json(jj_command);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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,10 +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 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)
|
||||
@@ -477,6 +496,26 @@ namespace Slic3r
|
||||
OnSelectedMachineChanged(previous_selected_machine, selected_machine);
|
||||
}
|
||||
|
||||
void DeviceManager::clear_other_devices()
|
||||
{
|
||||
// why: on agent swap, keep "My Devices" but drop the transient "Other Devices"
|
||||
// Those belong to the previous agent's network scan; the new agent's start_discovery re-populates its own.
|
||||
const auto my = get_my_machine_list();
|
||||
for (auto it = localMachineList.begin(); it != localMachineList.end();)
|
||||
{
|
||||
if (my.find(it->first) == my.end())
|
||||
{
|
||||
// not a "My Device" -> an "Other Device"
|
||||
delete it->second;
|
||||
it = localMachineList.erase(it);
|
||||
}
|
||||
else
|
||||
{
|
||||
++it;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool DeviceManager::set_selected_machine(std::string dev_id)
|
||||
{
|
||||
BOOST_LOG_TRIVIAL(info) << "set_selected_machine=" << dev_id
|
||||
@@ -572,6 +611,7 @@ namespace Slic3r
|
||||
}
|
||||
|
||||
selected_machine = dev_id;
|
||||
record_user_last_machine(selected_machine);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -830,26 +870,44 @@ namespace Slic3r
|
||||
int result = m_agent->get_user_print_info(&http_code, &body, provider);
|
||||
if (result == 0)
|
||||
{
|
||||
parse_user_print_info(body);
|
||||
// parse_user_print_info and on_machine_alive (SSDP for discovery) both mutate the same userMachineList map.
|
||||
// on_machine_alive mutates the map on the UI thread, do the same for parse_user_print_info.
|
||||
Slic3r::GUI::wxGetApp().CallAfter([this, body]() { parse_user_print_info(body); });
|
||||
}
|
||||
}
|
||||
|
||||
void DeviceManager::record_user_last_machine(const std::string& dev_id)
|
||||
{
|
||||
if (Slic3r::GUI::wxGetApp().app_config) {
|
||||
Slic3r::GUI::wxGetApp().app_config->set("user_last_selected_machine", dev_id);
|
||||
}
|
||||
}
|
||||
|
||||
std::string DeviceManager::get_user_last_machine() const
|
||||
{
|
||||
if (Slic3r::GUI::wxGetApp().app_config) {
|
||||
const auto& user_last_machine = Slic3r::GUI::wxGetApp().app_config->get("user_last_selected_machine");
|
||||
if (!user_last_machine.empty()) {
|
||||
return user_last_machine;
|
||||
} else if (m_agent) {
|
||||
return m_agent->get_user_selected_machine();
|
||||
}
|
||||
}
|
||||
|
||||
return "";
|
||||
}
|
||||
|
||||
void DeviceManager::load_last_machine()
|
||||
{
|
||||
// Get all available machines, include cloud machines and lan machines that have access right
|
||||
auto all_machines = get_my_machine_list();
|
||||
if (all_machines.empty())
|
||||
// Only reconnect the remembered cloud machine. Do not select an arbitrary
|
||||
// first machine: agent swaps intentionally leave the selection empty until
|
||||
// the new agent explicitly selects its configured printer.
|
||||
if (userMachineList.empty())
|
||||
return;
|
||||
|
||||
// Then connect to the machine we last selected if available
|
||||
const std::string last_monitor_machine = m_agent ? m_agent->get_user_selected_machine() : "";
|
||||
const auto last_machine = all_machines.find(last_monitor_machine);
|
||||
if (last_machine != all_machines.end()) {
|
||||
this->set_selected_machine(last_machine->second->get_dev_id());
|
||||
} else {
|
||||
// If not, then select the first available one
|
||||
this->set_selected_machine(all_machines.begin()->second->get_dev_id());
|
||||
}
|
||||
|
||||
const auto& last_monitor_machine = get_user_last_machine();
|
||||
if (userMachineList.find(last_monitor_machine) != userMachineList.end())
|
||||
set_selected_machine(last_monitor_machine);
|
||||
}
|
||||
|
||||
void DeviceManager::OnMachineBindStateChanged(MachineObject* obj, const std::string& new_state)
|
||||
|
||||
@@ -48,6 +48,13 @@ public:
|
||||
MachineObject* get_selected_machine();
|
||||
bool set_selected_machine(std::string dev_id);
|
||||
|
||||
// why: clears stale sidebar sync-status / AMS visuals. Public so the printer-agent
|
||||
// swap path can reuse it instead of duplicating the two sidebar calls.
|
||||
void OnSelectedMachineLost();
|
||||
|
||||
void record_user_last_machine(const std::string& dev_id);
|
||||
std::string get_user_last_machine() const;
|
||||
|
||||
// local machine
|
||||
void set_local_selected_machine(std::string dev_id) { local_selected_machine = dev_id; };
|
||||
MachineObject* get_local_selected_machine() const { return get_local_machine(local_selected_machine); }
|
||||
@@ -67,6 +74,8 @@ public:
|
||||
void erase_user_machine(std::string dev_id) { userMachineList.erase(dev_id); }
|
||||
void clean_user_info(bool keep_local_selection = false);
|
||||
|
||||
void clear_other_devices();
|
||||
|
||||
void load_last_machine();
|
||||
void update_user_machine_list_info(const std::string& provider);
|
||||
void parse_user_print_info(std::string body);
|
||||
@@ -96,11 +105,17 @@ 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();
|
||||
|
||||
void OnMachineBindStateChanged(MachineObject* obj, const std::string& new_state);
|
||||
void OnSelectedMachineLost();
|
||||
void OnSelectedMachineChanged(const std::string& pre_dev_id, const std::string& new_dev_id);
|
||||
|
||||
|
||||
|
||||
@@ -95,6 +95,10 @@ namespace Slic3r
|
||||
{
|
||||
result.id = ams_id + slot_id;
|
||||
}
|
||||
else if (type == DevAms::AMS_LITE_MIXED)
|
||||
{
|
||||
result.id = AMS_LITE_MIXED_TRAY_INDEX_OFFSET + slot_id;
|
||||
}
|
||||
else
|
||||
{
|
||||
result.id = ams_id * 4 + slot_id;
|
||||
@@ -117,6 +121,11 @@ namespace Slic3r
|
||||
{
|
||||
std::string ams_id = ams->second->GetAmsId();
|
||||
auto ams_type = ams->second->GetAmsType();
|
||||
// GetAmsType() maps mixed -> AMS_LITE; recover the mixed type so N9 trays index at 24+slot.
|
||||
if (ams_type == DevAms::AMS_LITE && ams->second->IsAmsLiteMixed())
|
||||
{
|
||||
ams_type = DevAms::AMS_LITE_MIXED;
|
||||
}
|
||||
for (auto tray = ams->second->GetTrays().begin(); tray != ams->second->GetTrays().end(); tray++)
|
||||
{
|
||||
int ams_id = atoi(ams->first.c_str());
|
||||
@@ -126,6 +135,10 @@ namespace Slic3r
|
||||
{
|
||||
tray_index = ams_id * 4 + tray_id;
|
||||
}
|
||||
else if (ams_type == DevAms::AMS_LITE_MIXED)
|
||||
{
|
||||
tray_index = AMS_LITE_MIXED_TRAY_INDEX_OFFSET + tray_id;
|
||||
}
|
||||
else if (ams_type == DevAms::N3S)
|
||||
{
|
||||
tray_index = ams_id + tray_id;
|
||||
@@ -149,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));
|
||||
@@ -186,7 +199,7 @@ namespace Slic3r
|
||||
}
|
||||
}
|
||||
FilamentInfo info;
|
||||
_parse_tray_info(atoi(tray.id.c_str()), 0, DevAms::DUMMY, tray, info);
|
||||
_parse_tray_info(atoi(tray.id.c_str()), 0, DevAms::EXT_SPOOL, tray, info);
|
||||
tray_filaments.emplace(std::make_pair(info.tray_id, info));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,457 @@
|
||||
#include "DevMapping.h"
|
||||
#include "DevMappingNozzle.h"
|
||||
|
||||
#include "DevNozzleRack.h"
|
||||
#include "DevNozzleSystem.h"
|
||||
|
||||
#include "DevUtil.h"
|
||||
#include "DevUtilBackend.h"
|
||||
|
||||
#include "libslic3r/MultiNozzleUtils.hpp"
|
||||
#include "libslic3r/Print.hpp"
|
||||
|
||||
#include "slic3r/GUI/DeviceManager.hpp"
|
||||
#include "slic3r/GUI/Plater.hpp"
|
||||
#include "slic3r/GUI/BackgroundSlicingProcess.hpp"
|
||||
|
||||
#include "slic3r/GUI/GUI_App.hpp"
|
||||
|
||||
#include <boost/lexical_cast.hpp>
|
||||
#include <nlohmann/json.hpp>
|
||||
using namespace nlohmann;
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
void MachineObject::clear_auto_nozzle_mapping()
|
||||
{
|
||||
if (m_nozzle_mapping_ptr) {
|
||||
m_nozzle_mapping_ptr->Clear();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// get auto nozzle mapping through AP
|
||||
// warnings:
|
||||
// (1) the fila_id here is 1 based index
|
||||
// (2) the AP wants the diameter string with 2 decimal places
|
||||
int DevNozzleMappingCtrl::CtrlGetAutoNozzleMappingV0(Slic3r::GUI::Plater* plater,
|
||||
const std::vector<FilamentInfo>& ams_mapping,
|
||||
int flow_cali_opt, int pa_value)
|
||||
{
|
||||
Clear();
|
||||
|
||||
m_plater = plater;
|
||||
if (!plater) {
|
||||
BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << ": plater is nullptr";
|
||||
return -1;
|
||||
}
|
||||
|
||||
GCodeProcessorResult* gcode_result = plater->background_process().get_current_gcode_result();
|
||||
if (!gcode_result) {
|
||||
BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << ": gcode_result is nullptr";
|
||||
return -1;
|
||||
}
|
||||
|
||||
const auto& result = gcode_result->nozzle_group_result;
|
||||
if (!result) {
|
||||
assert(false && "gcode_result->nozzle_group_result");
|
||||
BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << ": gcode_result->nozzle_group_result is NULL";
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (ams_mapping.empty()) {
|
||||
BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << ": the ams mapping is empty";
|
||||
return -1;// the ams mapping is empty
|
||||
}
|
||||
|
||||
json command_jj;
|
||||
command_jj["print"]["command"] = "get_auto_nozzle_mapping";
|
||||
m_sequence_id = std::to_string(MachineObject::m_sequence_id++);
|
||||
command_jj["print"]["sequence_id"] = m_sequence_id;
|
||||
command_jj["print"]["calibration"] = flow_cali_opt;
|
||||
command_jj["print"]["extrude_cali_manual_mode"] = pa_value;
|
||||
|
||||
// filament seq
|
||||
json filament_seq_jj;
|
||||
int max_fila_id = 0;
|
||||
std::unordered_set<int> filaid_set;
|
||||
for (int idx = 0; idx < gcode_result->filament_change_sequence.size(); idx++) {
|
||||
int fila_id = gcode_result->filament_change_sequence[idx];
|
||||
if (filaid_set.count(fila_id) == 0) {
|
||||
filaid_set.insert(fila_id);
|
||||
filament_seq_jj[fila_id + 1] = idx;
|
||||
max_fila_id = std::max(max_fila_id, fila_id + 1);
|
||||
}
|
||||
}
|
||||
|
||||
for (int fila_id = 0; fila_id <= max_fila_id; fila_id++) {
|
||||
if (filament_seq_jj[fila_id].is_null()) {
|
||||
filament_seq_jj[fila_id] = -1;// fill the used fila_id with -1
|
||||
}
|
||||
}
|
||||
|
||||
command_jj["print"]["filament_seq"] = filament_seq_jj;
|
||||
|
||||
// ams mapping
|
||||
std::vector<int> ams_mapping_vec(33, 0xFFFF);/* AP ask to fill them*/
|
||||
for (auto item : ams_mapping) {
|
||||
try {
|
||||
int ams_id = stoi(item.ams_id);
|
||||
int slot_id = !item.slot_id.empty() ? stoi(item.slot_id) : 0;
|
||||
ams_mapping_vec[item.id + 1] = (ams_id << 8) | slot_id;/*using ams_id << 8 | slot_id*/
|
||||
} catch (std::exception& e) {
|
||||
assert(false && "invalid ams_id or slot_id");
|
||||
BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << " exception: " << e.what();
|
||||
ams_mapping_vec[item.id + 1] = item.tray_id;
|
||||
}
|
||||
}
|
||||
command_jj["print"]["ams_mapping"] = ams_mapping_vec;
|
||||
|
||||
// filament info
|
||||
json filament_info_jj;
|
||||
for (int fila_id = 0; fila_id < ams_mapping.size(); fila_id++) {
|
||||
const auto& fila = ams_mapping[fila_id];
|
||||
const auto& nozzle_list = result->get_nozzles_for_filament(fila.id);
|
||||
if (nozzle_list.empty()) {
|
||||
assert(false && "nozzle_list should not be empty");
|
||||
BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << "nozzle_list should not be nullptr";
|
||||
continue;
|
||||
}
|
||||
|
||||
for (const auto& nozzle_info : nozzle_list) {
|
||||
json fila_item_jj;
|
||||
fila_item_jj["id"] = (fila.id + 1);
|
||||
fila_item_jj["direction"] = (nozzle_info.extruder_id == LOGIC_L_EXTRUDER_ID ? 1 : 2);
|
||||
fila_item_jj["group"] = nozzle_info.group_id;
|
||||
fila_item_jj["nozzle_d"] = s_get_diameter_str(nozzle_info.diameter);
|
||||
fila_item_jj["nozzle_v"] = (nozzle_info.volume_type == NozzleVolumeType::nvtHighFlow) ? "High Flow" : "Standard";
|
||||
fila_item_jj["cate"] = fila.filament_id;
|
||||
fila_item_jj["color"] = fila.color;
|
||||
filament_info_jj.push_back(fila_item_jj);
|
||||
}
|
||||
}
|
||||
|
||||
command_jj["print"]["fila_info"] = filament_info_jj;
|
||||
|
||||
// nozzle info
|
||||
json nozzle_info_jj;
|
||||
const auto& nozzle_system = m_obj->GetNozzleSystem();
|
||||
const auto& extruder_nozzles = nozzle_system->GetExtNozzles();
|
||||
for (const auto& nozzle : extruder_nozzles) {
|
||||
if (nozzle.second.IsNormal()) {
|
||||
json nozzle_item_jj;
|
||||
nozzle_item_jj["pos"] = nozzle.second.GetNozzleId();
|
||||
if (nozzle_system->GetReplaceNozzleTar().has_value() && nozzle_item_jj["pos"] == MAIN_EXTRUDER_ID) {
|
||||
nozzle_item_jj["pos"] = nozzle_system->GetReplaceNozzleTar().value();// special case of tar_id. see protocol definition
|
||||
}
|
||||
|
||||
nozzle_item_jj["nozzle_d"] = s_get_diameter_str(nozzle.second.GetNozzleDiameter());
|
||||
nozzle_item_jj["nozzle_v"] = DevNozzle::ToNozzleFlowString(nozzle.second.GetNozzleFlowType());
|
||||
nozzle_item_jj["wear"] = nozzle.second.GetNozzleWear();
|
||||
nozzle_item_jj["cate"] = nozzle.second.GetFilamentId();
|
||||
nozzle_item_jj["color"] = nozzle.second.GetFilamentColor();
|
||||
nozzle_info_jj.push_back(nozzle_item_jj);
|
||||
}
|
||||
}
|
||||
|
||||
const auto& rack_nozzles = nozzle_system->GetNozzleRack()->GetRackNozzles();
|
||||
for (const auto& nozzle : rack_nozzles) {
|
||||
if (nozzle.second.IsNormal()) {
|
||||
json nozzle_item_jj;
|
||||
nozzle_item_jj["pos"] = (nozzle.second.GetNozzleId() + 0x10);
|
||||
if (nozzle_system->GetReplaceNozzleTar().has_value() && nozzle_item_jj["pos"] == MAIN_EXTRUDER_ID) {
|
||||
nozzle_item_jj["pos"] = nozzle_system->GetReplaceNozzleTar().value();// special case of tar_id. see protocol definition
|
||||
}
|
||||
|
||||
nozzle_item_jj["nozzle_d"] = s_get_diameter_str(nozzle.second.GetNozzleDiameter());
|
||||
nozzle_item_jj["nozzle_v"] = DevNozzle::ToNozzleFlowString(nozzle.second.GetNozzleFlowType());
|
||||
nozzle_item_jj["wear"] = nozzle.second.GetNozzleWear();
|
||||
nozzle_item_jj["cate"] = nozzle.second.GetFilamentId();
|
||||
nozzle_item_jj["color"] = nozzle.second.GetFilamentColor();
|
||||
nozzle_info_jj.push_back(nozzle_item_jj);
|
||||
}
|
||||
}
|
||||
|
||||
command_jj["print"]["nozzle_info"] = nozzle_info_jj;
|
||||
m_req_version = NozzleMappingVersion::Version_V0;
|
||||
return m_obj->publish_json(command_jj);
|
||||
}
|
||||
|
||||
int DevNozzleMappingCtrl::CtrlGetAutoNozzleMappingV1(Slic3r::GUI::Plater* plater)
|
||||
{
|
||||
Clear();
|
||||
m_plater = plater;
|
||||
if (!m_plater) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
auto nozzle_group_res = DevUtilBackend::GetNozzleGroupResult(m_plater);
|
||||
if (!nozzle_group_res) {
|
||||
BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << ": gcode_result->nozzle_group_result is NULL";
|
||||
return -1;
|
||||
}
|
||||
|
||||
json command_jj;
|
||||
command_jj["print"]["command"] = "get_auto_nozzle_mapping";
|
||||
m_sequence_id = std::to_string(MachineObject::m_sequence_id++);
|
||||
command_jj["print"]["sequence_id"] = m_sequence_id;
|
||||
|
||||
json nozzle_group_info_jj;
|
||||
std::unordered_set<int> used_logic_groups;
|
||||
const auto& used_logic_nozzles = nozzle_group_res->get_used_nozzles_in_extruder();
|
||||
for (const auto& used_logic_nozzle : used_logic_nozzles) {
|
||||
if (used_logic_groups.count(used_logic_nozzle.group_id) != 0) {
|
||||
continue;
|
||||
}
|
||||
used_logic_groups.insert(used_logic_nozzle.group_id);
|
||||
|
||||
json nozzle_info;
|
||||
nozzle_info["id"] = used_logic_nozzle.group_id;
|
||||
nozzle_info["ext"] = (used_logic_nozzle.extruder_id + 1);
|
||||
|
||||
try {
|
||||
nozzle_info["dia"] = std::stof(used_logic_nozzle.diameter);
|
||||
} catch(const std::exception& e) {
|
||||
BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << ": e=" << e.what();
|
||||
continue;
|
||||
}
|
||||
|
||||
if (used_logic_nozzle.volume_type == NozzleVolumeType::nvtHighFlow) {
|
||||
nozzle_info["vol"] = "High Flow";
|
||||
} else if(used_logic_nozzle.volume_type == NozzleVolumeType::nvtStandard){
|
||||
nozzle_info["vol"] = "Standard";
|
||||
} else if(used_logic_nozzle.volume_type == NozzleVolumeType::nvtTPUHighFlow){
|
||||
nozzle_info["vol"] = "TPU Flow";
|
||||
} else {
|
||||
assert(0);
|
||||
continue;
|
||||
}
|
||||
|
||||
nozzle_group_info_jj.push_back(nozzle_info);
|
||||
}
|
||||
command_jj["print"]["group_info"] = nozzle_group_info_jj;
|
||||
|
||||
|
||||
m_req_version = NozzleMappingVersion::Version_V1;
|
||||
command_jj["print"]["version"] = (int)m_req_version;
|
||||
return m_obj->publish_json(command_jj);
|
||||
}
|
||||
|
||||
|
||||
void DevNozzleMappingCtrl::ParseAutoNozzleMapping(const json& print_jj)
|
||||
{
|
||||
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);
|
||||
DevJsonValParser::ParseVal(print_jj, "reason", m_mqtt_reason);
|
||||
DevJsonValParser::ParseVal(print_jj, "errno", m_errno);
|
||||
DevJsonValParser::ParseVal(print_jj, "detail", m_detail_json);
|
||||
DevJsonValParser::ParseVal(print_jj, "type", m_type);
|
||||
DevJsonValParser::ParseVal(print_jj, "version", m_ack_version);
|
||||
|
||||
if (print_jj.contains("mapping")) {
|
||||
m_nozzle_mapping_json = print_jj["mapping"];
|
||||
const auto& mapping = print_jj["mapping"].get<std::vector<int>>();
|
||||
for (int fila_id = 0; fila_id < mapping.size(); ++fila_id) {
|
||||
m_nozzle_mapping[fila_id] = mapping[fila_id];
|
||||
}
|
||||
}
|
||||
|
||||
m_flush_weight_base = GetFlushWeight(m_obj);
|
||||
BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << ": get_auto_nozzle_mapping: " << m_result;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DevNozzleMappingCtrl::Clear()
|
||||
{
|
||||
m_sequence_id.clear();
|
||||
m_result.clear();
|
||||
m_mqtt_reason.clear();
|
||||
m_type.clear();
|
||||
m_errno = 0;
|
||||
m_detail_msg.clear();
|
||||
m_detail_json.clear();
|
||||
m_nozzle_mapping.clear();
|
||||
m_nozzle_mapping_json.clear();
|
||||
|
||||
m_flush_weight_base = -1;
|
||||
m_flush_weight_current = -1;
|
||||
m_ack_version = NozzleMappingVersion::Version_V0;
|
||||
}
|
||||
|
||||
|
||||
void DevNozzleMappingCtrl::SetManualNozzleMappingByFila(int fila_id, int nozzle_pos_id)
|
||||
{
|
||||
if (nozzle_pos_id == MAIN_EXTRUDER_ID && m_obj->GetNozzleSystem()->GetReplaceNozzleTar().has_value()){
|
||||
nozzle_pos_id = m_obj->GetNozzleSystem()->GetReplaceNozzleTar().value();// special case of tar_id. see protocol definition
|
||||
}
|
||||
|
||||
if (m_nozzle_mapping[fila_id] != nozzle_pos_id) {
|
||||
m_nozzle_mapping[fila_id] = nozzle_pos_id;
|
||||
m_flush_weight_current = GetFlushWeight(m_obj);
|
||||
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << ": fila_id=" << fila_id << ", nozzle_pos_id=" << nozzle_pos_id;
|
||||
}
|
||||
|
||||
try {
|
||||
auto mapping = m_nozzle_mapping_json.get<std::vector<int>>();
|
||||
if (mapping.at(fila_id) != nozzle_pos_id) {
|
||||
mapping[fila_id] = nozzle_pos_id;
|
||||
m_nozzle_mapping_json = mapping;
|
||||
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << ": updated to " << m_nozzle_mapping_json.dump();
|
||||
}
|
||||
} catch (std::exception& e) {
|
||||
BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << ": exception: " << e.what();
|
||||
};
|
||||
}
|
||||
|
||||
static std::unordered_set<int> sGetLogicExtruders(const std::vector<MultiNozzleUtils::NozzleInfo>& nozzle_infos)
|
||||
{
|
||||
std::unordered_set<int> extruder_ids;
|
||||
for (const auto& nozzle : nozzle_infos) {
|
||||
if (nozzle.extruder_id >= 0) {
|
||||
extruder_ids.insert(nozzle.extruder_id);
|
||||
}
|
||||
}
|
||||
|
||||
return extruder_ids;
|
||||
};
|
||||
|
||||
|
||||
std::vector<int> DevNozzleMappingCtrl::GetMappedNozzlePosVecByFilaId(int fila_id) const
|
||||
{
|
||||
std::vector<int> physic_nozzle_pos_vec;
|
||||
auto nozzle_group_res = DevUtilBackend::GetNozzleGroupResult(m_plater);
|
||||
if (!nozzle_group_res) {
|
||||
return physic_nozzle_pos_vec;
|
||||
}
|
||||
|
||||
const auto& used_logic_nozzles = nozzle_group_res->get_nozzles_for_filament(fila_id);
|
||||
if (m_ack_version == NozzleMappingVersion::Version_V0) {
|
||||
if (auto iter = m_nozzle_mapping.find(fila_id); iter != m_nozzle_mapping.end()) {
|
||||
if (m_obj->GetNozzleSystem()->GetReplaceNozzleTar().has_value() &&
|
||||
iter->second == m_obj->GetNozzleSystem()->GetReplaceNozzleTar().value()) {
|
||||
physic_nozzle_pos_vec.push_back(MAIN_EXTRUDER_ID);
|
||||
} else {
|
||||
physic_nozzle_pos_vec.push_back(iter->second);
|
||||
}
|
||||
} else {
|
||||
const auto& used_logic_extruders = sGetLogicExtruders(used_logic_nozzles);
|
||||
if (used_logic_extruders.size() == 1 && *used_logic_extruders.begin() == LOGIC_L_EXTRUDER_ID) {
|
||||
physic_nozzle_pos_vec.push_back(1);
|
||||
return physic_nozzle_pos_vec; // only left extruder is used
|
||||
}
|
||||
}
|
||||
} else if (m_ack_version == NozzleMappingVersion::Version_V1) {
|
||||
for (const auto& used_logic_nozzle : used_logic_nozzles) {
|
||||
if (auto iter = m_nozzle_mapping.find(used_logic_nozzle.group_id); iter != m_nozzle_mapping.end()) {
|
||||
if (m_obj->GetNozzleSystem()->GetReplaceNozzleTar().has_value() &&
|
||||
iter->second == m_obj->GetNozzleSystem()->GetReplaceNozzleTar().value()) {
|
||||
physic_nozzle_pos_vec.push_back(MAIN_EXTRUDER_ID);
|
||||
} else {
|
||||
physic_nozzle_pos_vec.push_back(iter->second);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return physic_nozzle_pos_vec;
|
||||
}
|
||||
|
||||
wxString DevNozzleMappingCtrl::GetMappedNozzlePosStrByFilaId(int fila_id, const wxString& default_str) const
|
||||
{
|
||||
wxString display_str;
|
||||
const auto& physic_nozzle_pos_vec = GetMappedNozzlePosVecByFilaId(fila_id);
|
||||
for (int idx = 0; idx < physic_nozzle_pos_vec.size(); idx++) {
|
||||
int pos_id = physic_nozzle_pos_vec[idx];
|
||||
if (pos_id == MAIN_EXTRUDER_ID) {
|
||||
display_str += "R";
|
||||
} else if (pos_id == DEPUTY_EXTRUDER_ID) {
|
||||
display_str += "L";
|
||||
} else if (pos_id >= 0x10) {
|
||||
display_str += wxString::Format("R%d", pos_id - 0x10 + 1);// display 1~n for rack hotends
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (idx < physic_nozzle_pos_vec.size() - 1) {
|
||||
display_str += " ";
|
||||
}
|
||||
}
|
||||
|
||||
return !display_str.empty() ? display_str : default_str;
|
||||
}
|
||||
|
||||
// flush weight for multi-nozzle printers: total flush volume across all filament changes
|
||||
float DevNozzleMappingCtrl::GetFlushWeight(Slic3r::MachineObject* obj) const
|
||||
{
|
||||
auto plater = Slic3r::GUI::wxGetApp().plater();
|
||||
if (!plater) {
|
||||
BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << ": plater is nullptr";
|
||||
return -1;
|
||||
}
|
||||
|
||||
GCodeProcessorResult* gcode_result = plater->background_process().get_current_gcode_result();
|
||||
if (!gcode_result) {
|
||||
BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << ": gcode_result is nullptr";
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (gcode_result->filament_change_sequence.empty()) {
|
||||
BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << ": filament_change_sequence is empty";
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (!Slic3r::GUI::wxGetApp().preset_bundle) {
|
||||
BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << ": preset_bundle is nullptr";
|
||||
return -1;
|
||||
};
|
||||
|
||||
const std::vector<std::vector<std::vector<float>>>& flush_matrix = Slic3r::GUI::wxGetApp().preset_bundle->get_full_flush_matrix();
|
||||
|
||||
float total_flush_volume = 0;
|
||||
MultiNozzleUtils::NozzleStatusRecorder recorder;
|
||||
for (auto filament : gcode_result->filament_change_sequence) {
|
||||
const auto& nozzle_pos_vec = GetMappedNozzlePosVecByFilaId(filament);
|
||||
if (nozzle_pos_vec.empty()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
auto nozzle_pos = nozzle_pos_vec.at(0);
|
||||
if (nozzle_pos == -1){
|
||||
BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << ": nozzle_pos is -1 for fila_id=" << filament;
|
||||
continue;
|
||||
}
|
||||
|
||||
auto nozzle_info = obj->GetNozzleSystem()->GetNozzleByPosId(nozzle_pos);
|
||||
if (nozzle_info.IsEmpty()) {
|
||||
BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << ": nozzle_info IsEmpty for fila_id=" << filament << ", nozzle_pos=" << nozzle_pos;
|
||||
continue;
|
||||
}
|
||||
|
||||
int extruder_id = nozzle_info.GetLogicExtruderId();
|
||||
int nozzle_id = nozzle_info.GetNozzlePosId();
|
||||
int last_filament = recorder.get_filament_in_nozzle(nozzle_id);
|
||||
|
||||
if (last_filament != -1 && last_filament != filament) {
|
||||
if (flush_matrix.size() > extruder_id &&
|
||||
flush_matrix[extruder_id].size() > last_filament &&
|
||||
flush_matrix[extruder_id][last_filament].size() > filament){
|
||||
total_flush_volume += flush_matrix[extruder_id][last_filament][filament];
|
||||
}
|
||||
else{
|
||||
assert(false && "missing flush volume");
|
||||
BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << ": missing flush volume for extruder_id=" << extruder_id
|
||||
<< ", last_filament=" << last_filament << ", filament=" << filament;
|
||||
}
|
||||
}
|
||||
|
||||
recorder.set_nozzle_status(nozzle_id, filament);
|
||||
}
|
||||
|
||||
// estimate the flush weight: total_flush_volume * 1.26 * 0.001
|
||||
return total_flush_volume * 1.26 * 0.001;
|
||||
}
|
||||
|
||||
} // namespace Slic3r
|
||||
@@ -0,0 +1,108 @@
|
||||
/**
|
||||
* @file DevMappingNozzle.h
|
||||
* @brief Support nozzle mapping for hotend rack
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <nlohmann/json.hpp>
|
||||
|
||||
#include <wx/string.h>
|
||||
|
||||
namespace Slic3r
|
||||
{
|
||||
|
||||
namespace GUI
|
||||
{
|
||||
class Plater;
|
||||
}
|
||||
|
||||
struct FilamentInfo; // Orca: libslic3r/ProjectTask.hpp — forward-decl for CtrlGetAutoNozzleMappingV0 signature
|
||||
|
||||
class MachineObject;
|
||||
class DevNozzleMappingCtrl
|
||||
{
|
||||
friend class MachineObject;
|
||||
public:
|
||||
enum class NozzleMappingVersion : int {
|
||||
Version_V0 = 0, // V0 means support logical fila_id -> physical nozzle_id mapping
|
||||
Version_V1 = 1, // V1 means support logical nozzle_id -> physical nozzle_id mapping
|
||||
};
|
||||
|
||||
enum class ErrNoV1 : int {
|
||||
Success = 0,
|
||||
ErrorBadRequest3mf = 1,
|
||||
ErrorBadRequestVersion = 2,
|
||||
ErrorBadRequestGroupId = 3,
|
||||
ErrorNoValidNozzles = 4,
|
||||
ErrorBadRequestReadFail = 5,
|
||||
ErrorRackCaliFail = 6,
|
||||
ErrorRackMoving = 7,
|
||||
ErrorRackFull = 8,
|
||||
WarningNotEnoughNozzles = 9
|
||||
};
|
||||
|
||||
public:
|
||||
DevNozzleMappingCtrl(MachineObject* obj) : m_obj(obj) {};
|
||||
|
||||
public:
|
||||
void SetPlater(Slic3r::GUI::Plater* plater) { m_plater = plater; }
|
||||
void Clear();
|
||||
|
||||
bool HasResult() const { return !m_result.empty(); }
|
||||
std::string GetResultStr() const { return m_result; }
|
||||
|
||||
// mqtt error info
|
||||
std::string GetMqttReason() const { return m_mqtt_reason; }
|
||||
|
||||
// command error info
|
||||
int GetErrno() const { return m_errno; }
|
||||
ErrNoV1 GetErrnoV1() const { return static_cast<ErrNoV1>(m_errno); }
|
||||
std::string GetDetailMsg() const { return m_detail_msg; }
|
||||
|
||||
// nozzle mapping
|
||||
std::unordered_map<int, int> GetNozzleMappingMap() const { return m_nozzle_mapping; }
|
||||
nlohmann::json GetNozzleMappingJson() const { return m_nozzle_mapping_json; }
|
||||
void SetManualNozzleMappingByFila(int fila_id, int nozzle_pos_id);
|
||||
|
||||
std::vector<int> GetMappedNozzlePosVecByFilaId(int fila_id) const;// return empy if not mapped
|
||||
wxString GetMappedNozzlePosStrByFilaId(int fila_id, const wxString& default_str = "?") const;
|
||||
|
||||
// flush weight
|
||||
float GetFlushWeightBase() const { return m_flush_weight_base; }
|
||||
float GetFlushWeightCurrent() const { return m_flush_weight_current; }
|
||||
|
||||
public:
|
||||
void ParseAutoNozzleMapping(const nlohmann::json& print_jj);
|
||||
int CtrlGetAutoNozzleMappingV0(Slic3r::GUI::Plater* plater, const std::vector<FilamentInfo>& ams_mapping, int flow_cali_opt, int pa_value);
|
||||
int CtrlGetAutoNozzleMappingV1(Slic3r::GUI::Plater* plater);
|
||||
|
||||
private:
|
||||
float GetFlushWeight(Slic3r::MachineObject* obj) const;
|
||||
|
||||
private:
|
||||
MachineObject* m_obj;
|
||||
Slic3r::GUI::Plater* m_plater = nullptr;
|
||||
|
||||
std::string m_sequence_id;
|
||||
|
||||
std::string m_result;
|
||||
std::string m_mqtt_reason;
|
||||
std::string m_type; // auto or manual
|
||||
|
||||
int m_errno;
|
||||
std::string m_detail_msg;
|
||||
nlohmann::json m_detail_json;
|
||||
|
||||
nlohmann::json m_nozzle_mapping_json;
|
||||
std::unordered_map<int, int> m_nozzle_mapping; // v0: fila_id -> physical_nozzle_id, v1: logical_nozzle_id -> physical_nozzle_id
|
||||
|
||||
float m_flush_weight_base = -1;// the base weight for flush
|
||||
float m_flush_weight_current = -1;// the weight current
|
||||
|
||||
NozzleMappingVersion m_req_version = NozzleMappingVersion::Version_V0;
|
||||
NozzleMappingVersion m_ack_version = NozzleMappingVersion::Version_V0;
|
||||
};
|
||||
|
||||
}
|
||||
@@ -0,0 +1,112 @@
|
||||
#include "DevNozzleRack.h"
|
||||
#include "DevUtil.h"
|
||||
|
||||
#include "slic3r/GUI/DeviceManager.hpp"
|
||||
|
||||
wxDEFINE_EVENT(DEV_RACK_EVENT_READING_FINISHED, wxCommandEvent);
|
||||
namespace Slic3r
|
||||
{
|
||||
|
||||
DevNozzleRack::DevNozzleRack(DevNozzleSystem* nozzle_system)
|
||||
: wxEvtHandler(), m_nozzle_system(nozzle_system)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
|
||||
void DevNozzleRack::Reset()
|
||||
{
|
||||
m_position = RACK_POS_UNKNOWN;
|
||||
m_status = RACK_STATUS_UNKNOWN;
|
||||
m_rack_nozzles.clear();
|
||||
}
|
||||
|
||||
void DevNozzleRack::SendReadingFinished()
|
||||
{
|
||||
wxCommandEvent evt(DEV_RACK_EVENT_READING_FINISHED);
|
||||
evt.SetEventObject(this);
|
||||
wxPostEvent(this, evt);
|
||||
}
|
||||
|
||||
DevNozzle DevNozzleRack::GetNozzle(int idx) const
|
||||
{
|
||||
auto iter = m_rack_nozzles.find(idx);
|
||||
if (iter == m_rack_nozzles.end()) {
|
||||
DevNozzle nozzle;
|
||||
nozzle.SetOnRack(true);
|
||||
return nozzle;
|
||||
}
|
||||
|
||||
return iter->second;
|
||||
}
|
||||
|
||||
DevFirmwareVersionInfo DevNozzleRack::GetNozzleFirmwareInfo(int nozzle_id) const
|
||||
{
|
||||
auto iter = m_rack_nozzles_firmware.find(nozzle_id);
|
||||
return iter != m_rack_nozzles_firmware.end() ? iter->second : DevFirmwareVersionInfo();
|
||||
}
|
||||
|
||||
|
||||
bool DevNozzleRack::HasUnreliableNozzles() const
|
||||
{
|
||||
for (const auto& nozzle : m_rack_nozzles)
|
||||
{
|
||||
if (!nozzle.second.IsInfoReliable())
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool DevNozzleRack::HasUnknownNozzles() const
|
||||
{
|
||||
for (const auto& nozzle : m_rack_nozzles)
|
||||
{
|
||||
if (nozzle.second.IsUnknown())
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
int DevNozzleRack::GetKnownNozzleCount() const
|
||||
{
|
||||
int count = 0;
|
||||
for (const auto& nozzle : m_rack_nozzles)
|
||||
{
|
||||
if (!nozzle.second.IsEmpty() && !nozzle.second.IsUnknown())
|
||||
{
|
||||
count++;
|
||||
}
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
void DevNozzleRack::ParseRackInfo(const nlohmann::json& rack_info)
|
||||
{
|
||||
ParseRackInfoV1_0(rack_info);
|
||||
}
|
||||
|
||||
void DevNozzleRack::ParseRackInfoV1_0(const nlohmann::json& rack_info)
|
||||
{
|
||||
DevJsonValParser::ParseVal(rack_info, "stat", m_status, RACK_STATUS_UNKNOWN);
|
||||
if (m_status < RACK_STATUS_UNKNOWN || m_status >= RACK_STATUS_END)
|
||||
{
|
||||
m_status = RACK_STATUS_UNKNOWN; // Reset to default if out of range
|
||||
BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << ": Invalid rack status: " << m_status << ", reset";
|
||||
}
|
||||
|
||||
DevJsonValParser::ParseVal(rack_info, "pos", m_position, RACK_POS_UNKNOWN);
|
||||
if (m_position < RACK_POS_UNKNOWN || m_position >= RACK_POS_END)
|
||||
{
|
||||
m_position = RACK_POS_UNKNOWN; // Reset to default if out of range
|
||||
BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << ": Invalid rack position: " << m_position << ", reset";
|
||||
}
|
||||
|
||||
DevJsonValParser::ParseVal(rack_info, "info", m_cali_status);
|
||||
}
|
||||
|
||||
};
|
||||
@@ -0,0 +1,134 @@
|
||||
#pragma once
|
||||
#include "libslic3r/CommonDefs.hpp"
|
||||
#include "libslic3r/MultiNozzleUtils.hpp"
|
||||
#include "slic3r/Utils/json_diff.hpp"
|
||||
|
||||
#include "DevNozzleSystem.h"
|
||||
#include "DevFirmware.h"
|
||||
|
||||
#include <map>
|
||||
|
||||
// wx
|
||||
#include <wx/string.h>
|
||||
#include <wx/event.h>
|
||||
|
||||
wxDECLARE_EVENT(DEV_RACK_EVENT_READING_FINISHED, wxCommandEvent);
|
||||
|
||||
namespace Slic3r
|
||||
{
|
||||
// Previous definitions
|
||||
class DevNozzleSystem;
|
||||
|
||||
class DevNozzleRack: public wxEvtHandler
|
||||
{
|
||||
public:
|
||||
enum RackStatus : int
|
||||
{
|
||||
RACK_STATUS_UNKNOWN = -1,
|
||||
RACK_STATUS_IDLE = 0,
|
||||
RACK_STATUS_HOTEND_CENTRE = 1,
|
||||
RACK_STATUS_TOOLHEAD_CENTRE = 2,
|
||||
RACK_STATUS_CALIBRATE_HOTEND_RACK = 3,
|
||||
RACK_STATUS_CUT_MATERIAL = 4,
|
||||
RACK_STATUS_UNLOCK_HOTEND = 5,
|
||||
RACK_STATUS_LIFT_HOTEND_RACK = 6,
|
||||
RACK_STATUS_PLACE_HOTEND = 7,
|
||||
RACK_STATUS_PICK_HOTEND = 8,
|
||||
RACK_STATUS_LOCK_HOTEND = 9,
|
||||
RACK_STATUS_END,
|
||||
};
|
||||
|
||||
enum RackPos : int
|
||||
{
|
||||
RACK_POS_UNKNOWN = 0,
|
||||
RACK_POS_A_TOP = 1,
|
||||
RACK_POS_B_TOP = 2,
|
||||
RACK_POS_CENTRE = 3,
|
||||
RACK_POS_END,
|
||||
};
|
||||
|
||||
enum RackCaliStatus
|
||||
{
|
||||
Rack_CALI_UNKNOWN = -1,
|
||||
Rack_CALI_NOT = 0,
|
||||
Rack_CALI_OK = 1,
|
||||
};
|
||||
|
||||
public:
|
||||
DevNozzleRack(DevNozzleSystem* nozzle_system);
|
||||
~DevNozzleRack() = default;
|
||||
|
||||
public:
|
||||
// Is supported by the printer
|
||||
bool IsSupported() const { return m_is_supported; };
|
||||
void SetSupported(bool supported) { m_is_supported = supported; }
|
||||
|
||||
// getters
|
||||
DevNozzleSystem* GetNozzleSystem() const { return m_nozzle_system; }
|
||||
|
||||
RackPos GetPosition() const { return m_position; }
|
||||
RackStatus GetStatus() const { return m_status; }
|
||||
RackCaliStatus GetCaliStatus() const { return m_cali_status;}
|
||||
|
||||
DevNozzle GetNozzle(int idx) const;
|
||||
const std::map<int, DevNozzle>& GetRackNozzles() const { return m_rack_nozzles; }
|
||||
|
||||
// status
|
||||
bool HasUnreliableNozzles() const;
|
||||
bool HasUnknownNozzles() const;
|
||||
int GetKnownNozzleCount() const;
|
||||
|
||||
// refreshing
|
||||
int GetReadingIdx() const { return m_nozzle_system->GetReadingIdx(); }
|
||||
int GetReadingCount() const { return m_nozzle_system->GetReadingCount(); }
|
||||
void SendReadingFinished();
|
||||
|
||||
// firmware
|
||||
void AddNozzleFirmwareInfo(int nozzle_id, const DevFirmwareVersionInfo& info) { m_rack_nozzles_firmware[nozzle_id] = info; }
|
||||
void ClearNozzleFirmwareInfo() { m_rack_nozzles_firmware.clear(); }
|
||||
DevFirmwareVersionInfo GetNozzleFirmwareInfo(int nozzle_id) const;
|
||||
|
||||
// setters
|
||||
void Reset();
|
||||
void AddRackNozzle(DevNozzle& nozzle) { nozzle.SetOnRack(true); m_rack_nozzles[nozzle.m_nozzle_id] = nozzle; };
|
||||
void ClearRackNozzles() { m_rack_nozzles.clear(); }
|
||||
|
||||
public:
|
||||
void ParseRackInfo(const nlohmann::json& rack_info);
|
||||
|
||||
void CtrlRackPosMove(RackPos new_pos) const;
|
||||
void CtrlRackPosGoHome() const;
|
||||
|
||||
void CtrlRackConfirmNozzle(int rack_nozzle_id) const;
|
||||
void CtrlRackConfirmAll() const;
|
||||
|
||||
void CrtlRackReadNozzle(int rack_nozzle_id) const;
|
||||
void CtrlRackReadAll(bool gui_check = false) const;
|
||||
bool CtrlCanReadAll() const;
|
||||
|
||||
// the upgrade is not supported
|
||||
// the GUI interface is removed
|
||||
int CtrlRackUpgradeExtruderNozzle() const;
|
||||
int CtrlRackUpgradeRackNozzle(int rack_nozzle_id) const;;
|
||||
int CtrlRackUpgradeAll() const;;
|
||||
bool CtrlCanUpdateAll() const;
|
||||
|
||||
private:
|
||||
void ParseRackInfoV1_0(const nlohmann::json& rack_info);
|
||||
|
||||
int CtrlRackUpgrade(const std::string& module_str) const;
|
||||
|
||||
bool CheckRackMoveWarningDlg() const;
|
||||
|
||||
private:
|
||||
DevNozzleSystem* m_nozzle_system = nullptr;
|
||||
|
||||
bool m_is_supported = false; // Indicates if the nozzle rack is supported by the printer
|
||||
RackPos m_position = RACK_POS_UNKNOWN;
|
||||
RackStatus m_status = RACK_STATUS_UNKNOWN;
|
||||
RackCaliStatus m_cali_status = Rack_CALI_UNKNOWN;
|
||||
|
||||
std::map<int, DevNozzle> m_rack_nozzles; // Map of nozzle ID to DevNozzle objects
|
||||
std::map<int, DevFirmwareVersionInfo> m_rack_nozzles_firmware;
|
||||
};
|
||||
};
|
||||
@@ -0,0 +1,283 @@
|
||||
#include "DevNozzleRack.h"
|
||||
#include "DevUtil.h"
|
||||
#include "DevExtruderSystem.h"
|
||||
|
||||
#include "slic3r/GUI/DeviceManager.hpp"
|
||||
#include "slic3r/GUI/MsgDialog.hpp"
|
||||
#include "slic3r/GUI/I18N.hpp"
|
||||
|
||||
#include <wx/thread.h>
|
||||
|
||||
|
||||
namespace Slic3r
|
||||
{
|
||||
|
||||
void DevNozzleRack::CtrlRackPosMove(RackPos new_pos) const
|
||||
{
|
||||
if (!CheckRackMoveWarningDlg())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
int action_id = -1;
|
||||
if (new_pos == RACK_POS_A_TOP)
|
||||
{
|
||||
action_id = 1;
|
||||
}
|
||||
else if(new_pos == RACK_POS_B_TOP)
|
||||
{
|
||||
action_id = 2;
|
||||
}
|
||||
|
||||
if (action_id != -1)
|
||||
{
|
||||
json j;
|
||||
j["print"]["command"] = "nozzle_holder_ctrl";
|
||||
j["print"]["sequence_id"] = std::to_string(MachineObject::m_sequence_id++);
|
||||
j["print"]["action"] = action_id;
|
||||
m_nozzle_system->GetOwner()->publish_json(j);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void DevNozzleRack::CtrlRackPosGoHome() const
|
||||
{
|
||||
if (!CheckRackMoveWarningDlg())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
json j;
|
||||
j["print"]["command"] = "nozzle_holder_ctrl";
|
||||
j["print"]["sequence_id"] = std::to_string(MachineObject::m_sequence_id++);
|
||||
j["print"]["action"] = 0;
|
||||
m_nozzle_system->GetOwner()->publish_json(j);
|
||||
}
|
||||
|
||||
bool DevNozzleRack::CheckRackMoveWarningDlg() const
|
||||
{
|
||||
if (wxThread::IsMain())
|
||||
{
|
||||
static bool s_show_move_warning = true;
|
||||
if (s_show_move_warning)
|
||||
{
|
||||
Slic3r::GUI::MessageDialog dlg(nullptr, _L("The toolhead and hotend rack may move. Please keep your hands away from the chamber."),
|
||||
_L("Warning"), wxICON_WARNING | wxOK);
|
||||
dlg.show_dsa_button();
|
||||
if (dlg.ShowModal() != wxID_OK)
|
||||
{
|
||||
s_show_move_warning = !dlg.get_checkbox_state();
|
||||
return false;
|
||||
}
|
||||
|
||||
s_show_move_warning = !dlg.get_checkbox_state();
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void DevNozzleRack::CtrlRackConfirmNozzle(int rack_nozzle_id) const
|
||||
{
|
||||
json j;
|
||||
j["print"]["command"] = "nozzle_info_confirm";
|
||||
j["print"]["sequence_id"] = std::to_string(MachineObject::m_sequence_id++);
|
||||
j["print"]["id"] = (rack_nozzle_id + 16); // from 0x16
|
||||
m_nozzle_system->GetOwner()->publish_json(j);
|
||||
}
|
||||
|
||||
void DevNozzleRack::CtrlRackConfirmAll() const
|
||||
{
|
||||
json j;
|
||||
j["print"]["command"] = "nozzle_info_confirm";
|
||||
j["print"]["sequence_id"] = std::to_string(MachineObject::m_sequence_id++);
|
||||
j["print"]["id"] = 0xff;
|
||||
m_nozzle_system->GetOwner()->publish_json(j);
|
||||
}
|
||||
|
||||
void DevNozzleRack::CrtlRackReadNozzle(int rack_nozzle_id) const
|
||||
{
|
||||
json j;
|
||||
j["print"]["command"] = "holder_nozzle_refresh";
|
||||
j["print"]["sequence_id"] = std::to_string(MachineObject::m_sequence_id++);
|
||||
j["print"]["id"] = (rack_nozzle_id + 16); // from 0x16
|
||||
m_nozzle_system->GetOwner()->publish_json(j);
|
||||
}
|
||||
|
||||
|
||||
bool DevNozzleRack::CtrlCanReadAll() const
|
||||
{
|
||||
//is in print
|
||||
if (m_nozzle_system->GetOwner()->is_in_printing())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (m_nozzle_system->GetOwner()->is_in_upgrading()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
//if have nozzle
|
||||
bool has_nozzle_on_rack = false;
|
||||
for (const auto &nozzle_pair : m_rack_nozzles)
|
||||
{
|
||||
if (!nozzle_pair.second.IsEmpty())
|
||||
{
|
||||
has_nozzle_on_rack = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
bool has_nozzle_on_ext = false;
|
||||
if (m_nozzle_system->ContainsExtNozzle(MAIN_EXTRUDER_ID)) {
|
||||
has_nozzle_on_ext = true;
|
||||
}
|
||||
|
||||
if (!has_nozzle_on_rack && !has_nozzle_on_ext) {
|
||||
return false;
|
||||
}
|
||||
|
||||
//if is in loading
|
||||
if (m_nozzle_system->GetOwner()->ams_status_main == AMS_STATUS_MAIN_FILAMENT_CHANGE)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
auto ext = m_nozzle_system->GetOwner()->GetExtderSystem();
|
||||
if (ext && ext->IsBusyLoading()) return false;
|
||||
|
||||
if (GetReadingCount() > 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return m_status == RACK_STATUS_IDLE;
|
||||
}
|
||||
|
||||
void DevNozzleRack::CtrlRackReadAll(bool gui_check) const
|
||||
{
|
||||
if (gui_check && wxThread::IsMain())
|
||||
{
|
||||
#if 0
|
||||
if (!HasUnknownNozzles()) {
|
||||
Slic3r::GUI::MessageDialog dlg(nullptr, _L("Hotend information may be inaccurate. "
|
||||
"Would you like to re-read the hotend? (Hotend information may change during power-off)."),
|
||||
_L("Warning"), wxICON_WARNING | wxOK | wxYES);
|
||||
dlg.SetButtonLabel(wxID_OK, _L("I confirm all"));
|
||||
dlg.SetButtonLabel(wxID_YES, _L("Re-read all"));
|
||||
|
||||
int rtn = dlg.ShowModal();
|
||||
if (rtn == wxID_OK) {
|
||||
CtrlRackConfirmAll();
|
||||
} else if (rtn == wxID_YES) {
|
||||
if (CheckRackMoveWarningDlg()) {
|
||||
CtrlRackReadAll(false);
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (CheckRackMoveWarningDlg()) {
|
||||
CtrlRackReadAll(false);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
json j;
|
||||
j["print"]["command"] = "holder_nozzle_refresh";
|
||||
j["print"]["sequence_id"] = std::to_string(MachineObject::m_sequence_id++);
|
||||
j["print"]["id"] = 0xff;
|
||||
m_nozzle_system->GetOwner()->publish_json(j);
|
||||
}
|
||||
|
||||
|
||||
bool DevNozzleRack::CtrlCanUpdateAll() const
|
||||
{
|
||||
if (m_nozzle_system->GetOwner()->is_in_printing())
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
auto ext_nozzle = m_nozzle_system->GetExtNozzle(MAIN_EXTRUDER_ID);
|
||||
if (ext_nozzle.IsAbnormal())
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
auto ext_nozzle_firmware = m_nozzle_system->GetExtruderNozzleFirmware();
|
||||
if (!ext_nozzle_firmware.sw_new_ver.empty() &&
|
||||
ext_nozzle_firmware.sw_new_ver != ext_nozzle_firmware.sw_ver)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
for (auto val : m_rack_nozzles_firmware)
|
||||
{
|
||||
const auto& firmware_info = val.second;
|
||||
if (!firmware_info.sw_new_ver.empty() && firmware_info.sw_new_ver != firmware_info.sw_ver)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
else if (GetNozzle(val.first).IsAbnormal())
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
int DevNozzleRack::CtrlRackUpgradeExtruderNozzle() const
|
||||
{
|
||||
return CtrlRackUpgrade("wtm");
|
||||
}
|
||||
|
||||
int DevNozzleRack::CtrlRackUpgradeRackNozzle(int rack_nozzle_id) const
|
||||
{
|
||||
return CtrlRackUpgrade("wtm/" + std::to_string(0x10 + rack_nozzle_id));
|
||||
}
|
||||
|
||||
int DevNozzleRack::CtrlRackUpgradeAll() const
|
||||
{
|
||||
return CtrlRackUpgrade("wtm_all");
|
||||
}
|
||||
|
||||
int DevNozzleRack::CtrlRackUpgrade(const std::string& module_str) const
|
||||
{
|
||||
if (wxThread::IsMain())
|
||||
{
|
||||
if (GetReadingCount() > 0)
|
||||
{
|
||||
Slic3r::GUI::MessageDialog dlg(nullptr, _L("Reading the hotends, please wait."), _L("Warning"), wxICON_WARNING | wxOK);
|
||||
dlg.ShowModal();
|
||||
return -1;
|
||||
}
|
||||
|
||||
static bool s_show_upgrade_warning = true;
|
||||
if (s_show_upgrade_warning)
|
||||
{
|
||||
Slic3r::GUI::MessageDialog dlg(nullptr, _L("During the hotend upgrade, the toolhead will move. Don't reach into the chamber."),
|
||||
_L("Warning"), wxICON_WARNING | wxOK | wxCANCEL);
|
||||
dlg.show_dsa_button();
|
||||
dlg.SetButtonLabel(wxID_OK, _L("Update"));
|
||||
int rtn = dlg.ShowModal();
|
||||
s_show_upgrade_warning = !dlg.get_checkbox_state();
|
||||
if (rtn != wxID_OK)
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
json j;
|
||||
j["upgrade"]["command"] = "wtm_upgrade";
|
||||
j["upgrade"]["module"] = module_str;
|
||||
j["upgrade"]["src_id"] = 1;
|
||||
j["upgrade"]["sequence_id"] = std::to_string(MachineObject::m_sequence_id++);
|
||||
return m_nozzle_system->GetOwner()->publish_json(j);
|
||||
}
|
||||
|
||||
};
|
||||
@@ -1,35 +1,413 @@
|
||||
#include "DevExtruderSystem.h"
|
||||
|
||||
#include "DevNozzleRack.h"
|
||||
#include "DevNozzleSystem.h"
|
||||
#include "DevUtil.h"
|
||||
|
||||
#include "slic3r/GUI/DeviceManager.hpp"
|
||||
#include "slic3r/GUI/I18N.hpp"
|
||||
|
||||
#include "libslic3r/libslic3r.h"
|
||||
#include "libslic3r/Utils.hpp"
|
||||
|
||||
namespace Slic3r
|
||||
{
|
||||
|
||||
DevNozzle DevNozzleSystem::GetNozzle(int id) const
|
||||
// ---- DevNozzle: flow/volume conversions (Standard / High Flow / TPU High Flow) ----------------------
|
||||
// Device-reported U_FLOW nozzles map to nvtTPUHighFlow so a synced TPU-HF rack activates the H2C
|
||||
// change_filament_gcode TPU-kit branch. nvtHybrid is a slicer-only sentinel with no device
|
||||
// representation, so ToNozzleFlowType(nvtHybrid) falls through to NONE_FLOWTYPE.
|
||||
|
||||
NozzleFlowType DevNozzle::ToNozzleFlowType(const NozzleVolumeType& type)
|
||||
{
|
||||
if (m_nozzles.find(id) != m_nozzles.end())
|
||||
switch (type) {
|
||||
case NozzleVolumeType::nvtStandard: return NozzleFlowType::S_FLOW;
|
||||
case NozzleVolumeType::nvtHighFlow: return NozzleFlowType::H_FLOW;
|
||||
case NozzleVolumeType::nvtTPUHighFlow: return NozzleFlowType::U_FLOW;
|
||||
default: return NozzleFlowType::NONE_FLOWTYPE;
|
||||
}
|
||||
}
|
||||
|
||||
NozzleVolumeType DevNozzle::ToNozzleVolumeType(const NozzleFlowType& type)
|
||||
{
|
||||
switch (type) {
|
||||
case NozzleFlowType::S_FLOW: return NozzleVolumeType::nvtStandard;
|
||||
case NozzleFlowType::H_FLOW: return NozzleVolumeType::nvtHighFlow;
|
||||
case NozzleFlowType::U_FLOW: return NozzleVolumeType::nvtTPUHighFlow;
|
||||
default: {
|
||||
BOOST_LOG_TRIVIAL(warning) << __FUNCTION__ << "nozzle flow type None convert to nozzle volume type Standard";
|
||||
return NozzleVolumeType::nvtStandard;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
wxString DevNozzle::GetNozzleFlowTypeStr(NozzleFlowType type)
|
||||
{
|
||||
switch (type) {
|
||||
case NozzleFlowType::H_FLOW: return _L("High Flow");
|
||||
case NozzleFlowType::S_FLOW: return _L("Standard");
|
||||
case NozzleFlowType::U_FLOW: return _L("TPU High Flow");
|
||||
default: break;
|
||||
}
|
||||
|
||||
return _L("Unknown");
|
||||
}
|
||||
|
||||
// Untranslated flow-type literal used for filaments_blacklist.json nozzle_flows matching.
|
||||
// Keep the raw English strings; the translated GetNozzleFlowTypeStr must not be used for JSON matching.
|
||||
std::string DevNozzle::GetNozzleFlowTypeString(NozzleFlowType type)
|
||||
{
|
||||
switch (type) {
|
||||
case NozzleFlowType::H_FLOW: return "High Flow";
|
||||
case NozzleFlowType::S_FLOW: return "Standard";
|
||||
case NozzleFlowType::U_FLOW: return "TPU High Flow";
|
||||
default: return "Unknown";
|
||||
}
|
||||
}
|
||||
|
||||
// Untranslated flow-type literal serialized into the get_auto_nozzle_mapping payload.
|
||||
// Differs from GetNozzleFlowTypeString only in the fallback: "" (empty) rather than "Unknown".
|
||||
std::string DevNozzle::ToNozzleFlowString(const NozzleFlowType& type)
|
||||
{
|
||||
switch (type) {
|
||||
case NozzleFlowType::S_FLOW: return "Standard";
|
||||
case NozzleFlowType::H_FLOW: return "High Flow";
|
||||
case NozzleFlowType::U_FLOW: return "TPU High Flow";
|
||||
default: return std::string();
|
||||
}
|
||||
}
|
||||
|
||||
wxString DevNozzle::GetNozzleTypeStr(NozzleType type)
|
||||
{
|
||||
switch (type) {
|
||||
case Slic3r::ntHardenedSteel: return _L("Hardened Steel");
|
||||
case Slic3r::ntStainlessSteel: return _L("Stainless Steel");
|
||||
case Slic3r::ntTungstenCarbide: return _L("Tungsten Carbide");
|
||||
default: break;
|
||||
}
|
||||
|
||||
return _L("Unknown");
|
||||
}
|
||||
|
||||
// ---- DevNozzle: status ------------------------------------------------------------------------------
|
||||
|
||||
bool DevNozzle::IsInfoReliable() const
|
||||
{
|
||||
if (IsEmpty()) { return false; }
|
||||
return DevUtil::get_flag_bits(m_stat, 0, 1) == 0;
|
||||
}
|
||||
|
||||
bool DevNozzle::IsNormal() const
|
||||
{
|
||||
if (IsEmpty()) { return false; }
|
||||
return DevUtil::get_flag_bits(m_stat, 1, 2) == 0;
|
||||
}
|
||||
|
||||
bool DevNozzle::IsAbnormal() const
|
||||
{
|
||||
return DevUtil::get_flag_bits(m_stat, 1, 2) == (1 << 0);
|
||||
}
|
||||
|
||||
bool DevNozzle::IsUnknown() const
|
||||
{
|
||||
return DevUtil::get_flag_bits(m_stat, 1, 2) == (1 << 1);
|
||||
}
|
||||
|
||||
int DevNozzle::GetNozzlePosId() const
|
||||
{
|
||||
return IsOnRack() ? (m_nozzle_id + 0x10) : m_nozzle_id;
|
||||
}
|
||||
|
||||
NozzleDiameterType DevNozzle::GetNozzleDiameterType() const
|
||||
{
|
||||
if (is_approx(m_diameter, 0.2f))
|
||||
return NozzleDiameterType::NOZZLE_DIAMETER_0_2;
|
||||
else if (is_approx(m_diameter, 0.4f))
|
||||
return NozzleDiameterType::NOZZLE_DIAMETER_0_4;
|
||||
else if (is_approx(m_diameter, 0.6f))
|
||||
return NozzleDiameterType::NOZZLE_DIAMETER_0_6;
|
||||
else if (is_approx(m_diameter, 0.8f))
|
||||
return NozzleDiameterType::NOZZLE_DIAMETER_0_8;
|
||||
else
|
||||
return NozzleDiameterType::NONE_DIAMETER_TYPE;
|
||||
}
|
||||
|
||||
DevFirmwareVersionInfo DevNozzle::GetFirmwareInfo() const
|
||||
{
|
||||
const auto& nozzle_rack = m_nozzle_rack.lock();
|
||||
if (nozzle_rack) {
|
||||
if (IsOnRack()) {
|
||||
return nozzle_rack->GetNozzleFirmwareInfo(m_nozzle_id);
|
||||
} else if (m_nozzle_id == 0) {
|
||||
return nozzle_rack->GetNozzleSystem()->GetExtruderNozzleFirmware();
|
||||
}
|
||||
}
|
||||
|
||||
return DevFirmwareVersionInfo();
|
||||
}
|
||||
|
||||
// ---- DevNozzle: location ----------------------------------------------------------------------------
|
||||
|
||||
int DevNozzle::GetLogicExtruderId() const
|
||||
{
|
||||
int total_ext_count = GetTotalExtruderCount();
|
||||
if (total_ext_count == 1) {
|
||||
return LOGIC_UNIQUE_EXTRUDER_ID;
|
||||
} else if (total_ext_count == 2) {
|
||||
if (AtLeftExtruder()) {
|
||||
return LOGIC_L_EXTRUDER_ID;
|
||||
} else if (AtRightExtruder()) {
|
||||
return LOGIC_R_EXTRUDER_ID;
|
||||
}
|
||||
}
|
||||
|
||||
assert(0);
|
||||
return LOGIC_UNIQUE_EXTRUDER_ID;
|
||||
}
|
||||
|
||||
int DevNozzle::GetExtruderId() const
|
||||
{
|
||||
int total_ext_count = GetTotalExtruderCount();
|
||||
if (total_ext_count == 1) {
|
||||
return MAIN_EXTRUDER_ID;
|
||||
} else if (total_ext_count == 2) {
|
||||
if (AtRightExtruder()) {
|
||||
return MAIN_EXTRUDER_ID;
|
||||
} else if (AtLeftExtruder()) {
|
||||
return DEPUTY_EXTRUDER_ID;
|
||||
}
|
||||
}
|
||||
|
||||
return MAIN_EXTRUDER_ID;
|
||||
}
|
||||
|
||||
bool DevNozzle::AtLeftExtruder() const
|
||||
{
|
||||
assert(GetTotalExtruderCount() == 2);
|
||||
if (IsOnRack()) { return false; }
|
||||
return m_nozzle_id == DEPUTY_EXTRUDER_ID;
|
||||
}
|
||||
|
||||
bool DevNozzle::AtRightExtruder() const
|
||||
{
|
||||
assert(GetTotalExtruderCount() == 2);
|
||||
if (IsOnRack()) { return true; }
|
||||
return m_nozzle_id == MAIN_EXTRUDER_ID;
|
||||
}
|
||||
|
||||
int DevNozzle::GetTotalExtruderCount() const
|
||||
{
|
||||
auto rack = m_nozzle_rack.lock();
|
||||
if (rack) {
|
||||
MachineObject* obj = rack->GetNozzleSystem()->GetOwner();
|
||||
return obj->GetExtderSystem()->GetTotalExtderCount();
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
// ---- DevNozzleSystem --------------------------------------------------------------------------------
|
||||
|
||||
DevNozzleSystem::DevNozzleSystem(MachineObject* owner)
|
||||
: m_owner(owner), m_nozzle_rack(std::make_shared<DevNozzleRack>(this))
|
||||
{
|
||||
}
|
||||
|
||||
DevNozzle DevNozzleSystem::GetExtNozzle(int id) const
|
||||
{
|
||||
if (m_ext_nozzles.find(id) != m_ext_nozzles.end())
|
||||
{
|
||||
return m_nozzles.at(id);
|
||||
return m_ext_nozzles.at(id);
|
||||
}
|
||||
|
||||
return DevNozzle();
|
||||
}
|
||||
|
||||
void DevNozzleSystem::Reset()
|
||||
DevNozzle DevNozzleSystem::GetRackNozzle(int idx) const
|
||||
{
|
||||
m_nozzles.clear();
|
||||
m_extder_exist = 0;
|
||||
m_state = 0; // idle state
|
||||
return m_nozzle_rack->GetNozzle(idx);
|
||||
}
|
||||
|
||||
const std::map<int, DevNozzle>& DevNozzleSystem::GetRackNozzles() const
|
||||
{
|
||||
return m_nozzle_rack->GetRackNozzles();
|
||||
}
|
||||
|
||||
void DevNozzleSystem::SetSupportNozzleRack(bool supported)
|
||||
{
|
||||
m_nozzle_rack->SetSupported(supported);
|
||||
}
|
||||
|
||||
const std::vector<DevNozzle> DevNozzleSystem::CollectNozzles(int ext_loc, NozzleFlowType flow_type, float diameter) const
|
||||
{
|
||||
auto s_match = [&](const DevNozzle& nozzle) -> bool {
|
||||
if (nozzle.IsEmpty() || nozzle.IsAbnormal()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (diameter >= 0.0f && nozzle.m_diameter != diameter) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (m_owner->is_nozzle_flow_type_supported() && flow_type != nozzle.GetNozzleFlowType()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
std::vector<DevNozzle> result;
|
||||
|
||||
auto ext_nozzle = GetExtNozzle(ext_loc);
|
||||
if (s_match(ext_nozzle)) {
|
||||
result.push_back(ext_nozzle);
|
||||
}
|
||||
|
||||
if (ext_loc == MAIN_EXTRUDER_ID) {
|
||||
auto rack_nozzles = m_nozzle_rack->GetRackNozzles();
|
||||
for (auto rack_nozzle : rack_nozzles) {
|
||||
if (s_match(rack_nozzle.second)) {
|
||||
result.push_back(rack_nozzle.second);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
std::vector<MultiNozzleUtils::NozzleGroupInfo> DevNozzleSystem::GetNozzleGroups() const
|
||||
{
|
||||
std::vector<MultiNozzleUtils::NozzleGroupInfo> nozzle_groups;
|
||||
|
||||
auto nozzle_in_extruder = this->GetExtNozzles();
|
||||
for (auto& elem : nozzle_in_extruder) {
|
||||
auto& nozzle = elem.second;
|
||||
MultiNozzleUtils::NozzleGroupInfo info;
|
||||
info.extruder_id = nozzle.GetLogicExtruderId();
|
||||
info.diameter = format_diameter_to_str(nozzle.m_diameter);
|
||||
info.volume_type = DevNozzle::ToNozzleVolumeType(nozzle.m_nozzle_flow);
|
||||
info.nozzle_count = 1;
|
||||
nozzle_groups.emplace_back(std::move(info));
|
||||
}
|
||||
|
||||
auto nozzle_rack = this->GetNozzleRack();
|
||||
if (!nozzle_rack)
|
||||
return nozzle_groups;
|
||||
|
||||
auto nozzle_in_rack = nozzle_rack->GetRackNozzles(); // nozzles in rack
|
||||
for (auto& elem : nozzle_in_rack) {
|
||||
auto& nozzle = elem.second;
|
||||
if (nozzle.IsUnknown() || nozzle.IsAbnormal())
|
||||
continue;
|
||||
int extruder_id = nozzle.GetLogicExtruderId();
|
||||
std::string diameter = format_diameter_to_str(nozzle.m_diameter);
|
||||
NozzleVolumeType volume_type = DevNozzle::ToNozzleVolumeType(nozzle.m_nozzle_flow);
|
||||
|
||||
bool found = false;
|
||||
for (auto& group : nozzle_groups) {
|
||||
if (group.extruder_id == extruder_id &&
|
||||
group.volume_type == volume_type &&
|
||||
group.diameter == diameter) {
|
||||
found = true;
|
||||
group.nozzle_count += 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!found)
|
||||
nozzle_groups.emplace_back(diameter, volume_type, extruder_id, 1);
|
||||
}
|
||||
return nozzle_groups;
|
||||
}
|
||||
|
||||
bool DevNozzleSystem::IsRackMaximumInstalled() const
|
||||
{
|
||||
auto ext_nozzle = GetExtNozzle(MAIN_EXTRUDER_ID);
|
||||
if (ext_nozzle.IsEmpty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const auto& rack_nozzles = m_nozzle_rack->GetRackNozzles();
|
||||
if (rack_nozzles.size() < 6) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (auto item : rack_nozzles) {
|
||||
if (item.second.IsEmpty()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool DevNozzleSystem::HasUnreliableNozzles() const
|
||||
{
|
||||
for (auto nozzle : m_ext_nozzles) {
|
||||
if (!nozzle.second.IsInfoReliable()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if (m_nozzle_rack->HasUnreliableNozzles()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool DevNozzleSystem::HasUnknownNozzles() const
|
||||
{
|
||||
for (auto nozzle : m_ext_nozzles) {
|
||||
if (nozzle.second.IsUnknown()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if (m_nozzle_rack->HasUnknownNozzles()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void DevNozzleSystem::AddFirmwareInfoWTM(const DevFirmwareVersionInfo& info)
|
||||
{
|
||||
static const std::string s_wtm_prefix = "wtm/";
|
||||
auto pos = info.name.find(s_wtm_prefix);
|
||||
if (pos == std::string::npos) {
|
||||
m_ext_nozzle_firmware_info = info;
|
||||
} else {
|
||||
try {
|
||||
auto str = info.name.substr(s_wtm_prefix.size()); // remove "wtm/" prefix
|
||||
int rack_nozzle_id = std::stoi(str) - 0x10; // rack nozzle IDs start from 0x10
|
||||
m_nozzle_rack->AddNozzleFirmwareInfo(rack_nozzle_id, info);
|
||||
} catch (const std::exception& e) {
|
||||
BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << ": Invalid nozzle ID in firmware name: "
|
||||
<< info.name << ", error: " << e.what();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DevNozzleSystem::ClearFirmwareInfoWTM()
|
||||
{
|
||||
m_ext_nozzle_firmware_info = DevFirmwareVersionInfo();
|
||||
m_nozzle_rack->ClearNozzleFirmwareInfo();
|
||||
}
|
||||
|
||||
void DevNozzleSystem::ClearNozzles()
|
||||
{
|
||||
m_ext_nozzles.clear();
|
||||
m_nozzle_rack->ClearRackNozzles();
|
||||
}
|
||||
|
||||
// ---- parsing ----------------------------------------------------------------------------------------
|
||||
|
||||
static unordered_map<string, NozzleFlowType> _str2_nozzle_flow_type = {
|
||||
{"S", NozzleFlowType::S_FLOW},
|
||||
{"H", NozzleFlowType::H_FLOW},
|
||||
{"A", NozzleFlowType::S_FLOW},
|
||||
{"X", NozzleFlowType::S_FLOW}
|
||||
{"X", NozzleFlowType::S_FLOW},
|
||||
{"E", NozzleFlowType::H_FLOW}, // E3D high-flow
|
||||
{"U", NozzleFlowType::U_FLOW}, // TPU 1.75 high-flow -> nvtTPUHighFlow
|
||||
};
|
||||
|
||||
static unordered_map<string, NozzleType> _str2_nozzle_type = {
|
||||
@@ -57,6 +435,11 @@ static void s_parse_nozzle_type(const std::string& nozzle_type_str, DevNozzle& n
|
||||
nozzle.m_nozzle_type = _str2_nozzle_type[type_str];
|
||||
}
|
||||
}
|
||||
else if (nozzle_type_str == "N/A")
|
||||
{
|
||||
nozzle.m_nozzle_type = NozzleType::ntUndefine;
|
||||
nozzle.m_nozzle_flow = NozzleFlowType::NONE_FLOWTYPE;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -66,8 +449,9 @@ void DevNozzleSystemParser::ParseV1_0(const nlohmann::json& nozzletype_json,
|
||||
std::optional<int> flag_e3d)
|
||||
{
|
||||
//Since both the old and new protocols push data.
|
||||
// assert(system->m_nozzles.size() < 2);
|
||||
// assert(system->m_ext_nozzles.size() < 2);
|
||||
DevNozzle nozzle;
|
||||
nozzle.SetRack(system->GetNozzleRack());
|
||||
nozzle.m_nozzle_id = 0;
|
||||
nozzle.m_nozzle_flow = NozzleFlowType::S_FLOW; // default flow type
|
||||
|
||||
@@ -112,32 +496,88 @@ void DevNozzleSystemParser::ParseV1_0(const nlohmann::json& nozzletype_json,
|
||||
}
|
||||
}
|
||||
|
||||
system->m_nozzles[nozzle.m_nozzle_id] = nozzle;
|
||||
system->m_ext_nozzles[nozzle.m_nozzle_id] = nozzle;
|
||||
}
|
||||
|
||||
|
||||
void DevNozzleSystemParser::ParseV2_0(const json& nozzle_json, DevNozzleSystem* system)
|
||||
void DevNozzleSystemParser::ParseV2_0(const json& device_json, DevNozzleSystem* system)
|
||||
{
|
||||
system->Reset();
|
||||
|
||||
if (nozzle_json.contains("exist"))
|
||||
if (device_json.contains("nozzle"))
|
||||
{
|
||||
system->m_extder_exist = DevUtil::get_flag_bits(nozzle_json["exist"].get<int>(), 0, 16);
|
||||
const json& nozzle_json = device_json["nozzle"];
|
||||
if (nozzle_json.contains("exist"))
|
||||
{
|
||||
system->m_extder_exist = DevUtil::get_flag_bits(nozzle_json["exist"].get<int>(), 0, 16);
|
||||
}
|
||||
|
||||
if (nozzle_json.contains("state"))
|
||||
{
|
||||
system->m_state_0_4 = DevUtil::get_flag_bits(nozzle_json["state"].get<int>(), 0, 4);
|
||||
int new_reading_idx = DevUtil::get_flag_bits(nozzle_json["state"].get<int>(), 8, 4);
|
||||
int new_reading_count = DevUtil::get_flag_bits(nozzle_json["state"].get<int>(), 4, 4);
|
||||
if (system->m_reading_count != new_reading_count || system->m_reading_idx != new_reading_idx)
|
||||
{
|
||||
system->m_reading_idx = new_reading_idx;
|
||||
system->m_reading_count = new_reading_count;
|
||||
if (system->m_reading_count == 0)
|
||||
{
|
||||
system->m_nozzle_rack->SendReadingFinished();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// replace-nozzle state: a rack nozzle standing in for the toolhead position.
|
||||
// Absent for every non-rack printer's "nozzle" block => both stay std::nullopt (inert).
|
||||
if (nozzle_json.contains("src_id"))
|
||||
{
|
||||
system->m_replace_nozzle_src = std::make_optional(nozzle_json["src_id"].get<int>());
|
||||
}
|
||||
|
||||
if (nozzle_json.contains("tar_id"))
|
||||
{
|
||||
system->m_replace_nozzle_tar = std::make_optional(nozzle_json["tar_id"].get<int>());
|
||||
}
|
||||
|
||||
system->ClearNozzles();
|
||||
for (auto it = nozzle_json["info"].begin(); it != nozzle_json["info"].end(); it++)
|
||||
{
|
||||
DevNozzle nozzle_obj;
|
||||
nozzle_obj.SetRack(system->GetNozzleRack());
|
||||
|
||||
const auto& njon = it.value();
|
||||
nozzle_obj.m_nozzle_id = DevUtil::get_hex_bits(njon["id"].get<int>(), 0);
|
||||
nozzle_obj.m_diameter = njon["diameter"].get<float>();
|
||||
s_parse_nozzle_type(njon["type"].get<std::string>(), nozzle_obj);
|
||||
if (njon.contains("stat"))/*maybe not contains*/
|
||||
{
|
||||
nozzle_obj.SetStatus(njon["stat"].get<int>());
|
||||
}
|
||||
|
||||
DevJsonValParser::ParseVal(njon, "fila_id", nozzle_obj.m_fila_id);
|
||||
DevJsonValParser::ParseVal(njon, "wear", nozzle_obj.m_wear);
|
||||
if (njon.contains("p_t"))/*maybe not contains*/
|
||||
{
|
||||
nozzle_obj.m_nozzle_print_time = njon["p_t"].get<int>();
|
||||
}
|
||||
if (njon.contains("color_m"))/*maybe not contains*/
|
||||
{
|
||||
nozzle_obj.m_filament_clr = njon["color_m"].get<std::string>();
|
||||
}
|
||||
|
||||
if (DevUtil::get_hex_bits(njon["id"].get<int>(), 1) == 1)
|
||||
{
|
||||
system->m_nozzle_rack->AddRackNozzle(nozzle_obj);
|
||||
}
|
||||
else
|
||||
{
|
||||
system->m_ext_nozzles[nozzle_obj.m_nozzle_id] = nozzle_obj;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (nozzle_json.contains("state"))
|
||||
if (device_json.contains("holder"))
|
||||
{
|
||||
system->m_state = DevUtil::get_flag_bits(nozzle_json["state"].get<int>(), 0, 4);
|
||||
}
|
||||
|
||||
for (auto it = nozzle_json["info"].begin(); it != nozzle_json["info"].end(); it++)
|
||||
{
|
||||
DevNozzle nozzle_obj;
|
||||
const auto& njon = it.value();
|
||||
nozzle_obj.m_nozzle_id = njon["id"].get<int>();
|
||||
nozzle_obj.m_diameter = njon["diameter"].get<float>();
|
||||
s_parse_nozzle_type(njon["type"].get<std::string>(), nozzle_obj);
|
||||
system->m_nozzles[nozzle_obj.m_nozzle_id] = nozzle_obj;
|
||||
system->m_nozzle_rack->ParseRackInfo(device_json["holder"]);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1,28 +1,112 @@
|
||||
#pragma once
|
||||
#include "DevDefs.h"
|
||||
#include "DevFirmware.h"
|
||||
|
||||
#include "libslic3r/CommonDefs.hpp"
|
||||
#include "libslic3r/MultiNozzleUtils.hpp"
|
||||
#include "slic3r/Utils/json_diff.hpp"
|
||||
|
||||
#include <wx/string.h>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
|
||||
// Device flow-type mapping note (nozzle rack): the device U_FLOW value maps to nvtTPUHighFlow
|
||||
// (TPU High Flow), so a device-synced TPU-HF rack resolves to nvtTPUHighFlow and the H2C
|
||||
// change_filament_gcode TPU-kit branch can activate. nvtHybrid is a slicer-only sentinel with no
|
||||
// device representation, so it stays out of these device flow-type conversions.
|
||||
//
|
||||
// GetExtruderNozzleInfo (and its ExtruderNozzleInfos aggregate) is intentionally omitted here;
|
||||
// the SelectMachine slicing-vs-installed nozzle comparison collects its per-extruder NozzleDef
|
||||
// entries itself (s_get_slicing_extuder_nozzles).
|
||||
|
||||
namespace Slic3r
|
||||
{
|
||||
// Previous definitions
|
||||
class MachineObject;
|
||||
class DevNozzleRack;
|
||||
|
||||
struct DevNozzle
|
||||
{
|
||||
friend class DevNozzleSystemParser;
|
||||
|
||||
public:
|
||||
int m_nozzle_id = -1;
|
||||
NozzleFlowType m_nozzle_flow = NozzleFlowType::S_FLOW;// 0-common 1-high flow
|
||||
NozzleType m_nozzle_type = NozzleType::ntUndefine;// 0-stainless_steel 1-hardened_steel 5-tungsten_carbide
|
||||
float m_diameter = 0.0f;// unknown until reported by the printer
|
||||
|
||||
public:
|
||||
// flow/volume conversions (Standard / High Flow / TPU High Flow)
|
||||
static NozzleFlowType ToNozzleFlowType(const NozzleVolumeType& type);
|
||||
static NozzleVolumeType ToNozzleVolumeType(const NozzleFlowType& type);
|
||||
|
||||
static wxString GetNozzleFlowTypeStr(NozzleFlowType type);
|
||||
static std::string GetNozzleFlowTypeString(NozzleFlowType type);// untranslated literal ("High Flow"/"Standard"/"TPU High Flow") — the filament blacklist JSON matches these raw strings, so it must NOT use the translated GetNozzleFlowTypeStr (would break non-English locales)
|
||||
static std::string ToNozzleFlowString(const NozzleFlowType& type);// untranslated "Standard"/"High Flow"/"TPU High Flow" ("" for none) — the raw literal serialized into the get_auto_nozzle_mapping payload
|
||||
static wxString GetNozzleTypeStr(NozzleType type);
|
||||
|
||||
public:
|
||||
bool IsEmpty() const { return m_nozzle_id < 0; }
|
||||
|
||||
void SetRack(const std::weak_ptr<DevNozzleRack>& rack) { m_nozzle_rack = rack; }
|
||||
|
||||
int GetNozzleId() const { return m_nozzle_id; }
|
||||
int GetNozzlePosId() const;// physical position id: rack nozzle -> id + 0x10, else id
|
||||
NozzleType GetNozzleType() const { return m_nozzle_type; }
|
||||
NozzleFlowType GetNozzleFlowType() const { return m_nozzle_flow; }
|
||||
NozzleDiameterType GetNozzleDiameterType() const;
|
||||
float GetNozzleDiameter() const { return m_diameter; }
|
||||
float GetNozzleWear() const { return m_wear; }
|
||||
int GetNozzlePrintTime() const { return m_nozzle_print_time; }
|
||||
|
||||
// firmware (rack hotend WTM / extruder nozzle firmware)
|
||||
DevFirmwareVersionInfo GetFirmwareInfo() const;
|
||||
|
||||
// display
|
||||
wxString GetNozzleDiameterStr() const { return wxString::Format("%.1f mm", m_diameter); }
|
||||
wxString GetNozzleFlowTypeStr() const { return GetNozzleFlowTypeStr(m_nozzle_flow); }
|
||||
wxString GetNozzleTypeStr() const { return GetNozzleTypeStr(m_nozzle_type); }
|
||||
|
||||
std::string GetFilamentId() const { return m_fila_id; }
|
||||
std::string GetFilamentColor() const { return m_filament_clr; }
|
||||
|
||||
// location
|
||||
bool AtLeftExtruder() const;
|
||||
bool AtRightExtruder() const;
|
||||
|
||||
int GetLogicExtruderId() const;// warning: logical extruder id
|
||||
int GetExtruderId() const;// warning: physical extruder id
|
||||
|
||||
/* holder nozzle */
|
||||
bool IsOnRack() const { return m_on_rack; }
|
||||
bool IsInfoReliable() const;
|
||||
|
||||
bool IsNormal() const;
|
||||
bool IsAbnormal() const;
|
||||
bool IsUnknown() const;
|
||||
|
||||
void SetOnRack(bool on_rack) { m_on_rack = on_rack; }
|
||||
void SetStatus(int stat) { m_stat = stat; }
|
||||
|
||||
private:
|
||||
int GetTotalExtruderCount() const;
|
||||
|
||||
private:
|
||||
bool m_on_rack = false;
|
||||
|
||||
int m_stat = 0;
|
||||
float m_wear = 0.0f;
|
||||
|
||||
std::string m_fila_id; // main material
|
||||
std::string m_filament_clr; // main color
|
||||
|
||||
std::weak_ptr<DevNozzleRack> m_nozzle_rack; // weak pointer to the nozzle rack
|
||||
int m_nozzle_print_time{0};
|
||||
};
|
||||
|
||||
class DevNozzleSystem
|
||||
{
|
||||
friend class DevNozzleSystemParser;
|
||||
public:
|
||||
DevNozzleSystem(MachineObject* owner) : m_owner(owner) {}
|
||||
private:
|
||||
enum Status : int
|
||||
{
|
||||
@@ -31,27 +115,81 @@ namespace Slic3r
|
||||
};
|
||||
|
||||
public:
|
||||
bool ContainsNozzle(int id) const { return m_nozzles.find(id) != m_nozzles.end(); }
|
||||
DevNozzle GetNozzle(int id) const;
|
||||
const std::map<int, DevNozzle>& GetNozzles() const { return m_nozzles;}
|
||||
bool IsRefreshing() const { return m_state == 1; }
|
||||
DevNozzleSystem(MachineObject* owner);
|
||||
~DevNozzleSystem() = default;
|
||||
|
||||
public:
|
||||
MachineObject* GetOwner() const { return m_owner; }
|
||||
|
||||
// nozzle by position id: pos_id < 0x10 -> extruder nozzle, else rack nozzle at (pos_id - 0x10)
|
||||
DevNozzle GetNozzleByPosId(int pos_id) const { return pos_id < 0x10 ? GetExtNozzle(pos_id) : GetRackNozzle(pos_id - 0x10); }
|
||||
|
||||
// nozzles on extruder
|
||||
bool ContainsExtNozzle(int id) const { return m_ext_nozzles.find(id) != m_ext_nozzles.end(); }
|
||||
DevNozzle GetExtNozzle(int id) const;
|
||||
const std::map<int, DevNozzle>& GetExtNozzles() const { return m_ext_nozzles; }
|
||||
int GetExtNozzleCount() const { return (int) m_ext_nozzles.size(); }
|
||||
|
||||
// nozzles on rack
|
||||
void SetSupportNozzleRack(bool supported);
|
||||
std::shared_ptr<DevNozzleRack> GetNozzleRack() const { return m_nozzle_rack; }
|
||||
DevNozzle GetRackNozzle(int idx) const;
|
||||
const std::map<int, DevNozzle>& GetRackNozzles() const;
|
||||
|
||||
// nozzles on extruder and rack
|
||||
bool IsRackMaximumInstalled() const;// true when the main extruder + all 6 rack slots hold nozzles
|
||||
|
||||
// grouping (drives the MultiNozzleSyncDialog options)
|
||||
const std::vector<DevNozzle> CollectNozzles(int ext_loc, NozzleFlowType flow_type, float diameter = -1.0f) const;
|
||||
std::vector<MultiNozzleUtils::NozzleGroupInfo> GetNozzleGroups() const;
|
||||
|
||||
bool IsIdle() const { return m_state_0_4 == NOZZLE_SYSTEM_IDLE; }
|
||||
bool IsRefreshing() const { return m_state_0_4 == NOZZLE_SYSTEM_REFRESHING; }
|
||||
|
||||
bool HasUnreliableNozzles() const;// any extruder or rack nozzle whose reported info is not reliable
|
||||
bool HasUnknownNozzles() const; // any extruder or rack nozzle of unknown state
|
||||
|
||||
/* reading */
|
||||
int GetReadingIdx() const { return m_reading_idx; }
|
||||
int GetReadingCount() const { return m_reading_count; }
|
||||
|
||||
/* firmware */
|
||||
void AddFirmwareInfoWTM(const DevFirmwareVersionInfo& info);// route a "wtm/<id>" module version to the rack nozzle, else to the extruder nozzle
|
||||
void ClearFirmwareInfoWTM();
|
||||
DevFirmwareVersionInfo GetExtruderNozzleFirmware() const { return m_ext_nozzle_firmware_info; }
|
||||
|
||||
/* replace nozzle (device reports src/tar position while a rack nozzle stands in for the toolhead) */
|
||||
std::optional<int> GetReplaceNozzleSrc() const { return m_replace_nozzle_src; }
|
||||
std::optional<int> GetReplaceNozzleTar() const { return m_replace_nozzle_tar; }
|
||||
|
||||
private:
|
||||
void Reset();
|
||||
void ClearNozzles();
|
||||
|
||||
private:
|
||||
MachineObject* m_owner = nullptr;
|
||||
|
||||
int m_extder_exist = 0; //0- none exist 1-exist, unused
|
||||
int m_state = 0; //0-idle 1-checking, unused
|
||||
std::map<int, DevNozzle> m_nozzles;
|
||||
int m_extder_exist = 0; //0- none exist 1-exist, unused
|
||||
int m_state_0_4 = 0; //0-idle 1-refreshing
|
||||
|
||||
std::optional<int> m_replace_nozzle_src; // replace nozzle source position (device-reported)
|
||||
std::optional<int> m_replace_nozzle_tar; // replace nozzle target position (device-reported)
|
||||
|
||||
/* refreshing */
|
||||
int m_reading_idx = 0;
|
||||
int m_reading_count = 0;
|
||||
|
||||
// nozzles on extruder
|
||||
std::map<int, DevNozzle> m_ext_nozzles;
|
||||
DevFirmwareVersionInfo m_ext_nozzle_firmware_info;
|
||||
|
||||
// nozzles on rack
|
||||
std::shared_ptr<DevNozzleRack> m_nozzle_rack;
|
||||
};
|
||||
|
||||
class DevNozzleSystemParser
|
||||
{
|
||||
public:
|
||||
static void ParseV1_0(const nlohmann::json& nozzletype_json, const nlohmann::json& diameter_json, DevNozzleSystem* system, std::optional<int> flag_e3d);
|
||||
static void ParseV2_0(const json& nozzle_json, DevNozzleSystem* system);
|
||||
static void ParseV2_0(const json& device_json, DevNozzleSystem* system);
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
@@ -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,16 @@
|
||||
#include <stdexcept>
|
||||
|
||||
#include <boost/log/trivial.hpp>
|
||||
#include <boost/lexical_cast.hpp>
|
||||
#include <boost/format.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,10 +35,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);
|
||||
|
||||
// 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 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;};
|
||||
};
|
||||
|
||||
|
||||
@@ -104,4 +120,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
|
||||
@@ -0,0 +1,123 @@
|
||||
#include "DevUtilBackend.h"
|
||||
|
||||
#include "slic3r/GUI/BackgroundSlicingProcess.hpp"
|
||||
|
||||
#include "slic3r/GUI/PartPlate.hpp"
|
||||
#include "slic3r/GUI/Plater.hpp"
|
||||
#include "slic3r/GUI/GUI_App.hpp"
|
||||
|
||||
#include <boost/lexical_cast.hpp>
|
||||
#include <wx/string.h>
|
||||
|
||||
namespace Slic3r
|
||||
{
|
||||
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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},
|
||||
};
|
||||
|
||||
std::optional<Slic3r::DevFilamentDryingPreset> DevUtilBackend::GetFilamentDryingPreset(const std::string& fila_id)
|
||||
{
|
||||
if (fila_id.empty() || !GUI::wxGetApp().preset_bundle) {
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
for (auto iter = GUI::wxGetApp().preset_bundle->filaments.begin(); iter != GUI::wxGetApp().preset_bundle->filaments.end(); ++iter) {
|
||||
const Preset& filament_preset = *iter;
|
||||
const auto& config = filament_preset.config;
|
||||
if (filament_preset.filament_id == fila_id) {
|
||||
DevFilamentDryingPreset info;
|
||||
info.filament_id = fila_id;
|
||||
try {
|
||||
if (config.has("filament_dev_ams_drying_ams_limitations")) {
|
||||
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]);
|
||||
}
|
||||
}
|
||||
|
||||
if (config.has("filament_dev_ams_drying_temperature")) {
|
||||
info.filament_dev_ams_drying_temperature_on_idle[DevAmsType::N3F] = config.option<ConfigOptionFloats>("filament_dev_ams_drying_temperature")->get_at(0);
|
||||
info.filament_dev_ams_drying_temperature_on_idle[DevAmsType::N3S] = config.option<ConfigOptionFloats>("filament_dev_ams_drying_temperature")->get_at(1);
|
||||
info.filament_dev_ams_drying_temperature_on_print[DevAmsType::N3F] = config.option<ConfigOptionFloats>("filament_dev_ams_drying_temperature")->get_at(2);
|
||||
info.filament_dev_ams_drying_temperature_on_print[DevAmsType::N3S] = config.option<ConfigOptionFloats>("filament_dev_ams_drying_temperature")->get_at(3);
|
||||
}
|
||||
|
||||
if (config.has("filament_dev_ams_drying_time")) {
|
||||
info.filament_dev_ams_drying_time_on_idle[DevAmsType::N3F] = config.option<ConfigOptionFloats>("filament_dev_ams_drying_time")->get_at(0);
|
||||
info.filament_dev_ams_drying_time_on_idle[DevAmsType::N3S] = config.option<ConfigOptionFloats>("filament_dev_ams_drying_time")->get_at(1);
|
||||
info.filament_dev_ams_drying_time_on_print[DevAmsType::N3F] = config.option<ConfigOptionFloats>("filament_dev_ams_drying_time")->get_at(2);
|
||||
info.filament_dev_ams_drying_time_on_print[DevAmsType::N3S] = config.option<ConfigOptionFloats>("filament_dev_ams_drying_time")->get_at(3);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
if (config.has("filament_dev_drying_cooling_temperature")) {
|
||||
info.filament_dev_drying_cooling_temperature = config.option<ConfigOptionFloats>("filament_dev_drying_cooling_temperature")->get_at(0);
|
||||
}
|
||||
|
||||
return info;
|
||||
} catch (const std::exception& e) {
|
||||
BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << " exception: " << e.what();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
};// namespace Slic3r
|
||||
@@ -0,0 +1,47 @@
|
||||
/**
|
||||
* @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 <unordered_map>
|
||||
|
||||
// Forward declarations
|
||||
namespace Slic3r
|
||||
{
|
||||
class MachineObject;
|
||||
|
||||
namespace GUI
|
||||
{
|
||||
class Plater;
|
||||
}
|
||||
}; // namespace Slic3r::GUI
|
||||
|
||||
namespace Slic3r
|
||||
{
|
||||
|
||||
class DevUtilBackend
|
||||
{
|
||||
public:
|
||||
DevUtilBackend() = delete;
|
||||
|
||||
public:
|
||||
static MultiNozzleUtils::NozzleInfo GetNozzleInfo(const DevNozzle& dev_nozzle);
|
||||
|
||||
// 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
|
||||
@@ -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 };
|
||||
|
||||
@@ -23,6 +23,7 @@
|
||||
#include "fast_float/fast_float.h"
|
||||
|
||||
#include "DeviceCore/DevFilaSystem.h"
|
||||
#include "DeviceCore/DevFilaSwitch.h"
|
||||
#include "DeviceCore/DevExtensionTool.h"
|
||||
#include "DeviceCore/DevExtruderSystem.h"
|
||||
#include "DeviceCore/DevNozzleSystem.h"
|
||||
@@ -39,9 +40,16 @@
|
||||
#include "DeviceCore/DevHMS.h"
|
||||
|
||||
#include "DeviceCore/DevMapping.h"
|
||||
#include "DeviceCore/DevMappingNozzle.h"
|
||||
#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
|
||||
@@ -66,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) {
|
||||
@@ -551,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
|
||||
|
||||
@@ -566,12 +613,22 @@ MachineObject::MachineObject(DeviceManager* manager, NetworkAgent* agent, std::s
|
||||
m_extder_system = new DevExtderSystem(this);
|
||||
m_extension_tool = DevExtensionTool::Create(this);
|
||||
m_nozzle_system = new DevNozzleSystem(this);
|
||||
m_fila_system = new DevFilaSystem(this);
|
||||
m_fila_system = std::make_shared<DevFilaSystem>(this);
|
||||
m_fila_switch = new DevFilaSwitch(this);
|
||||
m_hms_system = new DevHMS(this);
|
||||
m_config = new DevConfig(this);
|
||||
|
||||
m_ctrl = new DevCtrl(this);
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -612,8 +669,9 @@ MachineObject::~MachineObject()
|
||||
delete m_ctrl;
|
||||
m_ctrl = nullptr;
|
||||
|
||||
delete m_fila_system;
|
||||
m_fila_system = nullptr;
|
||||
|
||||
delete m_fila_switch;
|
||||
m_fila_switch = nullptr;
|
||||
|
||||
delete m_hms_system;
|
||||
m_hms_system = nullptr;
|
||||
@@ -623,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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -692,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;
|
||||
@@ -710,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;
|
||||
}
|
||||
@@ -861,7 +955,14 @@ 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.
|
||||
// Inert for non-rack printers (rack firmware map is empty).
|
||||
m_nozzle_system->ClearFirmwareInfoWTM();
|
||||
}
|
||||
|
||||
void MachineObject::store_version_info(const DevFirmwareVersionInfo& info)
|
||||
@@ -874,6 +975,19 @@ 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;
|
||||
}
|
||||
|
||||
module_vers.emplace(info.name, info);
|
||||
@@ -1190,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) {
|
||||
@@ -1556,7 +1670,7 @@ int MachineObject::check_resume_condition()
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
int MachineObject::command_ams_change_filament(bool load, std::string ams_id, std::string slot_id, int old_temp, int new_temp)
|
||||
int MachineObject::command_ams_change_filament(bool load, std::string ams_id, std::string slot_id, int old_temp, int new_temp, std::optional<int> extruder_id)
|
||||
{
|
||||
json j;
|
||||
try {
|
||||
@@ -1588,6 +1702,13 @@ int MachineObject::command_ams_change_filament(bool load, std::string ams_id, st
|
||||
j["print"]["slot_id"] = atoi(slot_id.c_str());
|
||||
}
|
||||
|
||||
// Filament Track Switch: route the load to the chosen extruder. Only present when the
|
||||
// caller supplied a value (FTS-installed+ready path), so every other caller is unchanged.
|
||||
if (extruder_id.has_value())
|
||||
{
|
||||
j["print"]["extruder_id"] = *extruder_id;
|
||||
}
|
||||
|
||||
} catch (const std::exception &) {}
|
||||
return this->publish_json(j);
|
||||
}
|
||||
@@ -1833,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);
|
||||
@@ -1976,6 +2104,10 @@ int MachineObject::command_set_pa_calibration(const std::vector<PACalibResult> &
|
||||
j["print"]["filaments"][i]["n_coef"] = std::to_string(pa_calib_values[i].n_coef);
|
||||
else
|
||||
j["print"]["filaments"][i]["n_coef"] = "0.0";
|
||||
if (pa_calib_values[i].nozzle_pos_id >= 0) {
|
||||
j["print"]["filaments"][i]["nozzle_pos"] = pa_calib_values[i].nozzle_pos_id;
|
||||
j["print"]["filaments"][i]["nozzle_sn"] = pa_calib_values[i].nozzle_sn;
|
||||
}
|
||||
}
|
||||
|
||||
BOOST_LOG_TRIVIAL(info) << "extrusion_cali_set: " << j.dump();
|
||||
@@ -1994,6 +2126,10 @@ int MachineObject::command_delete_pa_calibration(const PACalibIndexInfo& pa_cali
|
||||
j["print"]["nozzle_id"] = _generate_nozzle_id(pa_calib.nozzle_volume_type, to_string_nozzle_diameter(pa_calib.nozzle_diameter)).ToStdString();
|
||||
j["print"]["filament_id"] = pa_calib.filament_id;
|
||||
j["print"]["cali_idx"] = pa_calib.cali_idx;
|
||||
if (pa_calib.nozzle_pos_id >= 0) {
|
||||
j["print"]["nozzle_pos"] = pa_calib.nozzle_pos_id;
|
||||
j["print"]["nozzle_sn"] = pa_calib.nozzle_sn;
|
||||
}
|
||||
j["print"]["nozzle_diameter"] = to_string_nozzle_diameter(pa_calib.nozzle_diameter);
|
||||
|
||||
BOOST_LOG_TRIVIAL(info) << "extrusion_cali_del: " << j.dump();
|
||||
@@ -2014,6 +2150,11 @@ int MachineObject::command_get_pa_calibration_tab(const PACalibExtruderInfo &cal
|
||||
j["print"]["nozzle_id"] = _generate_nozzle_id(calib_info.nozzle_volume_type, to_string_nozzle_diameter(calib_info.nozzle_diameter)).ToStdString();
|
||||
j["print"]["nozzle_diameter"] = to_string_nozzle_diameter(calib_info.nozzle_diameter);
|
||||
|
||||
if (calib_info.nozzle_pos_id >= 0) {
|
||||
j["print"]["nozzle_pos"] = calib_info.nozzle_pos_id;
|
||||
j["print"]["nozzle_sn"] = calib_info.nozzle_sn;
|
||||
}
|
||||
|
||||
BOOST_LOG_TRIVIAL(info) << "extrusion_cali_get: " << j.dump();
|
||||
request_tab_from_bbs = true;
|
||||
return this->publish_json(j);
|
||||
@@ -2040,6 +2181,10 @@ int MachineObject::commnad_select_pa_calibration(const PACalibIndexInfo& pa_cali
|
||||
j["print"]["slot_id"] = pa_calib_info.slot_id;
|
||||
j["print"]["cali_idx"] = pa_calib_info.cali_idx;
|
||||
j["print"]["filament_id"] = pa_calib_info.filament_id;
|
||||
if (pa_calib_info.nozzle_pos_id >= 0) {
|
||||
j["print"]["nozzle_pos"] = pa_calib_info.nozzle_pos_id;
|
||||
j["print"]["nozzle_sn"] = pa_calib_info.nozzle_sn;
|
||||
}
|
||||
j["print"]["nozzle_diameter"] = to_string_nozzle_diameter(pa_calib_info.nozzle_diameter);
|
||||
|
||||
BOOST_LOG_TRIVIAL(info) << "extrusion_cali_sel: " << j.dump();
|
||||
@@ -2158,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;
|
||||
@@ -2165,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;
|
||||
@@ -2379,6 +2604,7 @@ void MachineObject::reset()
|
||||
jobState_ = 0;
|
||||
m_plate_index = -1;
|
||||
device_cert_installed = false;
|
||||
clear_auto_nozzle_mapping();// reset nozzle mapping
|
||||
|
||||
// reset print_json
|
||||
json empty_j;
|
||||
@@ -2881,6 +3107,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()) {
|
||||
@@ -2904,6 +3131,7 @@ int MachineObject::parse_json(std::string tunnel, std::string payload, bool key_
|
||||
}
|
||||
|
||||
m_fan->ParseV2_0(jj);
|
||||
m_fila_switch->ParseFilaSwitchInfo(jj);
|
||||
|
||||
if (jj.contains("support_filament_backup")) {
|
||||
if (jj["support_filament_backup"].is_boolean()) {
|
||||
@@ -3008,6 +3236,8 @@ int MachineObject::parse_json(std::string tunnel, std::string payload, bool key_
|
||||
|
||||
|
||||
if (jj.contains("command")) {
|
||||
m_nozzle_mapping_ptr->ParseAutoNozzleMapping(jj);
|
||||
|
||||
if (jj["command"].get<std::string>() == "ams_change_filament") {
|
||||
if (jj.contains("errno")) {
|
||||
if (jj["errno"].is_number()) {
|
||||
@@ -3105,6 +3335,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);
|
||||
|
||||
@@ -3126,6 +3367,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) {
|
||||
@@ -3291,6 +3535,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")) {
|
||||
@@ -3731,7 +3985,7 @@ int MachineObject::parse_json(std::string tunnel, std::string payload, bool key_
|
||||
update_printer_preset_name();
|
||||
update_filament_list();
|
||||
if (jj.contains("ams")) {
|
||||
DevFilaSystemParser::ParseV1_0(jj, this, m_fila_system, key_field_only);
|
||||
DevFilaSystemParser::ParseV1_0(jj, this, m_fila_system.get(), key_field_only);
|
||||
}
|
||||
|
||||
/* vitrual tray*/
|
||||
@@ -4090,6 +4344,14 @@ int MachineObject::parse_json(std::string tunnel, std::string payload, bool key_
|
||||
pa_calib_result.nozzle_volume_type = convert_to_nozzle_type((*it)["nozzle_id"].get<std::string>());
|
||||
}
|
||||
|
||||
if ((*it).contains("nozzle_pos")) {
|
||||
pa_calib_result.nozzle_pos_id = (*it)["nozzle_pos"].get<int>();
|
||||
}
|
||||
|
||||
if ((*it).contains("nozzle_sn")) {
|
||||
pa_calib_result.nozzle_sn = (*it)["nozzle_sn"].get<std::string>();
|
||||
}
|
||||
|
||||
if (jj["nozzle_diameter"].is_number_float()) {
|
||||
pa_calib_result.nozzle_diameter = jj["nozzle_diameter"].get<float>();
|
||||
} else if (jj["nozzle_diameter"].is_string()) {
|
||||
@@ -4187,6 +4449,14 @@ int MachineObject::parse_json(std::string tunnel, std::string payload, bool key_
|
||||
pa_calib_result.nozzle_volume_type = NozzleVolumeType::nvtStandard;
|
||||
}
|
||||
|
||||
if (it->contains("nozzle_pos")) {
|
||||
pa_calib_result.nozzle_pos_id = (*it)["nozzle_pos"].get<int>();
|
||||
}
|
||||
|
||||
if (it->contains("nozzle_sn")) {
|
||||
pa_calib_result.nozzle_sn = (*it)["nozzle_sn"].get<std::string>();
|
||||
}
|
||||
|
||||
if ((*it)["k_value"].is_number_float())
|
||||
pa_calib_result.k_value = (*it)["k_value"].get<float>();
|
||||
else if ((*it)["k_value"].is_string())
|
||||
@@ -4283,6 +4553,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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -4374,6 +4655,40 @@ void MachineObject::set_ctt_dlg( wxString text){
|
||||
}
|
||||
}
|
||||
|
||||
void MachineObject::show_unsupported_dlg(int code)
|
||||
{
|
||||
// why: a dead control invites repeat clicks, and the frame is modeless - without the guard
|
||||
// every click stacks another one. Same shape as set_ctt_dlg above, including the reset on
|
||||
// both hide and close so a dismissed dialog can reappear on the next attempt.
|
||||
if (m_unsupported_dlg_shown) {
|
||||
return;
|
||||
}
|
||||
m_unsupported_dlg_shown = true;
|
||||
|
||||
// why: two codes so the user learns which kind of dead end this is - the slicer having no
|
||||
// translation for the command, or the printer's own config lacking the hardware to run it.
|
||||
const wxString text = (code == ORCA_NETWORK_ERR_CAP_NOT_AVAILABLE) ?
|
||||
_L("This printer is not configured with the hardware this control needs.") :
|
||||
_L("This control is not supported on this printer.");
|
||||
|
||||
// note: constructed directly rather than through CallAfter because every publish_json caller
|
||||
// is on the UI thread - clicks come from wx handlers, and the agent marshals its own push
|
||||
// callbacks back to main before parse_json runs. set_ctt_dlg relies on the same property.
|
||||
auto unsupported_dlg = new GUI::SecondaryCheckDialog(nullptr, wxID_ANY, _L("Warning"),
|
||||
GUI::SecondaryCheckDialog::VisibleButtons::ONLY_CONFIRM);
|
||||
unsupported_dlg->update_text(text);
|
||||
unsupported_dlg->Bind(wxEVT_SHOW, [this](auto& e) {
|
||||
if (!e.IsShown()) {
|
||||
m_unsupported_dlg_shown = false;
|
||||
}
|
||||
});
|
||||
unsupported_dlg->Bind(wxEVT_CLOSE_WINDOW, [this](auto& e) {
|
||||
e.Skip();
|
||||
m_unsupported_dlg_shown = false;
|
||||
});
|
||||
unsupported_dlg->on_show();
|
||||
}
|
||||
|
||||
int MachineObject::publish_gcode(std::string gcode_str)
|
||||
{
|
||||
json j;
|
||||
@@ -4887,6 +5202,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);
|
||||
@@ -5012,6 +5336,7 @@ void MachineObject::parse_new_info(json print)
|
||||
is_support_user_preset = get_flag_bits(fun, 11);
|
||||
is_support_door_open_check = get_flag_bits(fun, 12);
|
||||
is_support_nozzle_blob_detection = get_flag_bits(fun, 13);
|
||||
m_nozzle_system->SetSupportNozzleRack(get_flag_bits(fun, 60)); // H2C hotend rack support (device-side gate for the sync dialog)
|
||||
is_support_upgrade_kit = get_flag_bits(fun, 14);
|
||||
is_support_internal_timelapse = get_flag_bits(fun, 28);
|
||||
m_support_mqtt_homing = get_flag_bits(fun, 32);
|
||||
@@ -5039,6 +5364,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*/
|
||||
@@ -5048,6 +5390,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*/
|
||||
@@ -5074,7 +5417,10 @@ void MachineObject::parse_new_info(json print)
|
||||
|
||||
DevBed::ParseV2_0(device,m_bed);
|
||||
|
||||
if (device.contains("nozzle")) { DevNozzleSystemParser::ParseV2_0(device["nozzle"], m_nozzle_system); }
|
||||
// Pass the whole device json: ParseV2_0 reads ext/rack nozzles from device["nozzle"] and the
|
||||
// hotend-rack state from device["holder"] (rack data lives outside device["nozzle"]). It is a
|
||||
// no-op for devices that report neither key, so non-rack machines are unaffected.
|
||||
DevNozzleSystemParser::ParseV2_0(device, m_nozzle_system);
|
||||
if (device.contains("extruder")) { ExtderSystemParser::ParseV2_0(device["extruder"], m_extder_system);}
|
||||
if (device.contains("ext_tool")) { DevExtensionToolParser::ParseV2_0(device["ext_tool"], m_extension_tool); }
|
||||
|
||||
@@ -5523,7 +5869,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,11 +3,13 @@
|
||||
|
||||
#include <map>
|
||||
#include <mutex>
|
||||
#include <atomic>
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <memory>
|
||||
#include <chrono>
|
||||
#include <unordered_set>
|
||||
#include <optional>
|
||||
#include <boost/thread.hpp>
|
||||
#include <boost/nowide/fstream.hpp>
|
||||
#include "nlohmann/json.hpp"
|
||||
@@ -20,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>
|
||||
@@ -61,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
|
||||
@@ -76,21 +75,31 @@ 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;
|
||||
class DevExtderSystem;
|
||||
class DevFan;
|
||||
class DevFilaSystem;
|
||||
class DevFilaSwitch;
|
||||
class DevPrintOptions;
|
||||
class DevHMS;
|
||||
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
|
||||
{
|
||||
@@ -114,11 +123,21 @@ private:
|
||||
std::shared_ptr<DevExtensionTool> m_extension_tool;
|
||||
DevExtderSystem* m_extder_system;
|
||||
DevNozzleSystem* m_nozzle_system;
|
||||
DevFilaSystem* m_fila_system;
|
||||
std::shared_ptr<DevFilaSystem> m_fila_system;
|
||||
DevFilaSwitch* m_fila_switch;
|
||||
DevFan* m_fan;
|
||||
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;
|
||||
|
||||
@@ -131,11 +150,28 @@ private:
|
||||
/*Config*/
|
||||
DevConfig* m_config;
|
||||
|
||||
/* print-dispatch nozzle mapping (H2C hotend rack). Created unconditionally in the ctor so
|
||||
get_nozzle_mapping_result() is always valid; stays empty (no result attached) for every
|
||||
non-rack printer. */
|
||||
std::shared_ptr<DevNozzleMappingCtrl> m_nozzle_mapping_ptr;
|
||||
|
||||
public:
|
||||
MachineObject(DeviceManager* manager, NetworkAgent* agent, std::string name, std::string id, std::string ip);
|
||||
~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 {
|
||||
@@ -236,9 +272,11 @@ public:
|
||||
bool m_is_online;
|
||||
bool m_lan_mode_connection_state{false};
|
||||
bool m_set_ctt_dlg{ false };
|
||||
bool m_unsupported_dlg_shown{ false };
|
||||
void set_lan_mode_connection_state(bool state) {m_lan_mode_connection_state = state;};
|
||||
bool get_lan_mode_connection_state() {return m_lan_mode_connection_state;};
|
||||
void set_ctt_dlg( wxString text);
|
||||
void show_unsupported_dlg(int code);
|
||||
int parse_msg_count = 0;
|
||||
int keep_alive_count = 0;
|
||||
std::chrono::system_clock::time_point last_update_time; /* last received print data from machine */
|
||||
@@ -329,7 +367,12 @@ public:
|
||||
|
||||
DevNozzleSystem* GetNozzleSystem() const { return m_nozzle_system;}
|
||||
|
||||
DevFilaSystem* GetFilaSystem() const { return m_fila_system;}
|
||||
/* print-dispatch nozzle mapping (H2C hotend rack); result stays empty for non-rack printers */
|
||||
std::shared_ptr<DevNozzleMappingCtrl> get_nozzle_mapping_result() const { return m_nozzle_mapping_ptr; }
|
||||
void clear_auto_nozzle_mapping();// defined in DevMappingNozzle.cpp
|
||||
|
||||
std::shared_ptr<DevFilaSystem> GetFilaSystem() const { return m_fila_system;}
|
||||
DevFilaSwitch* GetFilaSwitch() const { return m_fila_switch;}
|
||||
bool HasAms() const;
|
||||
|
||||
DevLamp* GetLamp() const { return m_lamp; }
|
||||
@@ -365,6 +408,10 @@ 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;
|
||||
bool m_new_ver_list_exist = false;
|
||||
@@ -402,6 +449,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); }
|
||||
|
||||
@@ -542,6 +590,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};
|
||||
@@ -618,8 +667,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;
|
||||
@@ -649,6 +708,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;
|
||||
|
||||
@@ -686,6 +750,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);
|
||||
@@ -697,6 +762,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();
|
||||
@@ -733,7 +800,7 @@ public:
|
||||
int check_resume_condition();
|
||||
// ams controls
|
||||
//int command_ams_switch(int tray_index, int old_temp = 210, int new_temp = 210);
|
||||
int command_ams_change_filament(bool load, std::string ams_id, std::string slot_id, int old_temp = 210, int new_temp = 210);
|
||||
int command_ams_change_filament(bool load, std::string ams_id, std::string slot_id, int old_temp = 210, int new_temp = 210, std::optional<int> extruder_id = std::nullopt);
|
||||
int command_ams_user_settings(bool start_read_opt, bool tray_read_opt, bool remain_flag = false);
|
||||
int command_ams_switch_filament(bool switch_filament);
|
||||
int command_ams_air_print_detect(bool air_print_detect);
|
||||
@@ -788,6 +855,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
|
||||
@@ -861,6 +930,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,7 +2,19 @@
|
||||
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/wgtDeviceNozzleRack.h
|
||||
GUI/DeviceTab/wgtDeviceNozzleRack.cpp
|
||||
GUI/DeviceTab/wgtDeviceNozzleRackUpdate.h
|
||||
GUI/DeviceTab/wgtDeviceNozzleRackUpdate.cpp
|
||||
GUI/DeviceTab/wgtDeviceNozzleSelect.h
|
||||
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)
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,226 @@
|
||||
//**********************************************************/
|
||||
/* File: uiAMSBestPositionPopup.hpp
|
||||
* Description: The popup with suggest best ams position
|
||||
*
|
||||
//**********************************************************/
|
||||
|
||||
#pragma once
|
||||
#include "slic3r/GUI/Widgets/AMSItem.hpp"
|
||||
#include "slic3r/GUI/Widgets/Label.hpp"
|
||||
#include "slic3r/GUI/Widgets/PopupWindow.hpp"
|
||||
#include "slic3r/GUI/wxExtensions.hpp"
|
||||
#include "slic3r/GUI/Widgets/Button.hpp"
|
||||
#include "slic3r/GUI/DeviceCore/DevFilaSystem.h"
|
||||
#include "slic3r/GUI/DeviceCore/DevFilaSwitch.h"
|
||||
#include <algorithm>
|
||||
|
||||
#include <tuple>
|
||||
|
||||
namespace Slic3r { namespace GUI {
|
||||
|
||||
|
||||
class UiStyledAMSPanel : public wxPanel
|
||||
{
|
||||
public:
|
||||
UiStyledAMSPanel(wxWindow* parent,
|
||||
wxWindowID id = wxID_ANY,
|
||||
const wxPoint& pos = wxDefaultPosition,
|
||||
const wxSize& size = wxDefaultSize,
|
||||
const wxColour& borderColor = wxColour(200, 200, 200),
|
||||
const wxColour& bgColor = wxColour(255, 255, 255),
|
||||
bool borderDashed = true,
|
||||
wxString name = "",
|
||||
bool isTop = false);
|
||||
|
||||
protected:
|
||||
void OnPaint(wxPaintEvent& event);
|
||||
|
||||
private:
|
||||
|
||||
bool m_borderDashed;
|
||||
int m_borderWidth;
|
||||
int m_radius;
|
||||
wxColour m_borderColor;
|
||||
wxColour m_bgColor;
|
||||
wxString m_name;
|
||||
bool m_isTop;
|
||||
};
|
||||
|
||||
class UiStyledSwitchPanel : public wxPanel
|
||||
{
|
||||
public:
|
||||
UiStyledSwitchPanel(wxWindow* parent,
|
||||
wxWindowID id,
|
||||
const wxPoint& pos,
|
||||
const wxSize& size,
|
||||
const wxColour& borderColor,
|
||||
const wxColour& bgColor,
|
||||
bool borderDashed,
|
||||
int borderWidth,
|
||||
int radius,
|
||||
bool isTop);
|
||||
|
||||
|
||||
void AddToLeft(wxWindow* window, int proportion = 0, int flag = wxEXPAND, int border = 0);
|
||||
void AddToRight(wxWindow* window, int proportion = 0, int flag = wxEXPAND, int border = 0);
|
||||
void Clear(bool deleteWindows);
|
||||
|
||||
wxSizer* GetLeftSizer() { return m_leftSizer; }
|
||||
wxSizer* GetRightSizer() { return m_rightSizer; }
|
||||
void LayoutAndFit()
|
||||
{
|
||||
m_leftSizer->Layout();
|
||||
// m_leftSizer->Fit();
|
||||
m_rightSizer->Layout();
|
||||
// m_rightSizer->Fit();
|
||||
m_splitSizer->Layout();
|
||||
// m_splitSizer->Fit();
|
||||
m_contentSizer->Layout();
|
||||
// m_contentSizer->Fit();
|
||||
m_mainSizer->Layout();
|
||||
// m_mainSizer->Fit();
|
||||
Fit();
|
||||
}
|
||||
protected:
|
||||
void OnPaint(wxPaintEvent& event);
|
||||
private:
|
||||
|
||||
bool m_borderDashed;
|
||||
int m_borderWidth;
|
||||
int m_radius;
|
||||
wxColour m_borderColor;
|
||||
wxColour m_bgColor;
|
||||
bool m_isTop;
|
||||
|
||||
wxBoxSizer* m_mainSizer{nullptr};
|
||||
wxBoxSizer* m_contentSizer{nullptr};
|
||||
wxBoxSizer* m_splitSizer{nullptr};
|
||||
wxSizer* m_leftSizer{nullptr};
|
||||
wxSizer* m_rightSizer{nullptr};
|
||||
|
||||
static constexpr int labelHeight = 30;
|
||||
|
||||
};
|
||||
|
||||
|
||||
enum DataStatusType {
|
||||
ADJUST,
|
||||
OK,
|
||||
UNMATCHED
|
||||
};
|
||||
|
||||
class UiAMSSlot : public wxPanel
|
||||
{
|
||||
public:
|
||||
UiAMSSlot(wxWindow* parent,
|
||||
const std::vector<wxColour>& bgColours,
|
||||
const wxString& text,
|
||||
DataStatusType status,
|
||||
wxWindowID id = wxID_ANY,
|
||||
const wxPoint& pos = wxDefaultPosition,
|
||||
const wxSize& size = wxDefaultSize,
|
||||
double colourFactor = 1.0,
|
||||
double scaleFactor = 1.0);
|
||||
|
||||
|
||||
private:
|
||||
void OnPaint(wxPaintEvent&);
|
||||
void DrawRectangle(wxPaintDC& dc, const wxSize& cli);
|
||||
void DrawLine(wxPaintDC& dc, const wxSize& cli);
|
||||
wxColour LightenColour(const wxColour& original);
|
||||
bool IsDark(const wxColour& c)
|
||||
{
|
||||
int brightness = (c.Red() * 299 + c.Green() * 587 + c.Blue() * 114) / 1000;
|
||||
return brightness < 128; // 0-255 range, 128 mid
|
||||
}
|
||||
private:
|
||||
// Copied, not referenced: the slot repaints long after construction, so it must not
|
||||
// depend on the caller's vector staying alive.
|
||||
std::vector<wxColour> m_bgColours;
|
||||
wxString m_text;
|
||||
DataStatusType m_status;
|
||||
wxSize m_size;
|
||||
ScalableBitmap *m_ams_slot_readonly{nullptr};
|
||||
int m_rectangleW = 44;
|
||||
int m_rectangleH = 62;
|
||||
int rectangleW = 44;
|
||||
int rectangleH = 62;
|
||||
double m_colourFactor = 1.0f;
|
||||
double m_scaleFactor = 1.0f;
|
||||
// wxDECLARE_EVENT_TABLE();
|
||||
};
|
||||
|
||||
struct DataAmsSlotInfo
|
||||
{
|
||||
wxString amsName;
|
||||
wxString name;
|
||||
std::vector<wxColour> colours;
|
||||
double colourFactor;
|
||||
double scaleFactor;
|
||||
DataStatusType status;
|
||||
};
|
||||
|
||||
class UiAMS : public UiStyledAMSPanel
|
||||
{
|
||||
public:
|
||||
UiAMS(wxWindow* parent,
|
||||
const std::vector<DataAmsSlotInfo>& amsInfo,
|
||||
wxWindowID id,
|
||||
const wxPoint& pos,
|
||||
const wxSize& minSize);
|
||||
private:
|
||||
void init();
|
||||
std::vector<DataAmsSlotInfo> m_amsInfo;
|
||||
wxString m_amsTitle;
|
||||
wxSize m_minSize;
|
||||
};
|
||||
|
||||
|
||||
struct DataStatusParam {
|
||||
// int count = 0;
|
||||
int width = 0;
|
||||
int height = 0;
|
||||
std::vector<DataAmsSlotInfo> slots;
|
||||
};
|
||||
|
||||
class ReselectMachineDialog : public wxDialog
|
||||
{
|
||||
public:
|
||||
ReselectMachineDialog(wxWindow* parent);
|
||||
~ReselectMachineDialog();
|
||||
void Update(MachineObject* obj,
|
||||
const std::map<int, int>& best_pos_map,
|
||||
const std::vector<FilamentInfo>& ams_mapping,
|
||||
wxString save_time);
|
||||
|
||||
private:
|
||||
int CaculateSwitcherDistribution(MachineObject* obj, const std::map<int, int>& best_pos_map, const std::vector<FilamentInfo>& ams_mapping);
|
||||
wxString getTrayID(MachineObject* obj, const std::string& amsID, const std::string& slotID);
|
||||
void OnRefreshButton(wxCommandEvent& event);
|
||||
|
||||
private:
|
||||
int saveTimes{0};
|
||||
wxBoxSizer* mainSizer{nullptr};
|
||||
wxPanel* textPanel{nullptr};
|
||||
wxBoxSizer* textSizer{nullptr};
|
||||
Label* suggestText{nullptr};
|
||||
// wxHyperlinkCtrl* linkwiki{nullptr};
|
||||
Label* linkwiki{nullptr};
|
||||
Label* summaryText{nullptr};
|
||||
UiStyledSwitchPanel* filamentSwitch{nullptr};
|
||||
wxStaticText* filamentTips{nullptr};
|
||||
std::vector<std::vector<DataAmsSlotInfo>> inAAMS{};
|
||||
std::vector<std::vector<DataAmsSlotInfo>> inBAMS{};
|
||||
wxPanel* statusBar{nullptr};
|
||||
wxBoxSizer* btnSizer{nullptr};
|
||||
Button* m_buttonClose{nullptr };
|
||||
Button* m_buttonRefresh{ nullptr };
|
||||
std::vector<wxColour>colourAdjust{wxColour("#675AFF")};
|
||||
std::vector<wxColour>colourOK{wxColour("#FF8181")};
|
||||
std::vector<wxColour>colourUnused{wxColour("#FF818140")};
|
||||
// wxStaticBitmap* m_bitmapSelectMachine{nullptr};
|
||||
};
|
||||
|
||||
wxDECLARE_EVENT(wxEVT_REFRESH_DATA, wxCommandEvent);
|
||||
|
||||
}} // namespace Slic3r::GUI
|
||||
@@ -1,9 +1,9 @@
|
||||
//**********************************************************/
|
||||
/* File: uiAmsHumidityPopup.cpp
|
||||
/**********************************************************
|
||||
* File: uiAmsHumidityPopup.cpp
|
||||
* Description: The popup with DevAms Humidity
|
||||
*
|
||||
* \n class uiAmsHumidityPopup
|
||||
//**********************************************************/
|
||||
**********************************************************/
|
||||
|
||||
#include "uiAmsHumidityPopup.h"
|
||||
|
||||
@@ -23,16 +23,16 @@ uiAmsPercentHumidityDryPopup::uiAmsPercentHumidityDryPopup(wxWindow *parent)
|
||||
: wxDialog(parent, wxID_ANY, "")
|
||||
{
|
||||
Create();
|
||||
wxGetApp().UpdateDlgDarkUI(this);
|
||||
wxGetApp().UpdateDlgDarkUI(this); // Orca: apply dark-mode theming to the popup
|
||||
}
|
||||
|
||||
void uiAmsPercentHumidityDryPopup::Create()
|
||||
{
|
||||
// create images
|
||||
idle_img = ScalableBitmap(this, "ams_drying", 16);
|
||||
drying_img = ScalableBitmap(this, "ams_is_drying", 16);
|
||||
drying_img = ScalableBitmap(this, "ams_is_drying", 16); // Orca: use the drying-state icon
|
||||
|
||||
// background
|
||||
// background
|
||||
SetBackgroundColour(*wxWHITE);
|
||||
|
||||
// create title sizer
|
||||
@@ -147,7 +147,8 @@ void uiAmsPercentHumidityDryPopup::UpdateContents()
|
||||
// table grid
|
||||
const wxString& humidity_str = wxString::Format("%d%%", m_humidity_percent);
|
||||
m_humidity_label->SetLabel(humidity_str);
|
||||
const wxString& temp_str = wxString::Format(wxString::FromUTF8(u8"%d\u2103" /* °C */), (int)std::round(m_current_temperature));
|
||||
// Orca: format the temperature with an explicit UTF-8 \u2103 (no translation of a units-only format)
|
||||
const wxString& temp_str = wxString::Format(wxString::FromUTF8(u8"%d\u2103" /* \u00b0C */), (int)std::round(m_current_temperature));
|
||||
m_temperature_label->SetLabel(temp_str);
|
||||
|
||||
if (m_left_dry_time > 0)
|
||||
@@ -190,4 +191,4 @@ void uiAmsPercentHumidityDryPopup::msw_rescale()
|
||||
|
||||
} // namespace GUI
|
||||
|
||||
} // namespace Slic3r
|
||||
} // namespace Slic3r
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
//**********************************************************/
|
||||
/* File: uiAmsHumidityPopup.h
|
||||
/**********************************************************
|
||||
* File: uiAmsHumidityPopup.h
|
||||
* Description: The popup with DevAms Humidity
|
||||
*
|
||||
* \n class uiAmsHumidityPopup
|
||||
//**********************************************************/
|
||||
**********************************************************/
|
||||
|
||||
#pragma once
|
||||
#include "slic3r/GUI/Widgets/AMSItem.hpp"
|
||||
@@ -20,7 +20,7 @@ namespace Slic3r { namespace GUI {
|
||||
struct uiAmsHumidityInfo
|
||||
{
|
||||
std::string ams_id;
|
||||
AMSModel ams_type;
|
||||
AMSModel ams_type; // Orca: GUI-layer AMS enum; consumer (AMSItem.cpp) assigns an AMSModel
|
||||
int humidity_display_idx = -1;
|
||||
int humidity_percent = -1;
|
||||
float current_temperature;
|
||||
@@ -68,7 +68,7 @@ private:
|
||||
|
||||
wxStaticBitmap* m_dry_state_img;
|
||||
Label* m_dry_state;
|
||||
|
||||
|
||||
Label* m_humidity_header;
|
||||
Label* m_humidity_label;
|
||||
|
||||
@@ -81,4 +81,4 @@ private:
|
||||
wxSizer* m_sizer;
|
||||
};
|
||||
|
||||
}} // namespace Slic3r::GUI
|
||||
}} // namespace Slic3r::GUI
|
||||
|
||||
@@ -1,15 +1,16 @@
|
||||
//**********************************************************/
|
||||
/* File: uiDeviceUpdateVersion.cpp
|
||||
/**********************************************************
|
||||
* File: uiDeviceUpdateVersion.cpp
|
||||
* Description: The panel with firmware info
|
||||
*
|
||||
* \n class uiDeviceUpdateVersion
|
||||
//**********************************************************/
|
||||
**********************************************************/
|
||||
|
||||
#include "uiDeviceUpdateVersion.h"
|
||||
|
||||
#include "slic3r/GUI/I18N.hpp"
|
||||
#include "slic3r/GUI/GUI_App.hpp"
|
||||
#include "slic3r/GUI/wxExtensions.hpp"
|
||||
#include "slic3r/GUI/Widgets/Label.hpp"
|
||||
#include "slic3r/GUI/Widgets/Label.hpp" // Orca: explicit Label include
|
||||
|
||||
#include <wx/stattext.h>
|
||||
|
||||
@@ -27,6 +28,7 @@ uiDeviceUpdateVersion::uiDeviceUpdateVersion(wxWindow* parent,
|
||||
long style /*= wxTAB_TRAVERSAL*/)
|
||||
: wxPanel(parent, id, pos, size, style)
|
||||
{
|
||||
SetBackgroundColour(*wxWHITE);
|
||||
CreateWidgets();
|
||||
}
|
||||
|
||||
@@ -81,7 +83,6 @@ void uiDeviceUpdateVersion::CreateWidgets()
|
||||
serial_text->SetFont(font);
|
||||
version_text->SetFont(font);
|
||||
model_text->SetFont(font);
|
||||
|
||||
// The grid sizer
|
||||
wxFlexGridSizer* grid_sizer = new wxFlexGridSizer(0, 2, 0, 0);
|
||||
//grid_sizer->AddGrowableCol(1);
|
||||
@@ -103,12 +104,14 @@ void uiDeviceUpdateVersion::CreateWidgets()
|
||||
|
||||
grid_sizer->Add(version_hsizer, 0, wxEXPAND, 0);
|
||||
grid_sizer->Add(m_dev_version, 0, wxEXPAND | wxALL, FromDIP(5));
|
||||
|
||||
|
||||
// Updating
|
||||
wxSizer* main_sizer = new wxBoxSizer(wxVERTICAL);
|
||||
main_sizer->AddSpacer(FromDIP(40));
|
||||
main_sizer->AddSpacer(FromDIP(40)); // Orca: top spacing above the firmware-info grid
|
||||
main_sizer->Add(grid_sizer, 0, wxALIGN_LEFT, FromDIP(5));
|
||||
|
||||
SetSizer(main_sizer);
|
||||
Layout();
|
||||
}
|
||||
|
||||
wxGetApp().UpdateDarkUIWin(this);
|
||||
}
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
//**********************************************************/
|
||||
/* File: uiDeviceUpdateVersion.h
|
||||
/**********************************************************
|
||||
* File: uiDeviceUpdateVersion.h
|
||||
* Description: The panel with firmware info
|
||||
*
|
||||
* \n class uiDeviceUpdateVersion
|
||||
//**********************************************************/
|
||||
**********************************************************/
|
||||
|
||||
#pragma once
|
||||
#include <wx/panel.h>
|
||||
@@ -44,4 +44,4 @@ private:
|
||||
wxStaticText* m_dev_version;
|
||||
wxStaticBitmap* m_dev_upgrade_indicator;
|
||||
};
|
||||
};// end of namespace Slic3r::GUI
|
||||
};// end of namespace Slic3r::GUI
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,255 @@
|
||||
//**********************************************************/
|
||||
/* File: wgtDeviceNozzleRack.h
|
||||
* Description: The panel with rack and nozzles
|
||||
*
|
||||
* \n class wgtDeviceNozzleRackArea;
|
||||
* \n class wgtDeviceNozzleRackNozzleItem;
|
||||
* \n class wgtDeviceNozzleRackToolHead;
|
||||
* \n class wgtDeviceNozzleRackPos;
|
||||
//**********************************************************/
|
||||
|
||||
#pragma once
|
||||
#include "slic3r/GUI/DeviceCore/DevNozzleRack.h"
|
||||
|
||||
#include "slic3r/GUI/Widgets/StaticBox.hpp"
|
||||
#include "slic3r/GUI/Widgets/AnimaController.hpp"
|
||||
|
||||
#include <wx/panel.h>
|
||||
#include <wx/simplebook.h>
|
||||
#include <memory>
|
||||
|
||||
// Previous definitions
|
||||
class Button;
|
||||
class Label;
|
||||
class ScalableBitmap;
|
||||
class ScalableButton;
|
||||
namespace Slic3r
|
||||
{
|
||||
struct DevNozzle;
|
||||
class DevNozzleRack;
|
||||
namespace GUI
|
||||
{
|
||||
class wgtDeviceNozzleRackArea;
|
||||
class wgtDeviceNozzleRackNozzleItem;
|
||||
class wgtDeviceNozzleRackToolHead;
|
||||
class wgtDeviceNozzleRackPos;
|
||||
class wgtDeviceNozzleRackTitle;
|
||||
class wgtDeviceNozzleRackUpgradeDlg;
|
||||
}
|
||||
};
|
||||
|
||||
// Events
|
||||
wxDECLARE_EVENT(EVT_NOZZLE_RACK_NOZZLE_ITEM_SELECTED, wxCommandEvent);
|
||||
|
||||
namespace Slic3r::GUI
|
||||
{
|
||||
class wgtDeviceNozzleRack : public wxPanel
|
||||
{
|
||||
public:
|
||||
wgtDeviceNozzleRack(wxWindow* parent,
|
||||
wxWindowID id = wxID_ANY,
|
||||
const wxPoint& pos = wxDefaultPosition,
|
||||
const wxSize& size = wxDefaultSize,
|
||||
long style = wxTAB_TRAVERSAL);
|
||||
~wgtDeviceNozzleRack() = default;
|
||||
|
||||
public:
|
||||
void UpdateRackInfo(std::shared_ptr<DevNozzleRack> rack);
|
||||
void Rescale();
|
||||
|
||||
private:
|
||||
void CreateGui();
|
||||
|
||||
private:
|
||||
std::weak_ptr<DevNozzleRack> m_nozzle_rack;
|
||||
|
||||
// GUI
|
||||
wgtDeviceNozzleRackToolHead* m_toolhead_panel{ nullptr };
|
||||
wgtDeviceNozzleRackArea* m_rack_area{ nullptr };
|
||||
};
|
||||
|
||||
|
||||
class wgtDeviceNozzleRackToolHead : public wxPanel
|
||||
{
|
||||
public:
|
||||
wgtDeviceNozzleRackToolHead(wxWindow* parent) : wxPanel(parent) { CreateGui();}
|
||||
|
||||
public:
|
||||
void UpdateToolHeadInfo(const DevNozzle& extruder_nozzle);
|
||||
void Rescale();
|
||||
|
||||
private:
|
||||
void CreateGui();
|
||||
|
||||
private:
|
||||
bool m_extruder_nozzle_exist = false;
|
||||
std::string m_filament_color;
|
||||
|
||||
// GUI
|
||||
ScalableBitmap* m_extruder_nozzle_normal = nullptr;
|
||||
ScalableBitmap* m_extruder_nozzle_empty = nullptr;
|
||||
wxStaticBitmap* m_toolhead_icon;
|
||||
|
||||
Label* m_nozzle_diamenter_label;
|
||||
Label* m_nozzle_flowtype_label;
|
||||
};
|
||||
|
||||
|
||||
class wgtDeviceNozzleRackArea : public wxPanel
|
||||
{
|
||||
public:
|
||||
wgtDeviceNozzleRackArea(wxWindow* parent) : wxPanel(parent) { CreateGui();}
|
||||
|
||||
public:
|
||||
void UpdateRackInfo(std::weak_ptr<DevNozzleRack> rack);
|
||||
void Rescale();
|
||||
|
||||
private:
|
||||
void CreateGui();
|
||||
StaticBox* CreateNozzleBox(const std::vector<int> nozzle_idxes);
|
||||
wxSizer* CreateRefreshBook(wxPanel* parent);
|
||||
|
||||
// updates
|
||||
void UpdateNozzleItems(const std::unordered_map<int, wgtDeviceNozzleRackNozzleItem*>& nozzle_items,
|
||||
std::shared_ptr<DevNozzleRack> nozzle_rack);
|
||||
|
||||
// events
|
||||
void OnBtnHotendsInfos(wxCommandEvent& evt);
|
||||
void OnBtnReadAll(wxCommandEvent& evt);
|
||||
|
||||
private:
|
||||
std::weak_ptr<DevNozzleRack> m_nozzle_rack;
|
||||
DevNozzleRack::RackPos m_rack_pos = DevNozzleRack::RACK_POS_UNKNOWN;
|
||||
DevNozzleRack::RackStatus m_rack_status = DevNozzleRack::RACK_STATUS_UNKNOWN;
|
||||
|
||||
// GUI
|
||||
wxSimplebook* m_simple_book{ nullptr };
|
||||
wxPanel* m_panel_content{ nullptr };
|
||||
wxPanel* m_panel_refresh{ nullptr };
|
||||
|
||||
wgtDeviceNozzleRackTitle* m_title_nozzle_rack;
|
||||
wxBoxSizer* m_hotends_sizer;
|
||||
StaticBox* m_arow_nozzles_box;
|
||||
StaticBox* m_brow_nozzles_box;
|
||||
std::unordered_map<int, wgtDeviceNozzleRackNozzleItem*> m_nozzle_items;
|
||||
|
||||
wgtDeviceNozzleRackPos* m_rack_pos_panel;
|
||||
|
||||
Button* m_btn_hotends_infos;
|
||||
Button* m_btn_read_all;
|
||||
|
||||
/* refresh book */
|
||||
Label* m_progress_refresh{ nullptr };
|
||||
AnimaIcon* m_refresh_icon{ nullptr };
|
||||
|
||||
wgtDeviceNozzleRackUpgradeDlg* m_rack_upgrade_dlg = nullptr;
|
||||
};
|
||||
|
||||
class wgtDeviceNozzleRackPos : public wxPanel
|
||||
{
|
||||
public:
|
||||
explicit wgtDeviceNozzleRackPos(wxWindow* parent) : wxPanel(parent) { CreateGui();}
|
||||
|
||||
public:
|
||||
void UpdateRackPos(const std::shared_ptr<DevNozzleRack>& rack);
|
||||
void Rescale();
|
||||
|
||||
private:
|
||||
void CreateGui();
|
||||
|
||||
void UpdateRackPos(DevNozzleRack::RackPos new_pos,
|
||||
DevNozzleRack::RackStatus new_status,
|
||||
bool is_reading);
|
||||
|
||||
// events
|
||||
void OnMoveRackUp(wxCommandEvent& evt);
|
||||
void OnMoveRackDown(wxCommandEvent& evt);
|
||||
void OnBtnHomingRack(wxCommandEvent& evt);
|
||||
|
||||
private:
|
||||
std::weak_ptr<DevNozzleRack> m_rack;
|
||||
DevNozzleRack::RackPos m_rack_pos = DevNozzleRack::RACK_POS_UNKNOWN;
|
||||
DevNozzleRack::RackStatus m_rack_status = DevNozzleRack::RACK_STATUS_UNKNOWN;
|
||||
|
||||
// GUI
|
||||
StaticBox* m_rowup_panel;
|
||||
ScalableButton* m_btn_rowup;
|
||||
Label* m_label_rowup_status{ nullptr };
|
||||
Label* m_label_rowup{ nullptr };
|
||||
|
||||
StaticBox* m_rowbottom_panel;
|
||||
ScalableButton* m_btn_rowbottom_up;
|
||||
Label* m_label_rowbottom_status{ nullptr };
|
||||
Label* m_label_rowbottom{ nullptr };
|
||||
|
||||
ScalableButton* m_btn_homing{ nullptr };
|
||||
};
|
||||
|
||||
class wgtDeviceNozzleRackNozzleItem : public StaticBox
|
||||
{
|
||||
public:
|
||||
enum NOZZLE_STATUS
|
||||
{
|
||||
NOZZLE_EMPTY,
|
||||
NOZZLE_NORMAL,
|
||||
NOZZLE_UNKNOWN,
|
||||
NOZZLE_ERROR
|
||||
};
|
||||
|
||||
public:
|
||||
wgtDeviceNozzleRackNozzleItem(wxWindow* parent, int nozzle_id);
|
||||
|
||||
public:
|
||||
void Update(const std::shared_ptr<DevNozzleRack> rack, bool on_rack = true); // on_rack is false means extruder nozzle
|
||||
|
||||
int GetNozzleId() const { return m_nozzle_id; }
|
||||
void SetDisplayIdText(const wxString& text) { m_nozzle_label_id->SetLabel(text);};
|
||||
|
||||
void EnableSelect();;
|
||||
void SetSelected(bool selected);
|
||||
bool IsSelected() const { return m_is_selected; }
|
||||
|
||||
bool IsDisabled() const { return m_is_disabled; }
|
||||
void SetDisable(bool disabled);
|
||||
|
||||
void Rescale();
|
||||
|
||||
private:
|
||||
void CreateGui();
|
||||
|
||||
void SetNozzleStatus(NOZZLE_STATUS status, const wxString& str1, const wxString& str2, const std::string& color);
|
||||
|
||||
void OnBtnNozzleStatus(wxMouseEvent& evt);
|
||||
void OnItemSelected(wxMouseEvent& evt);
|
||||
|
||||
private:
|
||||
std::weak_ptr<DevNozzleRack> m_rack;
|
||||
|
||||
int m_nozzle_id; // internal id, from 0 to 5
|
||||
std::string m_filament_color;
|
||||
NOZZLE_STATUS m_status = NOZZLE_STATUS::NOZZLE_EMPTY;
|
||||
|
||||
// select
|
||||
bool m_is_selected = false;
|
||||
bool m_enable_select = false;
|
||||
ScalableBitmap* m_nozzle_selected_image{ nullptr };
|
||||
wxStaticBitmap* m_nozzle_selected_bitmap{ nullptr };
|
||||
|
||||
// enable or disable
|
||||
bool m_is_disabled = false;
|
||||
|
||||
// Images
|
||||
ScalableBitmap* m_nozzle_normal_image{ nullptr };
|
||||
ScalableBitmap* m_nozzle_empty_image{ nullptr };
|
||||
ScalableBitmap* m_nozzle_unknown_image{ nullptr };
|
||||
ScalableBitmap* m_nozzle_error_image{ nullptr };
|
||||
|
||||
// GUI
|
||||
wxStaticBitmap* m_nozzle_icon{ nullptr };
|
||||
Label* m_nozzle_label_id { nullptr };
|
||||
Label* m_nozzle_label_1{ nullptr };
|
||||
wxStaticBitmap* m_nozzle_status_icon = nullptr;
|
||||
Label* m_nozzle_label_2{ nullptr };
|
||||
};
|
||||
|
||||
};// end of namespace Slic3r::GUI
|
||||
@@ -0,0 +1,705 @@
|
||||
//**********************************************************/
|
||||
/* File: wgtDeviceNozzleRackUpdate.cpp
|
||||
* Description: The panel with rack updating
|
||||
*
|
||||
* \n class wgtDeviceNozzleRackUpdate
|
||||
//**********************************************************/
|
||||
|
||||
#include "wgtDeviceNozzleRackUpdate.h"
|
||||
|
||||
#include "slic3r/GUI/DeviceCore/DevNozzleSystem.h"
|
||||
#include "slic3r/GUI/DeviceCore/DevUpgrade.h"
|
||||
|
||||
#include "slic3r/GUI/MainFrame.hpp"
|
||||
#include "slic3r/GUI/GUI_App.hpp"
|
||||
#include "slic3r/GUI/I18N.hpp"
|
||||
#include "slic3r/GUI/MsgDialog.hpp"
|
||||
#include "slic3r/GUI/wxExtensions.hpp"
|
||||
|
||||
#include "slic3r/GUI/Widgets/Button.hpp"
|
||||
#include "slic3r/GUI/Widgets/Label.hpp"
|
||||
|
||||
#define WX_DIP_SIZE(x, y) wxSize(FromDIP(x), FromDIP(y))
|
||||
|
||||
static wxColour s_red_clr("#D01B1B");
|
||||
|
||||
namespace Slic3r::GUI
|
||||
{
|
||||
|
||||
wxDEFINE_EVENT(wxEVT_NOZZLE_JUMP_UPGRADE, wxCommandEvent);
|
||||
|
||||
wgtDeviceNozzleRackUpgradeDlg::wgtDeviceNozzleRackUpgradeDlg(wxWindow* parent, const std::shared_ptr<DevNozzleRack> rack)
|
||||
: DPIDialog(parent, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, wxCAPTION | wxCLOSE_BOX)
|
||||
{
|
||||
m_rack_upgrade_panel = new wgtDeviceNozzleRackUprade(this);
|
||||
m_rack_upgrade_panel->UpdateRackInfo(rack);
|
||||
|
||||
Bind(wxEVT_NOZZLE_JUMP_UPGRADE, [this](wxCommandEvent&) {
|
||||
EndModal(wxID_OK);
|
||||
});
|
||||
|
||||
auto main_sizer = new wxBoxSizer(wxVERTICAL);
|
||||
main_sizer->Add(m_rack_upgrade_panel, 0, wxEXPAND);
|
||||
SetSizer(main_sizer);
|
||||
Layout();
|
||||
Fit();
|
||||
|
||||
wxGetApp().UpdateDlgDarkUI(this);
|
||||
}
|
||||
|
||||
void wgtDeviceNozzleRackUpgradeDlg::UpdateRackInfo(const std::shared_ptr<DevNozzleRack> rack)
|
||||
{
|
||||
m_rack_upgrade_panel->UpdateRackInfo(rack);
|
||||
}
|
||||
|
||||
void wgtDeviceNozzleRackUpgradeDlg::on_dpi_changed(const wxRect& suggested_rect)
|
||||
{
|
||||
m_rack_upgrade_panel->Rescale();
|
||||
}
|
||||
|
||||
wgtDeviceNozzleRackUprade::wgtDeviceNozzleRackUprade(wxWindow* parent,
|
||||
wxWindowID id,
|
||||
const wxPoint& pos,
|
||||
const wxSize& size,
|
||||
long style)
|
||||
: wxPanel(parent, id, pos, size, style)
|
||||
{
|
||||
CreateGui();
|
||||
}
|
||||
|
||||
void wgtDeviceNozzleRackUprade::CreateGui()
|
||||
{
|
||||
SetBackgroundColour(*wxWHITE);
|
||||
|
||||
// Main vertical sizer
|
||||
auto* main_sizer = new wxBoxSizer(wxVERTICAL);
|
||||
|
||||
// Header: title + buttons
|
||||
auto* header_sizer = new wxBoxSizer(wxHORIZONTAL);
|
||||
|
||||
// Title label
|
||||
auto* title_label = new Label(this, _L("Hotends Info"));
|
||||
title_label->SetFont(Label::Head_14);
|
||||
header_sizer->Add(title_label, 1, wxALIGN_CENTER_VERTICAL | wxLEFT, FromDIP(20));
|
||||
|
||||
// Spacer
|
||||
header_sizer->AddStretchSpacer();
|
||||
main_sizer->Add(header_sizer, 0, wxEXPAND | wxTOP | wxRIGHT, FromDIP(10));
|
||||
|
||||
// "Nozzles"
|
||||
m_extruder_nozzle_item = new wgtDeviceNozzleRackHotendUpdate(this, "R");
|
||||
m_extruder_nozzle_item->UpdateColourStyle(wxColour("#F8F8F8"));
|
||||
m_extruder_nozzle_item->SetExtruderNozzleId(MAIN_EXTRUDER_ID);
|
||||
|
||||
main_sizer->Add(m_extruder_nozzle_item, 0, wxEXPAND | wxALL, FromDIP(12));
|
||||
for (int id = 0; id < 6; id ++)
|
||||
{
|
||||
auto item = new wgtDeviceNozzleRackHotendUpdate(this, wxString::Format("%d", id + 1));
|
||||
item->SetRackNozzleId(id);
|
||||
m_nozzle_items[id] = item;
|
||||
|
||||
main_sizer->Add(item, 0, wxEXPAND | wxALL, FromDIP(12));
|
||||
if (id < 5)
|
||||
{
|
||||
wxPanel* separator = new wxPanel(this);
|
||||
separator->SetMaxSize(wxSize(-1, FromDIP(1)));
|
||||
separator->SetMinSize(wxSize(-1, FromDIP(1)));
|
||||
separator->SetBackgroundColour(wxColour(238, 238, 238)); // Orca: grey300 as a literal, no equivalent colour macro in Orca
|
||||
main_sizer->Add(separator, 0, wxEXPAND | wxLEFT | wxRIGHT, FromDIP(12));
|
||||
}
|
||||
}
|
||||
|
||||
main_sizer->AddSpacer(FromDIP(20));
|
||||
|
||||
// Set sizer
|
||||
this->SetSizer(main_sizer);
|
||||
this->Layout();
|
||||
}
|
||||
|
||||
void wgtDeviceNozzleRackUprade::UpdateRackInfo(const std::shared_ptr<DevNozzleRack> rack)
|
||||
{
|
||||
m_nozzle_rack = rack;
|
||||
if (!rack) { return;}
|
||||
|
||||
// update the nozzles
|
||||
m_extruder_nozzle_item->UpdateExtruderNozzleInfo(rack);
|
||||
for (auto iter : m_nozzle_items)
|
||||
{
|
||||
iter.second->UpdateRackNozzleInfo(rack);
|
||||
}
|
||||
|
||||
// update layout
|
||||
Layout();
|
||||
}
|
||||
|
||||
void wgtDeviceNozzleRackUprade::OnBtnReadAll(wxCommandEvent& e)
|
||||
{
|
||||
if (auto rack = m_nozzle_rack.lock())
|
||||
{
|
||||
rack->CtrlRackReadAll(true);
|
||||
}
|
||||
}
|
||||
|
||||
void wgtDeviceNozzleRackUprade::Rescale()
|
||||
{
|
||||
m_extruder_nozzle_item->Rescale();
|
||||
for (auto& iter : m_nozzle_items)
|
||||
{
|
||||
iter.second->Rescale();
|
||||
}
|
||||
}
|
||||
|
||||
#define WGT_DEVICE_NOZZLE_RACK_HOTEND_UPDATE_DEFAULT_BG *wxWHITE
|
||||
wgtDeviceNozzleRackHotendUpdate::wgtDeviceNozzleRackHotendUpdate(wxWindow* parent, const wxString& idx_text)
|
||||
: StaticBox(parent, wxID_ANY)
|
||||
{
|
||||
CreateGui();
|
||||
|
||||
m_idx_label->SetLabel(idx_text);
|
||||
}
|
||||
|
||||
void wgtDeviceNozzleRackHotendUpdate::CreateGui()
|
||||
{
|
||||
SetBackgroundColour(WGT_DEVICE_NOZZLE_RACK_HOTEND_UPDATE_DEFAULT_BG);
|
||||
SetBorderColor(WGT_DEVICE_NOZZLE_RACK_HOTEND_UPDATE_DEFAULT_BG);
|
||||
SetCornerRadius(0);
|
||||
|
||||
//load nozzle hs image
|
||||
for (int i = 1; i <= 4; i++)
|
||||
{
|
||||
auto normalImage = new ScalableBitmap(this, "Nozzle_HS_01_0" + std::to_string(i * 2), 46);
|
||||
auto bigImage = new ScalableBitmap(this, "Big_Nozzle_HS_01_0" + std::to_string(i * 2), 216);
|
||||
nozzle_hs.push_back({normalImage, bigImage});
|
||||
}
|
||||
for (int i = 2; i <= 4; i++)
|
||||
{
|
||||
auto normalImage = new ScalableBitmap(this, "Nozzle_HH_01_0" + std::to_string(i * 2), 46);
|
||||
auto bigImage = new ScalableBitmap(this, "Big_Nozzle_HH_01_0" + std::to_string(i * 2), 216);
|
||||
nozzle_hh.push_back({normalImage, bigImage});
|
||||
}
|
||||
|
||||
auto* content_sizer = new wxBoxSizer(wxHORIZONTAL);
|
||||
|
||||
// Index
|
||||
m_idx_label = new Label(this);
|
||||
m_idx_label->SetFont(Label::Head_14);
|
||||
m_idx_label->SetBackgroundColour(WGT_DEVICE_NOZZLE_RACK_HOTEND_UPDATE_DEFAULT_BG);
|
||||
content_sizer->Add(m_idx_label, 0, wxALIGN_CENTER_VERTICAL | wxLEFT, FromDIP(25));
|
||||
|
||||
// Icon
|
||||
wxPanel* imagePanel = new wxPanel(this);
|
||||
imagePanel->SetBackgroundColour(WGT_DEVICE_NOZZLE_RACK_HOTEND_UPDATE_DEFAULT_BG);
|
||||
imagePanel->SetMaxSize(WX_DIP_SIZE(46, -1));
|
||||
imagePanel->SetMinSize(WX_DIP_SIZE(46, -1));
|
||||
imagePanel->SetSize(wxSize(FromDIP(46), FromDIP(-1)));
|
||||
wxBoxSizer* panelSizer = new wxBoxSizer(wxVERTICAL);
|
||||
imagePanel->SetSizer(panelSizer);
|
||||
|
||||
m_nozzle_empty_image = new ScalableBitmap(imagePanel, "dev_rack_nozzle_empty", 46);
|
||||
m_icon_bitmap = new wxStaticBitmap(imagePanel, wxID_ANY, m_nozzle_empty_image->bmp());
|
||||
panelSizer->Add(m_icon_bitmap, 0, wxALIGN_CENTER);
|
||||
content_sizer->Add(imagePanel, 0, wxALIGN_CENTER_VERTICAL | wxLEFT, FromDIP(25));
|
||||
m_icon_bitmap->Bind(wxEVT_ENTER_WINDOW, &wgtDeviceNozzleRackHotendUpdate::OnBitmapHoverEnter, this);
|
||||
m_icon_bitmap->Bind(wxEVT_LEAVE_WINDOW, &wgtDeviceNozzleRackHotendUpdate::OnBitmapHoverLeave, this);
|
||||
|
||||
// Diameter/type (vertical)
|
||||
wxPanel* type_panel = new wxPanel(this);
|
||||
type_panel->SetBackgroundColour(WGT_DEVICE_NOZZLE_RACK_HOTEND_UPDATE_DEFAULT_BG);
|
||||
auto* main_type_sizer = new wxBoxSizer(wxVERTICAL);
|
||||
auto* type_sizer_row_1 = new wxBoxSizer(wxHORIZONTAL);
|
||||
auto* type_sizer_row_2 = new wxBoxSizer(wxHORIZONTAL);
|
||||
|
||||
m_material_label = new Label(type_panel);
|
||||
m_material_label->SetFont(Label::Body_12);
|
||||
m_material_label->SetBackgroundColour(WGT_DEVICE_NOZZLE_RACK_HOTEND_UPDATE_DEFAULT_BG);
|
||||
|
||||
m_colour_box = new StaticBox(type_panel);
|
||||
m_colour_box->SetMaxSize(WX_DIP_SIZE(16, 16));
|
||||
m_colour_box->SetMinSize(WX_DIP_SIZE(16, 16));
|
||||
m_colour_box->SetCornerRadius(FromDIP(2));
|
||||
m_colour_box->SetSize(wxSize(FromDIP(16), FromDIP(16)));
|
||||
// m_colour_box->SetBackgroundColour(*wxRED);
|
||||
|
||||
// type_sizer_row_1->AddStretchSpacer();
|
||||
type_sizer_row_1->Add(m_colour_box, 0, wxALIGN_CENTER_VERTICAL | wxALIGN_LEFT);
|
||||
type_sizer_row_1->AddStretchSpacer(1);
|
||||
type_sizer_row_1->Add(m_material_label, 0, wxALIGN_CENTER_VERTICAL | wxLEFT, FromDIP(2));
|
||||
// type_sizer_row_1->AddStretchSpacer();
|
||||
|
||||
m_diameter_label = new Label(type_panel);
|
||||
m_diameter_label->SetFont(Label::Body_12);
|
||||
m_diameter_label->SetBackgroundColour(WGT_DEVICE_NOZZLE_RACK_HOTEND_UPDATE_DEFAULT_BG);
|
||||
|
||||
m_flowtype_label = new Label(type_panel);
|
||||
m_flowtype_label->SetFont(Label::Body_12);
|
||||
m_flowtype_label->SetBackgroundColour(WGT_DEVICE_NOZZLE_RACK_HOTEND_UPDATE_DEFAULT_BG);
|
||||
|
||||
m_type_label = new Label(type_panel);
|
||||
m_type_label->SetFont(Label::Body_12);
|
||||
m_type_label->SetBackgroundColour(WGT_DEVICE_NOZZLE_RACK_HOTEND_UPDATE_DEFAULT_BG);
|
||||
|
||||
type_sizer_row_2->Add(m_diameter_label, 0, wxALIGN_CENTER_VERTICAL | wxALIGN_LEFT );
|
||||
type_sizer_row_2->Add(m_flowtype_label, 0, wxALIGN_CENTER_VERTICAL | wxLEFT, FromDIP(2));
|
||||
type_sizer_row_2->Add(m_type_label, 1, wxALIGN_CENTER_VERTICAL | wxLEFT, FromDIP(2));
|
||||
|
||||
main_type_sizer->Add(type_sizer_row_1, 0, wxALIGN_LEFT);
|
||||
main_type_sizer->Add(type_sizer_row_2, 1, wxALIGN_LEFT | wxEXPAND | wxTOP, FromDIP(4));
|
||||
type_panel->SetSizer(main_type_sizer);
|
||||
type_panel->SetMaxSize(WX_DIP_SIZE(160, 40));
|
||||
type_panel->SetMinSize(WX_DIP_SIZE(160, 40));
|
||||
type_panel->SetSize(WX_DIP_SIZE(160, 40));
|
||||
|
||||
content_sizer->Add(type_panel, 1, wxALIGN_CENTER_VERTICAL | wxLEFT, FromDIP(12));
|
||||
|
||||
// SN and version (vertical)
|
||||
wxPanel* info_panel = new wxPanel(this);
|
||||
info_panel->SetBackgroundColour(WGT_DEVICE_NOZZLE_RACK_HOTEND_UPDATE_DEFAULT_BG);
|
||||
auto* info_sizer = new wxBoxSizer(wxVERTICAL);
|
||||
m_sn_label = new Label(info_panel);
|
||||
m_sn_label->SetFont(Label::Body_12);
|
||||
m_sn_label->SetBackgroundColour(WGT_DEVICE_NOZZLE_RACK_HOTEND_UPDATE_DEFAULT_BG);
|
||||
|
||||
auto* version_h_sizer = new wxBoxSizer(wxHORIZONTAL);
|
||||
m_version_label = new Label(info_panel);
|
||||
m_version_label->SetFont(Label::Body_12);
|
||||
m_version_label->SetBackgroundColour(WGT_DEVICE_NOZZLE_RACK_HOTEND_UPDATE_DEFAULT_BG);
|
||||
|
||||
m_version_new_label = new Label(info_panel);
|
||||
m_version_new_label->SetFont(Label::Body_12);
|
||||
m_version_new_label->SetBackgroundColour(WGT_DEVICE_NOZZLE_RACK_HOTEND_UPDATE_DEFAULT_BG);
|
||||
m_version_new_label->SetForegroundColour(wxColour(0, 168, 84)); // Green
|
||||
|
||||
version_h_sizer->Add(m_version_label, 0, wxALIGN_CENTER_VERTICAL);
|
||||
version_h_sizer->Add(m_version_new_label, 0, wxALIGN_CENTER_VERTICAL);
|
||||
info_sizer->Add(m_sn_label, 0, wxALIGN_LEFT);
|
||||
info_sizer->Add(version_h_sizer, 0, wxALIGN_LEFT | wxTOP, FromDIP(4));
|
||||
info_panel->SetSizer(info_sizer);
|
||||
info_panel->SetMaxSize(WX_DIP_SIZE(183, 40));
|
||||
info_panel->SetMinSize(WX_DIP_SIZE(183, 40));
|
||||
info_panel->SetSize(WX_DIP_SIZE(183, 40));
|
||||
|
||||
//Used Time
|
||||
m_used_time = new Label(this);
|
||||
m_used_time->SetFont(Label::Body_12);
|
||||
m_used_time->SetBackgroundColour(WGT_DEVICE_NOZZLE_RACK_HOTEND_UPDATE_DEFAULT_BG);
|
||||
|
||||
m_refresh_icon = new ScalableBitmap(this, "refresh_printer", 12);
|
||||
// m_in_refreh_icon = new ScalableBitmap(this, "refresh_nozzle", 12);
|
||||
m_error_icon = new ScalableBitmap(this, "error", 14);
|
||||
m_status_bitmap = new wxStaticBitmap(this, wxID_ANY, m_refresh_icon->bmp());
|
||||
m_status_bitmap->Bind(wxEVT_LEFT_UP, &wgtDeviceNozzleRackHotendUpdate::OnStatusIconClick, this);
|
||||
|
||||
std::vector<std::string> list{"refresh_nozzle_1", "refresh_nozzle_2", "refresh_nozzle_3", "refresh_nozzle_4"};
|
||||
// Orca: AnimaIcon has no per-instance size argument (it fixes the size internally), so none is passed.
|
||||
m_refreshing_icon = new AnimaIcon(this, wxID_ANY, list, "refresh_nozzle", 100);
|
||||
m_refreshing_icon->Show(false);
|
||||
|
||||
|
||||
m_status_label = new Label(this);
|
||||
m_status_label->SetFont(Label::Body_12);
|
||||
// m_status_label->SetForegroundColour(wxColour("#00AE42"));
|
||||
|
||||
content_sizer->Add(info_panel, 1, wxALIGN_CENTER_VERTICAL | wxLEFT, FromDIP(20));
|
||||
content_sizer->Add(m_used_time, 1, wxALIGN_CENTER_VERTICAL | wxLEFT, FromDIP(20));
|
||||
content_sizer->Add(m_status_label, 0, wxALIGN_CENTER_VERTICAL | wxLEFT, FromDIP(20));
|
||||
content_sizer->Add(m_status_bitmap, 0, wxALIGN_CENTER_VERTICAL | wxLEFT, FromDIP(2));
|
||||
content_sizer->Add(m_refreshing_icon, 0, wxALIGN_CENTER_VERTICAL | wxLEFT, FromDIP(2));
|
||||
content_sizer->AddSpacer(FromDIP(25));
|
||||
|
||||
auto* main_sizer = new wxBoxSizer(wxHORIZONTAL);
|
||||
main_sizer->Add(content_sizer, 0, wxEXPAND | wxTOP | wxBOTTOM, FromDIP(10));
|
||||
|
||||
SetSizer(main_sizer);
|
||||
Layout();
|
||||
}
|
||||
|
||||
void wgtDeviceNozzleRackHotendUpdate::OnStatusIconClick(wxMouseEvent& event)
|
||||
{
|
||||
if (m_status_label->GetLabel() == _L("Refresh"))
|
||||
{
|
||||
m_status_label->SetForegroundColour(wxColour("#A3A3A3"));
|
||||
m_status_label->SetLabel(_L("Refreshing"));
|
||||
m_status_bitmap->Show(false);
|
||||
// m_status_bitmap->Refresh();
|
||||
if(!m_refreshing_icon->IsPlaying())
|
||||
{
|
||||
m_refreshing_icon->Play();
|
||||
m_refreshing_icon->Show();
|
||||
}
|
||||
if (auto shared = m_nozzle_rack.lock())
|
||||
{
|
||||
shared->CrtlRackReadNozzle(m_rack_nozzle_id);
|
||||
}
|
||||
Layout();
|
||||
}
|
||||
|
||||
if (m_status_label->GetLabel() == _L("Error") /*&& m_nozzle_status == NOZZLE_STATUS_ABNORMAL*/)
|
||||
{
|
||||
if (auto shared = m_nozzle_rack.lock())
|
||||
{
|
||||
MessageDialog dlg(nullptr, _L("Hotend status abnormal, unavailable at present. Please upgrade the firmware"
|
||||
" and try again."), _L("Update"), wxICON_WARNING);
|
||||
dlg.AddButton(wxID_CANCEL, _L("Cancel"), false);
|
||||
dlg.AddButton(wxID_OK,_L("Jump to the upgrade page"), true);
|
||||
|
||||
if (dlg.ShowModal() == wxID_OK)
|
||||
{
|
||||
wxGetApp().mainframe->m_monitor->jump_to_Upgrade();
|
||||
|
||||
wxCommandEvent evt(wxEVT_NOZZLE_JUMP_UPGRADE, GetId());
|
||||
evt.SetEventObject(this);
|
||||
wxWindow* target = GetParent();
|
||||
if (target)
|
||||
{
|
||||
target = target->GetParent();
|
||||
if (target)
|
||||
{
|
||||
wxPostEvent(target, evt);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void wgtDeviceNozzleRackHotendUpdate::OnBitmapHoverEnter(wxMouseEvent& event)
|
||||
{
|
||||
int scaledW = FromDIP(240);
|
||||
int scaledH = FromDIP(240);
|
||||
|
||||
ScalableBitmap* scaledBmp{nullptr};
|
||||
if (m_nozzle_status == NOZZLE_STATUS_EMPTY || m_nozzle_status == NOZZLE_STATUS_UNKNOWN || !findNozzleImage)
|
||||
{
|
||||
if (!m_scaled_nozzle_empty_image) {m_scaled_nozzle_empty_image = new ScalableBitmap(this, "dev_rack_nozzle_empty", 216);}
|
||||
|
||||
scaledBmp = m_scaled_nozzle_empty_image;
|
||||
}
|
||||
else
|
||||
{
|
||||
scaledBmp = m_scaled_nozzle_image;
|
||||
}
|
||||
|
||||
m_hoverFrame = new wxFrame(nullptr, wxID_ANY, "",
|
||||
wxDefaultPosition, wxDefaultSize,
|
||||
wxFRAME_NO_TASKBAR | wxBORDER_NONE | wxTRANSPARENT_WINDOW);
|
||||
m_hoverFrame->SetBackgroundColour(WGT_DEVICE_NOZZLE_RACK_HOTEND_UPDATE_DEFAULT_BG);
|
||||
m_hoverFrame->SetSize(scaledW, scaledH);
|
||||
wxBoxSizer* frameSizer = new wxBoxSizer(wxVERTICAL);
|
||||
m_hoverFrame->SetSizer(frameSizer);
|
||||
|
||||
wxStaticBitmap* hoverBmp = new wxStaticBitmap(m_hoverFrame, wxID_ANY, scaledBmp->bmp());
|
||||
frameSizer->Add(hoverBmp, 0, wxALIGN_CENTER_HORIZONTAL | wxTOP | wxBOTTOM, FromDIP(12));
|
||||
|
||||
wxPoint mousePos = wxGetMousePosition();
|
||||
m_hoverFrame->SetPosition(wxPoint(mousePos.x + 10, mousePos.y));
|
||||
m_hoverFrame->Layout();
|
||||
m_hoverFrame->Show(true);
|
||||
|
||||
event.Skip();
|
||||
}
|
||||
|
||||
void wgtDeviceNozzleRackHotendUpdate::OnBitmapHoverLeave(wxMouseEvent& event)
|
||||
{
|
||||
if (m_hoverFrame)
|
||||
{
|
||||
m_hoverFrame->Destroy();
|
||||
m_hoverFrame = nullptr;
|
||||
}
|
||||
event.Skip();
|
||||
}
|
||||
|
||||
void wgtDeviceNozzleRackHotendUpdate::updateNozzleImage(const DevNozzle& nozzle)
|
||||
{
|
||||
if (m_nozzle_status == NOZZLE_STATUS_UNKNOWN || m_nozzle_status == NOZZLE_STATUS_EMPTY)
|
||||
{
|
||||
if (!m_nozzle_empty_image) {m_nozzle_empty_image = new ScalableBitmap(this, "dev_rack_nozzle_empty", 46);}
|
||||
m_icon_bitmap->SetBitmap(m_nozzle_empty_image->bmp());
|
||||
m_icon_bitmap->Refresh();
|
||||
return;
|
||||
}
|
||||
findNozzleImage = false;
|
||||
int index = -1;
|
||||
switch (nozzle.GetNozzleDiameterType()) {
|
||||
case NOZZLE_DIAMETER_0_2: index = 0; break;
|
||||
case NOZZLE_DIAMETER_0_4: index = 1; break;
|
||||
case NOZZLE_DIAMETER_0_6: index = 2; break;
|
||||
case NOZZLE_DIAMETER_0_8: index = 3; break;
|
||||
default: index = -1; break;
|
||||
}
|
||||
NozzleType nozzleType = nozzle.GetNozzleType();
|
||||
if (nozzleType == ntHardenedSteel || nozzleType == ntStainlessSteel)
|
||||
{
|
||||
if (nozzle.GetNozzleFlowType() == H_FLOW && index >= 1)
|
||||
{
|
||||
m_nozzle_image = nozzle_hh[index - 1][0];
|
||||
m_icon_bitmap->SetBitmap(m_nozzle_image->bmp());
|
||||
m_scaled_nozzle_image = nozzle_hh[index - 1][1];
|
||||
findNozzleImage = true;
|
||||
}
|
||||
else if (nozzle.GetNozzleFlowType() == S_FLOW && index >= 0)
|
||||
{
|
||||
m_nozzle_image = nozzle_hs[index][0];
|
||||
m_icon_bitmap->SetBitmap(m_nozzle_image->bmp());
|
||||
m_scaled_nozzle_image = nozzle_hs[index][1];
|
||||
findNozzleImage = true;
|
||||
}
|
||||
}
|
||||
if (!findNozzleImage)
|
||||
{
|
||||
if (!m_nozzle_empty_image) {m_nozzle_empty_image = new ScalableBitmap(this, "dev_rack_nozzle_empty", 46);}
|
||||
m_icon_bitmap->SetBitmap(m_nozzle_empty_image->bmp());
|
||||
}
|
||||
m_icon_bitmap->Refresh();
|
||||
}
|
||||
|
||||
void wgtDeviceNozzleRackHotendUpdate::UpdateColourStyle(const wxColour& clr)
|
||||
{
|
||||
SetBackgroundColour(clr);
|
||||
SetBorderColor(clr);
|
||||
|
||||
// BFS: Update all children background color
|
||||
auto children = GetChildren();
|
||||
while (!children.IsEmpty())
|
||||
{
|
||||
auto win = children.front();
|
||||
children.pop_front();
|
||||
win->SetBackgroundColour(clr);
|
||||
|
||||
for (auto child : win->GetChildren())
|
||||
{
|
||||
children.push_back(child);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void wgtDeviceNozzleRackHotendUpdate::UpdateExtruderNozzleInfo(const std::shared_ptr<DevNozzleRack> rack)
|
||||
{
|
||||
m_nozzle_rack = rack;
|
||||
if (rack)
|
||||
{
|
||||
DevNozzleSystem* nozzle_system = rack->GetNozzleSystem();
|
||||
if (nozzle_system)
|
||||
{
|
||||
UpdateInfo(nozzle_system->GetExtNozzle(m_ext_nozzle_id));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void wgtDeviceNozzleRackHotendUpdate::UpdateRackNozzleInfo(const std::shared_ptr<DevNozzleRack> rack)
|
||||
{
|
||||
m_nozzle_rack = rack;
|
||||
if (rack)
|
||||
{
|
||||
UpdateInfo(rack->GetNozzle(m_rack_nozzle_id));
|
||||
}
|
||||
}
|
||||
|
||||
void wgtDeviceNozzleRackHotendUpdate::UpdateInfo(const DevNozzle& nozzle)
|
||||
{
|
||||
/*update nozzle possition and background*/
|
||||
if (auto share = m_nozzle_rack.lock())
|
||||
{
|
||||
// if (share->GetReadingCount() > 0 && m_status_label->IsShown())
|
||||
if (share->GetReadingCount() > 0 && m_status_label->IsShown() && m_status_label->GetLabel() == _L("Refreshing"))
|
||||
{
|
||||
m_refreshing_icon->Play();
|
||||
m_refreshing_icon->Show();
|
||||
m_nozzle_status = NOZZLE_STATUS_DC;
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
m_refreshing_icon->Stop();
|
||||
m_refreshing_icon->Show(false);
|
||||
}
|
||||
}
|
||||
|
||||
wxString filamentDisplayName{};
|
||||
for (auto iter = GUI::wxGetApp().preset_bundle->filaments.begin(); iter != GUI::wxGetApp().preset_bundle->filaments.end(); ++iter)
|
||||
{
|
||||
const Preset& filament_preset = *iter;
|
||||
// const auto& config = filament_preset.config;
|
||||
if (filament_preset.filament_id == nozzle.GetFilamentId())
|
||||
{
|
||||
filamentDisplayName = wxString(filament_preset.alias);
|
||||
}
|
||||
}
|
||||
|
||||
if (nozzle.IsEmpty() && m_nozzle_status != NOZZLE_STATUS_EMPTY)
|
||||
{
|
||||
m_nozzle_status = NOZZLE_STATUS_EMPTY;
|
||||
|
||||
m_material_label->Show(false);
|
||||
m_colour_box->Show(false);
|
||||
|
||||
m_diameter_label->Show(false);
|
||||
m_flowtype_label->Show(false);
|
||||
m_type_label->SetLabel(_L("Empty"));
|
||||
m_type_label->SetForegroundColour(StateColor::darkModeColorFor(*wxBLACK));
|
||||
|
||||
m_sn_label->Show(false);
|
||||
m_version_label->Show(false);
|
||||
m_version_new_label->Show(false);
|
||||
|
||||
updateNozzleImage(nozzle);
|
||||
|
||||
m_used_time->Show(false);
|
||||
m_status_label->Show(false);
|
||||
m_status_bitmap->Show(false);
|
||||
|
||||
}
|
||||
else if (nozzle.IsNormal() && m_nozzle_status != NOZZLE_STATUS_NORMAL)
|
||||
{
|
||||
m_nozzle_status = NOZZLE_STATUS_NORMAL;
|
||||
|
||||
m_material_label->SetLabel(filamentDisplayName);
|
||||
m_colour_box->SetBackgroundColour(wxColour("#" + nozzle.GetFilamentColor()));
|
||||
m_colour_box->SetBorderColor(wxColour("#" + nozzle.GetFilamentColor()));
|
||||
m_material_label->Show(true);
|
||||
m_colour_box->Show(true);
|
||||
|
||||
m_diameter_label->Show(true);
|
||||
m_flowtype_label->Show(true);
|
||||
m_diameter_label->SetLabel(nozzle.GetNozzleDiameterStr());
|
||||
m_flowtype_label->SetLabel(nozzle.GetNozzleFlowTypeStr());
|
||||
m_type_label->SetLabel(nozzle.GetNozzleTypeStr());
|
||||
m_type_label->SetForegroundColour(StateColor::darkModeColorFor(*wxBLACK));
|
||||
|
||||
m_sn_label->Show(true);
|
||||
m_version_label->Show(true);
|
||||
|
||||
updateNozzleImage(nozzle);
|
||||
|
||||
m_used_time->Show(true);
|
||||
m_status_label->Show(false);
|
||||
m_status_bitmap->Show(false);
|
||||
}
|
||||
else if (nozzle.IsAbnormal() && m_nozzle_status != NOZZLE_STATUS_ABNORMAL)
|
||||
{
|
||||
m_nozzle_status = NOZZLE_STATUS_ABNORMAL;
|
||||
|
||||
m_material_label->SetLabel(filamentDisplayName);
|
||||
m_colour_box->SetBackgroundColour(wxColour("#" + nozzle.GetFilamentColor()));
|
||||
m_colour_box->SetBorderColor(wxColour("#" + nozzle.GetFilamentColor()));
|
||||
m_material_label->Show(true);
|
||||
m_colour_box->Show(true);
|
||||
|
||||
m_diameter_label->Show(true);
|
||||
m_flowtype_label->Show(true);
|
||||
m_diameter_label->SetLabel(nozzle.GetNozzleDiameterStr());
|
||||
m_flowtype_label->SetLabel(nozzle.GetNozzleFlowTypeStr());
|
||||
m_type_label->SetLabel(nozzle.GetNozzleTypeStr());
|
||||
m_type_label->SetForegroundColour(StateColor::darkModeColorFor(*wxBLACK));
|
||||
|
||||
updateNozzleImage(nozzle);
|
||||
|
||||
m_status_label->Show(true);
|
||||
m_status_bitmap->Show(true);
|
||||
m_status_label->SetForegroundColour(StateColor::darkModeColorFor(wxColour("#E14747")));
|
||||
m_status_label->SetLabel(_L("Error"));
|
||||
m_status_bitmap->SetBitmap(m_error_icon->bmp());
|
||||
m_status_bitmap->Refresh();
|
||||
}
|
||||
else if (nozzle.IsUnknown() && m_nozzle_status != NOZZLE_STATUS_UNKNOWN)
|
||||
{
|
||||
m_nozzle_status = NOZZLE_STATUS_UNKNOWN;
|
||||
|
||||
m_colour_box->Show(false);
|
||||
m_material_label->SetLabel(wxString("--"));
|
||||
m_material_label->Show(true);
|
||||
|
||||
m_diameter_label->Show(false);
|
||||
m_flowtype_label->Show(false);
|
||||
m_type_label->SetLabel(_L("Unknown"));
|
||||
m_type_label->SetForegroundColour(StateColor::darkModeColorFor(*wxBLACK));
|
||||
|
||||
m_sn_label->Show(true);
|
||||
m_version_label->Show(true);
|
||||
|
||||
updateNozzleImage(nozzle);
|
||||
|
||||
m_used_time->Show(true);
|
||||
m_status_label->Show(true);
|
||||
m_status_bitmap->Show(true);
|
||||
m_status_label->SetForegroundColour(wxColour("#009688")); // Orca: accent green
|
||||
m_status_label->SetLabel(_L("Refresh"));
|
||||
m_status_bitmap->SetBitmap(m_refresh_icon->bmp());
|
||||
m_status_bitmap->Refresh();
|
||||
}
|
||||
|
||||
// Update firmware info
|
||||
const DevFirmwareVersionInfo& firmware = nozzle.GetFirmwareInfo();
|
||||
if (nozzle.IsUnknown())
|
||||
{
|
||||
m_sn_label->SetLabel(wxString::Format("%s: --", _L("SN")));
|
||||
m_version_label->SetLabel(wxString::Format("%s: --", _L("Version")));
|
||||
m_version_new_label->Show(false);
|
||||
}
|
||||
if (nozzle.IsNormal() || nozzle.IsAbnormal())
|
||||
{
|
||||
if (firmware.isValid())
|
||||
{
|
||||
m_sn_label->SetLabel(wxString::Format("%s: %s", _L("SN"), firmware.sn));
|
||||
|
||||
if (!firmware.sw_new_ver.empty() && firmware.sw_new_ver != firmware.sw_ver)
|
||||
{
|
||||
m_version_label->SetLabel(wxString::Format("%s: %s > ", _L("Version"), firmware.sw_ver));
|
||||
m_version_new_label->SetLabel(wxString::Format("%s", firmware.sw_new_ver));
|
||||
m_version_new_label->Show(true);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_version_label->SetLabel(wxString::Format("%s:%s", _L("Version"), firmware.sw_ver));
|
||||
m_version_new_label->Show(false);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
m_sn_label->SetLabel(wxString::Format("%s: --", _L("SN")));
|
||||
m_version_label->SetLabel(wxString::Format("%s: --", _L("Version")));
|
||||
m_version_new_label->Show(false);
|
||||
}
|
||||
}
|
||||
|
||||
if (!nozzle.IsEmpty())
|
||||
{
|
||||
if (nozzle.IsUnknown())
|
||||
{
|
||||
m_used_time->SetLabel(wxString::Format(_L("Used Time: %s"), "--h"));
|
||||
}
|
||||
else
|
||||
{
|
||||
// Update used time
|
||||
int usedSeconds = nozzle.GetNozzlePrintTime();
|
||||
if (usedSeconds < 60)
|
||||
{
|
||||
m_used_time->SetLabel(wxString::Format(_L("Used Time: %s"), "0 h"));
|
||||
}
|
||||
else
|
||||
{
|
||||
int printTime = (usedSeconds >= 3600 ? usedSeconds / 3600 : usedSeconds / 60);
|
||||
std::string printTimeStr = (usedSeconds >= 3600 ? std::to_string(printTime) + " h" : std::to_string(printTime) + " min");
|
||||
m_used_time->SetLabel(wxString::Format(_L("Used Time: %s"), printTimeStr.c_str()));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
void wgtDeviceNozzleRackHotendUpdate::Rescale()
|
||||
{
|
||||
// update images
|
||||
if (m_nozzle_image) { m_nozzle_image->msw_rescale(); }
|
||||
if (m_nozzle_empty_image) { m_nozzle_empty_image->msw_rescale(); }
|
||||
if (m_nozzle_status == NOZZLE_STATUS_EMPTY && m_nozzle_empty_image)
|
||||
{
|
||||
m_icon_bitmap->SetBitmap(m_nozzle_empty_image->bmp());
|
||||
}
|
||||
else if (m_nozzle_image != nullptr)
|
||||
{
|
||||
m_icon_bitmap->SetBitmap(m_nozzle_image->bmp());
|
||||
}
|
||||
m_icon_bitmap->Refresh();
|
||||
}
|
||||
|
||||
};// namespace Slic3r::GUI
|
||||
@@ -0,0 +1,166 @@
|
||||
//**********************************************************/
|
||||
/* File: wgtDeviceNozzleRackUpdate.h
|
||||
* Description: The panel for updating hotends
|
||||
*
|
||||
* \n class wgtDeviceNozzleRackUpdate
|
||||
//**********************************************************/
|
||||
|
||||
#pragma once
|
||||
#include "slic3r/GUI/DeviceCore/DevNozzleRack.h"
|
||||
|
||||
#include "slic3r/GUI/GUI_Utils.hpp"
|
||||
#include "slic3r/GUI/Widgets/StaticBox.hpp"
|
||||
#include "slic3r/GUI/Widgets/AnimaController.hpp"
|
||||
|
||||
#include <wx/panel.h>
|
||||
#include <memory>
|
||||
|
||||
// Previous definitions
|
||||
class Button;
|
||||
class Label;
|
||||
class ScalableBitmap;
|
||||
class ScalableButton;
|
||||
namespace Slic3r
|
||||
{
|
||||
struct DevNozzle;
|
||||
class DevNozzleRack;
|
||||
namespace GUI
|
||||
{
|
||||
class wgtDeviceNozzleRackUprade;
|
||||
class wgtDeviceNozzleRackHotendUpdate;
|
||||
}
|
||||
};
|
||||
|
||||
namespace Slic3r::GUI
|
||||
{
|
||||
class wgtDeviceNozzleRackUpgradeDlg : public DPIDialog
|
||||
{
|
||||
public:
|
||||
wgtDeviceNozzleRackUpgradeDlg(wxWindow* parent, const std::shared_ptr<DevNozzleRack> rack);
|
||||
|
||||
public:
|
||||
void UpdateRackInfo(const std::shared_ptr<DevNozzleRack> rack);;
|
||||
|
||||
public:
|
||||
void on_dpi_changed(const wxRect& suggested_rect) override;
|
||||
|
||||
private:
|
||||
wgtDeviceNozzleRackUprade* m_rack_upgrade_panel;
|
||||
};
|
||||
|
||||
|
||||
class wgtDeviceNozzleRackUprade : public wxPanel
|
||||
{
|
||||
public:
|
||||
wgtDeviceNozzleRackUprade(wxWindow* parent,
|
||||
wxWindowID id = wxID_ANY,
|
||||
const wxPoint& pos = wxDefaultPosition,
|
||||
const wxSize& size = wxDefaultSize,
|
||||
long style = wxTAB_TRAVERSAL);
|
||||
~wgtDeviceNozzleRackUprade() = default;
|
||||
|
||||
public:
|
||||
void UpdateRackInfo(const std::shared_ptr<DevNozzleRack> rack);
|
||||
void Rescale();
|
||||
|
||||
private:
|
||||
void CreateGui();
|
||||
|
||||
void OnBtnReadAll(wxCommandEvent& e);
|
||||
|
||||
private:
|
||||
std::weak_ptr<DevNozzleRack> m_nozzle_rack;
|
||||
|
||||
// GUI
|
||||
wgtDeviceNozzleRackHotendUpdate* m_extruder_nozzle_item;
|
||||
std::unordered_map<int, wgtDeviceNozzleRackHotendUpdate*> m_nozzle_items;
|
||||
};
|
||||
|
||||
// Using for rack nozzle id or extruder nozzle id
|
||||
class wgtDeviceNozzleRackHotendUpdate : public StaticBox
|
||||
{
|
||||
public:
|
||||
wgtDeviceNozzleRackHotendUpdate(wxWindow* parent, const wxString& idx_text);
|
||||
|
||||
public:
|
||||
// Color
|
||||
void UpdateColourStyle(const wxColour& clr);
|
||||
|
||||
// extruder nozzle
|
||||
int GetExtruderNozzleId() const { return m_ext_nozzle_id; }
|
||||
void SetExtruderNozzleId(int ext_nozzle_id) { m_ext_nozzle_id = ext_nozzle_id; }
|
||||
void UpdateExtruderNozzleInfo(const std::shared_ptr<DevNozzleRack> rack);
|
||||
|
||||
// rack nozzle
|
||||
void SetRackNozzleId(int rack_nozzle_id) { m_rack_nozzle_id = rack_nozzle_id; }
|
||||
int GetRackNozzleId() const { return m_rack_nozzle_id; }
|
||||
void UpdateRackNozzleInfo(const std::shared_ptr<DevNozzleRack> rack);
|
||||
|
||||
// gui
|
||||
void Rescale();
|
||||
|
||||
private:
|
||||
void CreateGui();
|
||||
|
||||
void UpdateInfo(const DevNozzle& nozzle);
|
||||
|
||||
void OnBitmapHoverEnter(wxMouseEvent& event);
|
||||
void OnBitmapHoverLeave(wxMouseEvent& event);
|
||||
void OnStatusIconClick(wxMouseEvent& event);
|
||||
void updateNozzleImage(const DevNozzle& nozzle);
|
||||
|
||||
private:
|
||||
enum NozzleStatus : int
|
||||
{
|
||||
NOZZLE_STATUS_DC = -1,
|
||||
NOZZLE_STATUS_EMPTY,
|
||||
NOZZLE_STATUS_NORMAL,
|
||||
NOZZLE_STATUS_ABNORMAL,
|
||||
NOZZLE_STATUS_UNKNOWN,
|
||||
};
|
||||
|
||||
private:
|
||||
int m_ext_nozzle_id = -1;
|
||||
int m_rack_nozzle_id = -1;
|
||||
bool m_isRefreshFinish = false;
|
||||
bool findNozzleImage = false;
|
||||
|
||||
NozzleStatus m_nozzle_status = NOZZLE_STATUS_DC;
|
||||
std::weak_ptr<DevNozzleRack> m_nozzle_rack;
|
||||
|
||||
std::vector<std::vector<ScalableBitmap*>> nozzle_hs;
|
||||
std::vector<std::vector<ScalableBitmap*>> nozzle_hh;
|
||||
// GUI
|
||||
ScalableBitmap* m_nozzle_image = nullptr;
|
||||
ScalableBitmap* m_nozzle_empty_image = nullptr;
|
||||
ScalableBitmap* m_scaled_nozzle_image = nullptr;
|
||||
ScalableBitmap* m_scaled_nozzle_empty_image = nullptr;
|
||||
ScalableBitmap* m_refresh_icon = nullptr;
|
||||
// ScalableBitmap* m_in_refreh_icon = nullptr;
|
||||
AnimaIcon* m_refreshing_icon = nullptr;
|
||||
ScalableBitmap* m_error_icon = nullptr;
|
||||
|
||||
Label* m_idx_label;
|
||||
wxStaticBitmap* m_icon_bitmap{ nullptr };
|
||||
wxStaticBitmap* m_status_bitmap{ nullptr };
|
||||
|
||||
Label* m_material_label{ nullptr };
|
||||
StaticBox* m_colour_box{ nullptr };
|
||||
wxFrame* m_hoverFrame{ nullptr };
|
||||
|
||||
Label* m_status_label{ nullptr };
|
||||
Label* m_used_time{ nullptr };
|
||||
|
||||
Label* m_diameter_label;
|
||||
Label* m_flowtype_label;
|
||||
Label* m_type_label;
|
||||
ScalableButton* m_error_button{ nullptr };
|
||||
|
||||
Label* m_sn_label;
|
||||
Label* m_version_label;
|
||||
Label* m_version_new_label;
|
||||
};
|
||||
|
||||
wxDECLARE_EVENT(wxEVT_NOZZLE_JUMP_UPGRADE, wxCommandEvent);
|
||||
|
||||
};// end of namespace Slic3r::GUI
|
||||
@@ -0,0 +1,293 @@
|
||||
//**********************************************************/
|
||||
/* File: wgtDeviceNozzleSelect.cpp
|
||||
* Description: The panel to select nozzle
|
||||
*
|
||||
* \n class wgtDeviceNozzleSelect;
|
||||
//**********************************************************/
|
||||
|
||||
#include "wgtDeviceNozzleSelect.h"
|
||||
#include "wgtDeviceNozzleRack.h"
|
||||
|
||||
#include "slic3r/GUI/I18N.hpp"
|
||||
#include "slic3r/GUI/GUI_App.hpp"
|
||||
#include "slic3r/GUI/DeviceTab/wgtMsgBox.h"
|
||||
#include "slic3r/GUI/Widgets/Label.hpp" // Orca: explicit Label include
|
||||
|
||||
static wxColour s_gray_clr("#B0B0B0");
|
||||
static wxColour s_hgreen_clr("#009688"); // Orca: accent green
|
||||
static wxColour s_red_clr("#D01B1B");
|
||||
|
||||
static std::vector<int> a_nozzle_seq = {16, 18, 20, 17, 19, 21};
|
||||
|
||||
wxDEFINE_EVENT(EVT_NOZZLE_SELECT_CHANGED, wxCommandEvent);
|
||||
wxDEFINE_EVENT(EVT_NOZZLE_SELECT_CLICKED, wxCommandEvent);
|
||||
|
||||
namespace Slic3r::GUI {
|
||||
|
||||
wgtDeviceNozzleRackSelect::wgtDeviceNozzleRackSelect(wxWindow *parent) : wxPanel(parent, wxID_ANY) { CreateGui(); }
|
||||
|
||||
static wxPanel* s_create_title(wxWindow *parent, const wxString& text)
|
||||
{
|
||||
wxPanel *panel = new wxPanel(parent, wxID_ANY);
|
||||
panel->SetBackgroundColour(*wxWHITE);
|
||||
|
||||
auto title = new Label(panel, text);
|
||||
title->SetFont(::Label::Body_13);
|
||||
title->SetBackgroundColour(*wxWHITE);
|
||||
title->SetForegroundColour(0x909090);
|
||||
|
||||
auto split_line = new wxPanel(panel, wxID_ANY, wxDefaultPosition, wxDefaultSize, wxTAB_TRAVERSAL);
|
||||
split_line->SetBackgroundColour(wxColour(0xEE, 0xEE, 0xEE));
|
||||
split_line->SetMinSize(wxSize(-1, 1));
|
||||
split_line->SetMaxSize(wxSize(-1, 1));
|
||||
|
||||
wxBoxSizer *sizer = new wxBoxSizer(wxHORIZONTAL);
|
||||
sizer->Add(0, 0, 0, wxEXPAND, 0);
|
||||
sizer->Add(title, 0, wxALIGN_CENTER, 0);
|
||||
sizer->Add(split_line, 1, wxALIGN_CENTER_VERTICAL | wxEXPAND, 0);
|
||||
panel->SetSizer(sizer);
|
||||
panel->Layout();
|
||||
return panel;
|
||||
}
|
||||
|
||||
void wgtDeviceNozzleRackSelect::CreateGui()
|
||||
{
|
||||
wxSizer *main_sizer = new wxBoxSizer(wxVERTICAL);
|
||||
|
||||
wxColour tip_bg_clr("#FFF0E0");
|
||||
m_title_tips_dynamic = new wgtMsgBox(this);
|
||||
m_title_tips_dynamic->SetBackgroundColour(tip_bg_clr);
|
||||
m_title_tips_dynamic->SetBorderColor(wxColour("#FF6F00"));
|
||||
m_title_tips_dynamic->SetCornerRadius(1);
|
||||
m_title_tips_dynamic->SetBorderWidth(1);
|
||||
auto text_label = m_title_tips_dynamic->GetTextLabel();
|
||||
text_label->SetFont(::Label::Body_12);
|
||||
text_label->SetForegroundColour("#FF6F00");
|
||||
text_label->SetBackgroundColour(tip_bg_clr);
|
||||
text_label->SetLabel(_L("Dynamic nozzles are allocated on the current plate. Picking hotend is not supported."));
|
||||
text_label->Wrap(FromDIP(300));
|
||||
text_label->Fit();
|
||||
m_title_tips_dynamic->Layout();
|
||||
m_title_tips_dynamic->Refresh();
|
||||
|
||||
// nozzles
|
||||
wxGridSizer *nozzle_sizer = new wxGridSizer(2, 3, FromDIP(10), FromDIP(10));
|
||||
for (auto idx : a_nozzle_seq) {
|
||||
wgtDeviceNozzleRackNozzleItem *nozzle_item = new wgtDeviceNozzleRackNozzleItem(this, idx - 16);
|
||||
nozzle_item->EnableSelect();
|
||||
nozzle_item->Bind(EVT_NOZZLE_RACK_NOZZLE_ITEM_SELECTED, &wgtDeviceNozzleRackSelect::OnNozzleItemSelected, this);
|
||||
m_nozzle_items[idx] = nozzle_item;
|
||||
nozzle_sizer->Add(nozzle_item, 0);
|
||||
}
|
||||
|
||||
// toolhead area
|
||||
m_toolhead_nozzle_l = new wgtDeviceNozzleRackNozzleItem(this, 1);
|
||||
m_toolhead_nozzle_l->EnableSelect();
|
||||
m_toolhead_nozzle_l->SetDisplayIdText("L");
|
||||
m_toolhead_nozzle_l->Bind(EVT_NOZZLE_RACK_NOZZLE_ITEM_SELECTED, &wgtDeviceNozzleRackSelect::OnNozzleItemSelected, this);
|
||||
|
||||
m_toolhead_nozzle_r = new wgtDeviceNozzleRackNozzleItem(this, 0);
|
||||
m_toolhead_nozzle_r->EnableSelect();
|
||||
m_toolhead_nozzle_r->SetDisplayIdText("R");
|
||||
m_toolhead_nozzle_r->Bind(EVT_NOZZLE_RACK_NOZZLE_ITEM_SELECTED, &wgtDeviceNozzleRackSelect::OnNozzleItemSelected, this);
|
||||
|
||||
wxSizer* toolhead_sizer = new wxBoxSizer(wxHORIZONTAL);
|
||||
toolhead_sizer->Add(m_toolhead_nozzle_l, 0, wxRIGHT, FromDIP(5));
|
||||
toolhead_sizer->Add(m_toolhead_nozzle_r, 0, wxLEFT, FromDIP(5));
|
||||
|
||||
main_sizer->Add(m_title_tips_dynamic, 0, wxALIGN_CENTRE_VERTICAL | wxBOTTOM, FromDIP(10));
|
||||
main_sizer->Add(s_create_title(this, _L("Hotend Rack")), 0, wxEXPAND);
|
||||
main_sizer->Add(nozzle_sizer, 0, wxTOP | wxBOTTOM | wxALIGN_LEFT, FromDIP(10));
|
||||
main_sizer->Add(s_create_title(this, _L("ToolHead")), 0, wxEXPAND);
|
||||
main_sizer->AddSpacer(FromDIP(10));
|
||||
main_sizer->Add(toolhead_sizer, 0, wxALIGN_LEFT);
|
||||
|
||||
SetBackgroundColour(*wxWHITE);
|
||||
SetSizer(main_sizer);
|
||||
Layout();
|
||||
Fit();
|
||||
|
||||
wxGetApp().UpdateDarkUIWin(this);
|
||||
}
|
||||
|
||||
static void s_update_nozzle_info(wgtDeviceNozzleRackNozzleItem* item,
|
||||
std::shared_ptr<DevNozzleRack> rack,
|
||||
const DevNozzle& nozzle_info)
|
||||
{
|
||||
item->Update(rack, nozzle_info.IsOnRack());
|
||||
if (nozzle_info.IsUnknown()) {
|
||||
if (item->GetToolTipText() != _L("Nozzle information needs to be read")) {
|
||||
item->SetToolTip(_L("Nozzle information needs to be read"));
|
||||
}
|
||||
} else {
|
||||
item->SetToolTip(wxEmptyString);
|
||||
}
|
||||
}
|
||||
|
||||
void wgtDeviceNozzleRackSelect::UpdateNozzleInfos(std::shared_ptr<DevNozzleRack> rack)
|
||||
{
|
||||
m_nozzle_rack = rack;
|
||||
if (rack) {
|
||||
s_update_nozzle_info(m_toolhead_nozzle_l, rack, rack->GetNozzleSystem()->GetNozzleByPosId(DEPUTY_EXTRUDER_ID));
|
||||
s_update_nozzle_info(m_toolhead_nozzle_r, rack, rack->GetNozzleSystem()->GetNozzleByPosId(MAIN_EXTRUDER_ID));
|
||||
for (const auto& item : m_nozzle_items) {
|
||||
s_update_nozzle_info(item.second, rack, rack->GetNozzleSystem()->GetNozzleByPosId(item.first));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void s_enable_item_if_match(wgtDeviceNozzleRackNozzleItem* item,
|
||||
const DevNozzle& nozzle_info,
|
||||
const DevNozzle& selected_nozzle)
|
||||
{
|
||||
if (item) {
|
||||
if (nozzle_info.GetLogicExtruderId() != selected_nozzle.GetLogicExtruderId()) {
|
||||
item->SetDisable(true);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!nozzle_info.IsEmpty() && !nozzle_info.IsAbnormal() && !nozzle_info.IsUnknown() &&
|
||||
nozzle_info.GetNozzleType() == selected_nozzle.GetNozzleType() &&
|
||||
nozzle_info.GetNozzleDiameter() == selected_nozzle.GetNozzleDiameter() &&
|
||||
nozzle_info.GetNozzleFlowType() == selected_nozzle.GetNozzleFlowType()) {
|
||||
item->SetDisable(false);
|
||||
} else {
|
||||
item->SetDisable(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void wgtDeviceNozzleRackSelect::UpdatSelectedNozzle(std::shared_ptr<DevNozzleRack> rack, int selected_nozzle_pos_id)
|
||||
{
|
||||
m_nozzle_rack = rack;
|
||||
if (rack) {
|
||||
SetSelectedNozzle(rack->GetNozzleSystem()->GetNozzleByPosId(selected_nozzle_pos_id));
|
||||
if (m_enable_manual_nozzle_pick) {
|
||||
s_enable_item_if_match(m_toolhead_nozzle_r, rack->GetNozzleSystem()->GetNozzleByPosId(MAIN_EXTRUDER_ID), m_selected_nozzle);
|
||||
s_enable_item_if_match(m_toolhead_nozzle_l, rack->GetNozzleSystem()->GetNozzleByPosId(DEPUTY_EXTRUDER_ID), m_selected_nozzle);
|
||||
for (auto& item : m_nozzle_items) {
|
||||
s_enable_item_if_match(item.second, rack->GetNozzleSystem()->GetNozzleByPosId(item.first), m_selected_nozzle);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void wgtDeviceNozzleRackSelect::UpdatSelectedNozzles(std::shared_ptr<DevNozzleRack> rack,
|
||||
std::vector<int> selected_nozzle_pos_vec,
|
||||
bool use_dynamic_switch,
|
||||
std::optional<PrintFromType> /*print_from_type*/)
|
||||
{
|
||||
m_nozzle_rack = rack;
|
||||
m_enable_manual_nozzle_pick = !use_dynamic_switch;
|
||||
m_title_tips_dynamic->Show(use_dynamic_switch);
|
||||
|
||||
UpdateNozzleInfos(rack);
|
||||
if (!use_dynamic_switch) {
|
||||
if (selected_nozzle_pos_vec.size() > 0) {
|
||||
return UpdatSelectedNozzle(rack, selected_nozzle_pos_vec.at(0));
|
||||
} else {
|
||||
return ClearSelection();
|
||||
}
|
||||
}
|
||||
|
||||
if (rack) {
|
||||
ClearSelection();
|
||||
for (const auto& pos_id : selected_nozzle_pos_vec) {
|
||||
if (pos_id == MAIN_EXTRUDER_ID) {
|
||||
m_toolhead_nozzle_r->SetSelected(true);
|
||||
} else if (pos_id == DEPUTY_EXTRUDER_ID) {
|
||||
m_toolhead_nozzle_l->SetSelected(true);
|
||||
} else if (auto it = m_nozzle_items.find(pos_id); it != m_nozzle_items.end()) {
|
||||
it->second->SetSelected(true);
|
||||
}
|
||||
}
|
||||
|
||||
if (!m_toolhead_nozzle_l->IsSelected()) {
|
||||
m_toolhead_nozzle_l->SetDisable(true);
|
||||
}
|
||||
|
||||
if (!m_toolhead_nozzle_r->IsSelected()) {
|
||||
m_toolhead_nozzle_r->SetDisable(true);
|
||||
}
|
||||
|
||||
for (const auto& item : m_nozzle_items) {
|
||||
if (!item.second->IsSelected()) {
|
||||
item.second->SetDisable(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void wgtDeviceNozzleRackSelect::ClearSelection()
|
||||
{
|
||||
m_selected_nozzle = DevNozzle();
|
||||
m_toolhead_nozzle_l->SetSelected(false);
|
||||
m_toolhead_nozzle_r->SetSelected(false);
|
||||
for (auto &item : m_nozzle_items) { item.second->SetSelected(false); }
|
||||
}
|
||||
|
||||
void wgtDeviceNozzleRackSelect::SetSelectedNozzle(const DevNozzle &nozzle)
|
||||
{
|
||||
int new_selected_pos_id = nozzle.GetNozzlePosId();
|
||||
if (m_selected_nozzle.GetNozzlePosId() != new_selected_pos_id) {
|
||||
ClearSelection();
|
||||
|
||||
m_selected_nozzle = nozzle;
|
||||
if (new_selected_pos_id == MAIN_EXTRUDER_ID) {
|
||||
m_toolhead_nozzle_r->SetSelected(true);
|
||||
} else if (new_selected_pos_id == DEPUTY_EXTRUDER_ID) {
|
||||
m_toolhead_nozzle_l->SetSelected(true);
|
||||
} else if (auto it = m_nozzle_items.find(m_selected_nozzle.GetNozzlePosId()); it != m_nozzle_items.end()) {
|
||||
it->second->SetSelected(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
volatile int sGetNozzlePosId(wgtDeviceNozzleRackNozzleItem* item,
|
||||
wgtDeviceNozzleRackNozzleItem* l_item,
|
||||
wgtDeviceNozzleRackNozzleItem* r_item)
|
||||
{
|
||||
int to_select_pos_id = -1;
|
||||
if (item == l_item) {
|
||||
to_select_pos_id = DEPUTY_EXTRUDER_ID;
|
||||
} else if (item == r_item) {
|
||||
to_select_pos_id = MAIN_EXTRUDER_ID;
|
||||
} else {
|
||||
to_select_pos_id = item->GetNozzleId() + 0x10;
|
||||
}
|
||||
|
||||
return to_select_pos_id;
|
||||
}
|
||||
|
||||
void wgtDeviceNozzleRackSelect::OnNozzleItemSelected(wxCommandEvent &evt)
|
||||
{
|
||||
if (!m_enable_manual_nozzle_pick) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto *item = dynamic_cast<wgtDeviceNozzleRackNozzleItem *>(evt.GetEventObject());
|
||||
if (item; auto ptr = m_nozzle_rack.lock()) {
|
||||
int to_select_pos_id = sGetNozzlePosId(item, m_toolhead_nozzle_l, m_toolhead_nozzle_r);
|
||||
if (to_select_pos_id > -1 && to_select_pos_id != GetSelectedNozzlePosID()) {
|
||||
SetSelectedNozzle(ptr->GetNozzleSystem()->GetNozzleByPosId(to_select_pos_id));
|
||||
wxCommandEvent change_evt(EVT_NOZZLE_SELECT_CHANGED, GetId());
|
||||
change_evt.SetEventObject(this);
|
||||
ProcessEvent(change_evt);
|
||||
evt.Skip();
|
||||
} else {
|
||||
wxCommandEvent change_evt(EVT_NOZZLE_SELECT_CLICKED, GetId());
|
||||
change_evt.SetEventObject(this);
|
||||
ProcessEvent(change_evt);
|
||||
evt.Skip();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void wgtDeviceNozzleRackSelect::Rescale()
|
||||
{
|
||||
m_toolhead_nozzle_l->Rescale();
|
||||
m_toolhead_nozzle_r->Rescale();
|
||||
for (auto &item : m_nozzle_items) { item.second->Rescale(); }
|
||||
}
|
||||
|
||||
}; // namespace Slic3r::GUI
|
||||
@@ -0,0 +1,71 @@
|
||||
//**********************************************************/
|
||||
/* File: wgtDeviceNozzleSelect.h
|
||||
* Description: The panel to select nozzle
|
||||
*
|
||||
* \n class wgtDeviceNozzleSelect;
|
||||
//**********************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "slic3r/GUI/DeviceCore/DevNozzleSystem.h"
|
||||
|
||||
#include <wx/panel.h>
|
||||
|
||||
#include <memory>
|
||||
|
||||
// Previous definitions
|
||||
class Label;
|
||||
namespace Slic3r
|
||||
{
|
||||
struct DevNozzle;
|
||||
class DevNozzleRack;
|
||||
namespace GUI
|
||||
{
|
||||
class wgtDeviceNozzleRackNozzleItem;
|
||||
class wgtMsgBox;
|
||||
}
|
||||
};
|
||||
|
||||
wxDECLARE_EVENT(EVT_NOZZLE_SELECT_CHANGED, wxCommandEvent);
|
||||
wxDECLARE_EVENT(EVT_NOZZLE_SELECT_CLICKED, wxCommandEvent);
|
||||
namespace Slic3r::GUI
|
||||
{
|
||||
class wgtDeviceNozzleRackSelect : public wxPanel
|
||||
{
|
||||
public:
|
||||
wgtDeviceNozzleRackSelect(wxWindow* parent);
|
||||
~wgtDeviceNozzleRackSelect() = default;
|
||||
|
||||
public:
|
||||
int GetSelectedNozzlePosID() const { return m_selected_nozzle.GetNozzlePosId();}
|
||||
void UpdatSelectedNozzles(std::shared_ptr<DevNozzleRack> rack, std::vector<int> selected_nozzle_pos_vec, bool use_dynamic_switch, std::optional<PrintFromType> print_from_type);// for slicing with dynamic switch
|
||||
void Rescale();
|
||||
|
||||
private:
|
||||
void CreateGui();
|
||||
|
||||
void ClearSelection();
|
||||
void SetSelectedNozzle(const DevNozzle &nozzle);
|
||||
void UpdatSelectedNozzle(std::shared_ptr<DevNozzleRack> rack, int selected_nozzle_pos_id = -1); // for slicing without dynamic switch
|
||||
|
||||
void UpdateNozzleInfos(std::shared_ptr<DevNozzleRack> rack);
|
||||
|
||||
void OnNozzleItemSelected(wxCommandEvent& evt);
|
||||
|
||||
private:
|
||||
DevNozzle m_selected_nozzle;
|
||||
std::weak_ptr<DevNozzleRack> m_nozzle_rack;
|
||||
|
||||
// pick
|
||||
bool m_enable_manual_nozzle_pick = true;
|
||||
|
||||
// Label
|
||||
wgtMsgBox* m_title_tips_dynamic;
|
||||
|
||||
// GUI
|
||||
wgtDeviceNozzleRackNozzleItem * m_toolhead_nozzle_l{nullptr};
|
||||
wgtDeviceNozzleRackNozzleItem * m_toolhead_nozzle_r{nullptr};
|
||||
std::unordered_map<int, wgtDeviceNozzleRackNozzleItem *> m_nozzle_items; // from 16 to 21
|
||||
};
|
||||
|
||||
};// end of namespace Slic3r::GUI
|
||||
@@ -0,0 +1,43 @@
|
||||
#include "wgtMsgBox.h"
|
||||
|
||||
#include "slic3r/GUI/GUI_App.hpp"
|
||||
#include "slic3r/GUI/Widgets/Label.hpp"
|
||||
#include "slic3r/GUI/wxExtensions.hpp"
|
||||
|
||||
#include <wx/sizer.h>
|
||||
|
||||
namespace Slic3r::GUI
|
||||
{
|
||||
|
||||
wgtMsgBox::wgtMsgBox(wxWindow* parent)
|
||||
: StaticBox(parent, wxID_ANY)
|
||||
{
|
||||
CreateGUI();
|
||||
}
|
||||
|
||||
void wgtMsgBox::CreateGUI()
|
||||
{
|
||||
wxBoxSizer* mainSizer = new wxBoxSizer(wxHORIZONTAL);
|
||||
|
||||
m_txt_label = new Label(this);
|
||||
m_txt_label->SetFont(::Label::Body_13);
|
||||
m_txt_label->SetBackgroundColour(*wxWHITE);
|
||||
|
||||
auto padding_sizer = new wxBoxSizer(wxHORIZONTAL);
|
||||
padding_sizer->Add(m_txt_label, 0, wxALIGN_CENTER_VERTICAL | wxALL, FromDIP(5));
|
||||
mainSizer->Add(padding_sizer, 0, wxALIGN_CENTER_VERTICAL | wxALL, 1);
|
||||
|
||||
//Button* btnClose = CreateCloseButton();
|
||||
//wxButton* btnClose = CreateCloseButton();
|
||||
//mainSizer->Add(btnClose, 0, wxALIGN_CENTER_VERTICAL);
|
||||
|
||||
SetSizer(mainSizer);
|
||||
mainSizer->Fit(this);
|
||||
}
|
||||
|
||||
void wgtMsgBox::OnCloseClicked(wxCommandEvent& evt)
|
||||
{
|
||||
this->Hide();
|
||||
}
|
||||
|
||||
} // namespace Slic3r::GUI
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user