diff --git a/resources/web/data/text.js b/resources/web/data/text.js
index 4a1fd7ea51..33fbc89087 100644
--- a/resources/web/data/text.js
+++ b/resources/web/data/text.js
@@ -109,7 +109,7 @@ var LangText = {
orca1: "Edit Project Info",
orca2: "No model information",
orca3: "Stealth Mode",
- orca4: "This stops the transmission of data to Bambu's cloud services. Users who don't use BBL machines or use LAN mode only can safely turn on this function.",
+ orca4: "This disables all cloud features, including Orca Cloud profile syncing. Users who prefer to work entirely offline can enable this option.",
orca5: "Enable Stealth Mode.",
orca6: "Bambu Cloud",
orca7: "Orca Cloud Account",
@@ -117,6 +117,7 @@ var LangText = {
orca9: "Bambu Cloud Account",
orca10: "Not connected",
orca11: "Connected",
+ orca12: "Note: When Stealth Mode is enabled, your user profiles will not be backed up to Orca Cloud.",
},
ca_ES: {
t1: "Benvingut a Orca Slicer",
@@ -343,7 +344,6 @@ var LangText = {
orca1: "Editar información del proyecto",
orca2: "No hay información sobre el modelo",
orca3: "Modo sigiloso",
- orca4: "Esta función detiene la transmisión de datos a los servicios en la nube de Bambu. Los usuarios que no utilicen máquinas BBL o que solo usen el modo LAN pueden activar esta función con seguridad.",
orca5: "Activar modo sigiloso.",
orca6: "Bambu Cloud",
orca7: "Orca Cloud Account",
@@ -461,7 +461,6 @@ var LangText = {
orca1: "Modifica informazioni progetto",
orca2: "Nessuna informazione sul modello",
orca3: "Modalità invisibile",
- orca4: "Con questa modalità, la trasmissione dei dati ai servizi cloud di Bambu sarà interrotta. Gli utenti che non utilizzano macchine BBL o che usano solo la modalità LAN possono attivare questa funzione in modo sicuro.",
orca5: "Abilita la modalità invisibile.",
orca6: "Bambu Cloud",
orca7: "Orca Cloud Account",
@@ -1169,7 +1168,6 @@ var LangText = {
orca1: "Изменить информацию",
orca2: "Информация отсутствует",
orca3: "Режим конфиденциальности",
- orca4: "Это остановит передачу данных в облачные сервисы Bambu. Помешает только владельцам Bambu Lab, не использующим режим «Только LAN».",
orca5: "Включить режим конфиденциальности",
orca6: "Bambu Cloud",
orca7: "Orca Cloud Account",
@@ -1386,7 +1384,6 @@ var LangText = {
orca1: "Proje Bilgilerini Düzenle",
orca2: "Model bilgisi yok",
orca3: "Gizli Mod",
- orca4: "Bu, Bambu'nun bulut hizmetlerine veri iletimini durdurur. BBL makinelerini kullanmayan veya yalnızca LAN modunu kullanan kullanıcılar bu işlevi güvenle açabilir.",
orca5: "Gizli Modu etkinleştirin.",
orca6: "Bambu Cloud",
orca7: "Orca Cloud Account",
@@ -1505,7 +1502,6 @@ var LangText = {
orca1: "Edytuj informacje o projekcie",
orca2: "Brak informacji o modelu",
orca3: "Tryb «Niewidzialny»",
- orca4: "To wyłączy przesyłanie danych do usług chmurowych Bambu. Użytkownicy, którzy nie korzystają z maszyn BBL lub używają tylko trybu LAN, mogą bez obaw włączyć tę opcję.",
orca5: "Włącz tryb «Niewidzialny»",
orca6: "Bambu Cloud",
orca7: "Orca Cloud Account",
@@ -1624,7 +1620,6 @@ var LangText = {
orca1: "Editar Info do Projeto",
orca2: "Sem informação do modelo",
orca3: "Modo Furtivo",
- orca4: "Isso interrompe a transmissão de dados para os serviços de nuvem da Bambu. Usuários que não usam máquinas BBL ou usam somente o modo LAN podem ativar essa função com segurança.",
orca5: "Habilita Modo Furtivo.",
orca6: "Bambu Cloud",
orca7: "Orca Cloud Account",
@@ -1742,7 +1737,6 @@ var LangText = {
orca1: "Redaguoti projekto informaciją",
orca2: "Nėra informacijos apie modelį",
orca3: "Slaptas režimas",
- orca4: "Tai sustabdo duomenų perdavimą į Bambu debesijos paslaugas. Vartotojai, kurie nenaudoja BBL mašinų arba naudoja tik LAN režimą, gali drąsiai įjungti šią funkciją.",
orca5: "Įjungti slaptą režimą.",
orca6: "Bambu Cloud",
orca7: "Orca Cloud Account",
diff --git a/resources/web/guide/4orca/index.html b/resources/web/guide/4orca/index.html
index e9b5cf5df2..df61fa4d92 100644
--- a/resources/web/guide/4orca/index.html
+++ b/resources/web/guide/4orca/index.html
@@ -22,7 +22,9 @@
diff --git a/src/libslic3r/AppConfig.cpp b/src/libslic3r/AppConfig.cpp
index b35a754c2b..13782fbad2 100644
--- a/src/libslic3r/AppConfig.cpp
+++ b/src/libslic3r/AppConfig.cpp
@@ -93,6 +93,11 @@ bool AppConfig::get_stealth_mode()
return get_bool("stealth_mode");
}
+bool AppConfig::get_hide_login_side_panel()
+{
+ return get_bool("hide_login_side_panel");
+}
+
void AppConfig::reset()
{
m_storage.clear();
@@ -347,6 +352,9 @@ void AppConfig::set_defaults()
if (get("stealth_mode").empty()) {
set_bool("stealth_mode", false);
}
+ if (get("hide_login_side_panel").empty()) {
+ set_bool("hide_login_side_panel", false);
+ }
if (get("allow_abnormal_storage").empty()) {
set_bool("allow_abnormal_storage", false);
}
diff --git a/src/libslic3r/AppConfig.hpp b/src/libslic3r/AppConfig.hpp
index 33f243905a..76366784b0 100644
--- a/src/libslic3r/AppConfig.hpp
+++ b/src/libslic3r/AppConfig.hpp
@@ -85,6 +85,7 @@ public:
std::string get_language_code();
std::string get_hms_host();
bool get_stealth_mode();
+ bool get_hide_login_side_panel();
// Clear and reset to defaults.
void reset();
diff --git a/src/libslic3r/GCode.cpp b/src/libslic3r/GCode.cpp
index 2ad11a710d..eaaed09486 100644
--- a/src/libslic3r/GCode.cpp
+++ b/src/libslic3r/GCode.cpp
@@ -3211,18 +3211,24 @@ void GCode::_do_export(Print& print, GCodeOutputStream &file, ThumbnailsGenerato
if (is_bbl_printers) {
this->_print_first_layer_extruder_temperatures(file, print, machine_start_gcode, initial_extruder_id, true);
}
- // Orca: when activate_air_filtration is set on any extruder, find and set the highest during_print_exhaust_fan_speed
- bool activate_air_filtration_during_print = false;
- int during_print_exhaust_fan_speed = 0;
- for (const auto &extruder : m_writer->extruders()) {
- if (m_config.activate_air_filtration.get_at(extruder.id()) && m_config.activate_air_filtration_during_print.get_at(extruder.id())) {
- activate_air_filtration_during_print = true;
- during_print_exhaust_fan_speed = std::max(during_print_exhaust_fan_speed,
- m_config.during_print_exhaust_fan_speed.get_at(extruder.id()));
+
+ // Orca: when air filtration is supported, check if it needs to be activated during printing and set the exhaust fan speed accordingly
+ if (m_config.support_air_filtration.value) {
+ bool activate_air_filtration_during_print = false;
+ int during_print_exhaust_fan_speed = 0;
+
+ // Orca: when activate_air_filtration is set on any extruder, find and set the highest during_print_exhaust_fan_speed
+ for (const auto &extruder : m_writer->extruders()) {
+ if (m_config.activate_air_filtration.get_at(extruder.id()) && m_config.activate_air_filtration_during_print.get_at(extruder.id())) {
+ activate_air_filtration_during_print = true;
+ during_print_exhaust_fan_speed = std::max(during_print_exhaust_fan_speed,
+ m_config.during_print_exhaust_fan_speed.get_at(extruder.id()));
+ }
}
+
+ if (activate_air_filtration_during_print)
+ file.write(m_writer->set_exhaust_fan(during_print_exhaust_fan_speed));
}
- if (activate_air_filtration_during_print)
- file.write(m_writer->set_exhaust_fan(during_print_exhaust_fan_speed, true));
print.throw_if_canceled();
@@ -3523,16 +3529,23 @@ void GCode::_do_export(Print& print, GCodeOutputStream &file, ThumbnailsGenerato
if (activate_chamber_temp_control && max_chamber_temp > 0)
file.write(m_writer->set_chamber_temperature(0, false)); //close chamber_temperature
- bool activate_air_filtration_on_completion = false;
- int complete_print_exhaust_fan_speed = 0;
- for (const auto& extruder : m_writer->extruders()) {
- if (m_config.activate_air_filtration.get_at(extruder.id()) && m_config.activate_air_filtration_on_completion.get_at(extruder.id())) {
- activate_air_filtration_on_completion = true;
- complete_print_exhaust_fan_speed = std::max(complete_print_exhaust_fan_speed, m_config.complete_print_exhaust_fan_speed.get_at(extruder.id()));
+ // Orca: when air filtration is supported, check if it needs to be activated after print completion and set the exhaust fan speed accordingly
+ if (m_config.support_air_filtration.value) {
+ bool activate_air_filtration_on_completion = false;
+ int complete_print_exhaust_fan_speed = 0;
+
+ // Orca: when activate_air_filtration is set on any extruder, find and set the highest complete_print_exhaust_fan_speed
+ for (const auto& extruder : m_writer->extruders()) {
+ if (m_config.activate_air_filtration.get_at(extruder.id()) && m_config.activate_air_filtration_on_completion.get_at(extruder.id())) {
+ activate_air_filtration_on_completion = true;
+ complete_print_exhaust_fan_speed = std::max(complete_print_exhaust_fan_speed, m_config.complete_print_exhaust_fan_speed.get_at(extruder.id()));
+ }
}
+
+ if (activate_air_filtration_on_completion)
+ file.write(m_writer->set_exhaust_fan(complete_print_exhaust_fan_speed));
}
- if (activate_air_filtration_on_completion)
- file.write(m_writer->set_exhaust_fan(complete_print_exhaust_fan_speed, true));
+
// adds tags for time estimators
file.write_format(";%s\n", GCodeProcessor::reserved_tag(GCodeProcessor::ETags::Last_Line_M73_Placeholder).c_str());
file.write_format("; EXECUTABLE_BLOCK_END\n\n");
diff --git a/src/libslic3r/GCodeWriter.cpp b/src/libslic3r/GCodeWriter.cpp
index aed123ab55..3591d01b10 100644
--- a/src/libslic3r/GCodeWriter.cpp
+++ b/src/libslic3r/GCodeWriter.cpp
@@ -1206,13 +1206,19 @@ std::string GCodeWriter::set_additional_fan(unsigned int speed)
return gcode.str();
}
-std::string GCodeWriter::set_exhaust_fan( int speed,bool add_eol)
+std::string GCodeWriter::set_exhaust_fan(int speed)
{
std::ostringstream gcode;
gcode << "M106" << " P3" << " S" << (int)(speed / 100.0 * 255);
- if(add_eol)
- gcode << "\n";
+ if (GCodeWriter::full_gcode_comment) {
+ if (speed == 0)
+ gcode << " ; disable exhaust fan ";
+ else
+ gcode << " ; enable exhaust fan ";
+ }
+
+ gcode << "\n";
return gcode.str();
}
diff --git a/src/libslic3r/GCodeWriter.hpp b/src/libslic3r/GCodeWriter.hpp
index dc930ac713..3170a1e730 100644
--- a/src/libslic3r/GCodeWriter.hpp
+++ b/src/libslic3r/GCodeWriter.hpp
@@ -106,7 +106,7 @@ public:
std::string set_fan(unsigned int speed) const;
//BBS: set additional fan speed for BBS machine only
static std::string set_additional_fan(unsigned int speed);
- static std::string set_exhaust_fan(int speed,bool add_eol);
+ static std::string set_exhaust_fan(int speed);
//BBS
void set_object_start_str(std::string start_string) { m_gcode_label_objects_start = start_string; }
bool is_object_start_str_empty() { return m_gcode_label_objects_start.empty(); }
diff --git a/src/slic3r/GUI/GUI_App.cpp b/src/slic3r/GUI/GUI_App.cpp
index 8a1d317b97..bd71f001ee 100644
--- a/src/slic3r/GUI/GUI_App.cpp
+++ b/src/slic3r/GUI/GUI_App.cpp
@@ -12,6 +12,7 @@
#include
#include
#include
+#include
#include
// Localization headers: include libslic3r version first so everything in this file
@@ -4508,21 +4509,46 @@ std::string GUI_App::handle_web_request(std::string cmd)
boost::optional command = root.get_optional("command");
if (command.has_value()) {
std::string command_str = command.value();
- static const std::unordered_set stealth_blocked_commands = {
+ static const std::unordered_set stealth_blocked_info_commands = {
"get_login_info",
"get_orca_login_info",
"get_bambu_login_info",
+ };
+ static const std::unordered_set stealth_blocked_login_commands = {
"homepage_login_or_register",
"homepage_orca_login_or_register",
"homepage_bambu_login_or_register",
};
- if (app_config->get_stealth_mode() && stealth_blocked_commands.count(command_str)) {
+ if (app_config->get_stealth_mode() && stealth_blocked_info_commands.count(command_str)) {
CallAfter([this] {
if (mainframe && mainframe->m_webview)
mainframe->m_webview->SendCloudProvidersInfo();
});
return "";
}
+ if (app_config->get_stealth_mode() && stealth_blocked_login_commands.count(command_str)) {
+ CallAfter([this, command_str] {
+ MessageDialog dlg(mainframe,
+ _L("You are currently in Stealth Mode. To log into the Cloud, you need to disable Stealth Mode first."),
+ _L("Stealth Mode"),
+ wxOK | wxCANCEL | wxCENTRE);
+ dlg.SetButtonLabel(wxID_OK, _L("Quit Stealth Mode"));
+ if (dlg.ShowModal() == wxID_OK) {
+ app_config->set_bool("stealth_mode", false);
+ app_config->save();
+ if (mainframe && mainframe->m_webview)
+ mainframe->m_webview->SendCloudProvidersInfo();
+ // Continue with login
+ if (command_str == "homepage_login_or_register")
+ this->request_login(true);
+ else if (command_str == "homepage_orca_login_or_register")
+ this->request_login(true, ORCA_CLOUD_PROVIDER);
+ else if (command_str == "homepage_bambu_login_or_register")
+ this->request_login(true, BBL_CLOUD_PROVIDER);
+ }
+ });
+ return "";
+ }
if (command_str.compare("request_project_download") == 0) {
if (root.get_child_optional("data") != boost::none) {
pt::ptree data_node = root.get_child("data");
@@ -4812,6 +4838,8 @@ void GUI_App::handle_http_error(unsigned int status, std::string body, const std
wxQueueEvent(this, evt);
}
+static std::mutex conflict_ids_mutex;
+
void GUI_App::on_http_error(wxCommandEvent &evt)
{
int status = evt.GetInt();
@@ -4887,32 +4915,62 @@ void GUI_App::on_http_error(wxCommandEvent &evt)
return;
}
- static bool m_is_error_shown = false;
+ if (status == 409 && provider == ORCA_CLOUD_PROVIDER) {
+ BOOST_LOG_TRIVIAL(info) << "Http error 409.";
+ // Parse the conflict body to extract the error code and server profile id
+ int conflict_code = 0;
+ std::string conflict_setting_id;
+ try {
+ json conflict_body = json::parse(body_str);
+ if (conflict_body.contains("code"))
+ conflict_code = conflict_body["code"].get();
+ if (conflict_body.contains("server_profile") && conflict_body["server_profile"].contains("id")
+ && conflict_body["server_profile"]["id"].is_string())
+ conflict_setting_id = conflict_body["server_profile"]["id"].get();
+ } catch (...) {
+ BOOST_LOG_TRIVIAL(warning) << "Failed to parse 409 conflict body.";
+ }
+ auto* plater = wxGetApp().plater();
+ if (plater != nullptr && wxGetApp().imgui()->display_initialized()) {
+ std::string text;
+ if (conflict_code == -1) {
+ text = _u8L("Cloud sync conflict: this preset has a newer version in OrcaCloud.\n"
+ "Pull downloads the cloud copy. Force push overwrites it with your local preset.");
+ } else {
+ text = _u8L("Cloud sync conflict: a preset with this name already exists in OrcaCloud.\n"
+ "Pull downloads the cloud copy. Force push overwrites it with your local preset.");
+ }
+ plater->get_notification_manager()->push_orca_sync_conflict_notification(
+ text,
+ [this](wxEvtHandler*) {
+ // Runs on the GUI thread (on_http_error is a queued wx event); restart_sync_user_preset()
+ // already joins the old sync thread off the UI thread, so no extra thread is needed here.
+ if (is_closing() || !m_agent || !preset_bundle)
+ return false;
+ BOOST_LOG_TRIVIAL(info) << "Pulling Orca Cloud settings to resolve sync conflict.";
+ restart_sync_user_preset();
+ return true;
+ },
+ [this, conflict_setting_id](wxEvtHandler*) {
+ if (mainframe == nullptr)
+ return false;
+ MessageDialog
+ dlg(mainframe,
+ _L("Force push will overwrite the cloud copy with your local preset changes.\nDo you want to continue?"),
+ _L("Resolve cloud sync conflict"), wxCENTER | wxYES_NO | wxNO_DEFAULT | wxICON_WARNING);
+ if (dlg.ShowModal() != wxID_YES)
+ return false;
+
+ force_push_conflicting_preset(conflict_setting_id);
+ return true;
+ });
+ }
+ return;
+ }
+
// Show general error notification for Orca Cloud API failures (not Bambu)
if (provider == ORCA_CLOUD_PROVIDER && status >= 400 && code != HttpErrorVersionLimited) {
- wxString msg;
- if (!error.empty()) {
- msg = wxString::Format(_L("Failed to connect to OrcaCloud.\nPlease check your network connectivity\n(HTTP %u): %s"), status, wxString::FromUTF8(error));
- } else {
- msg = wxString::Format(_L("Failed to connect to OrcaCloud.\nPlease check your network connectivity\n(HTTP %u)"), status);
- }
-
- if (app_config->get_bool("developer_mode")) {
- // Use notification manager if ImGui is ready; fall back to wxMessageBox on Linux
- // where ImGui may not be initialized until the user switches to the Prepare tab.
- if (wxGetApp().plater() != nullptr && wxGetApp().imgui()->display_initialized()) {
- wxGetApp()
- .plater()
- ->get_notification_manager()
- ->push_notification(NotificationType::PlaterError, NotificationManager::NotificationLevel::WarningNotificationLevel,
- msg.ToUTF8().data());
- }
- }
-
- if (!m_is_error_shown) {
- m_is_error_shown = true;
- wxMessageBox(msg, _L("Cloud Error"), wxOK | wxICON_ERROR, wxGetApp().mainframe);
- }
+ BOOST_LOG_TRIVIAL(warning) << "API call to OrcaCloud failed with status=" << status;
}
}
@@ -6181,13 +6239,14 @@ void GUI_App::load_pending_vendors()
need_add_filaments.clear();
}
-void GUI_App::sync_preset(Preset* preset)
+void GUI_App::sync_preset(Preset* preset, bool force)
{
int result = -1;
unsigned int http_code = 200;
std::string updated_info;
long long update_time = 0;
// only sync user's preset
+ if (!m_agent) return;
if (!preset->is_user()) return;
auto setting_id = preset->setting_id;
@@ -6259,9 +6318,9 @@ void GUI_App::sync_preset(Preset* preset)
result = 0;
}
else {
- result = m_agent->put_setting(setting_id, preset->name, &values_map, &http_code);
+ result = m_agent->put_setting(setting_id, preset->name, &values_map, &http_code, ORCA_CLOUD_PROVIDER, force);
if (http_code >= 400) {
- result = 0;
+ result = 0;
updated_info = "hold";
BOOST_LOG_TRIVIAL(error) << "[sync_preset] put setting_id = " << setting_id << " failed, http_code = " << http_code;
} else {
@@ -6722,7 +6781,8 @@ void GUI_App::start_sync_user_preset(bool with_progress_dlg)
// Sync once immediately, then every 60 seconds.
while (!t.expired()) {
++tick_tock;
- if (tick_tock % 120 == 0) {
+ // Sync once immediately, then every 60s, or right away when a force-push asked for it.
+ if (tick_tock % 120 == 0 || m_sync_user_presets_now.exchange(false, std::memory_order_acq_rel)) {
tick_tock = 0;
if (m_agent) {
if (!m_agent->is_user_login()) {
@@ -6733,9 +6793,24 @@ void GUI_App::start_sync_user_preset(bool with_progress_dlg)
int total_count = 0;
sync_count = preset_bundle->prints.get_user_presets(preset_bundle, presets_to_sync);
+
+ auto sync_with_lock = [this](Preset& preset) {
+ bool force = false;
+ {
+ std::scoped_lock lock(conflict_ids_mutex);
+ auto it = std::find_if(m_pending_conflict_setting_ids.begin(), m_pending_conflict_setting_ids.end(),
+ [&preset](const std::string& id) { return id == preset.setting_id; });
+ if (it != m_pending_conflict_setting_ids.end()) {
+ force = true;
+ m_pending_conflict_setting_ids.erase(it);
+ }
+ }
+ sync_preset(&preset, force);
+ };
+
if (sync_count > 0) {
for (Preset& preset : presets_to_sync) {
- sync_preset(&preset);
+ sync_with_lock(preset);
boost::this_thread::sleep_for(boost::chrono::milliseconds(100));
}
}
@@ -6744,7 +6819,7 @@ void GUI_App::start_sync_user_preset(bool with_progress_dlg)
sync_count = preset_bundle->filaments.get_user_presets(preset_bundle, presets_to_sync);
if (sync_count > 0) {
for (Preset& preset : presets_to_sync) {
- sync_preset(&preset);
+ sync_with_lock(preset);
boost::this_thread::sleep_for(boost::chrono::milliseconds(100));
}
}
@@ -6753,7 +6828,7 @@ void GUI_App::start_sync_user_preset(bool with_progress_dlg)
sync_count = preset_bundle->printers.get_user_presets(preset_bundle, presets_to_sync);
if (sync_count > 0) {
for (Preset& preset : presets_to_sync) {
- sync_preset(&preset);
+ sync_with_lock(preset);
boost::this_thread::sleep_for(boost::chrono::milliseconds(100));
}
}
@@ -6930,6 +7005,35 @@ void GUI_App::restart_sync_user_preset()
}).detach();
}
+void GUI_App::force_push_conflicting_preset(const std::string& setting_id)
+{
+ if (setting_id.empty() || !preset_bundle)
+ return;
+
+ // Queue the id so the next push-sync re-uploads this preset with force=true.
+ {
+ std::scoped_lock lock(conflict_ids_mutex);
+ m_pending_conflict_setting_ids.push_back(setting_id);
+ }
+
+ // The 409 left this preset on "hold", which get_user_presets() skips. Restore it to
+ // "update" so the next push-sync re-includes it and consumes the queued force flag.
+ // (We must NOT pull from the cloud here as the Pull path does — that would overwrite
+ // the local changes the user is trying to force-push.)
+ PresetCollection* collections[] = {&preset_bundle->prints, &preset_bundle->filaments, &preset_bundle->printers};
+ for (PresetCollection* coll : collections) {
+ for (const Preset& preset : coll->get_presets()) {
+ if (preset.setting_id == setting_id && preset.sync_info == "hold") {
+ coll->set_sync_info_and_save(preset.name, preset.setting_id, "update", 0);
+ break;
+ }
+ }
+ }
+
+ // Nudge the sync loop to push on its next tick instead of waiting for the 60s cadence.
+ m_sync_user_presets_now.store(true, std::memory_order_release);
+}
+
void GUI_App::on_stealth_mode_enter()
{
stop_sync_user_preset();
diff --git a/src/slic3r/GUI/GUI_App.hpp b/src/slic3r/GUI/GUI_App.hpp
index 5b6bcc6581..dc0f1e52de 100644
--- a/src/slic3r/GUI/GUI_App.hpp
+++ b/src/slic3r/GUI/GUI_App.hpp
@@ -297,6 +297,7 @@ private:
NetworkAgent* m_agent { nullptr };
std::map need_delete_presets; // store setting ids of preset
std::vector m_create_preset_blocked { false, false, false, false, false, false }; // excceed limit
+ std::vector m_pending_conflict_setting_ids; // setting_id from the most recent 409 conflict
bool m_networking_compatible { false };
bool m_networking_need_update { false };
bool m_networking_cancel_update { false };
@@ -322,6 +323,7 @@ private:
boost::thread m_sync_update_thread;
std::shared_ptr m_user_sync_token;
std::atomic m_restart_sync_pending {false};
+ std::atomic m_sync_user_presets_now {false}; // request the sync loop to push user presets on its next tick
bool m_is_dark_mode{ false };
bool m_adding_script_handler { false };
bool m_side_popup_status{false};
@@ -529,10 +531,13 @@ public:
void add_pending_vendor_preset(const std::pair>& preset_data);
void load_pending_vendors();
- void sync_preset(Preset* preset);
+ void sync_preset(Preset* preset, bool force = false);
void start_sync_user_preset(bool with_progress_dlg = false);
void stop_sync_user_preset();
void restart_sync_user_preset();
+ // Resolve a cloud sync 409 by force-pushing the conflicting preset: clears the "hold"
+ // state the conflict left behind and queues it to be re-uploaded with force=true.
+ void force_push_conflicting_preset(const std::string& setting_id);
void on_stealth_mode_enter();
// Bundle subscription sync
diff --git a/src/slic3r/GUI/HMS.cpp b/src/slic3r/GUI/HMS.cpp
index f9f4d0b9f7..561b3750b2 100644
--- a/src/slic3r/GUI/HMS.cpp
+++ b/src/slic3r/GUI/HMS.cpp
@@ -4,6 +4,7 @@
#include "DeviceManager.hpp"
#include "DeviceCore/DevManager.h"
#include "DeviceCore/DevUtil.h"
+#include "libslic3r/AppConfig.hpp"
#include
@@ -13,6 +14,14 @@ static const char* HMS_LOCAL_IMG_PATH = "hms/local_image";
// the local HMS info
static unordered_set package_dev_id_types {"094", "239", "093", "22E"};
+// HMS should be disabled when stealth mode is on or networking is not installed
+static bool should_disable_hms()
+{
+ Slic3r::AppConfig* config = Slic3r::GUI::wxGetApp().app_config;
+ if (!config) return true;
+ return config->get_stealth_mode() || !config->get_bool("installed_networking");
+}
+
namespace Slic3r {
namespace GUI {
@@ -21,7 +30,7 @@ int get_hms_info_version(std::string& version)
AppConfig* config = wxGetApp().app_config;
if (!config)
return -1;
- if (config->get_stealth_mode())
+ if (should_disable_hms())
return -1;
std::string hms_host = config->get_hms_host();
if(hms_host.empty()) {
@@ -61,7 +70,7 @@ int HMSQuery::download_hms_related(const std::string& hms_type, const std::strin
AppConfig* config = wxGetApp().app_config;
if (!config) return -1;
- if (config->get_stealth_mode()) return -1;
+ if (should_disable_hms()) return -1;
std::string hms_host = wxGetApp().app_config->get_hms_host();
std::string lang;
@@ -546,7 +555,7 @@ wxString HMSQuery::query_print_image_action(const MachineObject* obj, int print_
::sprintf(buf, "%08X", print_error);
//The first three digits of SN number
const auto result = _query_error_image_action(get_dev_id_type(obj),std::string(buf), button_action);
- if (wxGetApp().app_config->get_stealth_mode() && result.Contains("http")) {
+ if (should_disable_hms() && result.Contains("http")) {
return wxEmptyString;
}
return result;
@@ -637,7 +646,7 @@ std::string get_hms_wiki_url(std::string error_code)
{
AppConfig* config = wxGetApp().app_config;
if (!config) return "";
- if (config->get_stealth_mode()) return "";
+ if (should_disable_hms()) return "";
std::string hms_host = wxGetApp().app_config->get_hms_host();
std::string lang_code = HMSQuery::hms_language_code();
@@ -663,7 +672,7 @@ std::string get_hms_wiki_url(std::string error_code)
std::string get_error_message(int error_code)
{
- if (wxGetApp().app_config->get_stealth_mode()) return "";
+ if (should_disable_hms()) return "";
char buf[64];
std::string result_str = "";
diff --git a/src/slic3r/GUI/NotificationManager.cpp b/src/slic3r/GUI/NotificationManager.cpp
index 81e28fec1a..5079dfecf0 100644
--- a/src/slic3r/GUI/NotificationManager.cpp
+++ b/src/slic3r/GUI/NotificationManager.cpp
@@ -791,6 +791,42 @@ void NotificationManager::PopNotification::render_hypertext(ImGuiWrapper& imgui,
}
+void NotificationManager::PopNotification::render_hyperlink_action(ImGuiWrapper& imgui, float text_x, float text_y,
+ const std::string& text, const char* button_id, const std::function& on_click)
+{
+ // Invisible button over the label
+ ImVec2 part_size = ImGui::CalcTextSize(text.c_str());
+ ImGui::SetCursorPosX(text_x - 4);
+ ImGui::SetCursorPosY(text_y - 5);
+ ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(.0f, .0f, .0f, .0f));
+ ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImVec4(.0f, .0f, .0f, .0f));
+ ImGui::PushStyleColor(ImGuiCol_ButtonActive, ImVec4(.0f, .0f, .0f, .0f));
+ if (imgui.button(button_id, part_size.x + 6, part_size.y + 10) && on_click)
+ on_click();
+ ImGui::PopStyleColor(3);
+
+ // Hover color
+ ImVec4 color = m_HyperTextColor;
+ if (ImGui::IsItemHovered(ImGuiHoveredFlags_RectOnly))
+ color = m_HyperTextColorHover;
+
+ // Text
+ push_style_color(ImGuiCol_Text, color, m_state == EState::FadingOut, m_current_fade_opacity);
+ ImGui::SetCursorPosX(text_x);
+ ImGui::SetCursorPosY(text_y);
+ imgui.text(text.c_str());
+ ImGui::PopStyleColor();
+
+ // Underline
+ ImVec2 lineEnd = ImGui::GetItemRectMax();
+ lineEnd.y -= 2;
+ ImVec2 lineStart = lineEnd;
+ lineStart.x = ImGui::GetItemRectMin().x;
+ ImGui::GetWindowDrawList()->AddLine(lineStart, lineEnd,
+ IM_COL32((int)(color.x * 255), (int)(color.y * 255), (int)(color.z * 255),
+ (int)(color.w * 255.f * (m_state == EState::FadingOut ? m_current_fade_opacity : 1.f))));
+}
+
void NotificationManager::PopNotification::render_close_button(ImGuiWrapper& imgui, const float win_size_x, const float win_size_y, const float win_pos_x, const float win_pos_y)
{
ensure_ui_inited();
@@ -2346,40 +2382,49 @@ bool NotificationManager::SharedProfilesNotification::on_text_click()
void NotificationManager::SharedProfilesNotification::render_hypertext(ImGuiWrapper& imgui,
const float text_x, const float text_y, const std::string text, bool more)
{
- // Invisible button
- ImVec2 part_size = ImGui::CalcTextSize(text.c_str());
- ImGui::SetCursorPosX(text_x - 4);
- ImGui::SetCursorPosY(text_y - 5);
- ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(.0f, .0f, .0f, .0f));
- ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImVec4(.0f, .0f, .0f, .0f));
- ImGui::PushStyleColor(ImGuiCol_ButtonActive, ImVec4(.0f, .0f, .0f, .0f));
- if (imgui.button("##browse_btn", part_size.x + 6, part_size.y + 10)) {
- if (on_text_click()) {
- close();
+ render_hyperlink_action(imgui, text_x, text_y, text, "##browse_btn",
+ [this] { if (on_text_click()) close(); });
+}
+
+void NotificationManager::OrcaSyncConflictNotification::init()
+{
+ PopNotification::init();
+ // Reserve a dedicated action row for the two conflict-resolution links.
+ m_lines_count = m_lines_count + 1;
+}
+
+void NotificationManager::OrcaSyncConflictNotification::render_text(ImGuiWrapper& imgui,
+ const float win_size_x, const float win_size_y,
+ const float win_pos_x, const float win_pos_y)
+{
+ float x_offset = m_left_indentation;
+ float shift_y = m_line_height;
+ float starting_y = m_line_height / 2;
+
+ int last_end = 0;
+ std::string line;
+ for (size_t i = 0; i < m_endlines.size(); i++) {
+ if (m_text1.size() >= m_endlines[i]) {
+ line = m_text1.substr(last_end, m_endlines[i] - last_end);
+ last_end = m_endlines[i];
+ if (m_text1.size() > m_endlines[i])
+ last_end += (m_text1[m_endlines[i]] == '\n' || m_text1[m_endlines[i]] == ' ' ? 1 : 0);
+ ImGui::SetCursorPosX(x_offset);
+ ImGui::SetCursorPosY(starting_y + i * shift_y);
+ imgui.text(line.c_str());
}
}
- ImGui::PopStyleColor(3);
- // Hover color
- ImVec4 HyperColor = m_HyperTextColor;
- if (ImGui::IsItemHovered(ImGuiHoveredFlags_RectOnly))
- HyperColor = m_HyperTextColorHover;
-
- // Text
- push_style_color(ImGuiCol_Text, HyperColor, m_state == EState::FadingOut, m_current_fade_opacity);
- ImGui::SetCursorPosX(text_x);
- ImGui::SetCursorPosY(text_y);
- imgui.text(text.c_str());
- ImGui::PopStyleColor();
-
- // Underline
- ImVec2 lineEnd = ImGui::GetItemRectMax();
- lineEnd.y -= 2;
- ImVec2 lineStart = lineEnd;
- lineStart.x = ImGui::GetItemRectMin().x;
- ImGui::GetWindowDrawList()->AddLine(lineStart, lineEnd,
- IM_COL32((int)(HyperColor.x * 255), (int)(HyperColor.y * 255), (int)(HyperColor.z * 255),
- (int)(HyperColor.w * 255.f * (m_state == EState::FadingOut ? m_current_fade_opacity : 1.f))));
+ const float action_y = starting_y + m_endlines.size() * shift_y;
+ const std::string pull_text = _u8L("Pull");
+ render_hyperlink_action(imgui, x_offset, action_y, pull_text, "##orca_sync_pull",
+ [this] { if (m_pull_callback && m_pull_callback(m_evt_handler)) close(); });
+ if (m_force_push_callback) {
+ const std::string force_push_text = _u8L("Force push");
+ const float force_x = x_offset + ImGui::CalcTextSize((pull_text + " ").c_str()).x;
+ render_hyperlink_action(imgui, force_x, action_y, force_push_text, "##orca_sync_force_push",
+ [this] { if (m_force_push_callback && m_force_push_callback(m_evt_handler)) close(); });
+ }
}
void NotificationManager::push_shared_profiles_notification(const std::string& explore_url)
@@ -2391,6 +2436,16 @@ void NotificationManager::push_shared_profiles_notification(const std::string& e
push_notification_data(std::make_unique(data, m_id_provider, m_evt_handler, explore_url), 0);
}
+void NotificationManager::push_orca_sync_conflict_notification(const std::string& text,
+ std::function pull_callback,
+ std::function force_push_callback)
+{
+ close_notification_of_type(NotificationType::OrcaSyncConflict);
+ NotificationData data{ NotificationType::OrcaSyncConflict, NotificationLevel::WarningNotificationLevel, 0, text };
+ push_notification_data(std::make_unique(
+ data, m_id_provider, m_evt_handler, std::move(pull_callback), std::move(force_push_callback)), 0);
+}
+
void NotificationManager::push_download_URL_progress_notification(size_t id, const std::string& text, std::function user_action_callback)
{
// If already exists
diff --git a/src/slic3r/GUI/NotificationManager.hpp b/src/slic3r/GUI/NotificationManager.hpp
index 5e8f4f7e95..006de5cff6 100644
--- a/src/slic3r/GUI/NotificationManager.hpp
+++ b/src/slic3r/GUI/NotificationManager.hpp
@@ -15,6 +15,8 @@
#include
#include
+#include
+#include
#include
#include
#include
@@ -162,6 +164,8 @@ enum class NotificationType
BBLMixUsePLAAndPETG,
BBLNozzleFilamentIncompatible,
OrcaSharedProfilesAvailable,
+ OrcaCloudAPIError,
+ OrcaSyncConflict,
NotificationTypeCount
};
@@ -274,6 +278,9 @@ public:
// Shared profiles available for selected printer
void push_shared_profiles_notification(const std::string& explore_url);
+ void push_orca_sync_conflict_notification(const std::string& text,
+ std::function pull_callback,
+ std::function force_push_callback);
// Download URL progress notif
void push_download_URL_progress_notification(size_t id, const std::string& text, std::function user_action_callback);
@@ -491,6 +498,11 @@ private:
const float text_x, const float text_y,
const std::string text,
bool more = false);
+ // Renders an underlined, hyperlink-style clickable label backed by an invisible button.
+ // on_click runs when pressed; the callback itself decides whether to close().
+ void render_hyperlink_action(ImGuiWrapper& imgui, float text_x, float text_y,
+ const std::string& text, const char* button_id,
+ const std::function& on_click);
virtual void bbl_render_block_notif_text(ImGuiWrapper& imgui,
const float win_size_x, const float win_size_y,
const float win_pos_x, const float win_pos_y);
@@ -887,6 +899,28 @@ private:
std::string m_explore_url;
bool m_dont_show_clicked{ false };
};
+
+ class OrcaSyncConflictNotification : public PopNotification
+ {
+ public:
+ OrcaSyncConflictNotification(const NotificationData& n, NotificationIDProvider& id_provider, wxEvtHandler* evt_handler,
+ std::function pull_callback,
+ std::function force_push_callback)
+ : PopNotification(n, id_provider, evt_handler)
+ , m_pull_callback(std::move(pull_callback))
+ , m_force_push_callback(std::move(force_push_callback))
+ {
+ m_multiline = true;
+ }
+ protected:
+ void init() override;
+ void render_text(ImGuiWrapper& imgui,
+ const float win_size_x, const float win_size_y,
+ const float win_pos_x, const float win_pos_y) override;
+
+ std::function m_pull_callback;
+ std::function m_force_push_callback;
+ };
class SlicingProgressNotification;
// in HintNotification.hpp
diff --git a/src/slic3r/GUI/Preferences.cpp b/src/slic3r/GUI/Preferences.cpp
index 5cef914e38..7a78e337f3 100644
--- a/src/slic3r/GUI/Preferences.cpp
+++ b/src/slic3r/GUI/Preferences.cpp
@@ -968,6 +968,11 @@ wxBoxSizer *PreferencesDialog::create_item_checkbox(wxString title, wxString too
if (m_sync_user_preset_checkbox) m_sync_user_preset_checkbox->Enable(!enabled);
if (m_bambu_cloud_checkbox) m_bambu_cloud_checkbox->Enable(!enabled);
}
+ else if (param == "hide_login_side_panel") {
+ if (wxGetApp().mainframe && wxGetApp().mainframe->m_webview) {
+ wxGetApp().mainframe->m_webview->SendCloudProvidersInfo();
+ }
+ }
#ifdef __WXMSW__
if (param == "associate_3mf") {
@@ -1605,9 +1610,12 @@ void PreferencesDialog::create_items()
auto item_region = create_item_region_combobox(_L("Login region"), "");
g_sizer->Add(item_region);
- auto item_stealth_mode = create_item_checkbox(_L("Stealth mode"), _L("This disables all cloud services e.g. Orca Cloud and Bambu Cloud. This stops the transmission of data to Bambu's cloud services too. Users who don't use BBL machines or use LAN mode only can safely turn on this function."), "stealth_mode");
+ auto item_stealth_mode = create_item_checkbox(_L("Stealth mode"), _L("This disables all cloud features, including Orca Cloud profile syncing. Users who prefer to work entirely offline can enable this option.\nNote: When Stealth Mode is enabled, your user profiles will not be backed up to Orca Cloud."), "stealth_mode");
g_sizer->Add(item_stealth_mode);
+ auto item_hide_login_side_panel = create_item_checkbox(_L("Hide login side panel"), _L("Hide the login side panel on the home page."), "hide_login_side_panel");
+ g_sizer->Add(item_hide_login_side_panel);
+
auto item_network_test = create_item_button(_L("Network test"), _L("Test") + " " + dots, "", _L("Open Network Test"), []() {
NetworkTestDialog dlg(wxGetApp().mainframe);
dlg.ShowModal();
diff --git a/src/slic3r/GUI/WebUserLoginDialog.cpp b/src/slic3r/GUI/WebUserLoginDialog.cpp
index 2a9b4cb984..4b8acc9455 100644
--- a/src/slic3r/GUI/WebUserLoginDialog.cpp
+++ b/src/slic3r/GUI/WebUserLoginDialog.cpp
@@ -177,11 +177,7 @@ ZUserLogin::ZUserLogin(std::shared_ptr cloud_agent)
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);
+ CentreOnParent();
}
wxGetApp().UpdateDlgDarkUI(this);
}
diff --git a/src/slic3r/GUI/WebViewDialog.cpp b/src/slic3r/GUI/WebViewDialog.cpp
index 2a88277680..20ed70e799 100644
--- a/src/slic3r/GUI/WebViewDialog.cpp
+++ b/src/slic3r/GUI/WebViewDialog.cpp
@@ -526,7 +526,7 @@ void WebViewPanel::SendCloudProvidersInfo()
json data;
json provider_array = json::array();
- if (!app_config->get_stealth_mode()) {
+ if (!app_config->get_hide_login_side_panel()) {
auto providers = app_config->get_cloud_providers();
for (const auto& p : providers) {
provider_array.push_back(p);
diff --git a/src/slic3r/Utils/BBLCloudServiceAgent.cpp b/src/slic3r/Utils/BBLCloudServiceAgent.cpp
index 2c17d117fc..846e4ce509 100644
--- a/src/slic3r/Utils/BBLCloudServiceAgent.cpp
+++ b/src/slic3r/Utils/BBLCloudServiceAgent.cpp
@@ -433,7 +433,7 @@ std::string BBLCloudServiceAgent::request_setting_id(std::string name, std::map<
return "";
}
-int BBLCloudServiceAgent::put_setting(std::string setting_id, std::string name, std::map* values_map, unsigned int* http_code)
+int BBLCloudServiceAgent::put_setting(std::string setting_id, std::string name, std::map* values_map, unsigned int* http_code, bool force)
{
auto& plugin = BBLNetworkPlugin::instance();
auto agent = plugin.get_agent();
diff --git a/src/slic3r/Utils/BBLCloudServiceAgent.hpp b/src/slic3r/Utils/BBLCloudServiceAgent.hpp
index b48a65b76c..9f03f0b839 100644
--- a/src/slic3r/Utils/BBLCloudServiceAgent.hpp
+++ b/src/slic3r/Utils/BBLCloudServiceAgent.hpp
@@ -70,7 +70,7 @@ public:
// Settings Synchronization
int get_user_presets(std::map>* user_presets) override;
std::string request_setting_id(std::string name, std::map* values_map, unsigned int* http_code) override;
- int put_setting(std::string setting_id, std::string name, std::map* values_map, unsigned int* http_code) override;
+ int put_setting(std::string setting_id, std::string name, std::map* values_map, unsigned int* http_code, bool force = false) override;
int get_setting_list(std::string bundle_version, ProgressFn pro_fn = nullptr, WasCancelledFn cancel_fn = nullptr) override;
int get_setting_list2(std::string bundle_version, CheckFn chk_fn, ProgressFn pro_fn = nullptr, WasCancelledFn cancel_fn = nullptr) override;
int delete_setting(std::string setting_id) override;
diff --git a/src/slic3r/Utils/ICloudServiceAgent.hpp b/src/slic3r/Utils/ICloudServiceAgent.hpp
index 6d131341f7..556c253641 100644
--- a/src/slic3r/Utils/ICloudServiceAgent.hpp
+++ b/src/slic3r/Utils/ICloudServiceAgent.hpp
@@ -247,7 +247,7 @@ public:
/**
* Update or create a preset with a known setting_id.
*/
- virtual int put_setting(std::string setting_id, std::string name, std::map* values_map, unsigned int* http_code) = 0;
+ virtual int put_setting(std::string setting_id, std::string name, std::map* values_map, unsigned int* http_code, bool force = false) = 0;
/**
* Trigger bulk download of user presets.
diff --git a/src/slic3r/Utils/NetworkAgent.cpp b/src/slic3r/Utils/NetworkAgent.cpp
index e59795c058..8a3d2953b0 100644
--- a/src/slic3r/Utils/NetworkAgent.cpp
+++ b/src/slic3r/Utils/NetworkAgent.cpp
@@ -383,11 +383,12 @@ int NetworkAgent::put_setting(std::string setting_id,
std::string name,
std::map* values_map,
unsigned int* http_code,
- const std::string& provider)
+ const std::string& provider,
+ bool force)
{
const auto cloud_agent = get_cloud_agent(provider);
if (cloud_agent)
- return cloud_agent->put_setting(std::move(setting_id), std::move(name), values_map, http_code);
+ return cloud_agent->put_setting(std::move(setting_id), std::move(name), values_map, http_code, force);
return -1;
}
diff --git a/src/slic3r/Utils/NetworkAgent.hpp b/src/slic3r/Utils/NetworkAgent.hpp
index 49a2e9f5d6..57a23737a2 100644
--- a/src/slic3r/Utils/NetworkAgent.hpp
+++ b/src/slic3r/Utils/NetworkAgent.hpp
@@ -93,7 +93,7 @@ public:
// NOTE: this should always call only OrcaCloud
int get_user_presets(std::map>* user_presets, const std::string& provider = ORCA_CLOUD_PROVIDER);
std::string request_setting_id(std::string name, std::map* values_map, unsigned int* http_code, const std::string& provider = ORCA_CLOUD_PROVIDER);
- int put_setting(std::string setting_id, std::string name, std::map* values_map, unsigned int* http_code, const std::string& provider = ORCA_CLOUD_PROVIDER);
+ int put_setting(std::string setting_id, std::string name, std::map* values_map, unsigned int* http_code, const std::string& provider = ORCA_CLOUD_PROVIDER, bool force = false);
int get_setting_list(std::string bundle_version, ProgressFn pro_fn = nullptr, WasCancelledFn cancel_fn = nullptr, const std::string& provider = ORCA_CLOUD_PROVIDER);
int get_setting_list2(std::string bundle_version, CheckFn chk_fn, ProgressFn pro_fn = nullptr, WasCancelledFn cancel_fn = nullptr, const std::string& provider = ORCA_CLOUD_PROVIDER);
int delete_setting(std::string setting_id, const std::string& provider = ORCA_CLOUD_PROVIDER);
diff --git a/src/slic3r/Utils/OrcaCloudServiceAgent.cpp b/src/slic3r/Utils/OrcaCloudServiceAgent.cpp
index 0e1027cd46..1b7f779914 100644
--- a/src/slic3r/Utils/OrcaCloudServiceAgent.cpp
+++ b/src/slic3r/Utils/OrcaCloudServiceAgent.cpp
@@ -56,6 +56,7 @@ constexpr const char* ORCA_DEFAULT_PUB_KEY = "sb_publishable_lvVe_whOi80SU9BPSxM
constexpr const char* ORCA_HEALTH_PATH = "/api/v1/health";
constexpr const char* ORCA_SYNC_PULL_PATH = "/api/v1/sync/pull";
constexpr const char* ORCA_SYNC_PUSH_PATH = "/api/v1/sync/push";
+constexpr const char* ORCA_SYNC_FORCE_PUSH_PATH = "/api/v1/sync/force-push";
constexpr const char* ORCA_SYNC_DELETE_PATH = "/api/v1/sync/delete";
constexpr const char* ORCA_PROFILES_PATH = "/api/v1/sync/profiles";
constexpr const char* ORCA_SUBSCRIPTIONS_PATH = "/api/v1/subscriptions";
@@ -965,7 +966,7 @@ std::string OrcaCloudServiceAgent::request_setting_id(std::string name, std::map
return "";
}
-int OrcaCloudServiceAgent::put_setting(std::string setting_id, std::string name, std::map* values_map, unsigned int* http_code)
+int OrcaCloudServiceAgent::put_setting(std::string setting_id, std::string name, std::map* values_map, unsigned int* http_code, bool force)
{
// Extract original_updated_time for Optimistic Concurrency Control
// If present, server will verify version before update. If absent, treated as insert.
@@ -989,7 +990,7 @@ int OrcaCloudServiceAgent::put_setting(std::string setting_id, std::string name,
}
}
- auto result = sync_push(setting_id, name, content, original_updated_time);
+ auto result = sync_push(setting_id, name, content, original_updated_time, force);
if (http_code) *http_code = result.http_code;
if (result.success) {
@@ -1208,11 +1209,11 @@ int OrcaCloudServiceAgent::sync_pull(
}
}
-SyncPushResult OrcaCloudServiceAgent::sync_push(
- const std::string& profile_id,
- const std::string& name,
- const nlohmann::json& content,
- const std::string& original_updated_time)
+SyncPushResult OrcaCloudServiceAgent::sync_push(const std::string& profile_id,
+ const std::string& name,
+ const nlohmann::json& content,
+ const std::string& original_updated_time,
+ bool force)
{
SyncPushResult result;
result.success = false;
@@ -1243,7 +1244,7 @@ SyncPushResult OrcaCloudServiceAgent::sync_push(
std::string response;
unsigned int http_code = 0;
- int http_result = http_post(ORCA_SYNC_PUSH_PATH, body_str, &response, &http_code);
+ int http_result = http_post(force ? ORCA_SYNC_FORCE_PUSH_PATH : ORCA_SYNC_PUSH_PATH, body_str, &response, &http_code);
result.http_code = http_code;
@@ -1888,7 +1889,7 @@ int OrcaCloudServiceAgent::http_post(const std::string& path, const std::string&
.on_error([&](std::string resp_body, std::string error, unsigned resp_status) {
result.success = false;
result.status = resp_status == 0 ? 404 : resp_status;
- result.body = body;
+ result.body = resp_body;
BOOST_LOG_TRIVIAL(error) << "OrcaCloudServiceAgent: HTTP error - " << error;
})
.timeout_max(30)
diff --git a/src/slic3r/Utils/OrcaCloudServiceAgent.hpp b/src/slic3r/Utils/OrcaCloudServiceAgent.hpp
index 4552b487d4..5c58987a0b 100644
--- a/src/slic3r/Utils/OrcaCloudServiceAgent.hpp
+++ b/src/slic3r/Utils/OrcaCloudServiceAgent.hpp
@@ -176,7 +176,12 @@ public:
// ========================================================================
int get_user_presets(std::map>* user_presets) override;
std::string request_setting_id(std::string name, std::map* values_map, unsigned int* http_code) override;
- int put_setting(std::string setting_id, std::string name, std::map* values_map, unsigned int* http_code) override;
+ int put_setting(std::string setting_id, std::string name, std::map* values_map, unsigned int* http_code, bool force = false) override;
+ SyncPushResult sync_push(const std::string& profile_id,
+ const std::string& name,
+ const nlohmann::json& content,
+ const std::string& original_updated_time = "",
+ bool force = false);
int get_setting_list(std::string bundle_version, ProgressFn pro_fn = nullptr, WasCancelledFn cancel_fn = nullptr) override;
int get_setting_list2(std::string bundle_version, CheckFn chk_fn, ProgressFn pro_fn = nullptr, WasCancelledFn cancel_fn = nullptr) override;
int delete_setting(std::string setting_id) override;
@@ -294,13 +299,6 @@ private:
std::function on_error
);
- SyncPushResult sync_push(
- const std::string& profile_id,
- const std::string& name,
- const nlohmann::json& content,
- const std::string& original_updated_time = ""
- );
-
// HTTP request helpers
int http_get(const std::string& path, std::string* response_body, unsigned int* http_code);
int http_post(const std::string& path, const std::string& body, std::string* response_body, unsigned int* http_code);