ConfigWizard: Make bundle installation more intelligent, fixes

This commit is contained in:
Vojtech Kral 2018-04-11 13:12:08 +02:00
parent aaa8f133c0
commit 31ea03feb0
11 changed files with 159 additions and 68 deletions

View File

@ -275,7 +275,7 @@ const Snapshot& SnapshotDB::take_snapshot(const AppConfig &app_config, Snapshot:
// Snapshot header. // Snapshot header.
snapshot.time_captured = Slic3r::Utils::get_current_time_utc(); snapshot.time_captured = Slic3r::Utils::get_current_time_utc();
snapshot.id = Slic3r::Utils::format_time_ISO8601Z(snapshot.time_captured); snapshot.id = Slic3r::Utils::format_time_ISO8601Z(snapshot.time_captured);
snapshot.slic3r_version_captured = *Semver::parse(SLIC3R_VERSION); snapshot.slic3r_version_captured = *Semver::parse(SLIC3R_VERSION); // XXX: have Semver Slic3r version
snapshot.comment = comment; snapshot.comment = comment;
snapshot.reason = reason; snapshot.reason = reason;
// Active presets at the time of the snapshot. // Active presets at the time of the snapshot.
@ -299,7 +299,7 @@ const Snapshot& SnapshotDB::take_snapshot(const AppConfig &app_config, Snapshot:
bundle.load_configbundle((data_dir / "vendor" / (cfg.name + ".ini")).string(), PresetBundle::LOAD_CFGBUNDLE_VENDOR_ONLY); bundle.load_configbundle((data_dir / "vendor" / (cfg.name + ".ini")).string(), PresetBundle::LOAD_CFGBUNDLE_VENDOR_ONLY);
for (const VendorProfile &vp : bundle.vendors) for (const VendorProfile &vp : bundle.vendors)
if (vp.id == cfg.name) if (vp.id == cfg.name)
cfg.version = *Semver::parse(vp.config_version); cfg.version = vp.config_version;
// Fill-in the min/max slic3r version from the config index, if possible. // Fill-in the min/max slic3r version from the config index, if possible.
try { try {
// Load the config index for the vendor. // Load the config index for the vendor.

View File

@ -8,6 +8,7 @@
#include <utility> #include <utility>
#include <assert.h> #include <assert.h>
#include <vector> #include <vector>
#include <stdexcept>
#include <boost/filesystem.hpp> #include <boost/filesystem.hpp>
#include <boost/nowide/cenv.hpp> #include <boost/nowide/cenv.hpp>
@ -228,9 +229,27 @@ bool AppConfig::version_check_enabled() const
bool AppConfig::slic3r_update_avail() const bool AppConfig::slic3r_update_avail() const
{ {
// FIXME: Update with Semver
// TODO: probably need to move semver to libslic3r
return version_check_enabled() && get("version_online") != SLIC3R_VERSION; return version_check_enabled() && get("version_online") != SLIC3R_VERSION;
} }
Semver AppConfig::get_slic3r_version() const
{
// TODO: move to Semver c-tor (???)
auto res = Semver::parse(get("version"));
if (! res) {
throw std::runtime_error(std::string("Could not parse Slic3r version string in application config."));
} else {
return *res;
}
}
void AppConfig::set_slic3r_version(const Semver &version)
{
set("version", version.to_string());
}
std::string AppConfig::config_path() std::string AppConfig::config_path()
{ {
return (boost::filesystem::path(Slic3r::data_dir()) / "slic3r.ini").make_preferred().string(); return (boost::filesystem::path(Slic3r::data_dir()) / "slic3r.ini").make_preferred().string();

View File

@ -6,6 +6,7 @@
#include <string> #include <string>
#include "libslic3r/Config.hpp" #include "libslic3r/Config.hpp"
#include "slic3r/Utils/Semver.hpp"
namespace Slic3r { namespace Slic3r {
@ -91,6 +92,8 @@ public:
// Whether the Slic3r version available online differs from this one // Whether the Slic3r version available online differs from this one
bool version_check_enabled() const; bool version_check_enabled() const;
bool slic3r_update_avail() const; bool slic3r_update_avail() const;
Semver get_slic3r_version() const;
void set_slic3r_version(const Semver &version);
// Get the default config path from Slic3r::data_dir(). // Get the default config path from Slic3r::data_dir().
static std::string config_path(); static std::string config_path();

View File

@ -15,6 +15,7 @@
#include "libslic3r/Utils.hpp" #include "libslic3r/Utils.hpp"
#include "PresetBundle.hpp" #include "PresetBundle.hpp"
#include "GUI.hpp" #include "GUI.hpp"
#include "slic3r/Utils/PresetUpdater.hpp"
namespace fs = boost::filesystem; namespace fs = boost::filesystem;
@ -699,12 +700,8 @@ ConfigWizard::~ConfigWizard() {}
bool ConfigWizard::run(wxWindow *parent, PresetBundle *preset_bundle) bool ConfigWizard::run(wxWindow *parent, PresetBundle *preset_bundle)
{ {
const auto profiles_dir = fs::path(resources_dir()) / "profiles"; // FIXME: this should be done always at app startup
for (fs::directory_iterator it(profiles_dir); it != fs::directory_iterator(); ++it) { PresetUpdater::init_vendors();
if (it->path().extension() == ".ini") {
PresetBundle::install_vendor_configbundle(it->path());
}
}
ConfigWizard wizard(parent); ConfigWizard wizard(parent);
if (wizard.ShowModal() == wxID_OK) { if (wizard.ShowModal() == wxID_OK) {

View File

@ -6,6 +6,7 @@
#include <fstream> #include <fstream>
#include <boost/filesystem.hpp> #include <boost/filesystem.hpp>
#include <boost/filesystem/fstream.hpp>
#include <boost/algorithm/string/predicate.hpp> #include <boost/algorithm/string/predicate.hpp>
#include <boost/nowide/cenv.hpp> #include <boost/nowide/cenv.hpp>
@ -24,14 +25,16 @@
#include "../../libslic3r/Utils.hpp" #include "../../libslic3r/Utils.hpp"
#include "../../libslic3r/PlaceholderParser.hpp" #include "../../libslic3r/PlaceholderParser.hpp"
using boost::property_tree::ptree;
namespace Slic3r { namespace Slic3r {
ConfigFileType guess_config_file_type(const boost::property_tree::ptree &tree) ConfigFileType guess_config_file_type(const ptree &tree)
{ {
size_t app_config = 0; size_t app_config = 0;
size_t bundle = 0; size_t bundle = 0;
size_t config = 0; size_t config = 0;
for (const boost::property_tree::ptree::value_type &v : tree) { for (const ptree::value_type &v : tree) {
if (v.second.empty()) { if (v.second.empty()) {
if (v.first == "background_processing" || if (v.first == "background_processing" ||
v.first == "last_output_path" || v.first == "last_output_path" ||
@ -59,6 +62,74 @@ ConfigFileType guess_config_file_type(const boost::property_tree::ptree &tree)
(bundle > config) ? CONFIG_FILE_TYPE_CONFIG_BUNDLE : CONFIG_FILE_TYPE_CONFIG; (bundle > config) ? CONFIG_FILE_TYPE_CONFIG_BUNDLE : CONFIG_FILE_TYPE_CONFIG;
} }
VendorProfile VendorProfile::from_ini(const boost::filesystem::path &path, bool load_all)
{
ptree tree;
boost::filesystem::ifstream ifs(path);
boost::property_tree::read_ini(ifs, tree);
return VendorProfile::from_ini(tree, path, load_all);
}
VendorProfile VendorProfile::from_ini(const ptree &tree, const boost::filesystem::path &path, bool load_all)
{
static const std::string printer_model_key = "printer_model:";
const std::string id = path.stem().string();
VendorProfile res(id);
auto get_or_throw = [&](const ptree &tree, const std::string &key) -> ptree::const_assoc_iterator
{
auto res = tree.find(key);
if (res == tree.not_found()) {
throw std::runtime_error((boost::format("Vendor Config Bundle `%1%` is not valid: Missing secion or key: `%2%`.") % id % key).str());
}
return res;
};
const auto &vendor_section = get_or_throw(tree, "vendor")->second;
res.name = get_or_throw(vendor_section, "name")->second.data();
auto config_version_str = get_or_throw(vendor_section, "config_version")->second.data();
auto config_version = Semver::parse(config_version_str);
if (! config_version) {
throw std::runtime_error((boost::format("Vendor Config Bundle `%1%` is not valid: Cannot parse config_version: `%2%`.") % id % config_version_str).str());
} else {
res.config_version = std::move(*config_version);
}
auto config_update_url = vendor_section.find("config_update_url");
if (config_update_url != vendor_section.not_found()) {
res.config_update_url = config_update_url->second.data();
}
if (! load_all) {
return res;
}
for (auto &section : tree) {
if (boost::starts_with(section.first, printer_model_key)) {
VendorProfile::PrinterModel model;
model.id = section.first.substr(printer_model_key.size());
model.name = section.second.get<std::string>("name", model.id);
section.second.get<std::string>("variants", "");
std::vector<std::string> variants;
if (Slic3r::unescape_strings_cstyle(section.second.get<std::string>("variants", ""), variants)) {
for (const std::string &variant_name : variants) {
if (model.variant(variant_name) == nullptr)
model.variants.emplace_back(VendorProfile::PrinterVariant(variant_name));
}
} else {
// Log error? // XXX
}
if (! model.id.empty() && ! model.variants.empty())
res.models.push_back(std::move(model));
}
}
return res;
}
// Suffix to be added to a modified preset name in the combo box. // Suffix to be added to a modified preset name in the combo box.
static std::string g_suffix_modified = " (modified)"; static std::string g_suffix_modified = " (modified)";
const std::string& Preset::suffix_modified() const std::string& Preset::suffix_modified()

View File

@ -3,8 +3,12 @@
#include <deque> #include <deque>
#include <boost/filesystem/path.hpp>
#include <boost/property_tree/ptree_fwd.hpp>
#include "../../libslic3r/libslic3r.h" #include "../../libslic3r/libslic3r.h"
#include "../../libslic3r/PrintConfig.hpp" #include "../../libslic3r/PrintConfig.hpp"
#include "slic3r/Utils/Semver.hpp"
class wxBitmap; class wxBitmap;
class wxChoice; class wxChoice;
@ -30,7 +34,7 @@ class VendorProfile
public: public:
std::string name; std::string name;
std::string id; std::string id;
std::string config_version; Semver config_version;
std::string config_update_url; std::string config_update_url;
struct PrinterVariant { struct PrinterVariant {
@ -54,7 +58,10 @@ public:
}; };
std::vector<PrinterModel> models; std::vector<PrinterModel> models;
VendorProfile(std::string id) : id(std::move(id)) {} VendorProfile(std::string id) : id(std::move(id)), config_version(0, 0, 0) {}
static VendorProfile from_ini(const boost::filesystem::path &path, bool load_all=true);
static VendorProfile from_ini(const boost::property_tree::ptree &tree, const boost::filesystem::path &path, bool load_all=true);
size_t num_variants() const { size_t n = 0; for (auto &model : models) n += model.variants.size(); return n; } size_t num_variants() const { size_t n = 0; for (auto &model : models) n += model.variants.size(); return n; }

View File

@ -105,7 +105,7 @@ void PresetBundle::setup_directories()
std::initializer_list<boost::filesystem::path> paths = { std::initializer_list<boost::filesystem::path> paths = {
data_dir, data_dir,
data_dir / "vendor", data_dir / "vendor",
data_dir / "cache", // TODO: rename as vendor-cache? (Check usage elsewhere!) data_dir / "cache",
#ifdef SLIC3R_PROFILE_USE_PRESETS_SUBDIR #ifdef SLIC3R_PROFILE_USE_PRESETS_SUBDIR
// Store the print/filament/printer presets into a "presets" directory. // Store the print/filament/printer presets into a "presets" directory.
data_dir / "presets", data_dir / "presets",
@ -672,42 +672,6 @@ static void flatten_configbundle_hierarchy(boost::property_tree::ptree &tree)
flatten_configbundle_hierarchy(tree, "printer"); flatten_configbundle_hierarchy(tree, "printer");
} }
static void load_vendor_profile(const boost::property_tree::ptree &tree, VendorProfile &vendor_profile)
{
const std::string printer_model_key = "printer_model:";
for (auto &section : tree) {
if (section.first == "vendor") {
// Load the names of the active presets.
for (auto &kvp : section.second) {
if (kvp.first == "name")
vendor_profile.name = kvp.second.data();
else if (kvp.first == "id")
vendor_profile.id = kvp.second.data();
else if (kvp.first == "config_version")
vendor_profile.config_version = kvp.second.data();
else if (kvp.first == "config_update_url")
vendor_profile.config_update_url = kvp.second.data();
}
} else if (boost::starts_with(section.first, printer_model_key)) {
VendorProfile::PrinterModel model;
model.id = section.first.substr(printer_model_key.size());
model.name = section.second.get<std::string>("name", model.id);
section.second.get<std::string>("variants", "");
std::vector<std::string> variants;
if (Slic3r::unescape_strings_cstyle(section.second.get<std::string>("variants", ""), variants)) {
for (const std::string &variant_name : variants) {
if (model.variant(variant_name) == nullptr)
model.variants.emplace_back(VendorProfile::PrinterVariant(variant_name));
}
} else {
// Log error?
}
if (! model.id.empty() && ! model.variants.empty())
vendor_profile.models.push_back(std::move(model));
}
}
}
// Load a config bundle file, into presets and store the loaded presets into separate files // Load a config bundle file, into presets and store the loaded presets into separate files
// of the local configuration directory. // of the local configuration directory.
void PresetBundle::install_vendor_configbundle(const std::string &src_path0) void PresetBundle::install_vendor_configbundle(const std::string &src_path0)
@ -738,10 +702,7 @@ size_t PresetBundle::load_configbundle(const std::string &path, unsigned int fla
const VendorProfile *vendor_profile = nullptr; const VendorProfile *vendor_profile = nullptr;
if (flags & (LOAD_CFGBNDLE_SYSTEM | LOAD_CFGBUNDLE_VENDOR_ONLY)) { if (flags & (LOAD_CFGBNDLE_SYSTEM | LOAD_CFGBUNDLE_VENDOR_ONLY)) {
boost::filesystem::path fspath(path); boost::filesystem::path fspath(path);
VendorProfile vp(fspath.stem().string()); auto vp = VendorProfile::from_ini(tree, fspath.stem().string());
load_vendor_profile(tree, vp);
if (vp.name.empty())
throw std::runtime_error(std::string("Vendor Config Bundle is not valid: Missing vendor name key."));
if (vp.num_variants() == 0) if (vp.num_variants() == 0)
return 0; return 0;
vendor_profile = &(*this->vendors.insert(vp).first); vendor_profile = &(*this->vendors.insert(vp).first);

View File

@ -1,9 +1,8 @@
#include "PresetUpdater.hpp" #include "PresetUpdater.hpp"
#include <iostream> // XXX
#include <thread> #include <thread>
#include <boost/algorithm/string.hpp> #include <boost/algorithm/string.hpp>
#include <boost/filesystem/path.hpp> #include <boost/filesystem.hpp>
#include <boost/filesystem/fstream.hpp> #include <boost/filesystem/fstream.hpp>
#include <wx/app.h> #include <wx/app.h>
@ -22,6 +21,7 @@ namespace Slic3r {
// TODO: proper URL // TODO: proper URL
// TODO: Actually, use index
static const std::string SLIC3R_VERSION_URL = "https://gist.githubusercontent.com/vojtechkral/4d8fd4a3b8699a01ec892c264178461c/raw/e9187c3e15ceaf1a90f29b7c43cf3ccc746140f0/slic3rPE.version"; static const std::string SLIC3R_VERSION_URL = "https://gist.githubusercontent.com/vojtechkral/4d8fd4a3b8699a01ec892c264178461c/raw/e9187c3e15ceaf1a90f29b7c43cf3ccc746140f0/slic3rPE.version";
enum { enum {
SLIC3R_VERSION_BODY_MAX = 256, SLIC3R_VERSION_BODY_MAX = 256,
@ -52,8 +52,6 @@ PresetUpdater::priv::priv(int event) :
void PresetUpdater::priv::download(const std::set<VendorProfile> vendors) const void PresetUpdater::priv::download(const std::set<VendorProfile> vendors) const
{ {
std::cerr << "PresetUpdater::priv::download()" << std::endl;
if (!version_check) { return; } if (!version_check) { return; }
// Download current Slic3r version // Download current Slic3r version
@ -64,7 +62,6 @@ void PresetUpdater::priv::download(const std::set<VendorProfile> vendors) const
}) })
.on_complete([&](std::string body, unsigned http_status) { .on_complete([&](std::string body, unsigned http_status) {
boost::trim(body); boost::trim(body);
std::cerr << "Got version: " << http_status << ", body: \"" << body << '"' << std::endl;
wxCommandEvent* evt = new wxCommandEvent(version_online_event); wxCommandEvent* evt = new wxCommandEvent(version_online_event);
evt->SetString(body); evt->SetString(body);
GUI::get_app()->QueueEvent(evt); GUI::get_app()->QueueEvent(evt);
@ -74,25 +71,19 @@ void PresetUpdater::priv::download(const std::set<VendorProfile> vendors) const
if (!preset_update) { return; } if (!preset_update) { return; }
// Donwload vendor preset bundles // Donwload vendor preset bundles
std::cerr << "Bundle vendors: " << vendors.size() << std::endl;
for (const auto &vendor : vendors) { for (const auto &vendor : vendors) {
std::cerr << "vendor: " << vendor.name << std::endl;
std::cerr << " URL: " << vendor.config_update_url << std::endl;
if (cancel) { return; } if (cancel) { return; }
// TODO: Proper caching // TODO: Proper caching
auto target_path = cache_path / vendor.id; auto target_path = cache_path / vendor.id;
target_path += ".ini"; target_path += ".ini";
std::cerr << "target_path: " << target_path << std::endl;
Http::get(vendor.config_update_url) Http::get(vendor.config_update_url)
.on_progress([this](Http::Progress, bool &cancel) { .on_progress([this](Http::Progress, bool &cancel) {
cancel = this->cancel; cancel = this->cancel;
}) })
.on_complete([&](std::string body, unsigned http_status) { .on_complete([&](std::string body, unsigned http_status) {
std::cerr << "Got ini: " << http_status << ", body: " << body.size() << std::endl;
fs::fstream file(target_path, std::ios::out | std::ios::binary | std::ios::trunc); fs::fstream file(target_path, std::ios::out | std::ios::binary | std::ios::trunc);
file.write(body.c_str(), body.size()); file.write(body.c_str(), body.size());
}) })
@ -121,7 +112,6 @@ PresetUpdater::~PresetUpdater()
void PresetUpdater::download(PresetBundle *preset_bundle) void PresetUpdater::download(PresetBundle *preset_bundle)
{ {
std::cerr << "PresetUpdater::download()" << std::endl;
// Copy the whole vendors data for use in the background thread // Copy the whole vendors data for use in the background thread
// Unfortunatelly as of C++11, it needs to be copied again // Unfortunatelly as of C++11, it needs to be copied again
@ -133,5 +123,23 @@ void PresetUpdater::download(PresetBundle *preset_bundle)
})); }));
} }
void PresetUpdater::init_vendors()
{
const auto vendors_rources = fs::path(resources_dir()) / "profiles";
const auto vendors_data = fs::path(Slic3r::data_dir()) / "vendor";
for (fs::directory_iterator it(vendors_rources); it != fs::directory_iterator(); ++it) {
if (it->path().extension() == ".ini") {
auto vp = VendorProfile::from_ini(it->path(), false);
const auto path_in_data = vendors_data / it->path().filename();
if (! fs::exists(path_in_data) || VendorProfile::from_ini(path_in_data, false).config_version < vp.config_version) {
// FIXME: update vendor bundle properly when snapshotting is ready
PresetBundle::install_vendor_configbundle(it->path());
}
}
}
}
} }

View File

@ -19,7 +19,9 @@ public:
PresetUpdater &operator=(const PresetUpdater &) = delete; PresetUpdater &operator=(const PresetUpdater &) = delete;
~PresetUpdater(); ~PresetUpdater();
void download(PresetBundle *preset_bundle); void download(PresetBundle *preset_bundle); // XXX
static void init_vendors();
private: private:
struct priv; struct priv;
std::unique_ptr<priv> p; std::unique_ptr<priv> p;

View File

@ -3,6 +3,7 @@
#include <string> #include <string>
#include <cstring> #include <cstring>
#include <ostream>
#include <boost/optional.hpp> #include <boost/optional.hpp>
#include <boost/format.hpp> #include <boost/format.hpp>
@ -10,6 +11,7 @@
namespace Slic3r { namespace Slic3r {
// FIXME:: operators=: leak, return
class Semver class Semver
{ {
@ -18,9 +20,22 @@ public:
struct Minor { const int i; Minor(int i) : i(i) {} }; struct Minor { const int i; Minor(int i) : i(i) {} };
struct Patch { const int i; Patch(int i) : i(i) {} }; struct Patch { const int i; Patch(int i) : i(i) {} };
Semver(int major, int minor, int patch,
boost::optional<std::string> metadata = boost::none,
boost::optional<std::string> prerelease = boost::none)
{
ver.major = major;
ver.minor = minor;
ver.patch = patch;
ver.metadata = metadata ? std::strcpy(ver.metadata, metadata->c_str()) : nullptr;
ver.prerelease = prerelease ? std::strcpy(ver.prerelease, prerelease->c_str()) : nullptr;
}
// TODO: throwing ctor ???
static boost::optional<Semver> parse(const std::string &str) static boost::optional<Semver> parse(const std::string &str)
{ {
semver_t ver; semver_t ver = semver_zero();
if (::semver_parse(str.c_str(), &ver) == 0) { if (::semver_parse(str.c_str(), &ver) == 0) {
return Semver(ver); return Semver(ver);
} else { } else {
@ -121,6 +136,8 @@ private:
semver_t ver; semver_t ver;
Semver(semver_t ver) : ver(ver) {} Semver(semver_t ver) : ver(ver) {}
static semver_t semver_zero() { return { 0, 0, 0, nullptr, nullptr }; }
}; };

View File

@ -61,7 +61,13 @@ bool check_unsaved_changes()
%code%{ RETVAL=Slic3r::GUI::check_unsaved_changes(); %}; %code%{ RETVAL=Slic3r::GUI::check_unsaved_changes(); %};
bool config_wizard(int fresh_start) bool config_wizard(int fresh_start)
%code%{ RETVAL=Slic3r::GUI::config_wizard(fresh_start != 0); %}; %code%{
try {
RETVAL = Slic3r::GUI::config_wizard(fresh_start != 0);
} catch (std::exception& e) {
croak("%s\n", e.what());
}
%};
void open_preferences_dialog(int preferences_event) void open_preferences_dialog(int preferences_event)
%code%{ Slic3r::GUI::open_preferences_dialog(preferences_event); %}; %code%{ Slic3r::GUI::open_preferences_dialog(preferences_event); %};