Improve startup of ConfigWizard (SPE-2457)

This commit is contained in:
Lukas Matena 2024-10-02 14:26:54 +02:00
parent 1bd7be797a
commit 68f8925461
26 changed files with 813 additions and 477 deletions

View File

@ -139,3 +139,4 @@ src/slic3r/Utils/PresetUpdater.cpp
src/slic3r/Utils/Process.cpp
src/slic3r/Utils/PrusaConnect.cpp
src/slic3r/Utils/Repetier.cpp
src/slic3r/Utils/PresetUpdaterWrapper.cpp

View File

@ -127,7 +127,6 @@ public:
TYPE_PHYSICAL_PRINTER,
// This type is here to support search through the Preferences
TYPE_PREFERENCES,
TYPE_WEBVIEW,
};
Type type = TYPE_INVALID;

View File

@ -356,6 +356,8 @@ set(SLIC3R_GUI_SOURCES
Utils/Bonjour.hpp
Utils/PresetUpdater.cpp
Utils/PresetUpdater.hpp
Utils/PresetUpdaterWrapper.cpp
Utils/PresetUpdaterWrapper.hpp
Utils/Process.cpp
Utils/Process.hpp
Utils/RaycastManager.cpp

View File

@ -62,12 +62,10 @@
#include "Field.hpp"
#include "DesktopIntegrationDialog.hpp"
#include "slic3r/Config/Snapshot.hpp"
#include "slic3r/Utils/PresetUpdater.hpp"
#include "format.hpp"
#include "MsgDialog.hpp"
#include "UnsavedChangesDialog.hpp"
#include "UpdatesUIManager.hpp"
#include "PresetArchiveDatabase.hpp"
#include "Plater.hpp" // #ysFIXME - implement getter for preset_archive_database from GetApp()???
#include "slic3r/Utils/AppUpdater.hpp"
#include "slic3r/GUI/I18N.hpp"
@ -86,30 +84,9 @@
namespace Slic3r {
namespace GUI {
using Config::Snapshot;
using Config::SnapshotDB;
ConfigWizardLoadingDialog::ConfigWizardLoadingDialog(wxWindow* parent, const wxString& message)
: wxDialog(parent, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, wxFRAME_FLOAT_ON_PARENT)
{
auto* text = new wxStaticText(this, wxID_ANY, message, wxDefaultPosition, wxDefaultSize, wxALIGN_CENTER_HORIZONTAL);
auto* vsizer = new wxBoxSizer(wxVERTICAL);
auto *top_sizer = new wxBoxSizer(wxVERTICAL);
vsizer->Add(text, 1, wxEXPAND);
top_sizer->Add(vsizer, 1, wxEXPAND | wxALL, 15);
SetSizer(top_sizer);
#ifdef _WIN32
wxGetApp().UpdateDlgDarkUI(this);
#endif
Fit();
}
// Configuration data structures extensions needed for the wizard
bool Bundle::load(fs::path source_path, BundleLocation location, bool ais_prusa_bundle)
{
this->preset_bundle = std::make_unique<PresetBundle>();
@ -138,6 +115,7 @@ bool Bundle::load(fs::path source_path, BundleLocation location, bool ais_prusa_
return true;
}
// Configuration data structures extensions needed for the wizard
Bundle::Bundle(Bundle &&other)
: preset_bundle(std::move(other.preset_bundle))
, vendor_profile(other.vendor_profile)
@ -151,7 +129,7 @@ BundleMap BundleMap::load()
{
BundleMap res;
const PresetArchiveDatabase* pad = wxGetApp().plater()->get_preset_archive_database();
const Slic3r::PresetUpdaterWrapper* preset_updater_wrapper = wxGetApp().get_preset_updater_wrapper();
const auto vendor_dir = (boost::filesystem::path(Slic3r::data_dir()) / "vendor").make_preferred();
const auto archive_dir = (boost::filesystem::path(Slic3r::data_dir()) / "cache" / "vendor").make_preferred();
const auto rsrc_vendor_dir = (boost::filesystem::path(resources_dir()) / "profiles").make_preferred();
@ -228,7 +206,7 @@ BundleMap BundleMap::load()
BOOST_LOG_TRIVIAL(error) << format("Could not load bundle %1% due to corrupted profile file %2%. Message: %3%", id, dir_entry.path().string(), e.what());
continue;
}
if (vp.repo_id.empty() || !pad->is_selected_repository_by_id(vp.repo_id)) {
if (vp.repo_id.empty() || !preset_updater_wrapper->is_selected_repository_by_id(vp.repo_id)) {
continue;
}
// Don't load
@ -655,7 +633,7 @@ PageUpdateManager::PageUpdateManager(ConfigWizard* parent_in)
const int em = em_unit(this);
manager = std::make_unique<RepositoryUpdateUIManager>(this, wxGetApp().plater()->get_preset_archive_database(), em);
manager = std::make_unique<RepositoryUpdateUIManager>(this, wxGetApp().get_preset_updater_wrapper(), em);
warning_text = new wxStaticText(this, wxID_ANY, _L("WARNING: Select at least one source."));
warning_text->SetFont(wxGetApp().bold_font());
@ -3327,7 +3305,7 @@ static std::string get_first_added_preset(const std::map<std::string, std::strin
return *diff.begin();
}
bool ConfigWizard::priv::apply_config(AppConfig *app_config, PresetBundle *preset_bundle, const PresetUpdater *updater, bool& apply_keeped_changes)
bool ConfigWizard::priv::apply_config(AppConfig *app_config, PresetBundle *preset_bundle, const PresetUpdaterWrapper *updater, bool& apply_keeped_changes)
{
wxString header, caption = _L("Configuration is edited in ConfigWizard");
const auto enabled_vendors = appconfig_new.vendors();
@ -3461,9 +3439,7 @@ bool ConfigWizard::priv::apply_config(AppConfig *app_config, PresetBundle *prese
if (install_bundles.size() > 0) {
// Install bundles from resources or cache / vendor.
// Don't create snapshot - we've already done that above if applicable.
GUI_App& app = wxGetApp();
const auto* archive_db = app.plater()->get_preset_archive_database();
bool install_result = updater->install_bundles_rsrc_or_cache_vendor(std::move(install_bundles), archive_db->get_selected_archive_repositories(), false);
bool install_result = updater->install_bundles_rsrc_or_cache_vendor(std::move(install_bundles), false);
if (!install_result)
return false;
} else {
@ -3594,7 +3570,7 @@ bool ConfigWizard::priv::apply_config(AppConfig *app_config, PresetBundle *prese
used_repo_ids.emplace_back(repo_id);
}
}
wxGetApp().plater()->get_preset_archive_database()->set_installed_printer_repositories(std::move(used_repo_ids));
wxGetApp().get_preset_updater_wrapper()->set_installed_printer_repositories(std::move(used_repo_ids));
// apply materials in app_config
for (const std::string& section_name : {AppConfig::SECTION_FILAMENTS, AppConfig::SECTION_MATERIALS})
@ -3705,17 +3681,8 @@ bool ConfigWizard::priv::check_sla_selected()
void ConfigWizard::priv::set_config_updated_from_archive(bool load_installed_printers, bool run_preset_updater)
{
if (run_preset_updater) {
// This block of preset_updater functions is done in GUI_App::run_wizard before ConfigWizard::run()
// It needs to be also done when repos are confirmed inside wizard.
// Possible optimalization - do not run this block if no repos were changed.
GUI_App& app = wxGetApp();
// Do blocking sync on every change of archive repos, so user is always offered recent profiles.
const SharedArchiveRepositoryVector &repos = app.plater()->get_preset_archive_database()->get_selected_archive_repositories();
app.preset_updater->sync_blocking(app.preset_bundle, &app, repos);
// Offer update installation. It used to be offered only when wizard run reason was RR_USER.
app.preset_updater->update_index_db();
app.preset_updater->config_update(app.app_config->orig_version(), PresetUpdater::UpdateParams::SHOW_TEXT_BOX, repos);
// TRN: Progress dialog title
wxGetApp().get_preset_updater_wrapper()->wizard_sync(wxGetApp().preset_bundle, wxGetApp().app_config->orig_version(), q, true, _L("Updating Configuration sources"));
// We have now probably changed data. We need to rebuild database from which wizards constructs.
// Just reload bundles and upadte installed printer from appconfig_new.
bundles = BundleMap::load();
@ -3751,8 +3718,7 @@ bool ConfigWizard::priv::any_installed_vendor_for_repo(const std::string& repo_i
static bool to_delete(PagePrinters* page, const std::set<std::string>& selected_uuids)
{
const PresetArchiveDatabase* pad = wxGetApp().plater()->get_preset_archive_database();
const SharedArchiveRepositoryVector& archs = pad->get_all_archive_repositories();
const SharedArchiveRepositoryVector& archs = wxGetApp().get_preset_updater_wrapper()->get_all_archive_repositories();
bool unselect_all = true;
@ -3768,14 +3734,14 @@ static bool to_delete(PagePrinters* page, const std::set<std::string>& selected_
static void unselect(PagePrinters* page)
{
const PresetArchiveDatabase* pad = wxGetApp().plater()->get_preset_archive_database();
const SharedArchiveRepositoryVector& archs = pad->get_all_archive_repositories();
const Slic3r::PresetUpdaterWrapper* puw = wxGetApp().get_preset_updater_wrapper();
const SharedArchiveRepositoryVector& archs = puw->get_all_archive_repositories();
bool unselect_all = true;
for (const auto* archive : archs) {
if (page->get_vendor_repo_id() == archive->get_manifest().id) {
if (pad->is_selected_repository_by_uuid(archive->get_uuid()))
if (puw->is_selected_repository_by_uuid(archive->get_uuid()))
unselect_all = false;
//break; ! don't break here, because there can be several archives with same repo_id
}
@ -3866,16 +3832,16 @@ void ConfigWizard::priv::load_pages_from_archive()
// fill vendors and printers pages from Update manager
auto pad = wxGetApp().plater()->get_preset_archive_database();
const auto* puw = wxGetApp().get_preset_updater_wrapper();
const SharedArchiveRepositoryVector& archs = pad->get_all_archive_repositories();
const SharedArchiveRepositoryVector& archs = puw->get_all_archive_repositories();
only_sla_mode = true;
bool is_primary_printer_page_set = false;
for (const auto* archive : archs) {
const auto& data = archive->get_manifest();
const bool is_selected_arch = pad->is_selected_repository_by_uuid(archive->get_uuid());
const bool is_selected_arch = puw->is_selected_repository_by_uuid(archive->get_uuid());
std::vector<const VendorProfile*> vendors;
const bool any_installed_vendor = any_installed_vendor_for_repo(data.id, vendors);
@ -4148,7 +4114,7 @@ bool ConfigWizard::run(RunReason reason, StartPage start_page)
if (ShowModal() == wxID_OK) {
bool apply_keeped_changes = false;
if (! p->apply_config(app.app_config, app.preset_bundle, app.preset_updater, apply_keeped_changes))
if (! p->apply_config(app.app_config, app.preset_bundle, app.get_preset_updater_wrapper(), apply_keeped_changes))
return false;
if (apply_keeped_changes)
@ -4169,7 +4135,8 @@ void ConfigWizard::update_login()
{
if (p->page_login && p->page_login->login_changed()) {
// repos changed - we need rebuild
wxGetApp().plater()->get_preset_archive_database()->sync_blocking();
// TRN: Progress dialog title
wxGetApp().get_preset_updater_wrapper()->wizard_sync(wxGetApp().preset_bundle, wxGetApp().app_config->orig_version(), this, false, _L("Updating Configuration sources"));
// now change PageUpdateManager
p->page_update_manager->manager->update();
}

View File

@ -22,15 +22,11 @@ namespace Slic3r {
class PresetBundle;
class PresetUpdater;
class AppConfig;
class PresetArchiveDatabase;
namespace GUI {
class ConfigWizardLoadingDialog : public wxDialog
{
public:
ConfigWizardLoadingDialog(wxWindow* parent, const wxString& message);
};
namespace DownloaderUtils {
class Worker : public wxBoxSizer
{
@ -102,8 +98,6 @@ private:
friend struct ConfigWizardPage;
};
}
}

View File

@ -132,9 +132,9 @@ void ConfigWizardWebViewPage::on_navigation_request(wxWebViewEvent &evt)
evt.Veto();
m_vetoed = true;
wxPostEvent(wxGetApp().plater(), Event<std::string>(EVT_LOGIN_VIA_WIZARD, into_u8(url)));
} else if (url.Find("accounts.google.com") != wxString::npos
|| url.Find("appleid.apple.com") != wxString::npos
|| url.Find("facebook.com") != wxString::npos)
} else if (url.Find("accounts.google.com") != wxNOT_FOUND
|| url.Find("appleid.apple.com") != wxNOT_FOUND
|| url.Find("facebook.com") != wxNOT_FOUND)
{
auto& sc = Utils::ServiceConfig::instance();
if (!m_evt_sent && !url.starts_with(GUI::from_u8(sc.account_url()))) {

View File

@ -26,7 +26,7 @@
#include "libslic3r/PrintConfig.hpp"
#include "libslic3r/PresetBundle.hpp"
#include "slic3r/Utils/PresetUpdater.hpp"
#include "slic3r/Utils/PresetUpdaterWrapper.hpp"
#include "BedShapeDialog.hpp"
#include "GUI.hpp"
#include "SavePresetDialog.hpp"
@ -702,7 +702,7 @@ struct ConfigWizard::priv
bool can_select_all();
bool on_bnt_finish();
bool check_and_install_missing_materials(Technology technology, const std::string &only_for_model_id = std::string());
bool apply_config(AppConfig *app_config, PresetBundle *preset_bundle, const PresetUpdater *updater, bool& apply_keeped_changes);
bool apply_config(AppConfig *app_config, PresetBundle *preset_bundle, const PresetUpdaterWrapper *updater, bool& apply_keeped_changes);
// #ys_FIXME_alise
void update_presets_in_config(const std::string& section, const std::string& alias_key, bool add);
//#ifdef __linux__

View File

@ -75,6 +75,7 @@
#include "GLCanvas3D.hpp"
#include "../Utils/PresetUpdater.hpp"
#include "../Utils/PresetUpdaterWrapper.hpp"
#include "../Utils/PrintHost.hpp"
#include "../Utils/Process.hpp"
#include "../Utils/MacDarkMode.hpp"
@ -847,17 +848,11 @@ void GUI_App::post_init()
// to popup a modal dialog on start without screwing combo boxes.
// This is ugly but I honestly found no better way to do it.
// Neither wxShowEvent nor wxWindowCreateEvent work reliably.
if (this->preset_updater) { // G-Code Viewer does not initialize preset_updater.
#if 0 // This code was moved to EVT_CONFIG_UPDATER_SYNC_DONE bind - after preset_updater finishes synchronization.
if (! this->check_updates(false))
// Configuration is not compatible and reconfigure was refused by the user. Application is closing.
return;
#endif
if (this->get_preset_updater_wrapper()) { // G-Code Viewer does not initialize preset_updater.
CallAfter([this] {
// preset_updater->sync downloads profile updates and than via event checks updates and incompatible presets. We need to run it on startup.
// start before cw so it is canceled by cw if needed?
this->preset_updater->sync(preset_bundle, this, plater()->get_preset_archive_database()->get_selected_archive_repositories());
this->get_preset_updater_wrapper()->sync_preset_updater(this, preset_bundle);
bool cw_showed = this->config_wizard_startup();
if (! cw_showed) {
// The CallAfter is needed as well, without it, GL extensions did not show.
@ -900,7 +895,6 @@ GUI_App::~GUI_App()
{
delete app_config;
delete preset_bundle;
delete preset_updater;
}
// If formatted for github, plaintext with OpenGL extensions enclosed into <details>.
@ -1435,7 +1429,7 @@ bool GUI_App::on_init_inner()
associate_stl_files();
#endif // __WXMSW__
preset_updater = new PresetUpdater();
m_preset_updater_wrapper = std::make_unique<PresetUpdaterWrapper>();
Bind(EVT_SLIC3R_VERSION_ONLINE, &GUI_App::on_version_read, this);
Bind(EVT_SLIC3R_EXPERIMENTAL_VERSION_ONLINE, [this](const wxCommandEvent& evt) {
if (this->plater_ != nullptr && (m_app_updater->get_triggered_by_user() || app_config->get("notify_release") == "all")) {
@ -3279,48 +3273,16 @@ bool GUI_App::run_wizard(ConfigWizard::RunReason reason, ConfigWizard::StartPage
{
wxCHECK_MSG(mainframe != nullptr, false, "Internal error: Main frame not created / null");
// Cancel sync before starting wizard to prevent two downloads at same time.
preset_updater->cancel_sync();
// Show login dialog before wizard.
#if 0
bool user_was_logged = plater()->get_user_account()->is_logged();
if (!user_was_logged) {
m_login_dialog = std::make_unique<LoginDialog>(mainframe, plater()->get_user_account());
m_login_dialog->ShowModal();
mainframe->RemoveChild(m_login_dialog.get());
m_login_dialog->Destroy();
// Destructor does not call Destroy.
m_login_dialog.reset();
}
#endif // 0
// ConfigWizard can take some time to start. Because it is a wxWidgets window, it has to be done
// in UI thread, so displaying a nice modal dialog and letting the CW start in a worker thread
// is not an option. Let's at least show a modeless dialog before the UI thread freezes.
// TRN: Text showing while the ConfigWizard is loading, so the user knows something is happening.
auto cw_loading_dlg = new ConfigWizardLoadingDialog(mainframe, _L("Loading Configuration Wizard..."));
cw_loading_dlg->CenterOnParent();
cw_loading_dlg->Show();
wxYield();
// We have to update repos
plater()->get_preset_archive_database()->sync_blocking();
if (reason == ConfigWizard::RunReason::RR_USER) {
// Since there might be new repos, we need to sync preset updater
const SharedArchiveRepositoryVector &repos = plater()->get_preset_archive_database()->get_selected_archive_repositories();
preset_updater->sync_blocking(preset_bundle, this, repos);
preset_updater->update_index_db();
// Offer update installation.
preset_updater->config_update(app_config->orig_version(), PresetUpdater::UpdateParams::SHOW_TEXT_BOX, repos);
}
// Loading of Config Wizard takes some time.
// First part is to download neccessary data.
// That is done on worker thread while nice modal progress is shown.
// TRN: Progress dialog title
get_preset_updater_wrapper()->wizard_sync(preset_bundle, app_config->orig_version(), mainframe, reason == ConfigWizard::RunReason::RR_USER, _L("Opening Configuration Wizard"));
// Then the wizard itself will start and that also takes time.
// But for now no ui is shown until then. (Showing modal progress dialog while showing another would be a headacke)
m_config_wizard = new ConfigWizard(mainframe);
cw_loading_dlg->Close();
const bool res = m_config_wizard->run(reason, start_page);
// !!! Deallocate memory after close ConfigWizard.
// Note, that mainframe is a parent of ConfigWizard.
// So, wizard will be destroyed only during destroying of mainframe
@ -3541,28 +3503,13 @@ bool GUI_App::config_wizard_startup()
bool GUI_App::check_updates(const bool invoked_by_user)
{
if (invoked_by_user) {
// do preset_updater sync so if user runs slicer for a long time, check for updates actually delivers updates.
// for preset_updater sync we need to sync archive database first
plater()->get_preset_archive_database()->sync_blocking();
// Now re-extract offline repos
std::string failed_paths;
if (!plater()->get_preset_archive_database()->extract_archives_with_check(failed_paths)) {
int cnt = std::count(failed_paths.begin(), failed_paths.end(), '\n') + 1;
// TRN: %1% contains paths from which loading failed. They are separated by \n, there is no \n at the end.
failed_paths = GUI::format(_L_PLURAL("It was not possible to extract data from %1%. The source will not be updated.",
"It was not possible to extract data for following local sources. They will not be updated.\n\n %1%", cnt), failed_paths);
show_error(nullptr, failed_paths);
}
// then its time for preset_updater sync
preset_updater->sync_blocking(preset_bundle, this, plater()->get_preset_archive_database()->get_selected_archive_repositories());
// and then we check updates
}
PresetUpdater::UpdateResult updater_result;
try {
preset_updater->update_index_db();
updater_result = preset_updater->config_update(app_config->orig_version(), invoked_by_user ? PresetUpdater::UpdateParams::SHOW_TEXT_BOX : PresetUpdater::UpdateParams::SHOW_NOTIFICATION, plater()->get_preset_archive_database()->get_selected_archive_repositories());
if (invoked_by_user)
{
updater_result = get_preset_updater_wrapper()->check_updates_on_user_request(preset_bundle, app_config->orig_version(), mainframe);
} else {
updater_result = get_preset_updater_wrapper()->check_updates_on_startup( app_config->orig_version());
}
if (updater_result == PresetUpdater::R_INCOMPAT_EXIT) {
mainframe->Close();
// Applicaiton is closing.
@ -3575,10 +3522,6 @@ bool GUI_App::check_updates(const bool invoked_by_user)
MsgNoUpdates dlg;
dlg.ShowModal();
}
}
catch (const std::exception & ex) {
show_error(nullptr, ex.what());
}
// Applicaiton will continue.
return true;
}

View File

@ -34,7 +34,7 @@ namespace Slic3r {
class AppConfig;
class PresetBundle;
class PresetUpdater;
class PresetUpdaterWrapper;
class ModelObject;
class PrintHostJobQueue;
class Model;
@ -357,11 +357,9 @@ public:
AppConfig* app_config{ nullptr };
PresetBundle* preset_bundle{ nullptr };
PresetUpdater* preset_updater{ nullptr };
MainFrame* mainframe{ nullptr };
Plater* plater_{ nullptr };
PresetUpdater* get_preset_updater() { return preset_updater; }
PresetUpdaterWrapper* get_preset_updater_wrapper() { return m_preset_updater_wrapper.get(); }
wxBookCtrlBase* tab_panel() const ;
int extruders_cnt() const;
@ -468,7 +466,7 @@ private:
std::map< ConfigMenuIDs, wxMenuItem*> m_config_menu_updatable_items;
ConfigWizard* m_config_wizard {nullptr};
std::unique_ptr<PresetUpdaterWrapper> m_preset_updater_wrapper;
};
DECLARE_APP(GUI_App)

View File

@ -111,6 +111,7 @@
#include "../Utils/PrintHost.hpp"
#include "../Utils/UndoRedo.hpp"
#include "../Utils/PresetUpdater.hpp"
#include "../Utils/PresetUpdaterWrapper.hpp"
#include "../Utils/Process.hpp"
#include "RemovableDriveManager.hpp"
#include "InstanceCheck.hpp"
@ -272,7 +273,6 @@ struct Plater::priv
Preview *preview;
std::unique_ptr<NotificationManager> notification_manager;
std::unique_ptr<UserAccount> user_account;
std::unique_ptr<PresetArchiveDatabase> preset_archive_database;
// Login dialog needs to be kept somewhere.
// It is created inside evt Bind. But it might be closed from another event.
LoginWebViewDialog* login_dialog { nullptr };
@ -625,7 +625,6 @@ Plater::priv::priv(Plater *q, MainFrame *main_frame)
, sidebar(new Sidebar(q))
, notification_manager(std::make_unique<NotificationManager>(q))
, user_account(std::make_unique<UserAccount>(q, wxGetApp().app_config, wxGetApp().get_instance_hash_string()))
, preset_archive_database(std::make_unique<PresetArchiveDatabase>(wxGetApp().app_config, q))
, m_worker{q, std::make_unique<NotificationProgressIndicator>(notification_manager.get()), "ui_worker"}
, m_sla_import_dlg{new SLAImportDialog{q}}
, delayed_scene_refresh(false)
@ -791,7 +790,7 @@ Plater::priv::priv(Plater *q, MainFrame *main_frame)
this->q->Bind(EVT_EXPORT_GCODE_NOTIFICAION_CLICKED, [this](ExportGcodeNotificationClickedEvent&) { this->q->export_gcode(true); });
this->q->Bind(EVT_PRESET_UPDATE_AVAILABLE_CLICKED, [](PresetUpdateAvailableClickedEvent&) {
GUI_App &app = wxGetApp();
app.get_preset_updater()->on_update_notification_confirm(app.plater()->get_preset_archive_database()->get_selected_archive_repositories());
app.get_preset_updater_wrapper()->on_update_notification_confirm();
});
this->q->Bind(EVT_REMOVABLE_DRIVE_EJECTED, [this, q](RemovableDriveEjectEvent &evt) {
if (evt.data.second) {
@ -903,11 +902,11 @@ Plater::priv::priv(Plater *q, MainFrame *main_frame)
#endif // SLIC3R_DESKTOP_INTEGRATION
#endif // __linux__
std::string service;
if (evt.GetString().Find(L"accounts.google.com") != wxString::npos) {
if (evt.GetString().Find(L"accounts.google.com") != wxNOT_FOUND) {
service = "google";
} else if (evt.GetString().Find(L"appleid.apple.com") != wxString::npos) {
} else if (evt.GetString().Find(L"appleid.apple.com") != wxNOT_FOUND) {
service = "apple";
} else if (evt.GetString().Find(L"facebook.com") != wxString::npos) {
} else if (evt.GetString().Find(L"facebook.com") != wxNOT_FOUND) {
service = "facebook";
}
wxString url = user_account->get_login_redirect_url(service);
@ -6996,16 +6995,6 @@ const NotificationManager * Plater::get_notification_manager() const
return p->notification_manager.get();
}
PresetArchiveDatabase* Plater::get_preset_archive_database()
{
return p->preset_archive_database.get();
}
const PresetArchiveDatabase* Plater::get_preset_archive_database() const
{
return p->preset_archive_database.get();
}
UserAccount* Plater::get_user_account()
{
return p->user_account.get();

View File

@ -360,9 +360,6 @@ public:
NotificationManager* get_notification_manager();
const NotificationManager* get_notification_manager() const;
PresetArchiveDatabase* get_preset_archive_database();
const PresetArchiveDatabase* get_preset_archive_database() const;
UserAccount* get_user_account();
const UserAccount* get_user_account() const;

View File

@ -6,6 +6,8 @@
#include "slic3r/GUI/GUI_App.hpp"
#include "slic3r/GUI/Plater.hpp"
#include "slic3r/GUI/UserAccount.hpp"
#include "slic3r/Utils/PresetUpdaterWrapper.hpp"
#include "slic3r/GUI/Field.hpp"
#include "libslic3r/Utils.hpp"
#include "libslic3r/AppConfig.hpp"
#include "libslic3r/miniz_extension.hpp"
@ -25,7 +27,6 @@
namespace pt = boost::property_tree;
namespace fs = boost::filesystem;
namespace Slic3r {
namespace GUI {
static const char* TMP_EXTENSION = ".download";
@ -191,7 +192,7 @@ void add_authorization_header(Http& http)
}
bool OnlineArchiveRepository::get_file_inner(const std::string& url, const fs::path& target_path) const
bool OnlineArchiveRepository::get_file_inner(const std::string& url, const fs::path& target_path, PresetUpdaterUIStatus* ui_status) const
{
bool res = false;
@ -210,10 +211,9 @@ bool OnlineArchiveRepository::get_file_inner(const std::string& url, const fs::p
//if (cancel) { cancel = true; }
})
.on_error([&](std::string body, std::string error, unsigned http_status) {
BOOST_LOG_TRIVIAL(error) << format("Error getting: `%1%`: HTTP %2%, %3%",
url,
http_status,
body);
BOOST_LOG_TRIVIAL(error) << format("Error getting: `%1%`: HTTP %2%, %3%", url, http_status, body);
ui_status->set_error(error);
res = false;
})
.on_complete([&](std::string body, unsigned /* http_status */) {
if (body.empty()) {
@ -225,30 +225,38 @@ bool OnlineArchiveRepository::get_file_inner(const std::string& url, const fs::p
fs::rename(tmp_path, target_path);
res = true;
})
.perform_sync();
.on_retry([&](int attempt, unsigned delay) {
return !ui_status->on_attempt(attempt, delay);
})
.perform_sync(ui_status->get_retry_policy());
return res;
}
bool OnlineArchiveRepository::get_archive(const fs::path& target_path) const
bool OnlineArchiveRepository::get_archive(const fs::path& target_path, PresetUpdaterUIStatus* ui_status) const
{
return get_file_inner(m_data.index_url.empty() ? m_data.url + "vendor_indices.zip" : m_data.index_url, target_path);
return get_file_inner(m_data.index_url.empty() ? m_data.url + "vendor_indices.zip" : m_data.index_url, target_path, ui_status);
}
bool OnlineArchiveRepository::get_file(const std::string& source_subpath, const fs::path& target_path, const std::string& repository_id) const
bool OnlineArchiveRepository::get_file(const std::string& source_subpath, const fs::path& target_path, const std::string& repository_id, PresetUpdaterUIStatus* ui_status) const
{
if (repository_id != m_data.id) {
BOOST_LOG_TRIVIAL(error) << "Error getting file " << source_subpath << ". The repository_id was not matching.";
return false;
}
ui_status->set_target(target_path.filename().string());
const std::string escaped_source_subpath = escape_path_by_element(source_subpath);
return get_file_inner(m_data.url + escaped_source_subpath, target_path);
return get_file_inner(m_data.url + escaped_source_subpath, target_path, ui_status);
}
bool OnlineArchiveRepository::get_ini_no_id(const std::string& source_subpath, const fs::path& target_path) const
bool OnlineArchiveRepository::get_ini_no_id(const std::string& source_subpath, const fs::path& target_path, PresetUpdaterUIStatus* ui_status) const
{
ui_status->set_target(target_path.filename().string());
const std::string escaped_source_subpath = escape_path_by_element(source_subpath);
return get_file_inner(m_data.url + escaped_source_subpath, target_path);
return get_file_inner(m_data.url + escaped_source_subpath, target_path, ui_status);
}
bool LocalArchiveRepository::get_file_inner(const fs::path& source_path, const fs::path& target_path) const
@ -277,7 +285,7 @@ bool LocalArchiveRepository::get_file_inner(const fs::path& source_path, const f
return true;
}
bool LocalArchiveRepository::get_file(const std::string& source_subpath, const fs::path& target_path, const std::string& repository_id) const
bool LocalArchiveRepository::get_file(const std::string& source_subpath, const fs::path& target_path, const std::string& repository_id, PresetUpdaterUIStatus* ui_status) const
{
if (repository_id != m_data.id) {
BOOST_LOG_TRIVIAL(error) << "Error getting file " << source_subpath << ". The repository_id was not matching.";
@ -285,11 +293,11 @@ bool LocalArchiveRepository::get_file(const std::string& source_subpath, const f
}
return get_file_inner(m_data.tmp_path / source_subpath, target_path);
}
bool LocalArchiveRepository::get_ini_no_id(const std::string& source_subpath, const fs::path& target_path) const
bool LocalArchiveRepository::get_ini_no_id(const std::string& source_subpath, const fs::path& target_path, PresetUpdaterUIStatus* ui_status) const
{
return get_file_inner(m_data.tmp_path / source_subpath, target_path);
}
bool LocalArchiveRepository::get_archive(const fs::path& target_path) const
bool LocalArchiveRepository::get_archive(const fs::path& target_path, PresetUpdaterUIStatus* ui_status) const
{
fs::path source_path = fs::path(m_data.tmp_path) / "vendor_indices.zip";
return get_file_inner(std::move(source_path), target_path);
@ -306,9 +314,8 @@ void LocalArchiveRepository::do_extract()
//-------------------------------------PresetArchiveDatabase-------------------------------------------------------------------------------------------------------------------------
PresetArchiveDatabase::PresetArchiveDatabase(AppConfig* app_config, wxEvtHandler* evt_handler)
PresetArchiveDatabase::PresetArchiveDatabase()
{
//
boost::system::error_code ec;
m_unq_tmp_path = fs::temp_directory_path() / fs::unique_path();
fs::create_directories(m_unq_tmp_path, ec);
@ -362,7 +369,8 @@ bool PresetArchiveDatabase::set_selected_repositories(const std::vector<std::str
bool PresetArchiveDatabase::extract_archives_with_check(std::string &msg)
{
extract_local_archives();
for (const std::pair<const std::string, bool>& pair : m_selected_repositories_uuid) {
// std::map<std::string, bool> m_selected_repositories_uuid
for (const auto& pair : m_selected_repositories_uuid) {
if (!pair.second) {
continue;
}
@ -868,7 +876,7 @@ std::string PresetArchiveDatabase::get_next_uuid()
}
namespace {
bool sync_inner(std::string& manifest)
bool sync_inner(std::string& manifest, PresetUpdaterUIStatus* ui_status)
{
bool ret = false;
std::string url = Utils::ServiceConfig::instance().preset_repo_repos_url();
@ -878,23 +886,33 @@ bool sync_inner(std::string& manifest)
.timeout_max(30)
.on_error([&](std::string body, std::string error, unsigned http_status) {
BOOST_LOG_TRIVIAL(error) << "Failed to get online archive source manifests: "<< body << " ; " << error << " ; " << http_status;
ui_status->set_error(error);
ret = false;
})
.on_complete([&](std::string body, unsigned /* http_status */) {
manifest = body;
ret = true;
})
.perform_sync();
.on_retry([&](int attempt, unsigned delay) {
return !ui_status->on_attempt(attempt, delay);
})
.perform_sync(ui_status->get_retry_policy());
return ret;
}
}
void PresetArchiveDatabase::sync_blocking()
bool PresetArchiveDatabase::sync_blocking(PresetUpdaterUIStatus* ui_status)
{
assert(ui_status);
std::string manifest;
if (!sync_inner(manifest))
return;
bool sync_res = false;
ui_status->set_target("Archive Database Mainfest");
sync_res = sync_inner(manifest, ui_status);
if (!sync_res) {
return false;
}
read_server_manifest(std::move(manifest));
return true;
}
}} // Slic3r::GUI
} // Slic3r

View File

@ -13,7 +13,8 @@
namespace Slic3r {
class AppConfig;
namespace GUI {
class PresetUpdaterUIStatus;
struct ArchiveRepositoryGetFileArgs {
boost::filesystem::path target_path;
@ -75,12 +76,12 @@ public:
{}
virtual ~ArchiveRepository() {}
// Gets vendor_indices.zip to target_path
virtual bool get_archive(const boost::filesystem::path& target_path) const = 0;
virtual bool get_archive(const boost::filesystem::path& target_path, PresetUpdaterUIStatus* ui_status) const = 0;
// Gets file if repository_id arg matches m_id.
// Should be used to get the most recent ini file and every missing resource.
virtual bool get_file(const std::string& source_subpath, const boost::filesystem::path& target_path, const std::string& repository_id) const = 0;
virtual bool get_file(const std::string& source_subpath, const boost::filesystem::path& target_path, const std::string& repository_id, PresetUpdaterUIStatus* ui_status) const = 0;
// Gets file without id check - for not yet encountered vendors only!
virtual bool get_ini_no_id(const std::string& source_subpath, const boost::filesystem::path& target_path) const = 0;
virtual bool get_ini_no_id(const std::string& source_subpath, const boost::filesystem::path& target_path, PresetUpdaterUIStatus* ui_status) const = 0;
const RepositoryManifest& get_manifest() const { return m_data; }
std::string get_uuid() const { return m_uuid; }
// Only local archvies can return false
@ -103,15 +104,15 @@ public:
}
}
// Gets vendor_indices.zip to target_path.
bool get_archive(const boost::filesystem::path& target_path) const override;
bool get_archive(const boost::filesystem::path& target_path, PresetUpdaterUIStatus* ui_status) const override;
// Gets file if repository_id arg matches m_id.
// Should be used to get the most recent ini file and every missing resource.
bool get_file(const std::string& source_subpath, const boost::filesystem::path& target_path, const std::string& repository_id) const override;
bool get_file(const std::string& source_subpath, const boost::filesystem::path& target_path, const std::string& repository_id, PresetUpdaterUIStatus* ui_status) const override;
// Gets file without checking id.
// Should be used only if no previous ini file exists.
bool get_ini_no_id(const std::string& source_subpath, const boost::filesystem::path& target_path) const override;
bool get_ini_no_id(const std::string& source_subpath, const boost::filesystem::path& target_path, PresetUpdaterUIStatus* ui_status) const override;
private:
bool get_file_inner(const std::string& url, const boost::filesystem::path& target_path) const;
bool get_file_inner(const std::string& url, const boost::filesystem::path& target_path, PresetUpdaterUIStatus* ui_status) const;
};
class LocalArchiveRepository : public ArchiveRepository
@ -120,13 +121,13 @@ public:
LocalArchiveRepository(const std::string& uuid, RepositoryManifest&& data, bool extracted) : ArchiveRepository(uuid, std::move(data)), m_extracted(extracted)
{}
// Gets vendor_indices.zip to target_path.
bool get_archive(const boost::filesystem::path& target_path) const override;
bool get_archive(const boost::filesystem::path& target_path, PresetUpdaterUIStatus* ui_status) const override;
// Gets file if repository_id arg matches m_id.
// Should be used to get the most recent ini file and every missing resource.
bool get_file(const std::string& source_subpath, const boost::filesystem::path& target_path, const std::string& repository_id) const override;
bool get_file(const std::string& source_subpath, const boost::filesystem::path& target_path, const std::string& repository_id, PresetUpdaterUIStatus* ui_status) const override;
// Gets file without checking id.
// Should be used only if no previous ini file exists.
bool get_ini_no_id(const std::string& source_subpath, const boost::filesystem::path& target_path) const override;
bool get_ini_no_id(const std::string& source_subpath, const boost::filesystem::path& target_path, PresetUpdaterUIStatus* ui_status) const override;
bool is_extracted() const override { return m_extracted; }
void do_extract() override;
@ -141,10 +142,10 @@ typedef std::vector<const ArchiveRepository*> SharedArchiveRepositoryVector;
class PresetArchiveDatabase
{
public:
PresetArchiveDatabase(AppConfig* app_config, wxEvtHandler* evt_handler);
PresetArchiveDatabase();
~PresetArchiveDatabase() {}
void sync_blocking();
// returns true if successfully got the data
bool sync_blocking(PresetUpdaterUIStatus* ui_status = nullptr);
// Do not use get_all_archive_repositories to perform any GET calls. Use get_selected_archive_repositories instead.
SharedArchiveRepositoryVector get_all_archive_repositories() const;
@ -179,6 +180,6 @@ private:
boost::uuids::random_generator m_uuid_generator;
};
}} // Slic3r::GUI
} // Slic3r
#endif // PresetArchiveDatabase

View File

@ -277,9 +277,9 @@ boost::filesystem::path AppUpdateDownloadDialog::get_download_path() const
// MsgUpdateConfig
MsgUpdateConfig::MsgUpdateConfig(const std::vector<Update> &updates, bool force_before_wizard/* = false*/) :
MsgDialog(nullptr, force_before_wizard ? _L("Opening Configuration Wizard") : _L("Configuration update"),
force_before_wizard ? _L("PrusaSlicer is not using the newest configuration available.\n"
MsgUpdateConfig::MsgUpdateConfig(const std::vector<Update> &updates, PresetUpdater::UpdateParams update_params) :
MsgDialog(nullptr, update_params == PresetUpdater::UpdateParams::FORCED_BEFORE_WIZARD ? _L("Opening Configuration Wizard") : _L("Configuration update"),
update_params == PresetUpdater::UpdateParams::FORCED_BEFORE_WIZARD ? _L("PrusaSlicer is not using the newest configuration available.\n"
"Configuration Wizard may not offer the latest printers, filaments and SLA materials to be installed.") :
_L("Configuration update is available"), wxICON_ERROR)
{
@ -333,12 +333,19 @@ MsgUpdateConfig::MsgUpdateConfig(const std::vector<Update> &updates, bool force_
content_sizer->Add(versions);
content_sizer->AddSpacer(2*VERT_SPACING);
add_button(wxID_OK, true, force_before_wizard ? _L("Install") : "OK");
if (force_before_wizard) {
if (update_params == PresetUpdater::UpdateParams::FORCED_BEFORE_WIZARD) {
add_button(wxID_OK, true, _L("Install"));
auto* btn = add_button(wxID_CLOSE, false, _L("Don't install"));
btn->Bind(wxEVT_BUTTON, [this](const wxCommandEvent&) { this->EndModal(wxID_CLOSE); });
}
add_button(wxID_CANCEL);
} else if (update_params == PresetUpdater::UpdateParams::SHOW_TEXT_BOX_YES_NO) {
add_button(wxID_OK, true, _L("Yes"));
add_button(wxID_CANCEL, false, _L("No"));
} else {
assert(update_params == PresetUpdater::UpdateParams::SHOW_TEXT_BOX);
add_button(wxID_OK, true, "OK");
add_button(wxID_CANCEL);
}
finalize();
}

View File

@ -15,6 +15,8 @@
#include "libslic3r/Semver.hpp"
#include "MsgDialog.hpp"
#include "slic3r/Utils/PresetUpdater.hpp"
class wxBoxSizer;
class wxCheckBox;
@ -102,7 +104,7 @@ public:
};
// force_before_wizard - indicates that check of updated is forced before ConfigWizard opening
MsgUpdateConfig(const std::vector<Update> &updates, bool force_before_wizard = false);
MsgUpdateConfig(const std::vector<Update> &updates, PresetUpdater::UpdateParams update_params);
MsgUpdateConfig(MsgUpdateConfig &&) = delete;
MsgUpdateConfig(const MsgUpdateConfig &) = delete;
MsgUpdateConfig &operator=(MsgUpdateConfig &&) = delete;

View File

@ -5,7 +5,7 @@
#include "UpdatesUIManager.hpp"
#include "I18N.hpp"
#include "wxExtensions.hpp"
#include "PresetArchiveDatabase.hpp"
#include "slic3r/Utils/PresetUpdaterWrapper.hpp"
#include "GUI.hpp"
#include "GUI_App.hpp"
@ -22,9 +22,9 @@ namespace fs = boost::filesystem;
namespace Slic3r {
namespace GUI {
RepositoryUpdateUIManager::RepositoryUpdateUIManager(wxWindow* parent, PresetArchiveDatabase* pad, int em) :
RepositoryUpdateUIManager::RepositoryUpdateUIManager(wxWindow* parent, Slic3r::PresetUpdaterWrapper* puw, int em) :
m_parent(parent)
,m_pad(pad)
,m_puw(puw)
,m_main_sizer(new wxBoxSizer(wxVERTICAL))
{
auto online_label = new wxStaticText(m_parent, wxID_ANY, _L("Online sources"));
@ -104,10 +104,10 @@ void RepositoryUpdateUIManager::fill_entries(bool init_selection/* = false*/)
m_online_entries.clear();
m_offline_entries.clear();
const SharedArchiveRepositoryVector& archs = m_pad->get_all_archive_repositories();
const SharedArchiveRepositoryVector& archs = m_puw->get_all_archive_repositories();
for (const auto* archive : archs) {
const std::string& uuid = archive->get_uuid();
if (init_selection && m_pad->is_selected_repository_by_uuid(uuid))
if (init_selection && m_puw->is_selected_repository_by_uuid(uuid))
m_selected_uuids.emplace(uuid);
const bool is_selected = m_selected_uuids.find(uuid) != m_selected_uuids.end();
@ -260,7 +260,7 @@ void RepositoryUpdateUIManager::update()
void RepositoryUpdateUIManager::remove_offline_repos(const std::string& id)
{
m_pad->remove_local_archive(id);
m_puw->remove_local_archive(id);
m_selected_uuids.erase(id);
check_selection();
@ -291,7 +291,7 @@ void RepositoryUpdateUIManager::load_offline_repos()
try {
fs::path input_path = fs::path(input_file);
std::string msg;
std::string uuid = m_pad->add_local_archive(input_path, msg);
std::string uuid = m_puw->add_local_archive(input_path, msg);
if (uuid.empty()) {
ErrorDialog(m_parent, from_u8(msg), false).ShowModal();
}
@ -314,7 +314,7 @@ bool RepositoryUpdateUIManager::set_selected_repositories()
std::string msg;
if (m_pad->set_selected_repositories(used_ids, msg)) {
if (m_puw->set_selected_repositories(used_ids, msg)) {
check_selection();
return true;
}
@ -328,7 +328,7 @@ bool RepositoryUpdateUIManager::set_selected_repositories()
void RepositoryUpdateUIManager::check_selection()
{
for (const auto& [uuid, is_selected] : m_pad->get_selected_repositories_uuid() )
for (const auto& [uuid, is_selected] : m_puw->get_selected_repositories_uuid() )
if ((is_selected && m_selected_uuids.find(uuid) == m_selected_uuids.end() )||
(!is_selected && m_selected_uuids.find(uuid) != m_selected_uuids.end())) {
m_is_selection_changed = true;
@ -338,7 +338,7 @@ void RepositoryUpdateUIManager::check_selection()
m_is_selection_changed = false;
}
ManagePresetRepositoriesDialog::ManagePresetRepositoriesDialog(PresetArchiveDatabase* pad)
ManagePresetRepositoriesDialog::ManagePresetRepositoriesDialog(Slic3r::PresetUpdaterWrapper* puw)
: DPIDialog(static_cast<wxWindow*>(wxGetApp().mainframe), wxID_ANY,
format_wxstr("%1% - %2%", SLIC3R_APP_NAME, _L("Manage Updates")),
wxDefaultPosition, wxDefaultSize, wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER)
@ -346,7 +346,7 @@ ManagePresetRepositoriesDialog::ManagePresetRepositoriesDialog(PresetArchiveData
this->SetFont(wxGetApp().normal_font());
const int em = em_unit();
m_manager = std::make_unique<RepositoryUpdateUIManager>(this, pad, em);
m_manager = std::make_unique<RepositoryUpdateUIManager>(this, puw, em);
auto sizer = m_manager->get_sizer();

View File

@ -14,10 +14,10 @@ class wxSizer;
class wxFlexGridSizer;
namespace Slic3r {
class PresetUpdaterWrapper;
namespace GUI {
class PresetArchiveDatabase;
class RepositoryUpdateUIManager
{
struct OnlineEntry {
@ -44,7 +44,7 @@ class RepositoryUpdateUIManager
boost::filesystem::path source_path;
};
PresetArchiveDatabase* m_pad { nullptr };
PresetUpdaterWrapper* m_puw { nullptr };
wxWindow* m_parent { nullptr };
wxSizer* m_main_sizer { nullptr };
@ -68,7 +68,7 @@ class RepositoryUpdateUIManager
public:
RepositoryUpdateUIManager() {}
RepositoryUpdateUIManager(wxWindow* parent, PresetArchiveDatabase* pad, int em);
RepositoryUpdateUIManager(wxWindow* parent, Slic3r::PresetUpdaterWrapper* puw, int em);
~RepositoryUpdateUIManager() {}
void update();
@ -83,7 +83,7 @@ public:
class ManagePresetRepositoriesDialog : public DPIDialog
{
public:
ManagePresetRepositoriesDialog(PresetArchiveDatabase* pad);
ManagePresetRepositoriesDialog(PresetUpdaterWrapper* puw);
~ManagePresetRepositoriesDialog() {}
protected:

View File

@ -26,17 +26,6 @@ std::string parse_tree_for_param(const pt::ptree& tree, const std::string& param
return {};
}
/*void parse_tree_for_param_vector(
const pt::ptree &tree, const std::string& param, std::vector<std::string>& results) {
for (const auto &section : tree) {
if (section.first == param) {
results.emplace_back(section.second.data());
} else {
parse_tree_for_param_vector(section.second, param, results);
}
}
}*/
pt::ptree parse_tree_for_subtree(const pt::ptree& tree, const std::string& param) {
for (const auto &section : tree) {
if (section.first == param) {

View File

@ -1539,9 +1539,9 @@ void LoginWebViewDialog::on_navigation_request(wxWebViewEvent &evt)
evt.Veto();
m_ret_val = into_u8(url);
EndModal(wxID_OK);
} else if (url.Find(L"accounts.google.com") != wxString::npos
|| url.Find(L"appleid.apple.com") != wxString::npos
|| url.Find(L"facebook.com") != wxString::npos) {
} else if (url.Find(L"accounts.google.com") != wxNOT_FOUND
|| url.Find(L"appleid.apple.com") != wxNOT_FOUND
|| url.Find(L"facebook.com") != wxNOT_FOUND) {
auto& sc = Utils::ServiceConfig::instance();
if (!m_evt_sent && !url.starts_with(GUI::from_u8(sc.account_url()))) {
wxCommandEvent* evt = new wxCommandEvent(EVT_OPEN_EXTERNAL_LOGIN);

View File

@ -136,6 +136,7 @@ struct Http::priv
Http::ErrorFn errorfn;
Http::ProgressFn progressfn;
Http::IPResolveFn ipresolvefn;
Http::RetryFn retryfn;
priv(const std::string &url);
~priv();
@ -395,6 +396,12 @@ void Http::priv::http_perform(const HttpRetryOpt& retry_opts)
std::chrono::milliseconds delay = std::chrono::milliseconds(randomized_delay(generator));
size_t num_retries = 0;
do {
// break if canceled outside
if (retryfn && !retryfn(num_retries + 1, num_retries < retry_opts.max_retries ? delay.count() : 0)) {
res = CURLE_ABORTED_BY_CALLBACK;
cancel = true;
break;
}
res = ::curl_easy_perform(curl);
if (res == CURLE_OK)
@ -409,6 +416,7 @@ void Http::priv::http_perform(const HttpRetryOpt& retry_opts)
<< "), retrying in " << delay.count() / 1000.0f << " s";
std::this_thread::sleep_for(delay);
delay = std::min(delay * 2, retry_opts.max_delay);
}
} while (retry);
@ -643,6 +651,12 @@ Http& Http::on_ip_resolve(IPResolveFn fn)
return *this;
}
Http& Http::on_retry(RetryFn fn)
{
if (p) { p->retryfn = std::move(fn); }
return *this;
}
Http& Http::cookie_file(const std::string& file_path)
{
if (p) {

View File

@ -61,6 +61,8 @@ public:
typedef std::function<void(Progress, bool& /* cancel */)> ProgressFn;
typedef std::function<void(std::string/* address */)> IPResolveFn;
//<bool - false if canceled(int - attempt number, unsigned - ms to next attempt, 0 if last)>
typedef std::function<bool(int, unsigned)> RetryFn;
Http(Http &&other);
@ -140,6 +142,8 @@ public:
// Called if curl_easy_getinfo resolved just used IP address.
Http& on_ip_resolve(IPResolveFn fn);
Http& on_retry(RetryFn fn);
Http& cookie_file(const std::string& file_path);
Http& cookie_jar(const std::string& file_path);
Http& set_referer(const std::string& referer);

View File

@ -31,7 +31,7 @@
#include "slic3r/GUI/GUI.hpp"
#include "slic3r/GUI/I18N.hpp"
#include "slic3r/GUI/UpdateDialogs.hpp"
#include "slic3r/GUI/ConfigWizard.hpp"
#include "slic3r/Utils/PresetUpdaterWrapper.hpp"
#include "slic3r/GUI/GUI_App.hpp"
#include "slic3r/GUI/Plater.hpp"
#include "slic3r/GUI/format.hpp"
@ -74,24 +74,8 @@ void copy_file_fix(const fs::path &source, const fs::path &target)
static constexpr const auto perms = fs::owner_read | fs::owner_write | fs::group_read | fs::others_read;
fs::permissions(target, perms);
}
/*std::string escape_string_url(const std::string& unescaped)
{
std::string ret_val;
CURL* curl = curl_easy_init();
if (curl) {
char* decoded = curl_easy_escape(curl, unescaped.c_str(), unescaped.size());
if (decoded) {
ret_val = std::string(decoded);
curl_free(decoded);
}
curl_easy_cleanup(curl);
}
return ret_val;
}*/
}
wxDEFINE_EVENT(EVT_CONFIG_UPDATER_SYNC_DONE, wxCommandEvent);
struct Update
{
fs::path source;
@ -170,18 +154,13 @@ struct PresetUpdater::priv
{
std::vector<Index> index_db;
bool enabled_version_check;
bool enabled_config_update;
std::string version_check_url;
fs::path cache_path;
fs::path cache_vendor_path;
fs::path rsrc_path;
fs::path vendor_path;
bool cancel;
std::thread thread;
bool has_waiting_updates { false };
Updates waiting_updates;
@ -190,16 +169,16 @@ struct PresetUpdater::priv
void set_download_prefs(const AppConfig *app_config);
void prune_tmps() const;
void clear_cache_vendor() const;
void sync_config(const VendorMap& vendors, const GUI::ArchiveRepository* archive);
void sync_config(const VendorMap& vendors, const ArchiveRepository* archive, PresetUpdaterUIStatus* ui_status);
void check_install_indices() const;
Updates get_config_updates(const Semver& old_slic3r_version) const;
bool perform_updates(Updates &&updates, const SharedArchiveRepositoryVector& repositories, bool snapshot = true) const;
bool perform_updates(Updates &&updates, const SharedArchiveRepositoryVector& repositories, PresetUpdaterUIStatus* ui_status, bool snapshot = true) const;
void set_waiting_updates(Updates u);
// checks existence and downloads resource to cache
void get_missing_resource(const GUI::ArchiveRepository* archive, const std::string& vendor, const std::string& filename, const std::string& repository_id_from_ini) const;
void get_missing_resource(const ArchiveRepository* archive, const std::string& vendor, const std::string& filename, const std::string& repository_id_from_ini, PresetUpdaterUIStatus* ui_status) const;
// checks existence and downloads resource to vendor or copy from cache to vendor
void get_or_copy_missing_resource(const GUI::ArchiveRepository* archive, const std::string& vendor, const std::string& filename, const std::string& repository_id_from_ini) const;
void get_or_copy_missing_resource(const ArchiveRepository* archive, const std::string& vendor, const std::string& filename, const std::string& repository_id_from_ini, PresetUpdaterUIStatus* ui_status) const;
void update_index_db();
};
@ -208,7 +187,7 @@ PresetUpdater::priv::priv()
, cache_vendor_path(cache_path / "vendor")
, rsrc_path(fs::path(resources_dir()) / "profiles")
, vendor_path(fs::path(Slic3r::data_dir()) / "vendor")
, cancel(false)
//, cancel(false)
{
set_download_prefs(GUI::wxGetApp().app_config);
// Install indicies from resources. Only installs those that are either missing or older than in resources.
@ -225,8 +204,6 @@ void PresetUpdater::priv::update_index_db()
// Pull relevant preferences from AppConfig
void PresetUpdater::priv::set_download_prefs(const AppConfig *app_config)
{
enabled_version_check = app_config->get("notify_release") != "none";
version_check_url = app_config->version_check_url();
enabled_config_update = app_config->get_bool("preset_update") && !app_config->legacy_datadir();
}
@ -253,7 +230,7 @@ void PresetUpdater::priv::clear_cache_vendor() const
}
// gets resource to cache/<vendor_name>/
void PresetUpdater::priv::get_missing_resource(const GUI::ArchiveRepository* archive, const std::string& vendor, const std::string& filename, const std::string& repository_id_from_ini) const
void PresetUpdater::priv::get_missing_resource(const ArchiveRepository* archive, const std::string& vendor, const std::string& filename, const std::string& repository_id_from_ini, PresetUpdaterUIStatus* ui_status) const
{
assert(!filename.empty() && !vendor.empty());
//if (filename.empty() || vendor.empty()) {
@ -282,13 +259,12 @@ void PresetUpdater::priv::get_missing_resource(const GUI::ArchiveRepository* arc
if (!fs::exists(file_in_cache.parent_path()))
fs::create_directory(file_in_cache.parent_path());
//std::string escaped_filename = escape_string_url(filename);
const std::string resource_subpath = GUI::format("%1%/%2%",vendor, filename);
archive->get_file(resource_subpath, file_in_cache, repository_id_from_ini);
archive->get_file(resource_subpath, file_in_cache, repository_id_from_ini, ui_status);
return;
}
// gets resource to vendor/<vendor_name>/
void PresetUpdater::priv::get_or_copy_missing_resource(const GUI::ArchiveRepository* archive, const std::string& vendor, const std::string& filename, const std::string& repository_id_from_ini) const
void PresetUpdater::priv::get_or_copy_missing_resource(const ArchiveRepository* archive, const std::string& vendor, const std::string& filename, const std::string& repository_id_from_ini, PresetUpdaterUIStatus* ui_status) const
{
assert(!filename.empty() && !vendor.empty());
@ -313,9 +289,8 @@ void PresetUpdater::priv::get_or_copy_missing_resource(const GUI::ArchiveReposit
if (!fs::exists(file_in_cache)) {
BOOST_LOG_TRIVIAL(info) << "Downloading resources missing in cache directory: " << vendor << " / " << filename;
//std::string escaped_filename = escape_string_url(filename);
const std::string resource_subpath = GUI::format("%1%/%2%", vendor, filename);
archive->get_file(resource_subpath, file_in_vendor, repository_id_from_ini);
archive->get_file(resource_subpath, file_in_vendor, repository_id_from_ini, ui_status);
return;
}
BOOST_LOG_TRIVIAL(debug) << "Copiing: " << file_in_cache << " to " << file_in_vendor;
@ -324,19 +299,22 @@ void PresetUpdater::priv::get_or_copy_missing_resource(const GUI::ArchiveReposit
// Download vendor indices. Also download new bundles if an index indicates there's a new one available.
// Both are saved in cache.
void PresetUpdater::priv::sync_config(const VendorMap& vendors, const GUI::ArchiveRepository* archive_repository)
void PresetUpdater::priv::sync_config(const VendorMap& vendors, const ArchiveRepository* archive_repository, PresetUpdaterUIStatus* ui_status)
{
BOOST_LOG_TRIVIAL(info) << "Syncing configuration cache";
if (!enabled_config_update) { return; }
assert(ui_status);
ui_status->set_target(archive_repository->get_manifest().id + " archive");
// Download profiles archive zip
fs::path archive_path(cache_path / "vendor_indices.zip");
if (!archive_repository->get_archive(archive_path)) {
BOOST_LOG_TRIVIAL(error) << "Download of vedor profiles archive zip failed.";
if (!archive_repository->get_archive(archive_path, ui_status)) {
BOOST_LOG_TRIVIAL(error) << "Download of vedor profiles archive zip of " << archive_repository->get_manifest().id << " repository has failed.";
return;
}
if (cancel) {
if (ui_status->get_canceled()) {
return;
}
@ -406,7 +384,7 @@ void PresetUpdater::priv::sync_config(const VendorMap& vendors, const GUI::Archi
// Update vendor preset bundles if in Vendor
// Over all indices from the cache directory:
for (auto &index : index_db) {
if (cancel) {
if (ui_status->get_canceled()) {
return;
}
auto archive_it = std::find_if(vendors_with_status.begin(), vendors_with_status.end(),
@ -452,7 +430,7 @@ void PresetUpdater::priv::sync_config(const VendorMap& vendors, const GUI::Archi
BOOST_LOG_TRIVIAL(error) << format("Could not load downloaded index %1% for vendor %2%: invalid index?", idx_path, vendor.name);
continue;
}
if (cancel)
if (ui_status->get_canceled())
return;
}
@ -480,9 +458,9 @@ void PresetUpdater::priv::sync_config(const VendorMap& vendors, const GUI::Archi
BOOST_LOG_TRIVIAL(info) << "Downloading new bundle for vendor: " << vendor.name;
const std::string source_subpath = GUI::format("%1%/%2%.ini", vendor.id, recommended.to_string());
const fs::path bundle_path = cache_path / (vendor.id + ".ini");
if (!archive_repository->get_file(source_subpath, bundle_path, vendor.repo_id))
if (!archive_repository->get_file(source_subpath, bundle_path, vendor.repo_id, ui_status))
continue;
if (cancel)
if (ui_status->get_canceled())
return;
// vp is fully loaded to get all resources
VendorProfile vp;
@ -499,7 +477,7 @@ void PresetUpdater::priv::sync_config(const VendorMap& vendors, const GUI::Archi
if (! res.empty()) {
try
{
get_missing_resource(archive_repository, vp.id, res, vendor.repo_id);
get_missing_resource(archive_repository, vp.id, res, vendor.repo_id, ui_status);
}
catch (const std::exception& e)
{
@ -507,7 +485,7 @@ void PresetUpdater::priv::sync_config(const VendorMap& vendors, const GUI::Archi
}
}
if (cancel)
if (ui_status->get_canceled())
return;
}
}
@ -539,7 +517,7 @@ void PresetUpdater::priv::sync_config(const VendorMap& vendors, const GUI::Archi
if (!fs::exists(ini_path_in_archive)){
// Download recommneded to vendor - we do not have any existing ini file so we have to use archive url.
const std::string source_subpath = GUI::format("%1%/%2%.ini", vendor.first, recommended.to_string());
if (!archive_repository->get_ini_no_id(source_subpath, ini_path_in_archive))
if (!archive_repository->get_ini_no_id(source_subpath, ini_path_in_archive, ui_status))
continue;
} else {
// check existing ini version
@ -560,7 +538,7 @@ void PresetUpdater::priv::sync_config(const VendorMap& vendors, const GUI::Archi
if (vp.config_version != recommended) {
// Take url from existing ini. This way we prevent downloading files from multiple sources.
const std::string source_subpath = GUI::format("%1%/%2%.ini", vp.id, recommended.to_string());
if (!archive_repository->get_file(source_subpath, ini_path_in_archive, vp.repo_id))
if (!archive_repository->get_file(source_subpath, ini_path_in_archive, vp.repo_id, ui_status))
continue;
}
}
@ -577,14 +555,14 @@ void PresetUpdater::priv::sync_config(const VendorMap& vendors, const GUI::Archi
if (!model.thumbnail.empty()) {
try
{
get_missing_resource(archive_repository, vp.id, model.thumbnail, vp.repo_id);
get_missing_resource(archive_repository, vp.id, model.thumbnail, vp.repo_id, ui_status);
}
catch (const std::exception& e)
{
BOOST_LOG_TRIVIAL(error) << "Failed to get " << model.thumbnail << " for " << vp.id << " " << model.id << ": " << e.what();
}
}
if (cancel)
if (ui_status->get_canceled())
return;
}
} else if (vendor.second == VendorStatus::IN_CACHE) {
@ -653,7 +631,7 @@ void PresetUpdater::priv::sync_config(const VendorMap& vendors, const GUI::Archi
continue;
}
const std::string source_subpath = GUI::format("%1%/%2%.ini", vp.id, recommended_archive.to_string());
if (!archive_repository->get_file(source_subpath, ini_path_in_archive, vp.repo_id)) {
if (!archive_repository->get_file(source_subpath, ini_path_in_archive, vp.repo_id, ui_status)) {
BOOST_LOG_TRIVIAL(error) << format("Failed to get new vendor .ini file when checking missing resources: %1%", ini_path_in_archive.string());
continue;
}
@ -670,7 +648,7 @@ void PresetUpdater::priv::sync_config(const VendorMap& vendors, const GUI::Archi
}
if (vp.config_version != recommended_archive) {
const std::string source_subpath = GUI::format("%1%/%2%.ini", vp.id, recommended_archive.to_string());
if (!archive_repository->get_file(source_subpath, ini_path_in_archive, vp.repo_id)) {
if (!archive_repository->get_file(source_subpath, ini_path_in_archive, vp.repo_id, ui_status)) {
BOOST_LOG_TRIVIAL(error) << format("Failed to open vendor .ini file when checking missing resources: %1%", ini_path_in_archive);
continue;
}
@ -694,14 +672,14 @@ void PresetUpdater::priv::sync_config(const VendorMap& vendors, const GUI::Archi
if (!model.thumbnail.empty()) {
try
{
get_missing_resource(archive_repository, vp.id, model.thumbnail, vp.repo_id);
get_missing_resource(archive_repository, vp.id, model.thumbnail, vp.repo_id, ui_status);
}
catch (const std::exception& e)
{
BOOST_LOG_TRIVIAL(error) << "Failed to get " << model.thumbnail << " for " << vp.id << " " << model.id << ": " << e.what();
}
}
if (cancel)
if (ui_status->get_canceled())
return;
}
} else if (vendor.second == VendorStatus::INSTALLED || vendor.second == VendorStatus::NEW_VERSION) {
@ -724,14 +702,14 @@ void PresetUpdater::priv::sync_config(const VendorMap& vendors, const GUI::Archi
if (!res.empty()) {
try
{
get_or_copy_missing_resource(archive_repository, vp.id, res, vp.repo_id);
get_or_copy_missing_resource(archive_repository, vp.id, res, vp.repo_id, ui_status);
}
catch (const std::exception& e)
{
BOOST_LOG_TRIVIAL(error) << "Failed to get " << res << " for " << vp.id << " " << model.id << ": " << e.what();
}
}
if (cancel)
if (ui_status->get_canceled())
return;
}
}
@ -962,7 +940,7 @@ Updates PresetUpdater::priv::get_config_updates(const Semver &old_slic3r_version
return updates;
}
bool PresetUpdater::priv::perform_updates(Updates &&updates, const SharedArchiveRepositoryVector& repositories, bool snapshot) const
bool PresetUpdater::priv::perform_updates(Updates &&updates, const SharedArchiveRepositoryVector& repositories, PresetUpdaterUIStatus* ui_status, bool snapshot) const
{
if (updates.incompats.size() > 0) {
if (snapshot) {
@ -1051,7 +1029,7 @@ bool PresetUpdater::priv::perform_updates(Updates &&updates, const SharedArchive
{
auto it = std::find_if(repositories.begin(), repositories.end(), [&vp](const auto* i){ return vp.repo_id == i->get_manifest().id; });
if (it != repositories.end())
get_or_copy_missing_resource((*it), vp.id, resource, vp.repo_id);
get_or_copy_missing_resource((*it), vp.id, resource, vp.repo_id, ui_status);
else {
BOOST_LOG_TRIVIAL(error) << "Failed to prepare " << resource << " for " << vp.id << " " << model.id << ": Missing record for source with repo_id " << vp.repo_id;
}
@ -1085,74 +1063,20 @@ PresetUpdater::PresetUpdater() :
PresetUpdater::~PresetUpdater()
{
if (p && p->thread.joinable()) {
// This will stop transfers being done by the thread, if any.
// Cancelling takes some time, but should complete soon enough.
p->cancel = true;
p->thread.join();
}
}
void PresetUpdater::sync(const PresetBundle *preset_bundle, wxEvtHandler* evt_handler,SharedArchiveRepositoryVector&& repositories)
{
p->set_download_prefs(GUI::wxGetApp().app_config);
if (!p->enabled_config_update) { return; }
p->thread = std::thread([this, &vendors = preset_bundle->vendors, repositories = std::move(repositories), evt_handler]() {
this->p->clear_cache_vendor();
this->p->prune_tmps();
for (const GUI::ArchiveRepository* archive : repositories) {
this->p->sync_config(vendors, archive);
}
wxCommandEvent* evt = new wxCommandEvent(EVT_CONFIG_UPDATER_SYNC_DONE);
evt_handler->QueueEvent(evt);
});
}
void PresetUpdater::cancel_sync()
{
if (p && p->thread.joinable()) {
// This will stop transfers being done by the thread, if any.
// Cancelling takes some time, but should complete soon enough.
p->cancel = true;
p->thread.join();
}
p->cancel = false;
}
void PresetUpdater::sync_blocking(const PresetBundle* preset_bundle, wxEvtHandler* evt_handler, const SharedArchiveRepositoryVector& repositories)
void PresetUpdater::sync_blocking(const VendorMap& vendors, const SharedArchiveRepositoryVector& repositories, PresetUpdaterUIStatus* ui_status)
{
p->set_download_prefs(GUI::wxGetApp().app_config);
if (!p->enabled_config_update) { return; }
this->p->clear_cache_vendor();
this->p->prune_tmps();
for (const GUI::ArchiveRepository* archive : repositories) {
this->p->sync_config(preset_bundle->vendors, archive);
for (const ArchiveRepository* archive : repositories) {
if (ui_status && ui_status->get_canceled()) {
break;
}
}
void PresetUpdater::slic3r_update_notify()
{
if (! p->enabled_version_check)
return;
auto* app_config = GUI::wxGetApp().app_config;
const auto ver_online_str = app_config->get("version_online");
const auto ver_online = Semver::parse(ver_online_str);
const auto ver_online_seen = Semver::parse(app_config->get("version_online_seen"));
if (ver_online) {
// Only display the notification if the version available online is newer AND if we haven't seen it before
if (*ver_online > Slic3r::SEMVER && (! ver_online_seen || *ver_online_seen < *ver_online)) {
GUI::MsgUpdateSlic3r notification(Slic3r::SEMVER, *ver_online);
notification.ShowModal();
if (notification.disable_version_check()) {
app_config->set("notify_release", "none");
p->enabled_version_check = false;
}
}
app_config->set("version_online_seen", ver_online_str);
this->p->sync_config(vendors, archive, ui_status);
}
}
@ -1175,7 +1099,7 @@ static bool reload_configs_update_gui()
return true;
}
PresetUpdater::UpdateResult PresetUpdater::config_update(const Semver& old_slic3r_version, UpdateParams params, const SharedArchiveRepositoryVector& repositories) const
PresetUpdater::UpdateResult PresetUpdater::config_update(const Semver& old_slic3r_version, UpdateParams params, const SharedArchiveRepositoryVector& repositories, PresetUpdaterUIStatus* ui_status) const
{
if (! p->enabled_config_update) { return R_NOOP; }
@ -1209,7 +1133,7 @@ PresetUpdater::UpdateResult PresetUpdater::config_update(const Semver& old_slic3
// This effectively removes the incompatible bundles:
// (snapshot is taken beforehand)
if (! p->perform_updates(std::move(updates), repositories) ||
if (! p->perform_updates(std::move(updates), repositories, ui_status) ||
! GUI::wxGetApp().run_wizard(GUI::ConfigWizard::RR_DATA_INCOMPAT))
return R_INCOMPAT_EXIT;
@ -1251,7 +1175,7 @@ PresetUpdater::UpdateResult PresetUpdater::config_update(const Semver& old_slic3
const auto res = dlg.ShowModal();
if (res == wxID_OK) {
BOOST_LOG_TRIVIAL(info) << "User wants to update...";
if (! p->perform_updates(std::move(updates), repositories) ||
if (! p->perform_updates(std::move(updates), repositories, ui_status) ||
! reload_configs_update_gui())
return R_INCOMPAT_EXIT;
return R_UPDATE_INSTALLED;
@ -1289,12 +1213,12 @@ PresetUpdater::UpdateResult PresetUpdater::config_update(const Semver& old_slic3
updates_msg.emplace_back(update.vendor, update.version.config_version, update.version.comment, std::move(changelog_url), std::move(printers));
}
GUI::MsgUpdateConfig dlg(updates_msg, params == UpdateParams::FORCED_BEFORE_WIZARD);
GUI::MsgUpdateConfig dlg(updates_msg, params);
const auto res = dlg.ShowModal();
if (res == wxID_OK) {
BOOST_LOG_TRIVIAL(debug) << "User agreed to perform the update";
if (! p->perform_updates(std::move(updates), repositories) ||
if (! p->perform_updates(std::move(updates), repositories, ui_status) ||
! reload_configs_update_gui())
return R_ALL_CANCELED;
return R_UPDATE_INSTALLED;
@ -1315,7 +1239,7 @@ PresetUpdater::UpdateResult PresetUpdater::config_update(const Semver& old_slic3
return R_NOOP;
}
bool PresetUpdater::install_bundles_rsrc_or_cache_vendor(std::vector<std::string> bundles, const SharedArchiveRepositoryVector& repositories, bool snapshot) const
bool PresetUpdater::install_bundles_rsrc_or_cache_vendor(std::vector<std::string> bundles, const SharedArchiveRepositoryVector& repositories, PresetUpdaterUIStatus* ui_status, bool snapshot) const
{
Updates updates;
@ -1418,10 +1342,10 @@ bool PresetUpdater::install_bundles_rsrc_or_cache_vendor(std::vector<std::string
}
}
return p->perform_updates(std::move(updates), repositories, snapshot);
return p->perform_updates(std::move(updates), repositories, ui_status, snapshot);
}
void PresetUpdater::on_update_notification_confirm(const SharedArchiveRepositoryVector& repositories)
void PresetUpdater::on_update_notification_confirm(const SharedArchiveRepositoryVector& repositories, PresetUpdaterUIStatus* ui_status)
{
if (!p->has_waiting_updates)
return;
@ -1439,12 +1363,12 @@ void PresetUpdater::on_update_notification_confirm(const SharedArchiveRepository
updates_msg.emplace_back(update.vendor, update.version.config_version, update.version.comment, std::move(changelog_url), std::move(printers));
}
GUI::MsgUpdateConfig dlg(updates_msg);
GUI::MsgUpdateConfig dlg(updates_msg, UpdateParams::SHOW_TEXT_BOX);
const auto res = dlg.ShowModal();
if (res == wxID_OK) {
BOOST_LOG_TRIVIAL(debug) << "User agreed to perform the update";
if (p->perform_updates(std::move(p->waiting_updates), repositories) &&
if (p->perform_updates(std::move(p->waiting_updates), repositories, ui_status) &&
reload_configs_update_gui()) {
p->has_waiting_updates = false;
}
@ -1454,28 +1378,10 @@ void PresetUpdater::on_update_notification_confirm(const SharedArchiveRepository
}
}
bool PresetUpdater::version_check_enabled() const
{
return p->enabled_version_check;
}
void PresetUpdater::update_index_db()
{
p->update_index_db();
}
void PresetUpdater::add_additional_archive(const std::string& archive_url, const std::string& download_url)
{
if (std::find_if(m_additional_archives.begin(), m_additional_archives.end(), [archive_url](const std::pair<std::string, std::string>& it) { return it.first == archive_url; }) == m_additional_archives.end()) {
m_additional_archives.emplace_back(archive_url, download_url);
}
}
void PresetUpdater::add_additional_archives(const std::vector<std::pair<std::string, std::string>>& archives)
{
for (const auto& pair : archives) {
add_additional_archive(pair.first, pair.second);
}
}
}

View File

@ -10,16 +10,16 @@
#include <memory>
#include <vector>
#include <wx/event.h>
namespace Slic3r {
class VendorProfile;
typedef std::map<std::string, VendorProfile> VendorMap;
class AppConfig;
class PresetBundle;
class Semver;
class PresetUpdaterUIStatus;
typedef std::vector<const GUI::ArchiveRepository*> SharedArchiveRepositoryVector;
typedef std::vector<const ArchiveRepository*> SharedArchiveRepositoryVector;
static constexpr const int SLIC3R_VERSION_BODY_MAX = 256;
@ -33,14 +33,7 @@ public:
PresetUpdater &operator=(const PresetUpdater &) = delete;
~PresetUpdater();
// If either version check or config updating is enabled, get the appropriate data in the background and cache it.
void sync(const PresetBundle *preset_bundle, wxEvtHandler* evt_handler, SharedArchiveRepositoryVector&& repositories);
void cancel_sync();
void sync_blocking(const PresetBundle* preset_bundle, wxEvtHandler* evt_handler, const SharedArchiveRepositoryVector& repositories);
// If version check is enabled, check if chaced online slic3r version is newer, notify if so.
void slic3r_update_notify();
void sync_blocking(const VendorMap& vendors, const SharedArchiveRepositoryVector& repositories, PresetUpdaterUIStatus* ui_status);
enum UpdateResult {
R_NOOP,
@ -55,35 +48,26 @@ public:
enum class UpdateParams {
SHOW_TEXT_BOX, // force modal textbox
SHOW_NOTIFICATION, // only shows notification
FORCED_BEFORE_WIZARD // indicates that check of updated is forced before ConfigWizard opening
FORCED_BEFORE_WIZARD, // indicates that check of updated is forced before ConfigWizard opening
SHOW_TEXT_BOX_YES_NO // like first option but different buttons in dialog
};
// If updating is enabled, check if updates are available in cache, if so, ask about installation.
// A false return value implies Slic3r should exit due to incompatibility of configuration.
// Providing old slic3r version upgrade profiles on upgrade of an application even in case
// that the config index installed from the Internet is equal to the index contained in the installation package.
UpdateResult config_update(const Semver &old_slic3r_version, UpdateParams params, const SharedArchiveRepositoryVector& repositories) const;
UpdateResult config_update(const Semver &old_slic3r_version, UpdateParams params, const SharedArchiveRepositoryVector& repositories, PresetUpdaterUIStatus* ui_status) const;
void update_index_db();
// "Update" a list of bundles from resources or cache/vendor (behaves like an online update).
bool install_bundles_rsrc_or_cache_vendor(std::vector<std::string> bundles, const SharedArchiveRepositoryVector& repositories, bool snapshot = true) const;
bool install_bundles_rsrc_or_cache_vendor(std::vector<std::string> bundles, const SharedArchiveRepositoryVector& repositories, PresetUpdaterUIStatus* ui_status, bool snapshot = true) const;
void on_update_notification_confirm(const SharedArchiveRepositoryVector& repositories);
void on_update_notification_confirm(const SharedArchiveRepositoryVector& repositories, PresetUpdaterUIStatus* ui_status);
bool version_check_enabled() const;
void add_additional_archive(const std::string& archive_url, const std::string& download_url);
void add_additional_archives(const std::vector<std::pair<std::string, std::string>>& archives);
private:
struct priv;
std::unique_ptr<priv> p;
std::vector<std::pair<std::string, std::string>> m_additional_archives;
};
//wxDECLARE_EVENT(EVT_SLIC3R_VERSION_ONLINE, wxCommandEvent);
//wxDECLARE_EVENT(EVT_SLIC3R_EXPERIMENTAL_VERSION_ONLINE, wxCommandEvent);
wxDECLARE_EVENT(EVT_CONFIG_UPDATER_SYNC_DONE, wxCommandEvent);
}
#endif

View File

@ -0,0 +1,354 @@
#include "PresetUpdaterWrapper.hpp"
#include "slic3r/GUI/GUI_App.hpp"
#include "slic3r/GUI/I18N.hpp"
#include "slic3r/GUI/MsgDialog.hpp"
#include "slic3r/GUI/format.hpp"
#include "libslic3r/PresetBundle.hpp"
#include "libslic3r/Semver.hpp"
#include "libslic3r/Utils.hpp"
using namespace std::chrono;
namespace Slic3r {
wxDEFINE_EVENT(EVT_PRESET_UPDATER_STATUS_END, PresetUpdaterStatusSimpleEvent);
wxDEFINE_EVENT(EVT_PRESET_UPDATER_STATUS_PRINT, PresetUpdaterStatusMessageEvent);
wxDEFINE_EVENT(EVT_CONFIG_UPDATER_SYNC_DONE, wxCommandEvent);
PresetUpdaterWrapper::PresetUpdaterWrapper()
: m_preset_updater(std::make_unique<PresetUpdater>())
, m_preset_archive_database(std::make_unique<PresetArchiveDatabase>())
{
}
PresetUpdaterWrapper::~PresetUpdaterWrapper()
{
if (m_worker_thread.joinable()) {
if (m_ui_status)
m_ui_status->set_canceled(true);
m_worker_thread.join();
}
}
bool PresetUpdaterWrapper::wizard_sync(const PresetBundle* preset_bundle, const Semver& old_slic3r_version, wxWindow* parent, bool full_sync, const wxString& headline)
{
assert(!m_modal_thread.joinable());
// Cancel sync before starting wizard to prevent two downloads at same time.
cancel_worker_thread();
PresetUpdaterUIStatus* ui_status = new PresetUpdaterUIStatus(PresetUpdaterUIStatus::PresetUpdaterRetryPolicy::PURP_5_TRIES);
Slic3r::ScopeGuard sg([ui_status]() { delete ui_status; });
GUI::ProgressUpdaterDialog* dialog = new GUI::ProgressUpdaterDialog(ui_status, parent, headline);
ui_status->set_handler(dialog);
VendorMap vendors_copy = preset_bundle->vendors;
auto worker_body = [ui_status, this, vendors_copy, full_sync]()
{
if (!m_preset_archive_database->sync_blocking(ui_status)) {
ui_status->end();
return;
}
if (ui_status->get_canceled()) { ui_status->end(); return; }
if (full_sync) {
// Since there might be new repos, we need to sync preset updater
const SharedArchiveRepositoryVector &repos = m_preset_archive_database->get_selected_archive_repositories();
m_preset_updater->sync_blocking(vendors_copy, repos, ui_status);
if (ui_status->get_canceled()) { ui_status->end(); return; }
m_preset_updater->update_index_db();
}
ui_status->end();
};
m_modal_thread = std::thread(worker_body);
// We need to call ShowModal here instead of prompting it from event callback.
// Otherwise UI thread would freez on job_thread.join();
dialog->CenterOnParent();
dialog->ShowModal();
m_modal_thread.join();
parent->RemoveChild(dialog);
dialog->Destroy();
ui_status->set_handler(nullptr);
// Only now it is possible to work with ui_status, that was previously used in worker thread.
if (std::string s = ui_status->get_error(); !s.empty()) {
std::string err_text = GUI::format(_u8L("Failed to download %1%"), ui_status->get_target());
GUI::ErrorDialog err_msg(nullptr, err_text + "\n\n" + s, false);
err_msg.ShowModal();
return false;
}
// Should m_preset_updater->config_update run even if there is cancel?
if (ui_status->get_canceled() /*&& !full_sync*/) {
return false;
}
// Offer update installation.
if (full_sync) {
const SharedArchiveRepositoryVector &repos = m_preset_archive_database->get_selected_archive_repositories();
m_preset_updater->config_update(old_slic3r_version, PresetUpdater::UpdateParams::SHOW_TEXT_BOX_YES_NO, repos, ui_status);
}
bool res = !ui_status->get_canceled();
return res;
}
PresetUpdater::UpdateResult PresetUpdaterWrapper::check_updates_on_user_request(const PresetBundle* preset_bundle, const Semver& old_slic3r_version, wxWindow* parent)
{
assert(!m_modal_thread.joinable());
cancel_worker_thread();
PresetUpdaterUIStatus* ui_status = new PresetUpdaterUIStatus(PresetUpdaterUIStatus::PresetUpdaterRetryPolicy::PURP_5_TRIES);
Slic3r::ScopeGuard sg([ui_status]() { delete ui_status; });
// TRN: Headline of Progress dialog
GUI::ProgressUpdaterDialog* dialog = new GUI::ProgressUpdaterDialog(ui_status, parent, _L("Checking for Configuration Updates"));
ui_status->set_handler(dialog);
VendorMap vendors_copy = preset_bundle->vendors;
std::string failed_paths;
PresetUpdater::UpdateResult updater_result = PresetUpdater::UpdateResult::R_ALL_CANCELED;
auto worker_body = [ui_status, this, vendors_copy, &failed_paths]()
{
if (!m_preset_archive_database->sync_blocking(ui_status)) {
ui_status->end();
return;
}
if (ui_status->get_canceled()) { ui_status->end(); return; }
m_preset_archive_database->extract_archives_with_check(failed_paths);
const SharedArchiveRepositoryVector &repos = m_preset_archive_database->get_selected_archive_repositories();
m_preset_updater->sync_blocking(vendors_copy, repos, ui_status);
if (ui_status->get_canceled()) { ui_status->end(); return; }
m_preset_updater->update_index_db();
ui_status->end();
};
m_modal_thread = std::thread(worker_body);
// We need to call ShowModal here instead of prompting it from event callback.
// Otherwise UI thread would freez on job_thread.join();
dialog->CenterOnParent();
dialog->ShowModal();
m_modal_thread.join();
parent->RemoveChild(dialog);
dialog->Destroy();
ui_status->set_handler(nullptr);
// Only now it is possible to work with ui_status, that was previously used in worker thread.
if (std::string s = ui_status->get_error(); !s.empty()) {
std::string err_text = GUI::format(_u8L("Failed to download %1%"), ui_status->get_target());
GUI::ErrorDialog err_msg(nullptr, s, false);
err_msg.ShowModal();
return PresetUpdater::UpdateResult::R_ALL_CANCELED;
}
if (ui_status->get_canceled()) {
return PresetUpdater::UpdateResult::R_ALL_CANCELED;
}
if (!failed_paths.empty()) {
int cnt = std::count(failed_paths.begin(), failed_paths.end(), '\n') + 1;
// TRN: %1% contains paths from which loading failed. They are separated by \n, there is no \n at the end.
failed_paths = GUI::format(_L_PLURAL("It was not possible to extract data from %1%. The source will not be updated.",
"It was not possible to extract data for following local sources. They will not be updated.\n\n %1%", cnt), failed_paths);
GUI::ErrorDialog err_msg(nullptr, failed_paths, false);
err_msg.ShowModal();
}
// preset_updater::config_update does show wxDialog
updater_result = m_preset_updater->config_update(old_slic3r_version, PresetUpdater::UpdateParams::SHOW_TEXT_BOX, m_preset_archive_database->get_selected_archive_repositories(), ui_status);
return updater_result;
}
PresetUpdater::UpdateResult PresetUpdaterWrapper::check_updates_on_startup(const Semver& old_slic3r_version)
{
if (m_modal_thread.joinable()) {
return PresetUpdater::UpdateResult::R_ALL_CANCELED;
}
PresetUpdaterUIStatus ui_status(PresetUpdaterUIStatus::PresetUpdaterRetryPolicy::PURP_NO_RETRY);
// preset_updater::config_update does show wxDialog
m_preset_updater->update_index_db();
return m_preset_updater->config_update(old_slic3r_version, PresetUpdater::UpdateParams::SHOW_NOTIFICATION, m_preset_archive_database->get_selected_archive_repositories(), &ui_status);
}
void PresetUpdaterWrapper::on_update_notification_confirm()
{
if (m_modal_thread.joinable()) {
return;
}
PresetUpdaterUIStatus ui_status(PresetUpdaterUIStatus::PresetUpdaterRetryPolicy::PURP_NO_RETRY);
// preset_updater::on_update_notification_confirm does show wxDialog
const SharedArchiveRepositoryVector &repos = m_preset_archive_database->get_selected_archive_repositories();
m_preset_updater->on_update_notification_confirm(repos, &ui_status);
}
bool PresetUpdaterWrapper::install_bundles_rsrc_or_cache_vendor(std::vector<std::string> bundles, bool snapshot/* = true*/) const
{
PresetUpdaterUIStatus ui_status(PresetUpdaterUIStatus::PresetUpdaterRetryPolicy::PURP_NO_RETRY);
const SharedArchiveRepositoryVector &repos = m_preset_archive_database->get_selected_archive_repositories();
return m_preset_updater->install_bundles_rsrc_or_cache_vendor(bundles, repos, &ui_status, snapshot);
}
void PresetUpdaterWrapper::sync_preset_updater(wxEvtHandler* end_evt_handler, const PresetBundle* preset_bundle)
{
cancel_worker_thread();
m_ui_status = new PresetUpdaterUIStatus(PresetUpdaterUIStatus::PresetUpdaterRetryPolicy::PURP_NO_RETRY);
VendorMap vendors_copy = preset_bundle->vendors;
auto worker_body = [ this, vendors_copy, end_evt_handler]()
{
const SharedArchiveRepositoryVector &repos = m_preset_archive_database->get_selected_archive_repositories();
m_preset_updater->sync_blocking(vendors_copy, repos, this->m_ui_status);
if (this->m_ui_status->get_canceled()) { return; }
wxCommandEvent* evt = new wxCommandEvent(EVT_CONFIG_UPDATER_SYNC_DONE);
wxQueueEvent(end_evt_handler, evt);
};
m_worker_thread = std::thread(worker_body);
}
void PresetUpdaterWrapper::cancel_worker_thread()
{
if (m_worker_thread.joinable()) {
if (m_ui_status) {
m_ui_status->set_canceled(true);
} else assert(true);
m_worker_thread.join();
if (m_ui_status) {
delete m_ui_status;
m_ui_status = nullptr;
}
}
}
const std::map<PresetUpdaterUIStatus::PresetUpdaterRetryPolicy, HttpRetryOpt> PresetUpdaterUIStatus::policy_map = {
{PresetUpdaterUIStatus::PresetUpdaterRetryPolicy::PURP_5_TRIES, {500ms, 5s, 4}},
{PresetUpdaterUIStatus::PresetUpdaterRetryPolicy::PURP_NO_RETRY, {0ms}}
};
PresetUpdaterUIStatus::PresetUpdaterUIStatus(PresetUpdaterUIStatus::PresetUpdaterRetryPolicy policy)
{
if (auto it = policy_map.find(policy); it != policy_map.end()) {
m_retry_policy = it->second;
} else {
assert(true);
m_retry_policy = {0ms};
}
}
bool PresetUpdaterUIStatus::on_attempt(int attempt, unsigned delay)
{
if (attempt == 1) {
// TRN: Text of progress dialog. %1% is a name of file.
set_status(GUI::format_wxstr(_L("Downloading Resources: %1%"), m_target));
} else {
// TRN: Text of progress dialog. %1% is a name of file. %2% is a number of attept.
set_status(GUI::format_wxstr(_L("Downloading Resources: %1%. Attempt %2%."), m_target, std::to_string(attempt)));
}
return get_canceled();
}
void PresetUpdaterUIStatus::set_target(const std::string& target)
{
m_target = target;
}
void PresetUpdaterUIStatus::set_status(const wxString& status)
{
if (m_evt_handler)
wxQueueEvent(m_evt_handler, new PresetUpdaterStatusMessageEvent(EVT_PRESET_UPDATER_STATUS_PRINT, status));
}
void PresetUpdaterUIStatus::end()
{
if (m_evt_handler)
wxQueueEvent(m_evt_handler, new PresetUpdaterStatusSimpleEvent(EVT_PRESET_UPDATER_STATUS_END));
}
namespace GUI {
ProgressUpdaterDialog::ProgressUpdaterDialog(PresetUpdaterUIStatus* ui_status, wxWindow* parent, const wxString first_line)
// TRN: Text of progress dialog.
:wxGenericProgressDialog(first_line, _L("Initializing"), 100, parent, wxPD_AUTO_HIDE|wxPD_APP_MODAL|wxPD_CAN_ABORT)
, PresetUpdaterUIStatusCancel(ui_status)
{
SetMinSize(wxSize(32 * wxGetApp().em_unit(),12 * wxGetApp().em_unit()));
Bind(EVT_PRESET_UPDATER_STATUS_END, &ProgressUpdaterDialog::on_end, this);
Bind(EVT_PRESET_UPDATER_STATUS_PRINT, &ProgressUpdaterDialog::on_set_status, this);
}
ProgressUpdaterDialog::~ProgressUpdaterDialog()
{
}
void ProgressUpdaterDialog::on_set_status(const PresetUpdaterStatusMessageEvent& evt)
{
if (!Pulse(evt.data)) {
set_cancel(true);
}
}
void ProgressUpdaterDialog::on_end(const PresetUpdaterStatusSimpleEvent& evt)
{
EndModal(0);
}
#if 0
CommonUpdaterDialog::CommonUpdaterDialog(PresetUpdaterUIStatus* ui_status, wxWindow* parent, const wxString first_line, int milisecond_until_cancel_shown)
: wxDialog(parent, wxID_ANY, wxEmptyString, wxDefaultPosition, wxDefaultSize, wxFRAME_FLOAT_ON_PARENT)
, PresetUpdaterUIStatusCancel(ui_status)
{
auto* headline = new wxStaticText(this, wxID_ANY, first_line, wxDefaultPosition, wxDefaultSize, wxALIGN_CENTER_HORIZONTAL);
m_status_text = new wxStaticText(this, wxID_ANY, _L("Initializing..."), wxDefaultPosition, wxDefaultSize, wxALIGN_CENTER_HORIZONTAL);
m_cancel_button = new wxButton(this, wxID_CANCEL, "Cancel");
// Layout using sizer
wxBoxSizer* hsizer = new wxBoxSizer(wxHORIZONTAL);
hsizer->Add(m_status_text, 1, wxALIGN_CENTER_VERTICAL | wxALL, 10);
hsizer->Add(m_cancel_button, 0, wxALIGN_CENTER_VERTICAL | wxALL, 10);
wxBoxSizer* vsizer = new wxBoxSizer(wxVERTICAL);
vsizer->Add(headline, 0, wxALIGN_CENTER | wxALL, 10);
vsizer->Add(hsizer, 0, wxEXPAND | wxALL, 5);
this->SetSizer(vsizer);
SetMinSize(wxSize(wxGetApp().em_unit() * 40, wxGetApp().em_unit() * 5));
m_cancel_button->Bind(wxEVT_BUTTON, [this](const wxCommandEvent& event) {
set_cancel(true);
m_status_text->SetLabel("Canceling...");
Update();
Fit();
});
if (milisecond_until_cancel_shown > 0) {
m_cancel_button->Show(false);
m_show_cancel_timer = new wxTimer(this);
Bind(wxEVT_TIMER, [this](wxTimerEvent&)
{
m_cancel_button->Show();
Layout();
Fit();
});
m_show_cancel_timer->StartOnce(milisecond_until_cancel_shown);
}
Bind(EVT_PRESET_UPDATER_STATUS_END, &CommonUpdaterDialog::on_end, this);
Bind(EVT_PRESET_UPDATER_STATUS_PRINT, &CommonUpdaterDialog::on_set_status, this);
#ifdef _WIN32
wxGetApp().UpdateDlgDarkUI(this);
#endif
Fit();
}
CommonUpdaterDialog::~CommonUpdaterDialog()
{
if (m_show_cancel_timer) {
m_show_cancel_timer->Stop();
delete m_show_cancel_timer;
}
}
void CommonUpdaterDialog::on_set_status(const PresetUpdaterStatusMessageEvent& evt)
{
m_status_text->SetLabel(evt.data);
Update();
Fit();
}
void CommonUpdaterDialog::on_end(const PresetUpdaterStatusSimpleEvent& evt)
{
EndModal(0);
}
DummyPresetUpdaterUIStatusHandler::DummyPresetUpdaterUIStatusHandler()
:wxEvtHandler()
{
Bind(EVT_PRESET_UPDATER_STATUS_END, &DummyPresetUpdaterUIStatusHandler::on_end, this);
Bind(EVT_PRESET_UPDATER_STATUS_PRINT, &DummyPresetUpdaterUIStatusHandler::on_set_status, this);
}
#endif
}
}

View File

@ -0,0 +1,215 @@
#ifndef slic3r_PresetUpdateWrapper_hpp_
#define slic3r_PresetUpdateWrapper_hpp_
#include "slic3r/GUI/PresetArchiveDatabase.hpp"
#include "slic3r/GUI/ConfigWizard.hpp"
#include "slic3r/GUI/Event.hpp"
#include "slic3r/Utils/PresetUpdater.hpp"
#include "slic3r/Utils/Http.hpp"
#include <wx/event.h>
#include <wx/dialog.h>
#include <wx/timer.h>
#include <wx/stattext.h>
#include <wx/button.h>
#include <wx/progdlg.h>
#include <memory>
#include <functional>
#include <thread>
namespace Slic3r {
using PresetUpdaterStatusSimpleEvent = GUI::SimpleEvent;
using PresetUpdaterStatusMessageEvent = GUI::Event<wxString>;
wxDECLARE_EVENT(EVT_PRESET_UPDATER_STATUS_END, PresetUpdaterStatusSimpleEvent);
wxDECLARE_EVENT(EVT_PRESET_UPDATER_STATUS_PRINT, PresetUpdaterStatusMessageEvent);
wxDECLARE_EVENT(EVT_CONFIG_UPDATER_SYNC_DONE, wxCommandEvent);
class PresetBundle;
class Semver;
// class that is passed to inner thread function.
// Each call from inner thread triggers event, that is handled by wx object on UI thread (m_evt_handler).
// Communication in opposite direction is done via pointer that should use only set_canceled. (PresetUpdaterUIStatusCancel)
class PresetUpdaterUIStatus
{
public:
enum class PresetUpdaterRetryPolicy
{
PURP_5_TRIES,
PURP_NO_RETRY,
};
// called from PresetUpdaterWrapper
PresetUpdaterUIStatus(PresetUpdaterUIStatus::PresetUpdaterRetryPolicy policy);
~PresetUpdaterUIStatus(){}
void set_handler(wxEvtHandler* evt_handler) {m_evt_handler = evt_handler;}
// called from worker thread
bool on_attempt(int attempt, unsigned delay);
void set_target(const std::string& target);
void set_status(const wxString& status);
void end();
bool get_canceled() const {return m_canceled.load(); }
HttpRetryOpt get_retry_policy() const { return m_retry_policy; }
std::string get_error() const { return m_error_msg; }
std::string get_target() const { return m_target; }
// called from PresetUpdaterUIStatusCancel (ui thread)
void set_canceled(bool val) { m_canceled.store(val); }
void set_error(const std::string& msg) { m_error_msg = msg; }
private:
wxEvtHandler* m_evt_handler {nullptr};
std::atomic<bool> m_canceled {false};
std::string m_error_msg;
std::string m_target;
HttpRetryOpt m_retry_policy;
static const std::map<PresetUpdaterUIStatus::PresetUpdaterRetryPolicy, HttpRetryOpt> policy_map;
};
// Purpose of this class:
// Serves as a hub a entering 2 classes: PresetArchiveDatabase and PresetUpdater
// PresetUpdater:
// - Does not contain outside getters / setters
// - Sync function can be run in worker thread
// ! other functions like config_update does show modal dialogs or notification and must be run in UI tread
// PresetArchiveDatabase:
// - Many functions gets or sets its inner data
// ! Including sync function
// - Does not use direct UI components f.e. dialog
// This class is accessible via wxGetApp().get_preset_updater_wrapper()
// but it should be done only in certain cases:
// 1) Sync of PresetUpdater
// - Runs on worker thread
// - Is called only during startup
// - Needs to be canceled before other operations
// - Ends by queueing EVT_CONFIG_UPDATER_SYNC_DONE
// 2) Callbacks of EVT_CONFIG_UPDATER_SYNC_DONE
// - Needs to be run in UI thread
// - Might chain (evt -> PresetUpdater function -> notification -> Plater -> PresetUpdater function -> dialog)
// 3) Check of update triggered by user
// - Runs most of the operations in own thread while having Modal Dialog for user
// - Used before and inside Config Wizard or when Check for Config Updates
// - Might use UI thread PresetUpdater functions after its thread is done
// - The inner thread is stored as m_modal_thread due to
// 4) Config Wizard run
// - Config Wizard often needs to get or set data of PresetArchiveDatabase
// - Config Wizard runs in UI thread as Modal Dialog
// Possibility of conflicts:
// 1 x 2 - No conflict due 2 being triggered only by end of 1
// 1 x 3 - No conflict due 3 calling Cancel on 1 before runing
// 1 x 4 - No conflict due 4 run after 3
// 2 x 2 - All 2 functions does create modal window and are triggered by ui buttons - ui thread might work on other events but ui should be inaccessible
// 2 x 3 - If 1 finnished (2 starts) and 3 is triggered by user - both are triggered via events
// - If order of events is event triggering 3 first and then event queued by 1 is second
// - 2 would run when inner thread of 3 changes data - Therefor functions of 2 must check if inner thread of 3 (m_modal_thread) is joinable
// 2 x 4 - No conflict due 2 and 4 run on both UI thread
// 3 x 4 - No conflict due either 3 blocking ui or even calling 4 only after it finnishes
class PresetUpdaterWrapper
{
public:
PresetUpdaterWrapper();
~PresetUpdaterWrapper();
// 1) Sync of PresetUpdater functions
// runs worker thread and leaves
void sync_preset_updater(wxEvtHandler* end_evt_handler, const PresetBundle* preset_bundle);
// 2) Callbacks of EVT_CONFIG_UPDATER_SYNC_DONE
// Runs on UI thread
PresetUpdater::UpdateResult check_updates_on_startup(const Semver& old_slic3r_version);
void on_update_notification_confirm();
// 3) Check of update triggered by user
// runs own thread but blocks until its done
bool wizard_sync(const PresetBundle* preset_bundle, const Semver& old_slic3r_version, wxWindow* parent, bool full_sync, const wxString& headline);
PresetUpdater::UpdateResult check_updates_on_user_request(const PresetBundle* preset_bundle, const Semver& old_slic3r_version, wxWindow* parent);
// 4) Config Wizard run
// These function are either const reading from m_preset_archive_database,
// Or sets inner data of m_preset_archive_database
// problem would be if at same time worker thread runs m_preset_archive_database->sync_blocking
bool is_selected_repository_by_id(const std::string& repo_id) const { return m_preset_archive_database->is_selected_repository_by_id(repo_id); }
bool is_selected_repository_by_uuid(const std::string& uuid) const { return m_preset_archive_database->is_selected_repository_by_uuid(uuid); }
SharedArchiveRepositoryVector get_all_archive_repositories() const { return m_preset_archive_database->get_all_archive_repositories(); }
SharedArchiveRepositoryVector get_selected_archive_repositories() const { return m_preset_archive_database->get_selected_archive_repositories();}
const std::map<std::string, bool>& get_selected_repositories_uuid() const { return m_preset_archive_database->get_selected_repositories_uuid(); }
bool set_selected_repositories(const std::vector<std::string>& used_uuids, std::string& msg) { return m_preset_archive_database->set_selected_repositories(used_uuids, msg); }
void set_installed_printer_repositories(const std::vector<std::string> &used_ids) { m_preset_archive_database->set_installed_printer_repositories(used_ids); }
void remove_local_archive(const std::string& uuid) { m_preset_archive_database->remove_local_archive(uuid); }
std::string add_local_archive(const boost::filesystem::path path, std::string& msg) { return m_preset_archive_database->add_local_archive(path, msg); }
bool install_bundles_rsrc_or_cache_vendor(std::vector<std::string> bundles, bool snapshot = true) const ;
private:
void cancel_worker_thread();
// Do not share these 2 out of PresetUpdaterWrapper
std::unique_ptr<PresetArchiveDatabase> m_preset_archive_database;
std::unique_ptr<PresetUpdater> m_preset_updater;
// m_worker_thread runs on background while m_modal_thread runs only when modal window exists.
std::thread m_worker_thread;
PresetUpdaterUIStatus* m_ui_status {nullptr};
std::thread m_modal_thread;
};
namespace GUI {
class PresetUpdaterUIStatusCancel
{
public:
PresetUpdaterUIStatusCancel(PresetUpdaterUIStatus* ui_status) : p_ui_status(ui_status) {}
~PresetUpdaterUIStatusCancel() {}
void set_cancel(bool c) {p_ui_status->set_canceled(c);}
private:
PresetUpdaterUIStatus* p_ui_status;
};
class ProgressUpdaterDialog : public wxGenericProgressDialog, public PresetUpdaterUIStatusCancel
{
public:
ProgressUpdaterDialog(PresetUpdaterUIStatus* ui_status, wxWindow* parent, const wxString first_line);
~ProgressUpdaterDialog();
void on_set_status(const PresetUpdaterStatusMessageEvent& evt);
void on_end(const PresetUpdaterStatusSimpleEvent& evt);
private:
};
#if 0
// basic dialog
class CommonUpdaterDialog : public wxDialog, public PresetUpdaterUIStatusCancel
{
public:
CommonUpdaterDialog(PresetUpdaterUIStatus* ui_status, wxWindow* parent, const wxString first_line, int milisecond_until_cancel_shown);
~CommonUpdaterDialog();
void on_set_status(const PresetUpdaterStatusMessageEvent& evt);
void on_end(const PresetUpdaterStatusSimpleEvent& evt);
private:
wxStaticText* m_status_text;
wxButton* m_cancel_button;
wxTimer* m_show_cancel_timer {nullptr};
};
// testing purpose dummy class
class DummyPresetUpdaterUIStatusHandler : public wxEvtHandler
{
public:
DummyPresetUpdaterUIStatusHandler();
~DummyPresetUpdaterUIStatusHandler() {}
void on_set_status(const PresetUpdaterStatusMessageEvent& evt) {}
void on_end(const PresetUpdaterStatusSimpleEvent& evt)
{
if(m_end_callback)
m_end_callback();
}
void set_end_callback(std::function<void(void)> callback) {m_end_callback = callback; }
private:
std::function<void(void)> m_end_callback;
};
#endif
} // namespace GUI
} // namespace Slic3r
#endif //slic3r_PresetUpdateWrapper_hpp_

View File

@ -23,31 +23,6 @@ namespace pt = boost::property_tree;
namespace Slic3r {
namespace
{
/*std::string escape_string(const std::string& unescaped)
{
std::string ret_val;
CURL* curl = curl_easy_init();
if (curl) {
char* decoded = curl_easy_escape(curl, unescaped.c_str(), unescaped.size());
if (decoded) {
ret_val = std::string(decoded);
curl_free(decoded);
}
curl_easy_cleanup(curl);
}
return ret_val;
}
std::string escape_path_by_element(const boost::filesystem::path& path)
{
std::string ret_val = escape_string(path.filename().string());
boost::filesystem::path parent(path.parent_path());
while (!parent.empty() && parent.string() != "/") // "/" check is for case "/file.gcode" was inserted. Then boost takes "/" as parent_path.
{
ret_val = escape_string(parent.filename().string()) + "/" + ret_val;
parent = parent.parent_path();
}
return ret_val;
}*/
boost::optional<std::string> get_error_message_from_response_body(const std::string& body)
{
@ -112,19 +87,6 @@ bool PrusaConnectNew::init_upload(PrintHostUpload upload_data, std::string& out)
const std::string upload_filename = upload_data.upload_path.filename().string();
std::string url = GUI::format("%1%/app/users/teams/%2%/uploads", get_host(), m_team_id);
std::string request_body_json = upload_data.data_json;
// GUI::format(
// "{"
// "\"filename\": \"%1%\", "
// "\"size\": %2%, "
// "\"path\": \"%3%\", "
// "\"force\": true, "
// "\"printer_uuid\": \"%4%\""
// "}"
// , upload_filename
// , file_size
// , upload_data.upload_path.generic_string()
// , m_uuid
//);
// replace plaholder filename
assert(request_body_json.find("%1%") != std::string::npos);
@ -189,20 +151,10 @@ bool PrusaConnectNew::upload(PrintHostUpload upload_data, ProgressFn progress_fn
}
const std::string name = get_name();
const std::string access_token = GUI::wxGetApp().plater()->get_user_account()->get_access_token();
// const std::string escaped_upload_path = upload_data.storage + "/" + escape_path_by_element(upload_data.upload_path.string());
// const std::string set_ready = upload_data.set_ready.empty() ? "" : "&set_ready=" + upload_data.set_ready;
// const std::string position = upload_data.position.empty() ? "" : "&position=" + upload_data.position;
// const std::string wait_until = upload_data.wait_until.empty() ? "" : "&wait_until=" + upload_data.wait_until;
const std::string url = GUI::format(
"%1%/app/teams/%2%/files/raw"
"?upload_id=%3%"
// "&force=true"
// "&printer_uuid=%4%"
// "&path=%5%"
// "%6%"
// "%7%"
// "%8%"
, get_host(), m_team_id, upload_id/*, m_uuid, escaped_upload_path, set_ready, position, wait_until*/);
, get_host(), m_team_id, upload_id);
bool res = true;