2018-04-06 14:49:33 +00:00
|
|
|
#include "Snapshot.hpp"
|
|
|
|
#include "../GUI/AppConfig.hpp"
|
2018-04-11 10:21:15 +00:00
|
|
|
#include "../GUI/PresetBundle.hpp"
|
2018-04-06 14:49:33 +00:00
|
|
|
#include "../Utils/Time.hpp"
|
|
|
|
|
|
|
|
#include <time.h>
|
|
|
|
|
|
|
|
#include <boost/algorithm/string/predicate.hpp>
|
|
|
|
#include <boost/algorithm/string/trim.hpp>
|
2018-04-20 08:26:23 +00:00
|
|
|
#include <boost/nowide/cstdio.hpp>
|
2018-04-06 14:49:33 +00:00
|
|
|
#include <boost/nowide/fstream.hpp>
|
|
|
|
#include <boost/property_tree/ini_parser.hpp>
|
|
|
|
#include <boost/property_tree/ptree.hpp>
|
|
|
|
|
2018-12-06 11:52:28 +00:00
|
|
|
#include "libslic3r/libslic3r.h"
|
|
|
|
#include "libslic3r/Config.hpp"
|
|
|
|
#include "libslic3r/FileParserError.hpp"
|
|
|
|
#include "libslic3r/Utils.hpp"
|
2018-04-06 14:49:33 +00:00
|
|
|
|
|
|
|
#define SLIC3R_SNAPSHOTS_DIR "snapshots"
|
|
|
|
#define SLIC3R_SNAPSHOT_FILE "snapshot.ini"
|
|
|
|
|
|
|
|
namespace Slic3r {
|
|
|
|
namespace GUI {
|
|
|
|
namespace Config {
|
|
|
|
|
|
|
|
void Snapshot::clear()
|
|
|
|
{
|
|
|
|
this->id.clear();
|
|
|
|
this->time_captured = 0;
|
|
|
|
this->slic3r_version_captured = Semver::invalid();
|
|
|
|
this->comment.clear();
|
|
|
|
this->reason = SNAPSHOT_UNKNOWN;
|
|
|
|
this->print.clear();
|
|
|
|
this->filaments.clear();
|
|
|
|
this->printer.clear();
|
|
|
|
}
|
|
|
|
|
|
|
|
void Snapshot::load_ini(const std::string &path)
|
|
|
|
{
|
|
|
|
this->clear();
|
|
|
|
|
|
|
|
auto throw_on_parse_error = [&path](const std::string &msg) {
|
|
|
|
throw file_parser_error(std::string("Failed loading the snapshot file. Reason: ") + msg, path);
|
|
|
|
};
|
|
|
|
|
|
|
|
// Load the snapshot.ini file.
|
|
|
|
boost::property_tree::ptree tree;
|
|
|
|
try {
|
|
|
|
boost::nowide::ifstream ifs(path);
|
|
|
|
boost::property_tree::read_ini(ifs, tree);
|
|
|
|
} catch (const std::ifstream::failure &err) {
|
|
|
|
throw file_parser_error(std::string("The snapshot file cannot be loaded. Reason: ") + err.what(), path);
|
|
|
|
} catch (const std::runtime_error &err) {
|
|
|
|
throw_on_parse_error(err.what());
|
|
|
|
}
|
|
|
|
|
|
|
|
// Parse snapshot.ini
|
|
|
|
std::string group_name_vendor = "Vendor:";
|
|
|
|
std::string key_filament = "filament";
|
2018-04-11 13:17:41 +00:00
|
|
|
std::string key_prefix_model = "model_";
|
2018-04-06 14:49:33 +00:00
|
|
|
for (auto §ion : tree) {
|
|
|
|
if (section.first == "snapshot") {
|
|
|
|
// Parse the common section.
|
|
|
|
for (auto &kvp : section.second) {
|
|
|
|
if (kvp.first == "id")
|
|
|
|
this->id = kvp.second.data();
|
|
|
|
else if (kvp.first == "time_captured") {
|
|
|
|
this->time_captured = Slic3r::Utils::parse_time_ISO8601Z(kvp.second.data());
|
|
|
|
if (this->time_captured == (time_t)-1)
|
|
|
|
throw_on_parse_error("invalid timestamp");
|
|
|
|
} else if (kvp.first == "slic3r_version_captured") {
|
|
|
|
auto semver = Semver::parse(kvp.second.data());
|
|
|
|
if (! semver)
|
|
|
|
throw_on_parse_error("invalid slic3r_version_captured semver");
|
|
|
|
this->slic3r_version_captured = *semver;
|
|
|
|
} else if (kvp.first == "comment") {
|
|
|
|
this->comment = kvp.second.data();
|
|
|
|
} else if (kvp.first == "reason") {
|
|
|
|
std::string rsn = kvp.second.data();
|
|
|
|
if (rsn == "upgrade")
|
|
|
|
this->reason = SNAPSHOT_UPGRADE;
|
|
|
|
else if (rsn == "downgrade")
|
|
|
|
this->reason = SNAPSHOT_DOWNGRADE;
|
2018-04-20 08:26:23 +00:00
|
|
|
else if (rsn == "before_rollback")
|
|
|
|
this->reason = SNAPSHOT_BEFORE_ROLLBACK;
|
2018-04-06 14:49:33 +00:00
|
|
|
else if (rsn == "user")
|
|
|
|
this->reason = SNAPSHOT_USER;
|
|
|
|
else
|
|
|
|
this->reason = SNAPSHOT_UNKNOWN;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else if (section.first == "presets") {
|
|
|
|
// Load the names of the active presets.
|
|
|
|
for (auto &kvp : section.second) {
|
|
|
|
if (kvp.first == "print") {
|
|
|
|
this->print = kvp.second.data();
|
|
|
|
} else if (boost::starts_with(kvp.first, "filament")) {
|
|
|
|
int idx = 0;
|
|
|
|
if (kvp.first == "filament" || sscanf(kvp.first.c_str(), "filament_%d", &idx) == 1) {
|
|
|
|
if (int(this->filaments.size()) <= idx)
|
|
|
|
this->filaments.resize(idx + 1, std::string());
|
|
|
|
this->filaments[idx] = kvp.second.data();
|
|
|
|
}
|
|
|
|
} else if (kvp.first == "printer") {
|
|
|
|
this->printer = kvp.second.data();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else if (boost::starts_with(section.first, group_name_vendor) && section.first.size() > group_name_vendor.size()) {
|
|
|
|
// Vendor specific section.
|
|
|
|
VendorConfig vc;
|
|
|
|
vc.name = section.first.substr(group_name_vendor.size());
|
|
|
|
for (auto &kvp : section.second) {
|
2018-04-11 13:17:41 +00:00
|
|
|
if (kvp.first == "version" || kvp.first == "min_slic3r_version" || kvp.first == "max_slic3r_version") {
|
2018-04-06 14:49:33 +00:00
|
|
|
// Version of the vendor specific config bundle bundled with this snapshot.
|
|
|
|
auto semver = Semver::parse(kvp.second.data());
|
|
|
|
if (! semver)
|
|
|
|
throw_on_parse_error("invalid " + kvp.first + " format for " + section.first);
|
|
|
|
if (kvp.first == "version")
|
2018-04-25 11:44:06 +00:00
|
|
|
vc.version.config_version = *semver;
|
2018-04-06 14:49:33 +00:00
|
|
|
else if (kvp.first == "min_slic3r_version")
|
2018-04-25 11:44:06 +00:00
|
|
|
vc.version.min_slic3r_version = *semver;
|
2018-04-06 14:49:33 +00:00
|
|
|
else
|
2018-04-25 11:44:06 +00:00
|
|
|
vc.version.max_slic3r_version = *semver;
|
2018-04-11 13:17:41 +00:00
|
|
|
} else if (boost::starts_with(kvp.first, key_prefix_model) && kvp.first.size() > key_prefix_model.size()) {
|
|
|
|
// Parse the printer variants installed for the current model.
|
|
|
|
auto &set_variants = vc.models_variants_installed[kvp.first.substr(key_prefix_model.size())];
|
|
|
|
std::vector<std::string> variants;
|
|
|
|
if (unescape_strings_cstyle(kvp.second.data(), variants))
|
|
|
|
for (auto &variant : variants)
|
|
|
|
set_variants.insert(std::move(variant));
|
|
|
|
}
|
2018-04-06 14:49:33 +00:00
|
|
|
}
|
2018-04-11 13:17:41 +00:00
|
|
|
this->vendor_configs.emplace_back(std::move(vc));
|
2018-04-06 14:49:33 +00:00
|
|
|
}
|
|
|
|
}
|
2018-04-20 08:26:23 +00:00
|
|
|
// Sort the vendors lexicographically.
|
|
|
|
std::sort(this->vendor_configs.begin(), this->vendor_configs.begin(),
|
|
|
|
[](const VendorConfig &cfg1, const VendorConfig &cfg2) { return cfg1.name < cfg2.name; });
|
2018-04-06 14:49:33 +00:00
|
|
|
}
|
|
|
|
|
2018-04-12 09:24:03 +00:00
|
|
|
static std::string reason_string(const Snapshot::Reason reason)
|
|
|
|
{
|
|
|
|
switch (reason) {
|
|
|
|
case Snapshot::SNAPSHOT_UPGRADE:
|
|
|
|
return "upgrade";
|
|
|
|
case Snapshot::SNAPSHOT_DOWNGRADE:
|
|
|
|
return "downgrade";
|
2018-04-20 08:26:23 +00:00
|
|
|
case Snapshot::SNAPSHOT_BEFORE_ROLLBACK:
|
|
|
|
return "before_rollback";
|
2018-04-12 09:24:03 +00:00
|
|
|
case Snapshot::SNAPSHOT_USER:
|
|
|
|
return "user";
|
|
|
|
case Snapshot::SNAPSHOT_UNKNOWN:
|
|
|
|
default:
|
|
|
|
return "unknown";
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-04-06 14:49:33 +00:00
|
|
|
void Snapshot::save_ini(const std::string &path)
|
|
|
|
{
|
|
|
|
boost::nowide::ofstream c;
|
|
|
|
c.open(path, std::ios::out | std::ios::trunc);
|
|
|
|
c << "# " << Slic3r::header_slic3r_generated() << std::endl;
|
|
|
|
|
|
|
|
// Export the common "snapshot".
|
|
|
|
c << std::endl << "[snapshot]" << std::endl;
|
|
|
|
c << "id = " << this->id << std::endl;
|
|
|
|
c << "time_captured = " << Slic3r::Utils::format_time_ISO8601Z(this->time_captured) << std::endl;
|
|
|
|
c << "slic3r_version_captured = " << this->slic3r_version_captured.to_string() << std::endl;
|
|
|
|
c << "comment = " << this->comment << std::endl;
|
2018-04-12 09:24:03 +00:00
|
|
|
c << "reason = " << reason_string(this->reason) << std::endl;
|
2018-04-06 14:49:33 +00:00
|
|
|
|
|
|
|
// Export the active presets at the time of the snapshot.
|
|
|
|
c << std::endl << "[presets]" << std::endl;
|
|
|
|
c << "print = " << this->print << std::endl;
|
|
|
|
c << "filament = " << this->filaments.front() << std::endl;
|
|
|
|
for (size_t i = 1; i < this->filaments.size(); ++ i)
|
|
|
|
c << "filament_" << std::to_string(i) << " = " << this->filaments[i] << std::endl;
|
|
|
|
c << "printer = " << this->printer << std::endl;
|
|
|
|
|
|
|
|
// Export the vendor configs.
|
|
|
|
for (const VendorConfig &vc : this->vendor_configs) {
|
|
|
|
c << std::endl << "[Vendor:" << vc.name << "]" << std::endl;
|
2018-04-25 11:44:06 +00:00
|
|
|
c << "version = " << vc.version.config_version.to_string() << std::endl;
|
|
|
|
c << "min_slic3r_version = " << vc.version.min_slic3r_version.to_string() << std::endl;
|
|
|
|
c << "max_slic3r_version = " << vc.version.max_slic3r_version.to_string() << std::endl;
|
2018-04-11 13:17:41 +00:00
|
|
|
// Export installed printer models and their variants.
|
|
|
|
for (const auto &model : vc.models_variants_installed) {
|
|
|
|
if (model.second.size() == 0)
|
|
|
|
continue;
|
|
|
|
const std::vector<std::string> variants(model.second.begin(), model.second.end());
|
|
|
|
const auto escaped = escape_strings_cstyle(variants);
|
|
|
|
c << "model_" << model.first << " = " << escaped << std::endl;
|
|
|
|
}
|
2018-04-06 14:49:33 +00:00
|
|
|
}
|
|
|
|
c.close();
|
|
|
|
}
|
|
|
|
|
|
|
|
void Snapshot::export_selections(AppConfig &config) const
|
|
|
|
{
|
|
|
|
assert(filaments.size() >= 1);
|
|
|
|
config.clear_section("presets");
|
|
|
|
config.set("presets", "print", print);
|
|
|
|
config.set("presets", "filament", filaments.front());
|
|
|
|
for (int i = 1; i < filaments.size(); ++i) {
|
|
|
|
char name[64];
|
|
|
|
sprintf(name, "filament_%d", i);
|
|
|
|
config.set("presets", name, filaments[i]);
|
|
|
|
}
|
|
|
|
config.set("presets", "printer", printer);
|
|
|
|
}
|
|
|
|
|
2018-04-11 10:21:15 +00:00
|
|
|
void Snapshot::export_vendor_configs(AppConfig &config) const
|
|
|
|
{
|
|
|
|
std::map<std::string, std::map<std::string, std::set<std::string>>> vendors;
|
|
|
|
for (const VendorConfig &vc : vendor_configs)
|
|
|
|
vendors[vc.name] = vc.models_variants_installed;
|
|
|
|
config.set_vendors(std::move(vendors));
|
|
|
|
}
|
|
|
|
|
2018-04-20 08:26:23 +00:00
|
|
|
// Perform a deep compare of the active print / filament / printer / vendor directories.
|
|
|
|
// Return true if the content of the current print / filament / printer / vendor directories
|
|
|
|
// matches the state stored in this snapshot.
|
|
|
|
bool Snapshot::equal_to_active(const AppConfig &app_config) const
|
|
|
|
{
|
|
|
|
// 1) Check, whether this snapshot contains the same set of active vendors, printer models and variants
|
|
|
|
// as app_config.
|
|
|
|
{
|
|
|
|
std::set<std::string> matched;
|
|
|
|
for (const VendorConfig &vc : this->vendor_configs) {
|
|
|
|
auto it_vendor_models_variants = app_config.vendors().find(vc.name);
|
|
|
|
if (it_vendor_models_variants == app_config.vendors().end() ||
|
|
|
|
it_vendor_models_variants->second != vc.models_variants_installed)
|
|
|
|
// There are more vendors enabled in the snapshot than currently installed.
|
|
|
|
return false;
|
|
|
|
matched.insert(vc.name);
|
|
|
|
}
|
|
|
|
for (const std::pair<std::string, std::map<std::string, std::set<std::string>>> &v : app_config.vendors())
|
|
|
|
if (matched.find(v.first) == matched.end() && ! v.second.empty())
|
|
|
|
// There are more vendors currently installed than enabled in the snapshot.
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
// 2) Check, whether this snapshot references the same set of ini files as the current state.
|
|
|
|
boost::filesystem::path data_dir = boost::filesystem::path(Slic3r::data_dir());
|
|
|
|
boost::filesystem::path snapshot_dir = boost::filesystem::path(Slic3r::data_dir()) / SLIC3R_SNAPSHOTS_DIR / this->id;
|
|
|
|
for (const char *subdir : { "print", "filament", "printer", "vendor" }) {
|
|
|
|
boost::filesystem::path path1 = data_dir / subdir;
|
|
|
|
boost::filesystem::path path2 = snapshot_dir / subdir;
|
|
|
|
std::vector<std::string> files1, files2;
|
|
|
|
for (auto &dir_entry : boost::filesystem::directory_iterator(path1))
|
2019-02-03 14:30:37 +00:00
|
|
|
if (Slic3r::is_ini_file(dir_entry))
|
2018-04-20 08:26:23 +00:00
|
|
|
files1.emplace_back(dir_entry.path().filename().string());
|
|
|
|
for (auto &dir_entry : boost::filesystem::directory_iterator(path2))
|
2019-02-03 14:30:37 +00:00
|
|
|
if (Slic3r::is_ini_file(dir_entry))
|
2018-04-20 08:26:23 +00:00
|
|
|
files2.emplace_back(dir_entry.path().filename().string());
|
|
|
|
std::sort(files1.begin(), files1.end());
|
|
|
|
std::sort(files2.begin(), files2.end());
|
|
|
|
if (files1 != files2)
|
|
|
|
return false;
|
|
|
|
for (const std::string &filename : files1) {
|
|
|
|
FILE *f1 = boost::nowide::fopen((path1 / filename).string().c_str(), "rb");
|
|
|
|
FILE *f2 = boost::nowide::fopen((path2 / filename).string().c_str(), "rb");
|
|
|
|
bool same = true;
|
|
|
|
if (f1 && f2) {
|
|
|
|
char buf1[4096];
|
|
|
|
char buf2[4096];
|
|
|
|
do {
|
|
|
|
size_t r1 = fread(buf1, 1, 4096, f1);
|
|
|
|
size_t r2 = fread(buf2, 1, 4096, f2);
|
|
|
|
if (r1 != r2 || memcmp(buf1, buf2, r1)) {
|
|
|
|
same = false;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
} while (! feof(f1) || ! feof(f2));
|
|
|
|
} else
|
|
|
|
same = false;
|
|
|
|
if (f1)
|
|
|
|
fclose(f1);
|
|
|
|
if (f2)
|
|
|
|
fclose(f2);
|
|
|
|
if (! same)
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2018-04-06 14:49:33 +00:00
|
|
|
size_t SnapshotDB::load_db()
|
|
|
|
{
|
|
|
|
boost::filesystem::path snapshots_dir = SnapshotDB::create_db_dir();
|
|
|
|
|
|
|
|
m_snapshots.clear();
|
|
|
|
|
|
|
|
// Walk over the snapshot directories and load their index.
|
|
|
|
std::string errors_cummulative;
|
|
|
|
for (auto &dir_entry : boost::filesystem::directory_iterator(snapshots_dir))
|
|
|
|
if (boost::filesystem::is_directory(dir_entry.status())) {
|
|
|
|
// Try to read "snapshot.ini".
|
|
|
|
boost::filesystem::path path_ini = dir_entry.path() / SLIC3R_SNAPSHOT_FILE;
|
|
|
|
Snapshot snapshot;
|
|
|
|
try {
|
|
|
|
snapshot.load_ini(path_ini.string());
|
|
|
|
} catch (const std::runtime_error &err) {
|
|
|
|
errors_cummulative += err.what();
|
|
|
|
errors_cummulative += "\n";
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
// Check that the name of the snapshot directory matches the snapshot id stored in the snapshot.ini file.
|
|
|
|
if (dir_entry.path().filename().string() != snapshot.id) {
|
|
|
|
errors_cummulative += std::string("Snapshot ID ") + snapshot.id + " does not match the snapshot directory " + dir_entry.path().filename().string() + "\n";
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
m_snapshots.emplace_back(std::move(snapshot));
|
|
|
|
}
|
2018-04-11 10:21:15 +00:00
|
|
|
// Sort the snapshots by their date/time.
|
|
|
|
std::sort(m_snapshots.begin(), m_snapshots.end(), [](const Snapshot &s1, const Snapshot &s2) { return s1.time_captured < s2.time_captured; });
|
2018-04-06 14:49:33 +00:00
|
|
|
if (! errors_cummulative.empty())
|
|
|
|
throw std::runtime_error(errors_cummulative);
|
|
|
|
return m_snapshots.size();
|
|
|
|
}
|
|
|
|
|
2018-04-09 15:03:37 +00:00
|
|
|
void SnapshotDB::update_slic3r_versions(std::vector<Index> &index_db)
|
|
|
|
{
|
|
|
|
for (Snapshot &snapshot : m_snapshots) {
|
|
|
|
for (Snapshot::VendorConfig &vendor_config : snapshot.vendor_configs) {
|
|
|
|
auto it = std::find_if(index_db.begin(), index_db.end(), [&vendor_config](const Index &idx) { return idx.vendor() == vendor_config.name; });
|
|
|
|
if (it != index_db.end()) {
|
2018-04-25 11:44:06 +00:00
|
|
|
Index::const_iterator it_version = it->find(vendor_config.version.config_version);
|
2018-04-09 15:03:37 +00:00
|
|
|
if (it_version != it->end()) {
|
2018-04-25 11:44:06 +00:00
|
|
|
vendor_config.version.min_slic3r_version = it_version->min_slic3r_version;
|
|
|
|
vendor_config.version.max_slic3r_version = it_version->max_slic3r_version;
|
2018-04-09 15:03:37 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-04-06 14:49:33 +00:00
|
|
|
static void copy_config_dir_single_level(const boost::filesystem::path &path_src, const boost::filesystem::path &path_dst)
|
|
|
|
{
|
|
|
|
if (! boost::filesystem::is_directory(path_dst) &&
|
|
|
|
! boost::filesystem::create_directory(path_dst))
|
|
|
|
throw std::runtime_error(std::string("Slic3r was unable to create a directory at ") + path_dst.string());
|
|
|
|
|
|
|
|
for (auto &dir_entry : boost::filesystem::directory_iterator(path_src))
|
2019-02-03 14:30:37 +00:00
|
|
|
if (Slic3r::is_ini_file(dir_entry))
|
2018-04-06 14:49:33 +00:00
|
|
|
boost::filesystem::copy_file(dir_entry.path(), path_dst / dir_entry.path().filename(), boost::filesystem::copy_option::overwrite_if_exists);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void delete_existing_ini_files(const boost::filesystem::path &path)
|
|
|
|
{
|
|
|
|
if (! boost::filesystem::is_directory(path))
|
|
|
|
return;
|
|
|
|
for (auto &dir_entry : boost::filesystem::directory_iterator(path))
|
|
|
|
if (boost::filesystem::is_regular_file(dir_entry.status()) && boost::algorithm::iends_with(dir_entry.path().filename().string(), ".ini"))
|
|
|
|
boost::filesystem::remove(dir_entry.path());
|
|
|
|
}
|
|
|
|
|
2018-04-10 14:27:42 +00:00
|
|
|
const Snapshot& SnapshotDB::take_snapshot(const AppConfig &app_config, Snapshot::Reason reason, const std::string &comment)
|
2018-04-06 14:49:33 +00:00
|
|
|
{
|
|
|
|
boost::filesystem::path data_dir = boost::filesystem::path(Slic3r::data_dir());
|
|
|
|
boost::filesystem::path snapshot_db_dir = SnapshotDB::create_db_dir();
|
|
|
|
|
|
|
|
// 1) Prepare the snapshot structure.
|
|
|
|
Snapshot snapshot;
|
|
|
|
// Snapshot header.
|
|
|
|
snapshot.time_captured = Slic3r::Utils::get_current_time_utc();
|
|
|
|
snapshot.id = Slic3r::Utils::format_time_ISO8601Z(snapshot.time_captured);
|
2018-04-11 11:12:08 +00:00
|
|
|
snapshot.slic3r_version_captured = *Semver::parse(SLIC3R_VERSION); // XXX: have Semver Slic3r version
|
2018-04-06 14:49:33 +00:00
|
|
|
snapshot.comment = comment;
|
|
|
|
snapshot.reason = reason;
|
|
|
|
// Active presets at the time of the snapshot.
|
|
|
|
snapshot.print = app_config.get("presets", "print");
|
|
|
|
snapshot.filaments.emplace_back(app_config.get("presets", "filament"));
|
|
|
|
snapshot.printer = app_config.get("presets", "printer");
|
|
|
|
for (unsigned int i = 1; i < 1000; ++ i) {
|
|
|
|
char name[64];
|
|
|
|
sprintf(name, "filament_%d", i);
|
|
|
|
if (! app_config.has("presets", name))
|
|
|
|
break;
|
|
|
|
snapshot.filaments.emplace_back(app_config.get("presets", name));
|
|
|
|
}
|
|
|
|
// Vendor specific config bundles and installed printers.
|
2018-04-11 10:21:15 +00:00
|
|
|
for (const std::pair<std::string, std::map<std::string, std::set<std::string>>> &vendor : app_config.vendors()) {
|
|
|
|
Snapshot::VendorConfig cfg;
|
|
|
|
cfg.name = vendor.first;
|
|
|
|
cfg.models_variants_installed = vendor.second;
|
2018-04-12 09:24:03 +00:00
|
|
|
for (auto it = cfg.models_variants_installed.begin(); it != cfg.models_variants_installed.end();)
|
|
|
|
if (it->second.empty())
|
|
|
|
cfg.models_variants_installed.erase(it ++);
|
|
|
|
else
|
|
|
|
++ it;
|
2018-04-11 10:21:15 +00:00
|
|
|
// Read the active config bundle, parse the config version.
|
|
|
|
PresetBundle bundle;
|
|
|
|
bundle.load_configbundle((data_dir / "vendor" / (cfg.name + ".ini")).string(), PresetBundle::LOAD_CFGBUNDLE_VENDOR_ONLY);
|
|
|
|
for (const VendorProfile &vp : bundle.vendors)
|
|
|
|
if (vp.id == cfg.name)
|
2018-04-25 11:44:06 +00:00
|
|
|
cfg.version.config_version = vp.config_version;
|
2018-04-11 10:21:15 +00:00
|
|
|
// Fill-in the min/max slic3r version from the config index, if possible.
|
|
|
|
try {
|
|
|
|
// Load the config index for the vendor.
|
|
|
|
Index index;
|
|
|
|
index.load(data_dir / "vendor" / (cfg.name + ".idx"));
|
2018-04-25 11:44:06 +00:00
|
|
|
auto it = index.find(cfg.version.config_version);
|
2018-04-11 10:21:15 +00:00
|
|
|
if (it != index.end()) {
|
2018-04-25 11:44:06 +00:00
|
|
|
cfg.version.min_slic3r_version = it->min_slic3r_version;
|
|
|
|
cfg.version.max_slic3r_version = it->max_slic3r_version;
|
2018-04-11 10:21:15 +00:00
|
|
|
}
|
2018-10-31 15:22:36 +00:00
|
|
|
} catch (const std::runtime_error & /* err */) {
|
2018-04-11 10:21:15 +00:00
|
|
|
}
|
|
|
|
snapshot.vendor_configs.emplace_back(std::move(cfg));
|
|
|
|
}
|
2018-04-06 14:49:33 +00:00
|
|
|
|
2018-04-10 14:27:42 +00:00
|
|
|
boost::filesystem::path snapshot_dir = snapshot_db_dir / snapshot.id;
|
|
|
|
boost::filesystem::create_directory(snapshot_dir);
|
|
|
|
|
2018-04-06 14:49:33 +00:00
|
|
|
// Backup the presets.
|
|
|
|
for (const char *subdir : { "print", "filament", "printer", "vendor" })
|
|
|
|
copy_config_dir_single_level(data_dir / subdir, snapshot_dir / subdir);
|
|
|
|
snapshot.save_ini((snapshot_dir / "snapshot.ini").string());
|
2018-04-11 10:21:15 +00:00
|
|
|
assert(m_snapshots.empty() || m_snapshots.back().time_captured <= snapshot.time_captured);
|
2018-04-06 14:49:33 +00:00
|
|
|
m_snapshots.emplace_back(std::move(snapshot));
|
|
|
|
return m_snapshots.back();
|
|
|
|
}
|
|
|
|
|
2018-04-20 08:26:23 +00:00
|
|
|
const Snapshot& SnapshotDB::restore_snapshot(const std::string &id, AppConfig &app_config)
|
2018-04-06 14:49:33 +00:00
|
|
|
{
|
|
|
|
for (const Snapshot &snapshot : m_snapshots)
|
|
|
|
if (snapshot.id == id) {
|
|
|
|
this->restore_snapshot(snapshot, app_config);
|
2018-04-20 08:26:23 +00:00
|
|
|
return snapshot;
|
2018-04-06 14:49:33 +00:00
|
|
|
}
|
|
|
|
throw std::runtime_error(std::string("Snapshot with id " + id + " was not found."));
|
|
|
|
}
|
|
|
|
|
|
|
|
void SnapshotDB::restore_snapshot(const Snapshot &snapshot, AppConfig &app_config)
|
|
|
|
{
|
|
|
|
boost::filesystem::path data_dir = boost::filesystem::path(Slic3r::data_dir());
|
|
|
|
boost::filesystem::path snapshot_db_dir = SnapshotDB::create_db_dir();
|
|
|
|
boost::filesystem::path snapshot_dir = snapshot_db_dir / snapshot.id;
|
|
|
|
// Remove existing ini files and restore the ini files from the snapshot.
|
|
|
|
for (const char *subdir : { "print", "filament", "printer", "vendor" }) {
|
|
|
|
delete_existing_ini_files(data_dir / subdir);
|
|
|
|
copy_config_dir_single_level(snapshot_dir / subdir, data_dir / subdir);
|
|
|
|
}
|
2018-04-11 10:21:15 +00:00
|
|
|
// Update AppConfig with the selections of the print / filament / printer profiles
|
|
|
|
// and about the installed printer types and variants.
|
2018-04-06 14:49:33 +00:00
|
|
|
snapshot.export_selections(app_config);
|
2018-04-11 10:21:15 +00:00
|
|
|
snapshot.export_vendor_configs(app_config);
|
2018-04-06 14:49:33 +00:00
|
|
|
}
|
|
|
|
|
2018-04-20 08:26:23 +00:00
|
|
|
bool SnapshotDB::is_on_snapshot(AppConfig &app_config) const
|
|
|
|
{
|
|
|
|
// Is the "on_snapshot" configuration value set?
|
|
|
|
std::string on_snapshot = app_config.get("on_snapshot");
|
|
|
|
if (on_snapshot.empty())
|
|
|
|
// No, we are not on a snapshot.
|
|
|
|
return false;
|
|
|
|
// Is the "on_snapshot" equal to the current configuration state?
|
|
|
|
auto it_snapshot = this->snapshot(on_snapshot);
|
|
|
|
if (it_snapshot != this->end() && it_snapshot->equal_to_active(app_config))
|
|
|
|
// Yes, we are on the snapshot.
|
|
|
|
return true;
|
|
|
|
// No, we are no more on a snapshot. Reset the state.
|
|
|
|
app_config.set("on_snapshot", "");
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
SnapshotDB::const_iterator SnapshotDB::snapshot_with_vendor_preset(const std::string &vendor_name, const Semver &config_version)
|
|
|
|
{
|
|
|
|
auto it_found = m_snapshots.end();
|
|
|
|
Snapshot::VendorConfig key;
|
|
|
|
key.name = vendor_name;
|
|
|
|
for (auto it = m_snapshots.begin(); it != m_snapshots.end(); ++ it) {
|
|
|
|
const Snapshot &snapshot = *it;
|
|
|
|
auto it_vendor_config = std::lower_bound(snapshot.vendor_configs.begin(), snapshot.vendor_configs.end(),
|
|
|
|
key, [](const Snapshot::VendorConfig &cfg1, const Snapshot::VendorConfig &cfg2) { return cfg1.name < cfg2.name; });
|
|
|
|
if (it_vendor_config != snapshot.vendor_configs.end() && it_vendor_config->name == vendor_name &&
|
2018-04-25 11:44:06 +00:00
|
|
|
config_version == it_vendor_config->version.config_version) {
|
2018-04-20 08:26:23 +00:00
|
|
|
// Vendor config found with the correct version.
|
|
|
|
// Save it, but continue searching, as we want the newest snapshot.
|
|
|
|
it_found = it;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return it_found;
|
|
|
|
}
|
|
|
|
|
|
|
|
SnapshotDB::const_iterator SnapshotDB::snapshot(const std::string &id) const
|
|
|
|
{
|
|
|
|
for (const_iterator it = m_snapshots.begin(); it != m_snapshots.end(); ++ it)
|
|
|
|
if (it->id == id)
|
|
|
|
return it;
|
|
|
|
return m_snapshots.end();
|
|
|
|
}
|
|
|
|
|
2018-04-06 14:49:33 +00:00
|
|
|
boost::filesystem::path SnapshotDB::create_db_dir()
|
|
|
|
{
|
|
|
|
boost::filesystem::path data_dir = boost::filesystem::path(Slic3r::data_dir());
|
|
|
|
boost::filesystem::path snapshots_dir = data_dir / SLIC3R_SNAPSHOTS_DIR;
|
|
|
|
for (const boost::filesystem::path &path : { data_dir, snapshots_dir }) {
|
|
|
|
boost::filesystem::path subdir = path;
|
|
|
|
subdir.make_preferred();
|
|
|
|
if (! boost::filesystem::is_directory(subdir) &&
|
|
|
|
! boost::filesystem::create_directory(subdir))
|
|
|
|
throw std::runtime_error(std::string("Slic3r was unable to create a directory at ") + subdir.string());
|
|
|
|
}
|
|
|
|
return snapshots_dir;
|
|
|
|
}
|
|
|
|
|
2018-04-09 15:03:37 +00:00
|
|
|
SnapshotDB& SnapshotDB::singleton()
|
|
|
|
{
|
|
|
|
static SnapshotDB instance;
|
2018-04-10 14:27:42 +00:00
|
|
|
static bool loaded = false;
|
2018-04-09 15:03:37 +00:00
|
|
|
if (! loaded) {
|
|
|
|
try {
|
|
|
|
loaded = true;
|
|
|
|
// Load the snapshot database.
|
|
|
|
instance.load_db();
|
|
|
|
// Load the vendor specific configuration indices.
|
|
|
|
std::vector<Index> index_db = Index::load_db();
|
|
|
|
// Update the min / max slic3r versions compatible with the configurations stored inside the snapshots
|
|
|
|
// based on the min / max slic3r versions defined by the vendor specific config indices.
|
|
|
|
instance.update_slic3r_versions(index_db);
|
2018-10-31 15:22:36 +00:00
|
|
|
} catch (std::exception & /* ex */) {
|
2018-04-09 15:03:37 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return instance;
|
|
|
|
}
|
|
|
|
|
2018-04-06 14:49:33 +00:00
|
|
|
} // namespace Config
|
|
|
|
} // namespace GUI
|
|
|
|
} // namespace Slic3r
|