mirror of
https://github.com/OrcaSlicer/OrcaSlicer.git
synced 2026-09-27 19:01:02 +00:00
Merge remote-tracking branch 'remote/main' into local-fp
This commit is contained in:
@@ -524,6 +524,10 @@ set(SLIC3R_GUI_SOURCES
|
||||
GUI/calib_dlg.cpp
|
||||
Utils/CalibUtils.cpp
|
||||
Utils/CalibUtils.hpp
|
||||
GUI/PrinterCloudAuthDialog.cpp
|
||||
GUI/PrinterCloudAuthDialog.hpp
|
||||
Utils/Obico.cpp
|
||||
Utils/Obico.hpp
|
||||
)
|
||||
|
||||
if (WIN32)
|
||||
@@ -573,7 +577,7 @@ source_group(TREE ${CMAKE_CURRENT_SOURCE_DIR} FILES ${SLIC3R_GUI_SOURCES})
|
||||
|
||||
encoding_check(libslic3r_gui)
|
||||
|
||||
target_link_libraries(libslic3r_gui libslic3r cereal::cereal imgui minilzo GLEW::GLEW OpenGL::GL hidapi ${wxWidgets_LIBRARIES} glfw libcurl OpenSSL::SSL OpenSSL::Crypto)
|
||||
target_link_libraries(libslic3r_gui libslic3r cereal::cereal imgui imguizmo minilzo GLEW::GLEW OpenGL::GL hidapi ${wxWidgets_LIBRARIES} glfw libcurl OpenSSL::SSL OpenSSL::Crypto)
|
||||
#target_link_libraries(libslic3r_gui libslic3r cereal imgui minilzo GLEW::GLEW OpenGL::GL hidapi libcurl OpenSSL::SSL OpenSSL::Crypto ${wxWidgets_LIBRARIES} glfw)
|
||||
|
||||
if (MSVC)
|
||||
|
||||
@@ -100,6 +100,7 @@ void CopyrightsDialog::fill_entries()
|
||||
{ "GLFW", "", "https://www.glfw.org" },
|
||||
{ "GNU gettext", "", "https://www.gnu.org/software/gettext" },
|
||||
{ "ImGUI", "", "https://github.com/ocornut/imgui" },
|
||||
{ "ImGuizmo", "", "https://github.com/CedricGuillemet/ImGuizmo" },
|
||||
{ "Libigl", "", "https://libigl.github.io" },
|
||||
{ "libnest2d", "", "https://github.com/tamasmeszaros/libnest2d" },
|
||||
{ "lib_fts", "", "https://www.forrestthewoods.com" },
|
||||
|
||||
@@ -419,6 +419,15 @@ void Camera::rotate_local_around_target(const Vec3d& rotation_rad)
|
||||
}
|
||||
}
|
||||
|
||||
void Camera::set_rotation(const Transform3d& rotation)
|
||||
{
|
||||
const Vec3d translation = m_view_matrix.translation() + m_view_rotation * m_target;
|
||||
m_view_rotation = Eigen::Quaterniond(rotation.matrix().template block<3, 3>(0, 0));
|
||||
m_view_rotation.normalize();
|
||||
m_view_matrix.fromPositionOrientationScale(m_view_rotation * (-m_target) + translation, m_view_rotation, Vec3d(1., 1., 1.));
|
||||
update_zenit();
|
||||
}
|
||||
|
||||
std::pair<double, double> Camera::calc_tight_frustrum_zs_around(const BoundingBoxf3& box)
|
||||
{
|
||||
std::pair<double, double> ret;
|
||||
|
||||
@@ -146,6 +146,8 @@ public:
|
||||
// rotate the camera around three axes parallel to the camera local axes and passing through m_target
|
||||
void rotate_local_around_target(const Vec3d& rotation_rad);
|
||||
|
||||
void set_rotation(const Transform3d& rotation);
|
||||
|
||||
// returns true if the camera z axis (forward) is pointing in the negative direction of the world z axis
|
||||
bool is_looking_downward() const { return get_dir_forward().dot(Vec3d::UnitZ()) < 0.0; }
|
||||
|
||||
|
||||
@@ -279,6 +279,7 @@ void ConfigManipulation::update_print_fff_config(DynamicPrintConfig* config, con
|
||||
config->opt_int("enforce_support_layers") == 0 &&
|
||||
! config->opt_bool("detect_thin_wall") &&
|
||||
! config->opt_bool("overhang_reverse") &&
|
||||
config->opt_enum<WallDirection>("wall_direction") == WallDirection::Auto &&
|
||||
config->opt_enum<TimelapseType>("timelapse_type") == TimelapseType::tlTraditional))
|
||||
{
|
||||
wxString msg_text = _(L("Spiral mode only works when wall loops is 1, support is disabled, top shell layers is 0, sparse infill density is 0 and timelapse type is traditional."));
|
||||
@@ -306,6 +307,7 @@ void ConfigManipulation::update_print_fff_config(DynamicPrintConfig* config, con
|
||||
new_conf.set_key_value("enforce_support_layers", new ConfigOptionInt(0));
|
||||
new_conf.set_key_value("detect_thin_wall", new ConfigOptionBool(false));
|
||||
new_conf.set_key_value("overhang_reverse", new ConfigOptionBool(false));
|
||||
new_conf.set_key_value("wall_direction", new ConfigOptionEnum<WallDirection>(WallDirection::Auto));
|
||||
new_conf.set_key_value("timelapse_type", new ConfigOptionEnum<TimelapseType>(tlTraditional));
|
||||
sparse_infill_density = 0;
|
||||
timelapse_type = TimelapseType::tlTraditional;
|
||||
@@ -554,6 +556,8 @@ void ConfigManipulation::toggle_print_fff_options(DynamicPrintConfig *config, co
|
||||
|
||||
toggle_field("top_shell_thickness", ! has_spiral_vase && has_top_solid_infill);
|
||||
toggle_field("bottom_shell_thickness", ! has_spiral_vase && has_bottom_solid_infill);
|
||||
|
||||
toggle_field("wall_direction", !has_spiral_vase);
|
||||
|
||||
// Gap fill is newly allowed in between perimeter lines even for empty infill (see GH #1476).
|
||||
toggle_field("gap_infill_speed", have_perimeters);
|
||||
@@ -732,7 +736,8 @@ void ConfigManipulation::toggle_print_fff_options(DynamicPrintConfig *config, co
|
||||
|
||||
bool has_detect_overhang_wall = config->opt_bool("detect_overhang_wall");
|
||||
bool has_overhang_reverse = config->opt_bool("overhang_reverse");
|
||||
bool allow_overhang_reverse = has_detect_overhang_wall && !has_spiral_vase;
|
||||
bool force_wall_direction = config->opt_enum<WallDirection>("wall_direction") != WallDirection::Auto;
|
||||
bool allow_overhang_reverse = has_detect_overhang_wall && !has_spiral_vase && !force_wall_direction;
|
||||
toggle_field("overhang_reverse", allow_overhang_reverse);
|
||||
toggle_line("overhang_reverse_threshold", allow_overhang_reverse && has_overhang_reverse);
|
||||
toggle_line("overhang_reverse_internal_only", allow_overhang_reverse && has_overhang_reverse);
|
||||
@@ -747,6 +752,16 @@ void ConfigManipulation::toggle_print_fff_options(DynamicPrintConfig *config, co
|
||||
|
||||
bool have_small_area_infill_flow_compensation = config->opt_bool("small_area_infill_flow_compensation");
|
||||
toggle_line("small_area_infill_flow_compensation_model", have_small_area_infill_flow_compensation);
|
||||
|
||||
|
||||
toggle_field("seam_slope_type", !has_spiral_vase);
|
||||
bool has_seam_slope = !has_spiral_vase && config->opt_enum<SeamScarfType>("seam_slope_type") != SeamScarfType::None;
|
||||
toggle_line("seam_slope_start_height", has_seam_slope);
|
||||
toggle_line("seam_slope_entire_loop", has_seam_slope);
|
||||
toggle_line("seam_slope_min_length", has_seam_slope);
|
||||
toggle_line("seam_slope_steps", has_seam_slope);
|
||||
toggle_line("seam_slope_inner_walls", has_seam_slope);
|
||||
toggle_field("seam_slope_min_length", !config->opt_bool("seam_slope_entire_loop"));
|
||||
}
|
||||
|
||||
void ConfigManipulation::update_print_sla_config(DynamicPrintConfig* config, const bool is_global_config/* = false*/)
|
||||
|
||||
@@ -3394,19 +3394,6 @@ bool ExportConfigsDialog::has_check_box_selected()
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ExportConfigsDialog::preset_is_not_compatible_bbl_printer(Preset *preset)
|
||||
{
|
||||
if (preset->type != Preset::Type::TYPE_PRINT && preset->type != Preset::Type::TYPE_FILAMENT) return true;
|
||||
PresetBundle * preset_bundle = wxGetApp().preset_bundle;
|
||||
vector<std::string> printers;
|
||||
get_filament_compatible_printer(preset, printers);
|
||||
if (printers.empty()) return true;
|
||||
Preset *printer_preset = preset_bundle->printers.find_preset(printers[0], false);
|
||||
if (!printer_preset) return true;
|
||||
if (!preset_bundle->is_bbl_vendor()) return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string ExportConfigsDialog::initial_file_path(const wxString &path, const std::string &sub_file_path)
|
||||
{
|
||||
std::string export_path = into_u8(path);
|
||||
@@ -3600,12 +3587,6 @@ void ExportConfigsDialog::select_curr_radiobox(std::vector<std::pair<RadioBox *,
|
||||
}else if (export_type == m_exprot_type.filament_bundle) {
|
||||
for (std::pair<std::string, std::vector<std::pair<std::string, Preset*>>> filament_name_to_preset : m_filament_name_to_presets) {
|
||||
if (filament_name_to_preset.second.empty()) continue;
|
||||
bool all_preset_is_compatible_third_printer = true;
|
||||
for (std::pair<std::string, Preset *> filament_preset : filament_name_to_preset.second) {
|
||||
if (!preset_is_not_compatible_bbl_printer(filament_preset.second))
|
||||
all_preset_is_compatible_third_printer = false;
|
||||
}
|
||||
if (all_preset_is_compatible_third_printer) continue;
|
||||
wxString filament_name = wxString::FromUTF8(filament_name_to_preset.first);
|
||||
m_preset_sizer->Add(create_checkbox(m_presets_window, filament_name, m_printer_name), 0, wxEXPAND | wxTOP | wxLEFT | wxRIGHT, FromDIP(5));
|
||||
}
|
||||
@@ -3621,12 +3602,6 @@ void ExportConfigsDialog::select_curr_radiobox(std::vector<std::pair<RadioBox *,
|
||||
} else if (export_type == m_exprot_type.filament_preset) {
|
||||
for (std::pair<std::string, std::vector<std::pair<std::string, Preset *>>> filament_name_to_preset : m_filament_name_to_presets) {
|
||||
if (filament_name_to_preset.second.empty()) continue;
|
||||
bool all_preset_is_compatible_third_printer = true;
|
||||
for (std::pair<std::string, Preset*> filament_preset : filament_name_to_preset.second) {
|
||||
if (!preset_is_not_compatible_bbl_printer(filament_preset.second))
|
||||
all_preset_is_compatible_third_printer = false;
|
||||
}
|
||||
if (all_preset_is_compatible_third_printer) continue;
|
||||
wxString filament_name = wxString::FromUTF8(filament_name_to_preset.first);
|
||||
m_preset_sizer->Add(create_checkbox(m_presets_window, filament_name, m_printer_name), 0, wxEXPAND | wxTOP | wxLEFT | wxRIGHT, FromDIP(5));
|
||||
}
|
||||
@@ -3821,7 +3796,6 @@ ExportConfigsDialog::ExportCase ExportConfigsDialog::archive_filament_bundle_to_
|
||||
std::string printer_vendor = printer_name_to_preset.first;
|
||||
if (printer_vendor.empty()) continue;
|
||||
Preset * filament_preset = printer_name_to_preset.second;
|
||||
if (preset_is_not_compatible_bbl_printer(filament_preset)) continue;
|
||||
if (vendor_to_filament_name.find(std::make_pair(printer_vendor, filament_preset->name)) != vendor_to_filament_name.end()) continue;
|
||||
vendor_to_filament_name.insert(std::make_pair(printer_vendor, filament_preset->name));
|
||||
std::string preset_path = boost::filesystem::path(filament_preset->file).make_preferred().string();
|
||||
@@ -3928,7 +3902,6 @@ ExportConfigsDialog::ExportCase ExportConfigsDialog::archive_filament_preset_to_
|
||||
}
|
||||
for (std::pair<std::string, Preset*> printer_name_preset : iter->second) {
|
||||
Preset * filament_preset = printer_name_preset.second;
|
||||
if (preset_is_not_compatible_bbl_printer(filament_preset)) continue;
|
||||
if (filament_presets.find(filament_preset->name) != filament_presets.end()) continue;
|
||||
filament_presets.insert(filament_preset->name);
|
||||
std::string preset_path = boost::filesystem::path(filament_preset->file).make_preferred().string();
|
||||
@@ -3967,7 +3940,6 @@ ExportConfigsDialog::ExportCase ExportConfigsDialog::archive_process_preset_to_f
|
||||
std::unordered_map<std::string, std::vector<Preset *>>::iterator iter = m_process_presets.find(printer_name);
|
||||
if (m_process_presets.end() != iter) {
|
||||
for (Preset *process_preset : iter->second) {
|
||||
if (preset_is_not_compatible_bbl_printer(process_preset)) continue;
|
||||
if (process_presets.find(process_preset->name) != process_presets.end()) continue;
|
||||
process_presets.insert(process_preset->name);
|
||||
std::string preset_path = boost::filesystem::path(process_preset->file).make_preferred().string();
|
||||
|
||||
@@ -262,7 +262,6 @@ private:
|
||||
void on_dpi_changed(const wxRect &suggested_rect) override;
|
||||
void show_export_result(const ExportCase &export_case);
|
||||
bool has_check_box_selected();
|
||||
bool preset_is_not_compatible_bbl_printer(Preset *preset);
|
||||
std::string initial_file_path(const wxString &path, const std::string &sub_file_path);
|
||||
std::string initial_file_name(const wxString &path, const std::string file_name);
|
||||
wxBoxSizer *create_export_config_item(wxWindow *parent);
|
||||
|
||||
@@ -588,6 +588,14 @@ void TextCtrl::BUILD() {
|
||||
EnterPressed enter(this);
|
||||
propagate_value();
|
||||
}), text_ctrl->GetId());
|
||||
} else {
|
||||
// Orca: adds logic that scrolls the parent if the text control doesn't have focus
|
||||
text_ctrl->Bind(wxEVT_MOUSEWHEEL, [text_ctrl](wxMouseEvent& event) {
|
||||
if (text_ctrl->HasFocus() && text_ctrl->GetScrollRange(wxVERTICAL) != 1)
|
||||
event.Skip(); // don't consume the event so that the text control will scroll
|
||||
else
|
||||
text_ctrl->GetParent()->ScrollLines((event.GetWheelRotation() > 0 ? -1 : 1) * event.GetLinesPerAction());
|
||||
});
|
||||
}
|
||||
|
||||
text_ctrl->Bind(wxEVT_LEFT_DOWN, ([temp](wxEvent &event)
|
||||
|
||||
@@ -85,6 +85,8 @@
|
||||
#endif
|
||||
#include <imgui/imgui_internal.h>
|
||||
|
||||
#include <imguizmo/ImGuizmo.h>
|
||||
|
||||
static constexpr const float TRACKBALLSIZE = 0.8f;
|
||||
|
||||
static Slic3r::ColorRGBA DEFAULT_BG_LIGHT_COLOR = { 0.906f, 0.906f, 0.906f, 1.0f };
|
||||
@@ -5535,6 +5537,70 @@ bool GLCanvas3D::_render_arrange_menu(float left, float right, float bottom, flo
|
||||
return settings_changed;
|
||||
}
|
||||
|
||||
static float identityMatrix[16] = {1.f, 0.f, 0.f, 0.f, 0.f, 1.f, 0.f, 0.f, 0.f, 0.f, 1.f, 0.f, 0.f, 0.f, 0.f, 1.f};
|
||||
static const float cameraProjection[16] = {1.f, 0.f, 0.f, 0.f, 0.f, 1.f, 0.f, 0.f, 0.f, 0.f, 1.f, 0.f, 0.f, 0.f, 0.f, 1.f};
|
||||
|
||||
void GLCanvas3D::_render_3d_navigator()
|
||||
{
|
||||
if (!wxGetApp().show_3d_navigator()) {
|
||||
return;
|
||||
}
|
||||
|
||||
ImGuizmo::BeginFrame();
|
||||
ImGuizmo::AllowAxisFlip(false);
|
||||
|
||||
auto& style = ImGuizmo::GetStyle();
|
||||
style.Colors[ImGuizmo::COLOR::DIRECTION_X] = ImGuiWrapper::to_ImVec4(ColorRGBA::Y());
|
||||
style.Colors[ImGuizmo::COLOR::DIRECTION_Y] = ImGuiWrapper::to_ImVec4(ColorRGBA::Z());
|
||||
style.Colors[ImGuizmo::COLOR::DIRECTION_Z] = ImGuiWrapper::to_ImVec4(ColorRGBA::X());
|
||||
strcpy(style.AxisLabels[ImGuizmo::Axis::Axis_X], "y");
|
||||
strcpy(style.AxisLabels[ImGuizmo::Axis::Axis_Y], "z");
|
||||
strcpy(style.AxisLabels[ImGuizmo::Axis::Axis_Z], "x");
|
||||
|
||||
float sc = get_scale();
|
||||
#ifdef WIN32
|
||||
const int dpi = get_dpi_for_window(wxGetApp().GetTopWindow());
|
||||
sc *= (float) dpi / (float) DPI_DEFAULT;
|
||||
#endif // WIN32
|
||||
|
||||
const ImGuiIO& io = ImGui::GetIO();
|
||||
const float viewManipulateLeft = 0;
|
||||
const float viewManipulateTop = io.DisplaySize.y;
|
||||
const float camDistance = 8.f;
|
||||
ImGuizmo::SetID(0);
|
||||
|
||||
Camera& camera = wxGetApp().plater()->get_camera();
|
||||
Transform3d m = Transform3d::Identity();
|
||||
m.matrix().block(0, 0, 3, 3) = camera.get_view_rotation().toRotationMatrix();
|
||||
// Rotate along X and Z axis for 90 degrees to have Y-up
|
||||
const auto coord_mapping_transform = Geometry::rotation_transform(Vec3d(0.5 * PI, 0, 0.5 * PI));
|
||||
m = m * coord_mapping_transform;
|
||||
float cameraView[16];
|
||||
for (unsigned int c = 0; c < 4; ++c) {
|
||||
for (unsigned int r = 0; r < 4; ++r) {
|
||||
cameraView[c * 4 + r] = m(r, c);
|
||||
}
|
||||
}
|
||||
|
||||
const float size = 128 * sc;
|
||||
const bool dirty = ImGuizmo::ViewManipulate(cameraView, cameraProjection, ImGuizmo::OPERATION::ROTATE, ImGuizmo::MODE::WORLD,
|
||||
identityMatrix, camDistance, ImVec2(viewManipulateLeft, viewManipulateTop - size),
|
||||
ImVec2(size, size), 0x10101010);
|
||||
|
||||
if (dirty) {
|
||||
for (unsigned int c = 0; c < 4; ++c) {
|
||||
for (unsigned int r = 0; r < 4; ++r) {
|
||||
m(r, c) = cameraView[c * 4 + r];
|
||||
}
|
||||
}
|
||||
// Rotate back
|
||||
m = m * (coord_mapping_transform.inverse());
|
||||
camera.set_rotation(m);
|
||||
|
||||
request_extra_frame();
|
||||
}
|
||||
}
|
||||
|
||||
#define ENABLE_THUMBNAIL_GENERATOR_DEBUG_OUTPUT 0
|
||||
#if ENABLE_THUMBNAIL_GENERATOR_DEBUG_OUTPUT
|
||||
static void debug_output_thumbnail(const ThumbnailData& thumbnail_data)
|
||||
@@ -7324,6 +7390,8 @@ void GLCanvas3D::_render_overlays()
|
||||
}*/
|
||||
}
|
||||
m_labels.render(sorted_instances);
|
||||
|
||||
_render_3d_navigator();
|
||||
}
|
||||
|
||||
void GLCanvas3D::_render_style_editor()
|
||||
|
||||
@@ -1181,6 +1181,7 @@ private:
|
||||
//BBS: GUI refactor: adjust main toolbar position
|
||||
bool _render_orient_menu(float left, float right, float bottom, float top);
|
||||
bool _render_arrange_menu(float left, float right, float bottom, float top);
|
||||
void _render_3d_navigator();
|
||||
// render thumbnail using the default framebuffer
|
||||
void render_thumbnail_legacy(ThumbnailData& thumbnail_data, unsigned int w, unsigned int h, const ThumbnailsParams& thumbnail_params, PartPlateList& partplate_list, ModelObjectPtrs& model_objects, const GLVolumeCollection& volumes, std::vector<ColorRGBA>& extruder_colors, GLShaderProgram* shader, Camera::EType camera_type);
|
||||
|
||||
|
||||
@@ -329,6 +329,9 @@ private:
|
||||
bool show_gcode_window() const { return m_show_gcode_window; }
|
||||
void toggle_show_gcode_window();
|
||||
|
||||
bool show_3d_navigator() const { return app_config->get_bool("show_3d_navigator"); }
|
||||
void toggle_show_3d_navigator() const { app_config->set_bool("show_3d_navigator", !show_3d_navigator()); }
|
||||
|
||||
wxString get_inf_dialog_contect () {return m_info_dialog_content;};
|
||||
|
||||
std::vector<std::string> split_str(std::string src, std::string separator);
|
||||
|
||||
@@ -1815,7 +1815,7 @@ void ObjectGridTable::init_cols(ObjectGrid *object_grid)
|
||||
m_col_data.push_back(col);
|
||||
|
||||
//first column for plate_index
|
||||
col = new ObjectGridCol(coString, "plate_index", L(" "), true, false, false, false, wxALIGN_CENTRE); //bool only_object, bool icon, bool edit, bool config
|
||||
col = new ObjectGridCol(coString, "plate_index", " ", true, false, false, false, wxALIGN_CENTRE); //bool only_object, bool icon, bool edit, bool config
|
||||
m_col_data.push_back(col);
|
||||
|
||||
//second column for module name
|
||||
|
||||
@@ -2567,6 +2567,15 @@ void MainFrame::init_menubar_as_editor()
|
||||
this, [this]() { return m_tabpanel->GetSelection() == tpPreview; },
|
||||
[this]() { return wxGetApp().show_gcode_window(); }, this);
|
||||
|
||||
append_menu_check_item(
|
||||
viewMenu, wxID_ANY, _L("Show 3D Navigator"), _L("Show 3D navigator in Prepare and Preview scene"),
|
||||
[this](wxCommandEvent&) {
|
||||
wxGetApp().toggle_show_3d_navigator();
|
||||
m_plater->get_current_canvas3D()->post_event(SimpleEvent(wxEVT_PAINT));
|
||||
},
|
||||
this, [this]() { return m_tabpanel->GetSelection() == TabPosition::tp3DEditor || m_tabpanel->GetSelection() == TabPosition::tpPreview; },
|
||||
[this]() { return wxGetApp().show_3d_navigator(); }, this);
|
||||
|
||||
append_menu_item(
|
||||
viewMenu, wxID_ANY, _L("Reset Window Layout"), _L("Reset to default window layout"),
|
||||
[this](wxCommandEvent&) { m_plater->reset_window_layout(); }, "", this,
|
||||
|
||||
@@ -2356,13 +2356,18 @@ void PartPlate::generate_logo_polygon(ExPolygon &logo_polygon)
|
||||
{
|
||||
if (m_shape.size() == 4)
|
||||
{
|
||||
//rectangle case
|
||||
auto preset_bundle = wxGetApp().preset_bundle;
|
||||
|
||||
bool is_bbl_vendor = false;
|
||||
if (preset_bundle)
|
||||
is_bbl_vendor = preset_bundle->is_bbl_vendor();
|
||||
//rectangle case
|
||||
for (int i = 0; i < 4; i++)
|
||||
{
|
||||
const Vec2d& p = m_shape[i];
|
||||
if ((i == 0) || (i == 1)) {
|
||||
logo_polygon.contour.append({ scale_(p(0)), scale_(p(1) - 12.f) });
|
||||
}
|
||||
logo_polygon.contour.append({scale_(p(0)), scale_(is_bbl_vendor ? p(1) - 12.f : p(1))});
|
||||
}
|
||||
else {
|
||||
logo_polygon.contour.append({ scale_(p(0)), scale_(p(1)) });
|
||||
}
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
#include "PhysicalPrinterDialog.hpp"
|
||||
#include "PresetComboBoxes.hpp"
|
||||
#include "PrinterCloudAuthDialog.hpp"
|
||||
|
||||
#include <cstddef>
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <boost/algorithm/string.hpp>
|
||||
#include <boost/regex.hpp>
|
||||
|
||||
#include <wx/sizer.h>
|
||||
#include <wx/stattext.h>
|
||||
@@ -124,6 +126,8 @@ void PhysicalPrinterDialog::build_printhost_settings(ConfigOptionsGroup* m_optgr
|
||||
this->update();
|
||||
if (opt_key == "print_host")
|
||||
this->update_printhost_buttons();
|
||||
if (opt_key == "printhost_port")
|
||||
this->update_ports();
|
||||
};
|
||||
|
||||
m_optgroup->append_single_option_line("host_type");
|
||||
@@ -161,12 +165,31 @@ void PhysicalPrinterDialog::build_printhost_settings(ConfigOptionsGroup* m_optgr
|
||||
show_error(this, text);
|
||||
return;
|
||||
}
|
||||
|
||||
wxString msg;
|
||||
bool result;
|
||||
{
|
||||
// Show a wait cursor during the connection test, as it is blocking UI.
|
||||
wxBusyCursor wait;
|
||||
result = host->test(msg);
|
||||
|
||||
if (!result && host->is_cloud()) {
|
||||
PrinterCloudAuthDialog dlg(this->GetParent(), host.get());
|
||||
dlg.ShowModal();
|
||||
|
||||
auto api_key = dlg.GetApiKey();
|
||||
m_config->opt_string("printhost_apikey") = api_key;
|
||||
result = !api_key.empty();
|
||||
if (result) {
|
||||
if (Field* print_host_webui_field = this->m_optgroup->get_field("printhost_apikey"); print_host_webui_field) {
|
||||
if (TextInput* temp_input = dynamic_cast<TextInput*>(print_host_webui_field->getWindow()); temp_input) {
|
||||
if (wxTextCtrl* temp = temp_input->GetTextCtrl()) {
|
||||
temp->SetValue(wxString(api_key));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (result)
|
||||
show_info(this, host->get_test_ok_msg(), _L("Success!"));
|
||||
@@ -311,6 +334,42 @@ void PhysicalPrinterDialog::build_printhost_settings(ConfigOptionsGroup* m_optgr
|
||||
update();
|
||||
}
|
||||
|
||||
void PhysicalPrinterDialog::update_ports() {
|
||||
const PrinterTechnology tech = Preset::printer_technology(*m_config);
|
||||
if (tech == ptFFF) {
|
||||
const auto opt = m_config->option<ConfigOptionEnum<PrintHostType>>("host_type");
|
||||
if (opt->value == htObico) {
|
||||
auto build_web_ui = [](DynamicPrintConfig* config) {
|
||||
auto host = config->opt_string("print_host");
|
||||
auto port = config->opt_string("printhost_port");
|
||||
auto api_key = config->opt_string("printhost_apikey");
|
||||
if (host.empty() || port.empty()) {
|
||||
return std::string();
|
||||
}
|
||||
boost::regex re("\\[(\\d+)\\]");
|
||||
boost::smatch match;
|
||||
if (!boost::regex_search(port, match, re))
|
||||
return std::string();
|
||||
if (match.size() <= 1) {
|
||||
return std::string();
|
||||
}
|
||||
boost::format urlFormat("%1%/printers/%2%/control");
|
||||
urlFormat % host % match[1];
|
||||
return urlFormat.str();
|
||||
};
|
||||
auto url = build_web_ui(m_config);
|
||||
if (Field* print_host_webui_field = m_optgroup->get_field("print_host_webui"); print_host_webui_field) {
|
||||
if (TextInput* temp_input = dynamic_cast<TextInput*>(print_host_webui_field->getWindow()); temp_input) {
|
||||
if (wxTextCtrl* temp = temp_input->GetTextCtrl()) {
|
||||
temp->SetValue(wxString(url));
|
||||
m_config->opt_string("print_host_webui") = url;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void PhysicalPrinterDialog::update_printhost_buttons()
|
||||
{
|
||||
std::unique_ptr<PrintHost> host(PrintHost::get_print_host(m_config));
|
||||
@@ -415,6 +474,12 @@ void PhysicalPrinterDialog::update(bool printer_change)
|
||||
if (wxTextCtrl* temp = dynamic_cast<wxTextCtrl*>(printhost_field->getWindow()); temp && temp->GetValue() == L"https://connect.prusa3d.com") {
|
||||
temp->SetValue(wxString());
|
||||
}
|
||||
|
||||
if (TextInput* temp_input = dynamic_cast<TextInput*>(printhost_field->getWindow()); temp_input) {
|
||||
if (wxTextCtrl* temp = temp_input->GetTextCtrl(); temp &&temp->GetValue() == L"https://app.obico.io") {
|
||||
temp->SetValue(wxString());
|
||||
}
|
||||
}
|
||||
}
|
||||
if (opt->value == htPrusaLink) { // PrusaConnect does NOT allow http digest
|
||||
m_optgroup->show_field("printhost_authorization_type");
|
||||
@@ -436,6 +501,18 @@ void PhysicalPrinterDialog::update(bool printer_change)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (opt->value == htObico) {
|
||||
supports_multiple_printers = true;
|
||||
if (Field* printhost_field = m_optgroup->get_field("print_host"); printhost_field) {
|
||||
if (TextInput* temp_input = dynamic_cast<TextInput*>(printhost_field->getWindow()); temp_input) {
|
||||
if (wxTextCtrl* temp = temp_input->GetTextCtrl(); temp && temp->GetValue().IsEmpty()) {
|
||||
temp->SetValue(L"https://app.obico.io");
|
||||
m_config->opt_string("print_host") = "https://app.obico.io";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
m_optgroup->set_value("host_type", int(PrintHostType::htOctoPrint), false);
|
||||
|
||||
@@ -61,6 +61,7 @@ public:
|
||||
void update_preset_input();
|
||||
void update_printhost_buttons();
|
||||
void update_printers();
|
||||
void update_ports();
|
||||
|
||||
protected:
|
||||
void on_dpi_changed(const wxRect& suggested_rect) override;
|
||||
|
||||
@@ -0,0 +1,105 @@
|
||||
#include "PrinterCloudAuthDialog.hpp"
|
||||
#include <wx/sizer.h>
|
||||
#include <wx/toolbar.h>
|
||||
#include <wx/textdlg.h>
|
||||
|
||||
#include <wx/wx.h>
|
||||
#include <wx/fileconf.h>
|
||||
#include <wx/file.h>
|
||||
#include <wx/wfstream.h>
|
||||
|
||||
#include <boost/cast.hpp>
|
||||
#include <boost/lexical_cast.hpp>
|
||||
|
||||
#include <nlohmann/json.hpp>
|
||||
#include "MainFrame.hpp"
|
||||
#include <boost/dll.hpp>
|
||||
|
||||
#include <sstream>
|
||||
#include <slic3r/GUI/Widgets/WebView.hpp>
|
||||
//------------------------------------------
|
||||
// PrinterCloundAuthDialog
|
||||
//------------------------------------------
|
||||
namespace Slic3r { namespace GUI {
|
||||
|
||||
PrinterCloudAuthDialog::PrinterCloudAuthDialog(wxWindow* parent, PrintHost* host)
|
||||
: wxDialog((wxWindow*) (wxGetApp().mainframe), wxID_ANY, "Login"), m_host(host)
|
||||
{
|
||||
SetBackgroundColour(*wxWHITE);
|
||||
// Url
|
||||
host->get_login_url(m_TargetUrl);
|
||||
BOOST_LOG_TRIVIAL(info) << "login url = " << m_TargetUrl.ToStdString();
|
||||
|
||||
// Create the webview
|
||||
m_browser = WebView::CreateWebView(this, m_TargetUrl);
|
||||
if (m_browser == nullptr) {
|
||||
wxLogError("Could not init m_browser");
|
||||
return;
|
||||
}
|
||||
m_browser->Hide();
|
||||
m_browser->SetSize(0, 0);
|
||||
|
||||
// Connect the webview events
|
||||
Bind(wxEVT_WEBVIEW_NAVIGATING, &PrinterCloudAuthDialog::OnNavigationRequest, this, m_browser->GetId());
|
||||
Bind(wxEVT_WEBVIEW_NAVIGATED, &PrinterCloudAuthDialog::OnNavigationComplete, this, m_browser->GetId());
|
||||
Bind(wxEVT_WEBVIEW_LOADED, &PrinterCloudAuthDialog::OnDocumentLoaded, this, m_browser->GetId());
|
||||
Bind(wxEVT_WEBVIEW_NEWWINDOW, &PrinterCloudAuthDialog::OnNewWindow, this, m_browser->GetId());
|
||||
Bind(wxEVT_WEBVIEW_SCRIPT_MESSAGE_RECEIVED, &PrinterCloudAuthDialog::OnScriptMessage, this, m_browser->GetId());
|
||||
|
||||
// UI
|
||||
SetTitle(_L("Login"));
|
||||
// Set a more sensible size for web browsing
|
||||
wxSize pSize = FromDIP(wxSize(650, 840));
|
||||
SetSize(pSize);
|
||||
|
||||
int screenheight = wxSystemSettings::GetMetric(wxSYS_SCREEN_Y, NULL);
|
||||
int screenwidth = wxSystemSettings::GetMetric(wxSYS_SCREEN_X, NULL);
|
||||
int MaxY = (screenheight - pSize.y) > 0 ? (screenheight - pSize.y) / 2 : 0;
|
||||
wxPoint tmpPT((screenwidth - pSize.x) / 2, MaxY);
|
||||
Move(tmpPT);
|
||||
}
|
||||
|
||||
PrinterCloudAuthDialog::~PrinterCloudAuthDialog() {}
|
||||
|
||||
void PrinterCloudAuthDialog::OnNavigationRequest(wxWebViewEvent& evt)
|
||||
{
|
||||
//todo
|
||||
}
|
||||
|
||||
void PrinterCloudAuthDialog::OnNavigationComplete(wxWebViewEvent& evt)
|
||||
{
|
||||
m_browser->Show();
|
||||
Layout();
|
||||
//fortest
|
||||
//WebView::RunScript(m_browser, "window.wx.postMessage('This is a web message')");
|
||||
}
|
||||
|
||||
void PrinterCloudAuthDialog::OnDocumentLoaded(wxWebViewEvent& evt)
|
||||
{
|
||||
// todo
|
||||
}
|
||||
|
||||
void PrinterCloudAuthDialog::OnNewWindow(wxWebViewEvent& evt) {
|
||||
|
||||
}
|
||||
|
||||
void PrinterCloudAuthDialog::OnScriptMessage(wxWebViewEvent& evt)
|
||||
{
|
||||
wxString str_input = evt.GetString();
|
||||
try {
|
||||
json j = json::parse(into_u8(str_input));
|
||||
wxString strCmd = j["command"];
|
||||
if (strCmd == "login_token") {
|
||||
auto token = j["data"]["token"];
|
||||
m_host->set_api_key(token);
|
||||
m_apikey = token;
|
||||
}
|
||||
Close();
|
||||
} catch (std::exception& e) {
|
||||
wxMessageBox(e.what(), "parse json failed", wxICON_WARNING);
|
||||
Close();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
} // namespace Slic3r::GUI
|
||||
@@ -0,0 +1,51 @@
|
||||
#ifndef slic3r_GUI_PrinterCloudAuthDialog_hpp_
|
||||
#define slic3r_GUI_PrinterCloudAuthDialog_hpp_
|
||||
|
||||
#include <wx/wx.h>
|
||||
#include <wx/font.h>
|
||||
#include <wx/colour.h>
|
||||
#include <wx/string.h>
|
||||
#include <wx/sizer.h>
|
||||
#include <wx/dialog.h>
|
||||
#include "wx/webview.h"
|
||||
|
||||
#if wxUSE_WEBVIEW_IE
|
||||
#include "wx/msw/webview_ie.h"
|
||||
#endif
|
||||
#if wxUSE_WEBVIEW_EDGE
|
||||
#include "wx/msw/webview_edge.h"
|
||||
#endif
|
||||
|
||||
#include "GUI_Utils.hpp"
|
||||
#include "PrintHost.hpp"
|
||||
|
||||
namespace Slic3r { namespace GUI {
|
||||
|
||||
class PrinterCloudAuthDialog : public wxDialog
|
||||
{
|
||||
protected:
|
||||
wxWebView* m_browser;
|
||||
wxString m_TargetUrl;
|
||||
|
||||
wxString m_javascript;
|
||||
wxString m_response_js;
|
||||
PrintHost* m_host;
|
||||
std::string m_apikey;
|
||||
|
||||
public:
|
||||
PrinterCloudAuthDialog(wxWindow* parent, PrintHost* host);
|
||||
~PrinterCloudAuthDialog();
|
||||
|
||||
std::string GetApiKey() { return m_apikey; };
|
||||
|
||||
void OnNavigationRequest(wxWebViewEvent& evt);
|
||||
void OnNavigationComplete(wxWebViewEvent& evt);
|
||||
void OnDocumentLoaded(wxWebViewEvent& evt);
|
||||
void OnNewWindow(wxWebViewEvent& evt);
|
||||
void OnScriptMessage(wxWebViewEvent& evt);
|
||||
|
||||
};
|
||||
|
||||
}} // namespace Slic3r::GUI
|
||||
|
||||
#endif
|
||||
+33
-3
@@ -1551,6 +1551,24 @@ void Tab::on_value_change(const std::string& opt_key, const boost::any& value)
|
||||
}
|
||||
}
|
||||
|
||||
if(opt_key == "make_overhang_printable"){
|
||||
if(m_config->opt_bool("make_overhang_printable")){
|
||||
wxString msg_text = _(
|
||||
L("Enabling this option will modify the model's shape. If your print requires precise dimensions or is part of an "
|
||||
"assembly, it's important to double-check whether this change in geometry impacts the functionality of your print."));
|
||||
msg_text += "\n\n" + _(L("Are you sure you want to enable this option?"));
|
||||
MessageDialog dialog(wxGetApp().plater(), msg_text, "", wxICON_WARNING | wxYES | wxNO);
|
||||
dialog.SetButtonLabel(wxID_YES, _L("Enable"));
|
||||
dialog.SetButtonLabel(wxID_NO, _L("Cancel"));
|
||||
if (dialog.ShowModal() == wxID_NO) {
|
||||
DynamicPrintConfig new_conf = *m_config;
|
||||
new_conf.set_key_value("make_overhang_printable", new ConfigOptionBool(false));
|
||||
m_config_manipulation.apply(m_config, &new_conf);
|
||||
wxGetApp().plater()->update();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(opt_key=="layer_height"){
|
||||
auto min_layer_height_from_nozzle=wxGetApp().preset_bundle->full_config().option<ConfigOptionFloats>("min_layer_height")->values;
|
||||
auto max_layer_height_from_nozzle=wxGetApp().preset_bundle->full_config().option<ConfigOptionFloats>("max_layer_height")->values;
|
||||
@@ -1958,6 +1976,12 @@ void TabPrint::build()
|
||||
optgroup->append_single_option_line("seam_position", "seam");
|
||||
optgroup->append_single_option_line("staggered_inner_seams", "seam");
|
||||
optgroup->append_single_option_line("seam_gap","seam");
|
||||
optgroup->append_single_option_line("seam_slope_type", "seam#scarf-joint-seam");
|
||||
optgroup->append_single_option_line("seam_slope_start_height", "seam#scarf-joint-seam");
|
||||
optgroup->append_single_option_line("seam_slope_entire_loop", "seam#scarf-joint-seam");
|
||||
optgroup->append_single_option_line("seam_slope_min_length", "seam#scarf-joint-seam");
|
||||
optgroup->append_single_option_line("seam_slope_steps", "seam#scarf-joint-seam");
|
||||
optgroup->append_single_option_line("seam_slope_inner_walls", "seam#scarf-joint-seam");
|
||||
optgroup->append_single_option_line("role_based_wipe_speed","seam");
|
||||
optgroup->append_single_option_line("wipe_speed", "seam");
|
||||
optgroup->append_single_option_line("wipe_on_loops","seam");
|
||||
@@ -1999,6 +2023,7 @@ void TabPrint::build()
|
||||
optgroup = page->new_optgroup(L("Walls and surfaces"), L"param_advanced");
|
||||
optgroup->append_single_option_line("wall_sequence");
|
||||
optgroup->append_single_option_line("is_infill_first");
|
||||
optgroup->append_single_option_line("wall_direction");
|
||||
optgroup->append_single_option_line("print_flow_ratio");
|
||||
optgroup->append_single_option_line("top_solid_infill_flow_ratio");
|
||||
optgroup->append_single_option_line("bottom_solid_infill_flow_ratio");
|
||||
@@ -2008,13 +2033,12 @@ void TabPrint::build()
|
||||
optgroup->append_single_option_line("reduce_crossing_wall");
|
||||
optgroup->append_single_option_line("max_travel_detour_distance");
|
||||
|
||||
optgroup = page->new_optgroup(L("Small Area Infill Flow Compensation (experimental)"), L"param_advanced");
|
||||
optgroup->append_single_option_line("small_area_infill_flow_compensation");
|
||||
optgroup->append_single_option_line("small_area_infill_flow_compensation", "small-area-infill-flow-compensation");
|
||||
Option option = optgroup->get_option("small_area_infill_flow_compensation_model");
|
||||
option.opt.full_width = true;
|
||||
option.opt.is_code = true;
|
||||
option.opt.height = 15;
|
||||
optgroup->append_single_option_line(option);
|
||||
optgroup->append_single_option_line(option, "small-area-infill-flow-compensation");
|
||||
|
||||
optgroup = page->new_optgroup(L("Bridging"), L"param_advanced");
|
||||
optgroup->append_single_option_line("bridge_flow");
|
||||
@@ -3543,6 +3567,12 @@ void TabPrinter::build_fff()
|
||||
optgroup->append_single_option_line("extruder_clearance_height_to_rod");
|
||||
optgroup->append_single_option_line("extruder_clearance_height_to_lid");
|
||||
|
||||
optgroup = page->new_optgroup(L("Adaptive bed mesh"));
|
||||
optgroup->append_single_option_line("bed_mesh_min", "adaptive_bed_mesh");
|
||||
optgroup->append_single_option_line("bed_mesh_max", "adaptive_bed_mesh");
|
||||
optgroup->append_single_option_line("bed_mesh_probe_distance", "adaptive_bed_mesh");
|
||||
optgroup->append_single_option_line("adaptive_bed_mesh_margin", "adaptive_bed_mesh");
|
||||
|
||||
optgroup = page->new_optgroup(L("Accessory") /*, L"param_accessory"*/);
|
||||
optgroup->append_single_option_line("nozzle_type");
|
||||
optgroup->append_single_option_line("nozzle_hrc");
|
||||
|
||||
@@ -0,0 +1,218 @@
|
||||
#include "Obico.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
#include <sstream>
|
||||
#include <exception>
|
||||
#include <boost/format.hpp>
|
||||
#include <boost/foreach.hpp>
|
||||
#include <boost/log/trivial.hpp>
|
||||
#include <boost/property_tree/ptree.hpp>
|
||||
#include <boost/property_tree/json_parser.hpp>
|
||||
#include <boost/algorithm/string/predicate.hpp>
|
||||
#include <boost/asio.hpp>
|
||||
#include <boost/algorithm/string/split.hpp>
|
||||
#include <boost/nowide/convert.hpp>
|
||||
|
||||
#include <curl/curl.h>
|
||||
#include <wx/progdlg.h>
|
||||
|
||||
#include "slic3r/GUI/GUI.hpp"
|
||||
#include "slic3r/GUI/I18N.hpp"
|
||||
#include "slic3r/GUI/GUI_App.hpp"
|
||||
#include "slic3r/GUI/format.hpp"
|
||||
#include "Http.hpp"
|
||||
#include "libslic3r/AppConfig.hpp"
|
||||
#include "Bonjour.hpp"
|
||||
#include "slic3r/GUI/BonjourDialog.hpp"
|
||||
|
||||
namespace fs = boost::filesystem;
|
||||
namespace pt = boost::property_tree;
|
||||
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
Obico::Obico(DynamicPrintConfig* config) :
|
||||
m_host(config->opt_string("print_host")),
|
||||
m_web_ui(config->opt_string("print_host_webui")),
|
||||
m_cafile(config->opt_string("printhost_cafile")),
|
||||
m_port(config->opt_string("printhost_port")),
|
||||
m_apikey(config->opt_string("printhost_apikey")),
|
||||
m_ssl_revoke_best_effort(config->opt_bool("printhost_ssl_ignore_revoke"))
|
||||
{}
|
||||
|
||||
const char* Obico::get_name() const { return "Obico"; }
|
||||
|
||||
void Obico::set_api_key(const std::string auth_api_key) { m_apikey = auth_api_key; }
|
||||
|
||||
std::string Obico::get_host() const {
|
||||
return m_host;
|
||||
}
|
||||
void Obico::set_auth(Http& http) const
|
||||
{
|
||||
http.header("Authorization", "Bearer " + m_apikey);
|
||||
if (!m_cafile.empty()) {
|
||||
http.ca_file(m_cafile);
|
||||
}
|
||||
}
|
||||
|
||||
bool Obico::get_login_url(wxString& auth_url) const
|
||||
{
|
||||
auth_url = make_url("o/authorize?response_type=token&client_id=OrcaSlicer&hide_navbar=true");
|
||||
return true;
|
||||
}
|
||||
|
||||
wxString Obico::get_test_ok_msg() const { return _(L("Connected to Obico successfully!")); }
|
||||
|
||||
wxString Obico::get_test_failed_msg(wxString& msg) const
|
||||
{
|
||||
return GUI::format_wxstr("%s: %s", _L("Could not connect to Obico"), msg.Truncate(256));
|
||||
}
|
||||
|
||||
bool Obico::test(wxString& msg) const
|
||||
{
|
||||
if (m_apikey.empty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool res = true;
|
||||
const char* name = get_name();
|
||||
auto url = make_url("api/v1/version/");
|
||||
|
||||
BOOST_LOG_TRIVIAL(info) << boost::format("%1%: Get version at: %2%") % name % url;
|
||||
// Here we do not have to add custom "Host" header - the url contains host filled by user and libCurl will set the header by itself.
|
||||
auto http = Http::get(std::move(url));
|
||||
set_auth(http);
|
||||
http.on_error([&](std::string body, std::string error, unsigned status) {
|
||||
BOOST_LOG_TRIVIAL(error) << boost::format("%1%: Error getting version: %2%, HTTP %3%, body: `%4%`") % name % error % status %
|
||||
body;
|
||||
res = false;
|
||||
msg = format_error(body, error, status);
|
||||
})
|
||||
.on_complete([&, this](std::string body, unsigned) {
|
||||
BOOST_LOG_TRIVIAL(debug) << boost::format("%1%: Got version: %2%") % name % body;
|
||||
})
|
||||
#ifdef WIN32
|
||||
.ssl_revoke_best_effort(m_ssl_revoke_best_effort)
|
||||
.on_ip_resolve([&](std::string address) {
|
||||
// Workaround for Windows 10/11 mDNS resolve issue, where two mDNS resolves in succession fail.
|
||||
// Remember resolved address to be reused at successive REST API call.
|
||||
msg = GUI::from_u8(address);
|
||||
})
|
||||
#endif // WIN32
|
||||
.perform_sync();
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
bool Obico::get_printers(wxArrayString& printers) const
|
||||
{
|
||||
const char* name = get_name();
|
||||
bool res = false;
|
||||
auto url = make_url("api/v1/printers/");
|
||||
BOOST_LOG_TRIVIAL(info) << boost::format("%1%: List printers at: %2%") % name % url;
|
||||
|
||||
auto http = Http::get(std::move(url));
|
||||
set_auth(http);
|
||||
|
||||
http.on_error([&](std::string body, std::string error, unsigned status) {
|
||||
BOOST_LOG_TRIVIAL(error) << boost::format("%1%: Error listing printers: %2%, HTTP %3%, body: `%4%`") % name % error % status %
|
||||
body;
|
||||
})
|
||||
.on_complete([&](std::string body, unsigned http_status) {
|
||||
BOOST_LOG_TRIVIAL(debug) << boost::format("%1%: Got printers: %2%, HTTP status: %3%") % name % body % http_status;
|
||||
if (http_status != 200)
|
||||
throw HostNetworkError(GUI::format(_L("HTTP status: %1%\nMessage body: \"%2%\""), http_status, body));
|
||||
|
||||
std::stringstream ss(body);
|
||||
pt::ptree ptree;
|
||||
try {
|
||||
pt::read_json(ss, ptree);
|
||||
} catch (const pt::ptree_error& err) {
|
||||
throw HostNetworkError(
|
||||
GUI::format(_L("Parsing of host response failed.\nMessage body: \"%1%\"\nError: \"%2%\""), body, err.what()));
|
||||
}
|
||||
|
||||
const auto error = ptree.get_optional<std::string>("error");
|
||||
if (error)
|
||||
throw HostNetworkError(*error);
|
||||
|
||||
try {
|
||||
BOOST_FOREACH (boost::property_tree::ptree::value_type& v, ptree) {
|
||||
const auto name = v.second.get<std::string>("name");
|
||||
const auto port = v.second.get<std::string>("id");
|
||||
printers.push_back(Slic3r::GUI::from_u8(name + " [" + port + "]"));
|
||||
}
|
||||
} catch (const std::exception& err) {
|
||||
throw HostNetworkError(
|
||||
GUI::format(_L("Enumeration of host printers failed.\nMessage body: \"%1%\"\nError: \"%2%\""), body, err.what()));
|
||||
}
|
||||
res = true;
|
||||
})
|
||||
.perform_sync();
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
PrintHostPostUploadActions Obico::get_post_upload_actions() const {
|
||||
return PrintHostPostUploadAction::StartPrint;
|
||||
}
|
||||
|
||||
bool Obico::upload(PrintHostUpload upload_data, ProgressFn prorgess_fn, ErrorFn error_fn, InfoFn info_fn) const
|
||||
{
|
||||
const char* name = get_name();
|
||||
const auto upload_filename = upload_data.upload_path.filename();
|
||||
const auto upload_parent_path = upload_data.upload_path.parent_path();
|
||||
wxString test_msg;
|
||||
if (!test(test_msg)) {
|
||||
error_fn(std::move(test_msg));
|
||||
return false;
|
||||
}
|
||||
|
||||
bool res = true;
|
||||
auto url = make_url("api/v1/g_code_files/");
|
||||
|
||||
auto http = Http::post(url); // std::move(url));
|
||||
set_auth(http);
|
||||
http.form_add("print", upload_data.post_action == PrintHostPostUploadAction::StartPrint ? "true" : "false")
|
||||
.form_add("path", upload_parent_path.string()) // XXX: slashes on windows ???
|
||||
.form_add("printer_id", m_port)
|
||||
.form_add("filename", upload_filename.string())
|
||||
.form_add_file("file", upload_data.source_path.string(), upload_filename.string())
|
||||
|
||||
.on_complete([&](std::string body, unsigned status) {
|
||||
BOOST_LOG_TRIVIAL(debug) << boost::format("%1%: File uploaded: HTTP %2%: %3%") % name % status % body;
|
||||
})
|
||||
.on_error([&](std::string body, std::string error, unsigned status) {
|
||||
BOOST_LOG_TRIVIAL(error) << boost::format("%1%: Error uploading file to %2%: %3%, HTTP %4%, body: `%5%`") % name % url % error %
|
||||
status % body;
|
||||
error_fn(format_error(body, error, status));
|
||||
res = false;
|
||||
})
|
||||
.on_progress([&](Http::Progress progress, bool& cancel) {
|
||||
prorgess_fn(std::move(progress), cancel);
|
||||
if (cancel) {
|
||||
// Upload was canceled
|
||||
BOOST_LOG_TRIVIAL(info) << name << ": Upload canceled";
|
||||
res = false;
|
||||
}
|
||||
})
|
||||
#ifdef WIN32
|
||||
.ssl_revoke_best_effort(m_ssl_revoke_best_effort)
|
||||
#endif
|
||||
.perform_sync();
|
||||
return res;
|
||||
}
|
||||
|
||||
std::string Obico::make_url(const std::string& path) const
|
||||
{
|
||||
if (m_host.find("http://") == 0 || m_host.find("https://") == 0) {
|
||||
if (m_host.back() == '/') {
|
||||
return (boost::format("%1%%2%") % m_host % path).str();
|
||||
} else {
|
||||
return (boost::format("%1%/%2%") % m_host % path).str();
|
||||
}
|
||||
} else {
|
||||
return (boost::format("http://%1%/%2%") % m_host % path).str();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
#ifndef slic3r_Obico_hpp_
|
||||
#define slic3r_Obico_hpp_
|
||||
|
||||
#include <string>
|
||||
#include <wx/string.h>
|
||||
#include <boost/optional.hpp>
|
||||
#include <boost/asio/ip/address.hpp>
|
||||
|
||||
#include "PrintHost.hpp"
|
||||
#include "libslic3r/PrintConfig.hpp"
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
class DynamicPrintConfig;
|
||||
class Http;
|
||||
class Obico : public PrintHost
|
||||
{
|
||||
public:
|
||||
Obico(DynamicPrintConfig* config);
|
||||
~Obico() override = default;
|
||||
|
||||
const char* get_name() const override;
|
||||
virtual bool can_test() const { return true; };
|
||||
bool has_auto_discovery() const override { return false; }
|
||||
bool is_cloud() const override { return true; }
|
||||
bool get_login_url(wxString& auth_url) const override;
|
||||
void set_api_key(const std::string auth_api_key) override;
|
||||
std::string get_host() const override;
|
||||
|
||||
wxString get_test_ok_msg() const override;
|
||||
wxString get_test_failed_msg(wxString& msg) const override;
|
||||
virtual bool test(wxString& curl_msg) const override;
|
||||
bool get_printers(wxArrayString& printers) const override;
|
||||
PrintHostPostUploadActions get_post_upload_actions() const;
|
||||
bool upload(PrintHostUpload upload_data, ProgressFn prorgess_fn, ErrorFn error_fn, InfoFn info_fn) const override;
|
||||
|
||||
protected:
|
||||
virtual void set_auth(Http& http) const;
|
||||
private:
|
||||
std::string m_host;
|
||||
std::string m_port;
|
||||
std::string m_apikey;
|
||||
std::string m_cafile;
|
||||
std::string m_web_ui;
|
||||
bool m_ssl_revoke_best_effort;
|
||||
|
||||
std::string make_url(const std::string& path) const;
|
||||
};
|
||||
} // namespace Slic3r
|
||||
|
||||
#endif
|
||||
@@ -20,6 +20,7 @@
|
||||
#include "Repetier.hpp"
|
||||
#include "MKS.hpp"
|
||||
#include "../GUI/PrintHostDialogs.hpp"
|
||||
#include "Obico.hpp"
|
||||
|
||||
namespace fs = boost::filesystem;
|
||||
using boost::optional;
|
||||
@@ -54,6 +55,7 @@ PrintHost* PrintHost::get_print_host(DynamicPrintConfig *config)
|
||||
case htPrusaLink: return new PrusaLink(config);
|
||||
case htPrusaConnect: return new PrusaConnect(config);
|
||||
case htMKS: return new MKS(config);
|
||||
case htObico: return new Obico(config);
|
||||
default: return nullptr;
|
||||
}
|
||||
} else {
|
||||
|
||||
@@ -70,6 +70,11 @@ public:
|
||||
|
||||
static PrintHost* get_print_host(DynamicPrintConfig *config);
|
||||
|
||||
//Support for cloud webui login
|
||||
virtual bool is_cloud() const { return false; }
|
||||
virtual bool get_login_url(wxString& auth_url) const { return false; }
|
||||
virtual void set_api_key(const std::string auth_api_key) {}
|
||||
|
||||
protected:
|
||||
virtual wxString format_error(const std::string &body, const std::string &error, unsigned status) const;
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user