mirror of
https://github.com/OrcaSlicer/OrcaSlicer.git
synced 2026-06-15 08:23:00 +00:00
Compare commits
5 Commits
main
...
feature/ca
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
88901a969f | ||
|
|
5d0c640f7b | ||
|
|
d861e8af22 | ||
|
|
6186436b23 | ||
|
|
604f15e20d |
31
.github/workflows/build_orca.yml
vendored
31
.github/workflows/build_orca.yml
vendored
@@ -117,6 +117,15 @@ jobs:
|
||||
run: |
|
||||
./build_release_macos.sh -s -n -x ${{ !vars.SELF_HOSTED && '-1' || '' }} -a ${{ inputs.arch }} -t 10.15
|
||||
|
||||
- name: Generate system presets cache (macOS)
|
||||
if: runner.os == 'macOS' && !inputs.macos-combine-only
|
||||
working-directory: ${{ github.workspace }}
|
||||
shell: bash
|
||||
run: |
|
||||
tool=$(find build/${{ inputs.arch }} -name generate_system_cache -type f | head -1)
|
||||
profiles=$(find build/${{ inputs.arch }} -path "*/Resources/profiles" -type d | head -1)
|
||||
"$tool" --path "$profiles" --log_level 2
|
||||
|
||||
- name: Pack macOS app bundle ${{ inputs.arch }}
|
||||
if: runner.os == 'macOS' && !inputs.macos-combine-only
|
||||
working-directory: ${{ github.workspace }}
|
||||
@@ -292,6 +301,14 @@ jobs:
|
||||
# WindowsSDKVersion: '10.0.26100.0\'
|
||||
run: .\build_release_vs.bat slicer
|
||||
|
||||
- name: Generate system presets cache (Windows)
|
||||
if: runner.os == 'Windows'
|
||||
shell: pwsh
|
||||
run: |
|
||||
$profiles = Get-ChildItem -Recurse -Path build -Directory -Filter profiles |
|
||||
Where-Object { $_.FullName -match 'resources' } | Select-Object -First 1
|
||||
.\build\src\Release\generate_system_cache.exe --path $profiles.FullName --log_level 2
|
||||
|
||||
- name: Create installer Win
|
||||
if: runner.os == 'Windows' && !vars.SELF_HOSTED
|
||||
working-directory: ${{ github.workspace }}/build
|
||||
@@ -400,6 +417,20 @@ jobs:
|
||||
retention-days: 5
|
||||
if-no-files-found: error
|
||||
|
||||
- name: Generate system presets cache (Linux)
|
||||
if: runner.os == 'Linux'
|
||||
shell: bash
|
||||
run: |
|
||||
./build/src/Release/generate_system_cache --path build/package/resources/profiles --log_level 2
|
||||
# Re-pack the AppImage so the cache is included
|
||||
appimage=$(find build -maxdepth 1 -name "OrcaSlicer_Linux_AppImage*.AppImage" | head -1)
|
||||
chmod +x "$appimage"
|
||||
"$appimage" --appimage-extract
|
||||
cp build/package/resources/profiles/system_presets_cache.cache squashfs-root/resources/profiles/
|
||||
appimagetool=$(find build -name "appimagetool.AppImage" | head -1)
|
||||
ARCH=$(uname -m) "$appimagetool" --appimage-extract-and-run squashfs-root "$appimage"
|
||||
rm -rf squashfs-root
|
||||
|
||||
- name: Run external slicer regression tests
|
||||
if: runner.os == 'Linux'
|
||||
timeout-minutes: 20
|
||||
|
||||
@@ -111,6 +111,11 @@ if(ORCA_TOOLS)
|
||||
endif()
|
||||
target_link_libraries(OrcaSlicer_profile_validator libslic3r boost_headeronly libcurl OpenSSL::SSL OpenSSL::Crypto)
|
||||
target_compile_definitions(OrcaSlicer_profile_validator PRIVATE -DBOOST_ALL_NO_LIB -DBOOST_USE_WINAPI_VERSION=0x602 -DBOOST_SYSTEM_USE_UTF8)
|
||||
|
||||
# generate_system_cache: pre-generates resources/profiles/system_presets_cache.cache for CI bundling.
|
||||
add_executable(generate_system_cache dev-utils/generate_system_cache.cpp)
|
||||
target_link_libraries(generate_system_cache libslic3r boost_headeronly)
|
||||
target_compile_definitions(generate_system_cache PRIVATE -DBOOST_ALL_NO_LIB -DBOOST_USE_WINAPI_VERSION=0x602 -DBOOST_SYSTEM_USE_UTF8)
|
||||
endif()
|
||||
|
||||
# Create a slic3r executable
|
||||
|
||||
86
src/dev-utils/generate_system_cache.cpp
Normal file
86
src/dev-utils/generate_system_cache.cpp
Normal file
@@ -0,0 +1,86 @@
|
||||
#include "libslic3r/PresetBundle.hpp"
|
||||
#include "libslic3r/PresetBundleCache.hpp"
|
||||
#include "libslic3r/Utils.hpp"
|
||||
|
||||
#include <boost/filesystem.hpp>
|
||||
#include <boost/log/trivial.hpp>
|
||||
#include <boost/program_options.hpp>
|
||||
#include <iostream>
|
||||
|
||||
using namespace Slic3r;
|
||||
namespace fs = boost::filesystem;
|
||||
namespace po = boost::program_options;
|
||||
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
po::options_description desc("OrcaSlicer System Cache Generator\nUsage");
|
||||
// clang-format off
|
||||
desc.add_options()
|
||||
("help,h", "Show help")
|
||||
#ifdef __APPLE__
|
||||
("path,p", po::value<std::string>()->default_value("../../../../../../../resources/profiles"), "Path to profiles directory")
|
||||
#else
|
||||
("path,p", po::value<std::string>()->default_value("../../../resources/profiles"), "Path to profiles directory")
|
||||
#endif
|
||||
("log_level,l", po::value<int>()->default_value(2), "Log level (0=trace, 2=info, 4=error)");
|
||||
// clang-format on
|
||||
|
||||
po::variables_map vm;
|
||||
try {
|
||||
po::store(po::parse_command_line(argc, argv, desc), vm);
|
||||
if (vm.count("help")) { std::cout << desc << "\n"; return 0; }
|
||||
po::notify(vm);
|
||||
} catch (const po::error& e) {
|
||||
std::cerr << "Error: " << e.what() << "\n" << desc << "\n";
|
||||
return 1;
|
||||
}
|
||||
|
||||
const std::string profiles_path = vm["path"].as<std::string>();
|
||||
const int log_level = vm["log_level"].as<int>();
|
||||
|
||||
if (!fs::exists(profiles_path) || !fs::is_directory(profiles_path)) {
|
||||
std::cerr << "Error: '" << profiles_path << "' is not a valid directory\n";
|
||||
return 1;
|
||||
}
|
||||
|
||||
set_logging_level(log_level);
|
||||
// In validation_mode, load_system_presets_from_json uses data_dir() directly
|
||||
// (no /system/ suffix), so point data_dir at the profiles directory.
|
||||
set_data_dir(profiles_path);
|
||||
set_resources_dir(fs::path(profiles_path).parent_path().make_preferred().string());
|
||||
|
||||
// load_presets creates user preset dirs under data_dir().
|
||||
const fs::path user_dir = fs::path(data_dir()) / PRESET_USER_DIR;
|
||||
if (!fs::exists(user_dir))
|
||||
fs::create_directories(user_dir);
|
||||
|
||||
AppConfig app_config;
|
||||
app_config.set("preset_folder", "default");
|
||||
|
||||
auto preset_bundle = std::make_unique<PresetBundle>();
|
||||
preset_bundle->set_is_validation_mode(true);
|
||||
preset_bundle->set_default_suppressed(true);
|
||||
|
||||
std::cout << "Loading system presets from: " << profiles_path << "\n";
|
||||
|
||||
try {
|
||||
preset_bundle->load_presets(app_config, ForwardCompatibilitySubstitutionRule::EnableSilent);
|
||||
} catch (const std::exception& ex) {
|
||||
std::cerr << "Failed to load presets: " << ex.what() << "\n";
|
||||
return 1;
|
||||
}
|
||||
|
||||
const std::string cache_path =
|
||||
(fs::path(profiles_path) / "system_presets_cache.cache").make_preferred().string();
|
||||
|
||||
PresetBundleCache::SystemPresetsCache cache;
|
||||
cache.capture(*preset_bundle, profiles_path);
|
||||
cache.save(cache_path);
|
||||
|
||||
std::cout << "Cache written: " << cache_path << "\n"
|
||||
<< " Vendor profiles: " << cache.vendor_profiles.size() << "\n"
|
||||
<< " Print presets: " << cache.print_presets.size() << "\n"
|
||||
<< " Filament presets: " << cache.filament_presets.size() << "\n"
|
||||
<< " Printer presets: " << cache.printer_presets.size() << "\n";
|
||||
return 0;
|
||||
}
|
||||
@@ -344,6 +344,8 @@ set(lisbslic3r_sources
|
||||
Polyline.hpp
|
||||
PresetBundle.cpp
|
||||
PresetBundle.hpp
|
||||
PresetBundleCache.cpp
|
||||
PresetBundleCache.hpp
|
||||
Preset.cpp
|
||||
Preset.hpp
|
||||
PrincipalComponents2D.cpp
|
||||
|
||||
@@ -707,6 +707,7 @@ void Preset::save(DynamicPrintConfig* parent_config)
|
||||
idx_file.replace_extension(".info");
|
||||
this->save_info(idx_file.string());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void Preset::reload(Preset const &parent)
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
#include <cassert>
|
||||
#include <chrono>
|
||||
#include <ctime>
|
||||
|
||||
#include "PresetBundle.hpp"
|
||||
#include "PresetBundleCache.hpp"
|
||||
#include "PrintConfig.hpp"
|
||||
#include "libslic3r.h"
|
||||
#include "I18N.hpp"
|
||||
@@ -520,6 +522,8 @@ PresetsConfigSubstitutions PresetBundle::load_presets(AppConfig &config, Forward
|
||||
|
||||
//BBS: add config related logs
|
||||
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(" enter, substitution_rule %1%, preferred printer_model_id %2%")%substitution_rule%preferred_selection.printer_model_id;
|
||||
const auto startup_t0 = std::chrono::steady_clock::now();
|
||||
|
||||
//BBS: change system config to json
|
||||
std::tie(substitutions, errors_cummulative) = this->load_system_presets_from_json(substitution_rule);
|
||||
|
||||
@@ -539,6 +543,12 @@ PresetsConfigSubstitutions PresetBundle::load_presets(AppConfig &config, Forward
|
||||
|
||||
set_calibrate_printer("");
|
||||
|
||||
{
|
||||
const auto total_ms = std::chrono::duration_cast<std::chrono::milliseconds>(
|
||||
std::chrono::steady_clock::now() - startup_t0).count();
|
||||
BOOST_LOG_TRIVIAL(info) << "PresetBundle: all presets loaded in " << total_ms << " ms";
|
||||
}
|
||||
|
||||
//BBS: add config related logs
|
||||
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(" finished, returned substitutions %1%")%substitutions.size();
|
||||
return substitutions;
|
||||
@@ -947,6 +957,8 @@ PresetsConfigSubstitutions PresetBundle::load_user_presets(std::string user, For
|
||||
bundles.m_bundles.clear();
|
||||
bundles.WriteUnlock();
|
||||
|
||||
const auto user_load_t0 = std::chrono::steady_clock::now();
|
||||
|
||||
// Load bundle metadata from _local directory first
|
||||
fs::path local_dir(folder / PRESET_LOCAL_DIR);
|
||||
if (fs::exists(local_dir)) {
|
||||
@@ -965,7 +977,6 @@ PresetsConfigSubstitutions PresetBundle::load_user_presets(std::string user, For
|
||||
metadata.filament_presets.clear();
|
||||
metadata.printer_presets.clear();
|
||||
|
||||
// Add the profiles
|
||||
this->prints.load_presets(bundle_dir, PRESET_PRINT_NAME, substitutions, substitution_rule, [&](Preset& preset) {
|
||||
metadata.print_presets.push_back(preset.name);
|
||||
}, PresetOrigin(PresetOrigin::Kind::LocalBundle, metadata.id));
|
||||
@@ -1002,7 +1013,6 @@ PresetsConfigSubstitutions PresetBundle::load_user_presets(std::string user, For
|
||||
metadata.printer_presets.clear();
|
||||
metadata.is_subscribed = true;
|
||||
|
||||
// Load presets from bundle (same logic as __local__)
|
||||
this->prints.load_presets(bundle_dir, PRESET_PRINT_NAME, substitutions, substitution_rule, [&](Preset& preset) {
|
||||
metadata.print_presets.push_back(preset.name);
|
||||
}, PresetOrigin(PresetOrigin::Kind::SubscribedBundle, metadata.id));
|
||||
@@ -1023,34 +1033,41 @@ PresetsConfigSubstitutions PresetBundle::load_user_presets(std::string user, For
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// BBS do not load sla_print
|
||||
// BBS: change directoties by design
|
||||
try {
|
||||
std::string print_selected_preset_name = prints.get_selected_preset().name;
|
||||
this->prints.load_presets(dir_user_presets, PRESET_PRINT_NAME, substitutions, substitution_rule);
|
||||
prints.select_preset_by_name(print_selected_preset_name, false);
|
||||
} catch (const std::runtime_error &err) {
|
||||
errors_cummulative += err.what();
|
||||
// BBS: change directories by design
|
||||
|
||||
{
|
||||
const auto json_t0 = std::chrono::steady_clock::now();
|
||||
try {
|
||||
std::string sel = prints.get_selected_preset().name;
|
||||
this->prints.load_presets(dir_user_presets, PRESET_PRINT_NAME, substitutions, substitution_rule);
|
||||
prints.select_preset_by_name(sel, false);
|
||||
} catch (const std::runtime_error& err) { errors_cummulative += err.what(); }
|
||||
try {
|
||||
std::string sel = filaments.get_selected_preset().name;
|
||||
this->filaments.load_presets(dir_user_presets, PRESET_FILAMENT_NAME, substitutions, substitution_rule);
|
||||
filaments.select_preset_by_name(sel, false);
|
||||
} catch (const std::runtime_error& err) { errors_cummulative += err.what(); }
|
||||
try {
|
||||
std::string sel = printers.get_selected_preset().name;
|
||||
this->printers.load_presets(dir_user_presets, PRESET_PRINTER_NAME, substitutions, substitution_rule);
|
||||
printers.select_preset_by_name(sel, false);
|
||||
} catch (const std::runtime_error& err) { errors_cummulative += err.what(); }
|
||||
if (!errors_cummulative.empty()) throw Slic3r::RuntimeError(errors_cummulative);
|
||||
|
||||
const auto json_ms = std::chrono::duration_cast<std::chrono::milliseconds>(
|
||||
std::chrono::steady_clock::now() - json_t0).count();
|
||||
BOOST_LOG_TRIVIAL(info) << "PresetBundle: user presets loaded from JSON in " << json_ms << " ms";
|
||||
}
|
||||
try {
|
||||
std::string filament_selected_preset_name = filaments.get_selected_preset().name;
|
||||
this->filaments.load_presets(dir_user_presets, PRESET_FILAMENT_NAME, substitutions, substitution_rule);
|
||||
filaments.select_preset_by_name(filament_selected_preset_name, false);
|
||||
} catch (const std::runtime_error &err) {
|
||||
errors_cummulative += err.what();
|
||||
|
||||
{
|
||||
const auto ms = std::chrono::duration_cast<std::chrono::milliseconds>(
|
||||
std::chrono::steady_clock::now() - user_load_t0).count();
|
||||
BOOST_LOG_TRIVIAL(info) << "PresetBundle: user + bundle presets loaded in " << ms << " ms";
|
||||
}
|
||||
try {
|
||||
std::string printer_selected_preset_name = printers.get_selected_preset().name;
|
||||
this->printers.load_presets(dir_user_presets, PRESET_PRINTER_NAME, substitutions, substitution_rule);
|
||||
printers.select_preset_by_name(printer_selected_preset_name, false);
|
||||
} catch (const std::runtime_error &err) {
|
||||
errors_cummulative += err.what();
|
||||
}
|
||||
if (!errors_cummulative.empty()) throw Slic3r::RuntimeError(errors_cummulative);
|
||||
|
||||
this->update_multi_material_filament_presets();
|
||||
this->update_compatible(PresetSelectCompatibleType::Never);
|
||||
|
||||
set_calibrate_printer("");
|
||||
|
||||
return PresetsConfigSubstitutions();
|
||||
@@ -2178,9 +2195,60 @@ std::pair<PresetsConfigSubstitutions, std::string> PresetBundle::load_system_pre
|
||||
if (validation_mode)
|
||||
dir = (boost::filesystem::path(data_dir())).make_preferred();
|
||||
|
||||
// Try loading from binary cache first (skips JSON parsing on cache hit).
|
||||
// partial_dirty_vendors is non-empty when some vendors changed but others are still cached.
|
||||
std::set<std::string> partial_dirty_vendors;
|
||||
if (!validation_mode) {
|
||||
const auto t0 = std::chrono::steady_clock::now();
|
||||
PresetBundleCache::SystemPresetsCache cache;
|
||||
const std::string cache_file = PresetBundleCache::SystemPresetsCache::cache_path();
|
||||
if (cache.load(cache_file)) {
|
||||
std::set<std::string> dirty;
|
||||
if (cache.get_dirty_vendors(dir.string(), dirty)) {
|
||||
if (dirty.empty()) {
|
||||
// Full hit — every vendor is unchanged.
|
||||
cache.apply(*this);
|
||||
update_system_maps();
|
||||
const auto ms = std::chrono::duration_cast<std::chrono::milliseconds>(
|
||||
std::chrono::steady_clock::now() - t0).count();
|
||||
BOOST_LOG_TRIVIAL(info) << "PresetBundle: system presets loaded from cache in " << ms << " ms";
|
||||
return {PresetsConfigSubstitutions{}, ""};
|
||||
}
|
||||
// Partial hit — restore clean vendors from cache, re-parse only dirty ones.
|
||||
BOOST_LOG_TRIVIAL(info) << "PresetBundle: partial cache hit, " << dirty.size()
|
||||
<< " vendor(s) changed — re-parsing those only";
|
||||
cache.apply_partial(*this, dirty);
|
||||
partial_dirty_vendors = std::move(dirty);
|
||||
}
|
||||
// else: structural mismatch (format version or option count changed) → full re-parse.
|
||||
}
|
||||
|
||||
if (partial_dirty_vendors.empty()) {
|
||||
// No partial hit — try bundled cache shipped with the installer (first launch).
|
||||
{
|
||||
const std::string bundled_dir =
|
||||
(boost::filesystem::path(resources_dir()) / "profiles").make_preferred().string();
|
||||
PresetBundleCache::SystemPresetsCache bundled;
|
||||
if (bundled.load(PresetBundleCache::SystemPresetsCache::bundled_cache_path()) &&
|
||||
bundled.is_valid(bundled_dir)) {
|
||||
bundled.apply(*this);
|
||||
update_system_maps();
|
||||
// Promote to user cache so subsequent launches skip this check.
|
||||
bundled.save(cache_file);
|
||||
const auto ms = std::chrono::duration_cast<std::chrono::milliseconds>(
|
||||
std::chrono::steady_clock::now() - t0).count();
|
||||
BOOST_LOG_TRIVIAL(info) << "PresetBundle: system presets loaded from bundled cache in " << ms << " ms";
|
||||
return {PresetsConfigSubstitutions{}, ""};
|
||||
}
|
||||
}
|
||||
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << " cache miss, falling back to JSON load";
|
||||
}
|
||||
}
|
||||
|
||||
const auto json_load_t0 = std::chrono::steady_clock::now();
|
||||
PresetsConfigSubstitutions substitutions;
|
||||
std::string errors_cummulative;
|
||||
bool first = true;
|
||||
bool first = partial_dirty_vendors.empty(); // false in partial mode: clean vendors already applied
|
||||
std::vector<std::string> vendor_names;
|
||||
// store all vendor names in vendor_names
|
||||
for (auto& dir_entry : boost::filesystem::directory_iterator(dir)) {
|
||||
@@ -2202,6 +2270,9 @@ std::pair<PresetsConfigSubstitutions, std::string> PresetBundle::load_system_pre
|
||||
std::vector<std::string> other_vendors;
|
||||
other_vendors.reserve(vendor_names.size());
|
||||
for (auto& vn : vendor_names) {
|
||||
// In partial mode, skip vendors already loaded from cache.
|
||||
if (!partial_dirty_vendors.empty() && !partial_dirty_vendors.count(vn))
|
||||
continue;
|
||||
if (vn == ORCA_FILAMENT_LIBRARY)
|
||||
orca_lib_vendor = vn;
|
||||
else if (!(validation_mode && !vendor_to_validate.empty() && vn != vendor_to_validate))
|
||||
@@ -2281,6 +2352,24 @@ std::pair<PresetsConfigSubstitutions, std::string> PresetBundle::load_system_pre
|
||||
}
|
||||
|
||||
this->update_system_maps();
|
||||
|
||||
{
|
||||
const auto json_ms = std::chrono::duration_cast<std::chrono::milliseconds>(
|
||||
std::chrono::steady_clock::now() - json_load_t0).count();
|
||||
BOOST_LOG_TRIVIAL(info) << "PresetBundle: system presets loaded from JSON in " << json_ms << " ms";
|
||||
}
|
||||
|
||||
// Persist a binary cache so the next startup can skip JSON parsing.
|
||||
if (!validation_mode && errors_cummulative.empty()) {
|
||||
const auto save_t0 = std::chrono::steady_clock::now();
|
||||
PresetBundleCache::SystemPresetsCache cache;
|
||||
cache.capture(*this, dir.string());
|
||||
cache.save(PresetBundleCache::SystemPresetsCache::cache_path());
|
||||
const auto save_ms = std::chrono::duration_cast<std::chrono::milliseconds>(
|
||||
std::chrono::steady_clock::now() - save_t0).count();
|
||||
BOOST_LOG_TRIVIAL(info) << "PresetBundle: system presets cache saved in " << save_ms << " ms";
|
||||
}
|
||||
|
||||
//BBS: add config related logs
|
||||
BOOST_LOG_TRIVIAL(debug) << __FUNCTION__ << boost::format(" finished, errors_cummulative %1%")%errors_cummulative;
|
||||
return std::make_pair(std::move(substitutions), errors_cummulative);
|
||||
|
||||
346
src/libslic3r/PresetBundleCache.cpp
Normal file
346
src/libslic3r/PresetBundleCache.cpp
Normal file
@@ -0,0 +1,346 @@
|
||||
#include "PresetBundleCache.hpp"
|
||||
|
||||
#include <sstream>
|
||||
|
||||
#include <boost/crc.hpp>
|
||||
#include <boost/filesystem.hpp>
|
||||
#include <boost/log/trivial.hpp>
|
||||
#include <boost/nowide/fstream.hpp>
|
||||
#include <cereal/archives/binary.hpp>
|
||||
#include <cereal/types/map.hpp>
|
||||
#include <cereal/types/polymorphic.hpp>
|
||||
#include <cereal/types/string.hpp>
|
||||
#include <cereal/types/vector.hpp>
|
||||
|
||||
#include "PresetBundle.hpp"
|
||||
#include "PrintConfig.hpp"
|
||||
#include "Semver.hpp"
|
||||
#include "Utils.hpp"
|
||||
|
||||
namespace Slic3r {
|
||||
namespace PresetBundleCache {
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Binary cache file format: raw 20-byte header followed by cereal blob.
|
||||
// -------------------------------------------------------------------------
|
||||
static constexpr uint32_t CACHE_MAGIC = 0x4F52435A; // "ORCZ"
|
||||
static constexpr uint32_t CACHE_FILE_VERSION = 1;
|
||||
|
||||
#pragma pack(push, 1)
|
||||
struct CacheFileHeader {
|
||||
uint32_t magic;
|
||||
uint32_t file_version;
|
||||
uint64_t data_size;
|
||||
uint32_t crc32;
|
||||
};
|
||||
#pragma pack(pop)
|
||||
static_assert(sizeof(CacheFileHeader) == 20, "CacheFileHeader must be 20 bytes");
|
||||
|
||||
template<class T>
|
||||
static void save_blob(const std::string& path, const T& obj)
|
||||
{
|
||||
std::ostringstream oss(std::ios::out | std::ios::binary);
|
||||
{
|
||||
cereal::BinaryOutputArchive ar(oss);
|
||||
ar(obj);
|
||||
}
|
||||
const std::string blob = oss.str();
|
||||
boost::crc_32_type crc;
|
||||
crc.process_bytes(blob.data(), blob.size());
|
||||
try {
|
||||
boost::filesystem::create_directories(boost::filesystem::path(path).parent_path());
|
||||
boost::nowide::ofstream ofs(path, std::ios::binary | std::ios::trunc);
|
||||
if (!ofs.is_open()) {
|
||||
BOOST_LOG_TRIVIAL(warning) << "PresetBundleCache: cannot open for writing: " << path;
|
||||
return;
|
||||
}
|
||||
CacheFileHeader hdr;
|
||||
hdr.magic = CACHE_MAGIC;
|
||||
hdr.file_version = CACHE_FILE_VERSION;
|
||||
hdr.data_size = static_cast<uint64_t>(blob.size());
|
||||
hdr.crc32 = crc.checksum();
|
||||
ofs.write(reinterpret_cast<const char*>(&hdr), sizeof(hdr));
|
||||
ofs.write(blob.data(), static_cast<std::streamsize>(blob.size()));
|
||||
} catch (const std::exception& e) {
|
||||
BOOST_LOG_TRIVIAL(warning) << "PresetBundleCache: write failed (" << path << "): " << e.what();
|
||||
}
|
||||
}
|
||||
|
||||
template<class T>
|
||||
static bool load_blob(const std::string& path, T& obj)
|
||||
{
|
||||
try {
|
||||
boost::nowide::ifstream ifs(path, std::ios::binary);
|
||||
if (!ifs.is_open())
|
||||
return false;
|
||||
CacheFileHeader hdr;
|
||||
if (!ifs.read(reinterpret_cast<char*>(&hdr), sizeof(hdr)))
|
||||
return false;
|
||||
if (hdr.magic != CACHE_MAGIC || hdr.file_version != CACHE_FILE_VERSION)
|
||||
return false;
|
||||
if (hdr.data_size == 0 || hdr.data_size > 512u * 1024u * 1024u)
|
||||
return false;
|
||||
std::string blob(hdr.data_size, '\0');
|
||||
if (!ifs.read(&blob[0], static_cast<std::streamsize>(hdr.data_size)))
|
||||
return false;
|
||||
boost::crc_32_type crc;
|
||||
crc.process_bytes(blob.data(), blob.size());
|
||||
if (crc.checksum() != hdr.crc32) {
|
||||
BOOST_LOG_TRIVIAL(warning) << "PresetBundleCache: CRC32 mismatch: " << path;
|
||||
return false;
|
||||
}
|
||||
std::istringstream iss(blob, std::ios::in | std::ios::binary);
|
||||
cereal::BinaryInputArchive ar(iss);
|
||||
ar(obj);
|
||||
return true;
|
||||
} catch (const std::exception& e) {
|
||||
BOOST_LOG_TRIVIAL(warning) << "PresetBundleCache: load failed (" << path << "): " << e.what();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// Helpers
|
||||
// -------------------------------------------------------------------------
|
||||
static std::string vendor_root_json(const std::string& system_dir, const std::string& vendor_id)
|
||||
{
|
||||
return (boost::filesystem::path(system_dir) / (vendor_id + ".json")).make_preferred().string();
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------------------
|
||||
// SystemPresetsCache
|
||||
// -------------------------------------------------------------------------
|
||||
std::string SystemPresetsCache::cache_path()
|
||||
{
|
||||
return (boost::filesystem::path(data_dir()) / PRESET_SYSTEM_DIR / "system_presets_cache.cache")
|
||||
.make_preferred().string();
|
||||
}
|
||||
|
||||
std::string SystemPresetsCache::bundled_cache_path()
|
||||
{
|
||||
return (boost::filesystem::path(resources_dir()) / "profiles" / "system_presets_cache.cache")
|
||||
.make_preferred().string();
|
||||
}
|
||||
|
||||
bool SystemPresetsCache::is_valid(const std::string& system_dir) const
|
||||
{
|
||||
std::set<std::string> dummy;
|
||||
return get_dirty_vendors(system_dir, dummy) && dummy.empty();
|
||||
}
|
||||
|
||||
bool SystemPresetsCache::get_dirty_vendors(const std::string& system_dir, std::set<std::string>& out_dirty) const
|
||||
{
|
||||
out_dirty.clear();
|
||||
if (format_version != FORMAT_VERSION || config_options_count != print_config_def.options.size())
|
||||
return false;
|
||||
std::map<std::string, std::string> current;
|
||||
try {
|
||||
for (const auto& entry : boost::filesystem::directory_iterator(system_dir)) {
|
||||
const std::string path = entry.path().string();
|
||||
if (!Slic3r::is_json_file(path))
|
||||
continue;
|
||||
const std::string vendor_name = entry.path().stem().string();
|
||||
Semver ver = get_version_from_json(path);
|
||||
if (ver.valid())
|
||||
current[vendor_name] = ver.to_string();
|
||||
}
|
||||
} catch (const std::exception& e) {
|
||||
BOOST_LOG_TRIVIAL(warning) << "PresetBundleCache: directory scan failed: " << e.what();
|
||||
return false;
|
||||
}
|
||||
// A vendor was removed from disk — safest to force a full re-parse.
|
||||
for (const auto& [name, ver] : vendor_versions)
|
||||
if (current.find(name) == current.end())
|
||||
return false;
|
||||
// Collect vendors with changed or new version strings.
|
||||
for (const auto& [name, ver] : current) {
|
||||
auto it = vendor_versions.find(name);
|
||||
if (it == vendor_versions.end() || it->second != ver)
|
||||
out_dirty.insert(name);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void SystemPresetsCache::capture(const PresetBundle& bundle, const std::string& system_dir)
|
||||
{
|
||||
format_version = FORMAT_VERSION;
|
||||
config_options_count = print_config_def.options.size();
|
||||
vendor_versions.clear();
|
||||
vendor_profiles.clear();
|
||||
print_presets.clear();
|
||||
filament_presets.clear();
|
||||
printer_presets.clear();
|
||||
sla_print_presets.clear();
|
||||
sla_material_presets.clear();
|
||||
config_maps = bundle.m_config_maps;
|
||||
filament_id_maps = bundle.m_filament_id_maps;
|
||||
|
||||
for (const auto& [id, vp] : bundle.vendors) {
|
||||
CachedVendorProfile cvp;
|
||||
cvp.id = vp.id;
|
||||
cvp.name = vp.name;
|
||||
cvp.config_version = vp.config_version.valid() ? vp.config_version.to_string() : "";
|
||||
cvp.config_update_url = vp.config_update_url;
|
||||
cvp.changelog_url = vp.changelog_url;
|
||||
for (const auto& model : vp.models) {
|
||||
CachedPrinterModel cm;
|
||||
cm.id = model.id;
|
||||
cm.name = model.name;
|
||||
cm.model_id = model.model_id;
|
||||
cm.family = model.family;
|
||||
cm.technology = static_cast<int>(model.technology);
|
||||
for (const auto& v : model.variants)
|
||||
cm.variants.push_back({v.name});
|
||||
cm.default_materials = model.default_materials;
|
||||
cm.not_support_bed_types = model.not_support_bed_types;
|
||||
cm.bed_model = model.bed_model;
|
||||
cm.bed_texture = model.bed_texture;
|
||||
cm.image_bed_type = model.image_bed_type;
|
||||
cm.bottom_texture_end_name = model.bottom_texture_end_name;
|
||||
cm.use_double_extruder_default_texture = model.use_double_extruder_default_texture;
|
||||
cm.bottom_texture_rect = model.bottom_texture_rect;
|
||||
cm.middle_texture_rect = model.middle_texture_rect;
|
||||
cm.hotend_model = model.hotend_model;
|
||||
cvp.models.push_back(std::move(cm));
|
||||
}
|
||||
for (const auto& f : vp.default_filaments)
|
||||
cvp.default_filaments.push_back(f);
|
||||
for (const auto& m : vp.default_sla_materials)
|
||||
cvp.default_sla_materials.push_back(m);
|
||||
vendor_profiles.push_back(std::move(cvp));
|
||||
Semver ver = get_version_from_json(vendor_root_json(system_dir, id));
|
||||
vendor_versions[id] = ver.valid() ? ver.to_string() : "";
|
||||
}
|
||||
|
||||
auto capture_col = [](const PresetCollection& coll, std::vector<CachedPreset>& out) {
|
||||
for (const Preset& p : coll()) {
|
||||
if (!p.is_system)
|
||||
continue;
|
||||
CachedPreset cp;
|
||||
cp.type = static_cast<int>(p.type);
|
||||
cp.name = p.name;
|
||||
cp.alias = p.alias;
|
||||
cp.file = p.file;
|
||||
cp.version = p.version.valid() ? p.version.to_string() : "";
|
||||
cp.vendor_id = (p.vendor != nullptr) ? p.vendor->id : "";
|
||||
cp.filament_id = p.filament_id;
|
||||
cp.setting_id = p.setting_id;
|
||||
cp.description = p.description;
|
||||
cp.renamed_from = p.renamed_from;
|
||||
cp.is_system = p.is_system;
|
||||
cp.is_visible = p.is_visible;
|
||||
cp.m_from_orca_filament_lib = p.m_from_orca_filament_lib;
|
||||
cp.config = p.config;
|
||||
out.push_back(std::move(cp));
|
||||
}
|
||||
};
|
||||
capture_col(bundle.prints, print_presets);
|
||||
capture_col(bundle.filaments, filament_presets);
|
||||
capture_col(bundle.printers, printer_presets);
|
||||
capture_col(bundle.sla_prints, sla_print_presets);
|
||||
capture_col(bundle.sla_materials, sla_material_presets);
|
||||
}
|
||||
|
||||
void SystemPresetsCache::apply(PresetBundle& bundle) const
|
||||
{
|
||||
apply_partial(bundle, {});
|
||||
}
|
||||
|
||||
void SystemPresetsCache::apply_partial(PresetBundle& bundle, const std::set<std::string>& skip_vendor_ids) const
|
||||
{
|
||||
bundle.reset(false);
|
||||
for (const auto& cvp : vendor_profiles) {
|
||||
if (skip_vendor_ids.count(cvp.id))
|
||||
continue;
|
||||
VendorProfile vp(cvp.id);
|
||||
vp.name = cvp.name;
|
||||
vp.config_update_url = cvp.config_update_url;
|
||||
vp.changelog_url = cvp.changelog_url;
|
||||
if (!cvp.config_version.empty()) {
|
||||
auto v = Semver::parse(cvp.config_version);
|
||||
if (v) vp.config_version = *v;
|
||||
}
|
||||
for (const auto& cm : cvp.models) {
|
||||
VendorProfile::PrinterModel model;
|
||||
model.id = cm.id;
|
||||
model.name = cm.name;
|
||||
model.model_id = cm.model_id;
|
||||
model.family = cm.family;
|
||||
model.technology = static_cast<PrinterTechnology>(cm.technology);
|
||||
for (const auto& v : cm.variants)
|
||||
model.variants.emplace_back(v.name);
|
||||
model.default_materials = cm.default_materials;
|
||||
model.not_support_bed_types = cm.not_support_bed_types;
|
||||
model.bed_model = cm.bed_model;
|
||||
model.bed_texture = cm.bed_texture;
|
||||
model.image_bed_type = cm.image_bed_type;
|
||||
model.bottom_texture_end_name = cm.bottom_texture_end_name;
|
||||
model.use_double_extruder_default_texture = cm.use_double_extruder_default_texture;
|
||||
model.bottom_texture_rect = cm.bottom_texture_rect;
|
||||
model.middle_texture_rect = cm.middle_texture_rect;
|
||||
model.hotend_model = cm.hotend_model;
|
||||
vp.models.push_back(std::move(model));
|
||||
}
|
||||
for (const auto& f : cvp.default_filaments)
|
||||
vp.default_filaments.insert(f);
|
||||
for (const auto& m : cvp.default_sla_materials)
|
||||
vp.default_sla_materials.insert(m);
|
||||
bundle.vendors.emplace(cvp.id, std::move(vp));
|
||||
}
|
||||
|
||||
auto apply_col = [&](const std::vector<CachedPreset>& cached,
|
||||
PresetCollection& coll,
|
||||
bool is_filaments) {
|
||||
for (const auto& cp : cached) {
|
||||
if (skip_vendor_ids.count(cp.vendor_id))
|
||||
continue;
|
||||
Semver version;
|
||||
if (!cp.version.empty()) {
|
||||
auto v = Semver::parse(cp.version);
|
||||
if (v) version = *v;
|
||||
}
|
||||
DynamicPrintConfig config = cp.config;
|
||||
Preset& p = coll.load_preset(cp.file, cp.name, std::move(config), /*select=*/false, version);
|
||||
p.is_system = true;
|
||||
p.is_visible = cp.is_visible;
|
||||
p.alias = cp.alias;
|
||||
p.renamed_from = cp.renamed_from;
|
||||
p.filament_id = cp.filament_id;
|
||||
p.setting_id = cp.setting_id;
|
||||
p.description = cp.description;
|
||||
p.m_from_orca_filament_lib = cp.m_from_orca_filament_lib;
|
||||
if (!cp.vendor_id.empty()) {
|
||||
auto it = bundle.vendors.find(cp.vendor_id);
|
||||
if (it != bundle.vendors.end())
|
||||
p.vendor = &it->second;
|
||||
}
|
||||
if (is_filaments)
|
||||
coll.set_printer_hold_alias(p.alias, p);
|
||||
}
|
||||
};
|
||||
apply_col(print_presets, bundle.prints, false);
|
||||
apply_col(filament_presets, bundle.filaments, true);
|
||||
apply_col(printer_presets, bundle.printers, false);
|
||||
apply_col(sla_print_presets, bundle.sla_prints, false);
|
||||
apply_col(sla_material_presets, bundle.sla_materials, false);
|
||||
|
||||
// config_maps and filament_id_maps are derived from ORCA_FILAMENT_LIBRARY.
|
||||
// If that vendor is in skip_vendor_ids it will be re-loaded from JSON and will
|
||||
// overwrite these; otherwise the cached values are still valid.
|
||||
bundle.m_config_maps = config_maps;
|
||||
bundle.m_filament_id_maps = filament_id_maps;
|
||||
// Caller must invoke bundle.update_system_maps() after all loading is done.
|
||||
}
|
||||
|
||||
bool SystemPresetsCache::load(const std::string& path)
|
||||
{
|
||||
return load_blob(path, *this);
|
||||
}
|
||||
|
||||
void SystemPresetsCache::save(const std::string& path) const
|
||||
{
|
||||
save_blob(path, *this);
|
||||
}
|
||||
|
||||
} // namespace PresetBundleCache
|
||||
} // namespace Slic3r
|
||||
138
src/libslic3r/PresetBundleCache.hpp
Normal file
138
src/libslic3r/PresetBundleCache.hpp
Normal file
@@ -0,0 +1,138 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
#include <map>
|
||||
#include <set>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include <cereal/archives/binary.hpp>
|
||||
#include <cereal/cereal.hpp>
|
||||
#include <cereal/types/map.hpp>
|
||||
#include <cereal/types/polymorphic.hpp>
|
||||
#include <cereal/types/string.hpp>
|
||||
#include <cereal/types/vector.hpp>
|
||||
|
||||
#include "PrintConfig.hpp"
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
class PresetBundle;
|
||||
|
||||
namespace PresetBundleCache {
|
||||
|
||||
// ---- Vendor profile structures ----
|
||||
|
||||
struct CachedPrinterVariant {
|
||||
std::string name;
|
||||
template<class Archive> void serialize(Archive& ar) { ar(name); }
|
||||
};
|
||||
|
||||
struct CachedPrinterModel {
|
||||
std::string id, name, model_id, family;
|
||||
int technology = 0;
|
||||
std::vector<CachedPrinterVariant> variants;
|
||||
std::vector<std::string> default_materials;
|
||||
std::vector<std::string> not_support_bed_types;
|
||||
std::string bed_model, bed_texture, image_bed_type;
|
||||
std::string bottom_texture_end_name, use_double_extruder_default_texture;
|
||||
std::string bottom_texture_rect, middle_texture_rect, hotend_model;
|
||||
|
||||
template<class Archive>
|
||||
void serialize(Archive& ar)
|
||||
{
|
||||
ar(id, name, model_id, family, technology, variants, default_materials,
|
||||
not_support_bed_types, bed_model, bed_texture, image_bed_type,
|
||||
bottom_texture_end_name, use_double_extruder_default_texture,
|
||||
bottom_texture_rect, middle_texture_rect, hotend_model);
|
||||
}
|
||||
};
|
||||
|
||||
struct CachedVendorProfile {
|
||||
std::string id, name, config_version, config_update_url, changelog_url;
|
||||
std::vector<CachedPrinterModel> models;
|
||||
std::vector<std::string> default_filaments;
|
||||
std::vector<std::string> default_sla_materials;
|
||||
|
||||
template<class Archive>
|
||||
void serialize(Archive& ar)
|
||||
{
|
||||
ar(id, name, config_version, config_update_url, changelog_url,
|
||||
models, default_filaments, default_sla_materials);
|
||||
}
|
||||
};
|
||||
|
||||
// ---- Per-preset cache entry ----
|
||||
|
||||
struct CachedPreset {
|
||||
int type = 0;
|
||||
std::string name, alias, file, version;
|
||||
std::string vendor_id;
|
||||
std::string filament_id, setting_id, description;
|
||||
std::string base_id, user_id, sync_info;
|
||||
long long updated_time = 0;
|
||||
std::vector<std::string> renamed_from;
|
||||
bool is_system = true;
|
||||
bool is_visible = true;
|
||||
bool m_from_orca_filament_lib = false;
|
||||
DynamicPrintConfig config;
|
||||
|
||||
template<class Archive>
|
||||
void serialize(Archive& ar)
|
||||
{
|
||||
ar(type, name, alias, file, version, vendor_id, filament_id, setting_id,
|
||||
description, base_id, user_id, sync_info, updated_time, renamed_from,
|
||||
is_system, is_visible, m_from_orca_filament_lib, config);
|
||||
}
|
||||
};
|
||||
|
||||
// ---- System preset cache ----
|
||||
// Single blob at <data_dir>/system/system_presets_cache.cache
|
||||
// Covers all vendor profiles and system presets.
|
||||
// Invalidated when any vendor version string changes or config option count changes.
|
||||
|
||||
struct SystemPresetsCache {
|
||||
static constexpr uint32_t FORMAT_VERSION = 2;
|
||||
|
||||
uint32_t format_version = FORMAT_VERSION;
|
||||
size_t config_options_count = 0;
|
||||
|
||||
std::map<std::string, std::string> vendor_versions;
|
||||
std::vector<CachedVendorProfile> vendor_profiles;
|
||||
|
||||
std::vector<CachedPreset> print_presets;
|
||||
std::vector<CachedPreset> filament_presets;
|
||||
std::vector<CachedPreset> printer_presets;
|
||||
std::vector<CachedPreset> sla_print_presets;
|
||||
std::vector<CachedPreset> sla_material_presets;
|
||||
|
||||
std::map<std::string, DynamicPrintConfig> config_maps;
|
||||
std::map<std::string, std::string> filament_id_maps;
|
||||
|
||||
template<class Archive>
|
||||
void serialize(Archive& ar)
|
||||
{
|
||||
ar(format_version, config_options_count, vendor_versions,
|
||||
vendor_profiles,
|
||||
print_presets, filament_presets, printer_presets,
|
||||
sla_print_presets, sla_material_presets,
|
||||
config_maps, filament_id_maps);
|
||||
}
|
||||
|
||||
static std::string cache_path();
|
||||
static std::string bundled_cache_path();
|
||||
bool is_valid(const std::string& system_dir) const;
|
||||
// Returns false on structural mismatch (full re-parse required).
|
||||
// On true, populates out_dirty with vendor IDs whose version strings changed.
|
||||
// Empty out_dirty means fully valid (cache hit, no re-parse needed).
|
||||
bool get_dirty_vendors(const std::string& system_dir, std::set<std::string>& out_dirty) const;
|
||||
void capture(const PresetBundle& bundle, const std::string& system_dir);
|
||||
void apply(PresetBundle& bundle) const;
|
||||
// Same as apply() but skips vendors listed in skip_vendor_ids and their presets.
|
||||
void apply_partial(PresetBundle& bundle, const std::set<std::string>& skip_vendor_ids) const;
|
||||
bool load(const std::string& path);
|
||||
void save(const std::string& path) const;
|
||||
};
|
||||
|
||||
} // namespace PresetBundleCache
|
||||
} // namespace Slic3r
|
||||
@@ -1133,6 +1133,123 @@ int GuideFrame::GetFilamentInfo( std::string VendorDirectory, json & pFilaList,
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool GuideFrame::BuildProfileDataFromPresetBundle()
|
||||
{
|
||||
PresetBundle* pb = wxGetApp().preset_bundle;
|
||||
if (!pb || pb->vendors.empty())
|
||||
return false;
|
||||
|
||||
try {
|
||||
// Models from vendor profiles
|
||||
for (const auto& [vendor_id, vp] : pb->vendors) {
|
||||
for (const auto& model : vp.models) {
|
||||
std::string nozzle_str;
|
||||
for (const auto& v : model.variants) {
|
||||
if (!nozzle_str.empty()) nozzle_str += ";";
|
||||
nozzle_str += v.name;
|
||||
}
|
||||
std::string materials_str;
|
||||
for (const auto& m : model.default_materials) {
|
||||
if (!materials_str.empty()) materials_str += ";";
|
||||
materials_str += m;
|
||||
}
|
||||
boost::filesystem::path cover_path =
|
||||
(boost::filesystem::path(resources_dir()) / "profiles" / vendor_id / (model.id + "_cover.png"))
|
||||
.make_preferred();
|
||||
if (!boost::filesystem::exists(cover_path))
|
||||
cover_path =
|
||||
(boost::filesystem::path(resources_dir()) / "web/image/printer" / (model.id + "_cover.png"))
|
||||
.make_preferred();
|
||||
|
||||
json entry;
|
||||
entry["model"] = model.id;
|
||||
entry["name"] = model.name;
|
||||
entry["vendor"] = vendor_id;
|
||||
entry["nozzle_diameter"] = nozzle_str;
|
||||
entry["materials"] = materials_str;
|
||||
entry["cover"] = cover_path.string();
|
||||
entry["nozzle_selected"] = "";
|
||||
entry["sub_path"] = "";
|
||||
m_ProfileJson["model"].push_back(entry);
|
||||
}
|
||||
}
|
||||
|
||||
// Machine map: preset name -> {model, nozzle variant}
|
||||
for (const Preset& p : pb->printers()) {
|
||||
if (!p.is_system) continue;
|
||||
const auto* printer_model = p.config.option<ConfigOptionString>("printer_model");
|
||||
const auto* printer_variant = p.config.option<ConfigOptionString>("printer_variant");
|
||||
if (!printer_model || printer_model->value.empty() || !printer_variant) continue;
|
||||
|
||||
json mach;
|
||||
mach["model"] = printer_model->value;
|
||||
mach["nozzle"] = printer_variant->value;
|
||||
m_ProfileJson["machine"][p.name] = mach;
|
||||
}
|
||||
|
||||
// Filament map from system filament presets (vendor/type already resolved in config)
|
||||
for (const Preset& p : pb->filaments()) {
|
||||
if (!p.is_system) continue;
|
||||
const auto* fila_vendor = p.config.option<ConfigOptionStrings>("filament_vendor");
|
||||
const auto* fila_type = p.config.option<ConfigOptionStrings>("filament_type");
|
||||
const auto* compat_printers = p.config.option<ConfigOptionStrings>("compatible_printers");
|
||||
|
||||
std::string vendor = (fila_vendor && !fila_vendor->values.empty()) ? fila_vendor->values[0] : "";
|
||||
std::string type = (fila_type && !fila_type->values.empty()) ? fila_type->values[0] : "";
|
||||
|
||||
std::string model_list;
|
||||
if (compat_printers) {
|
||||
for (const std::string& pname : compat_printers->values) {
|
||||
if (m_ProfileJson["machine"].contains(pname)) {
|
||||
std::string m = m_ProfileJson["machine"][pname]["model"];
|
||||
std::string n = m_ProfileJson["machine"][pname]["nozzle"];
|
||||
model_list += "[" + m + "++" + n + "]";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
json ff;
|
||||
ff["name"] = p.name;
|
||||
ff["sub_path"] = p.file;
|
||||
ff["vendor"] = vendor;
|
||||
ff["type"] = type;
|
||||
ff["models"] = model_list;
|
||||
ff["selected"] = 0;
|
||||
m_ProfileJson["filament"][p.name] = ff;
|
||||
}
|
||||
|
||||
// Process list from visible system print presets
|
||||
for (const Preset& p : pb->prints()) {
|
||||
if (!p.is_system || !p.is_visible) continue;
|
||||
json entry;
|
||||
entry["name"] = p.name;
|
||||
entry["sub_path"] = p.file;
|
||||
m_ProfileJson["process"].push_back(entry);
|
||||
}
|
||||
|
||||
// Set filament library path for any GetFilamentInfo calls on custom filaments
|
||||
auto lib_name = boost::filesystem::path(PresetBundle::ORCA_FILAMENT_LIBRARY).replace_extension(".json");
|
||||
if (boost::filesystem::exists(vendor_dir / lib_name))
|
||||
m_OrcaFilaLibPath = (vendor_dir / PresetBundle::ORCA_FILAMENT_LIBRARY).string();
|
||||
else
|
||||
m_OrcaFilaLibPath = (rsrc_vendor_dir / PresetBundle::ORCA_FILAMENT_LIBRARY).string();
|
||||
|
||||
BOOST_LOG_TRIVIAL(info) << "GuideFrame: built profile data from preset_bundle ("
|
||||
<< m_ProfileJson["model"].size() << " models, "
|
||||
<< m_ProfileJson["machine"].size() << " machines, "
|
||||
<< m_ProfileJson["filament"].size() << " filaments)";
|
||||
return !m_ProfileJson["machine"].empty();
|
||||
} catch (const std::exception& e) {
|
||||
BOOST_LOG_TRIVIAL(warning) << "GuideFrame::BuildProfileDataFromPresetBundle failed: " << e.what()
|
||||
<< " — falling back to JSON loading";
|
||||
m_ProfileJson["model"] = json::array();
|
||||
m_ProfileJson["machine"] = json::object();
|
||||
m_ProfileJson["filament"] = json::object();
|
||||
m_ProfileJson["process"] = json::array();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
int GuideFrame::LoadProfileData()
|
||||
{
|
||||
try {
|
||||
@@ -1157,52 +1274,53 @@ int GuideFrame::LoadProfileData()
|
||||
}
|
||||
}
|
||||
|
||||
// load the default filament library first
|
||||
std::set<std::string> loaded_vendors;
|
||||
auto filament_library_name = boost::filesystem::path(PresetBundle::ORCA_FILAMENT_LIBRARY).replace_extension(".json");
|
||||
if (boost::filesystem::exists(vendor_dir / filament_library_name)) {
|
||||
m_OrcaFilaLibPath = (vendor_dir / PresetBundle::ORCA_FILAMENT_LIBRARY).string();
|
||||
LoadProfileFamily(PresetBundle::ORCA_FILAMENT_LIBRARY, (vendor_dir / filament_library_name).string());
|
||||
} else {
|
||||
m_OrcaFilaLibPath = (rsrc_vendor_dir / PresetBundle::ORCA_FILAMENT_LIBRARY).string();
|
||||
LoadProfileFamily(PresetBundle::ORCA_FILAMENT_LIBRARY, (rsrc_vendor_dir / filament_library_name).string());
|
||||
}
|
||||
loaded_vendors.insert(PresetBundle::ORCA_FILAMENT_LIBRARY);
|
||||
|
||||
//load custom bundle from user data path
|
||||
boost::filesystem::directory_iterator endIter;
|
||||
for (boost::filesystem::directory_iterator iter(vendor_dir); iter != endIter; iter++) {
|
||||
if (!boost::filesystem::is_directory(*iter)) {
|
||||
wxString strVendor = from_u8(iter->path().string()).BeforeLast('.');
|
||||
strVendor = strVendor.AfterLast('\\');
|
||||
strVendor = strVendor.AfterLast('/');
|
||||
|
||||
wxString strExtension = from_u8(iter->path().string()).AfterLast('.').Lower();
|
||||
if(strExtension.CmpNoCase("json") != 0 || loaded_vendors.find(w2s(strVendor)) != loaded_vendors.end())
|
||||
continue;
|
||||
|
||||
LoadProfileFamily(w2s(strVendor), iter->path().string());
|
||||
loaded_vendors.insert(w2s(strVendor));
|
||||
if (!BuildProfileDataFromPresetBundle()) {
|
||||
// Fallback: preset_bundle not ready or empty — load from JSON files.
|
||||
std::set<std::string> loaded_vendors;
|
||||
auto filament_library_name = boost::filesystem::path(PresetBundle::ORCA_FILAMENT_LIBRARY).replace_extension(".json");
|
||||
if (boost::filesystem::exists(vendor_dir / filament_library_name)) {
|
||||
m_OrcaFilaLibPath = (vendor_dir / PresetBundle::ORCA_FILAMENT_LIBRARY).string();
|
||||
LoadProfileFamily(PresetBundle::ORCA_FILAMENT_LIBRARY, (vendor_dir / filament_library_name).string());
|
||||
} else {
|
||||
m_OrcaFilaLibPath = (rsrc_vendor_dir / PresetBundle::ORCA_FILAMENT_LIBRARY).string();
|
||||
LoadProfileFamily(PresetBundle::ORCA_FILAMENT_LIBRARY, (rsrc_vendor_dir / filament_library_name).string());
|
||||
}
|
||||
if (m_destroy)
|
||||
return 0;
|
||||
}
|
||||
loaded_vendors.insert(PresetBundle::ORCA_FILAMENT_LIBRARY);
|
||||
|
||||
boost::filesystem::directory_iterator others_endIter;
|
||||
for (boost::filesystem::directory_iterator iter(rsrc_vendor_dir); iter != others_endIter; iter++) {
|
||||
if (!boost::filesystem::is_directory(*iter)) {
|
||||
wxString strVendor = from_u8(iter->path().string()).BeforeLast('.');
|
||||
strVendor = strVendor.AfterLast('\\');
|
||||
strVendor = strVendor.AfterLast('/');
|
||||
wxString strExtension = from_u8(iter->path().string()).AfterLast('.').Lower();
|
||||
if (strExtension.CmpNoCase("json") != 0 || loaded_vendors.find(w2s(strVendor)) != loaded_vendors.end())
|
||||
continue;
|
||||
boost::filesystem::directory_iterator endIter;
|
||||
for (boost::filesystem::directory_iterator iter(vendor_dir); iter != endIter; iter++) {
|
||||
if (!boost::filesystem::is_directory(*iter)) {
|
||||
wxString strVendor = from_u8(iter->path().string()).BeforeLast('.');
|
||||
strVendor = strVendor.AfterLast('\\');
|
||||
strVendor = strVendor.AfterLast('/');
|
||||
|
||||
LoadProfileFamily(w2s(strVendor), iter->path().string());
|
||||
loaded_vendors.insert(w2s(strVendor));
|
||||
wxString strExtension = from_u8(iter->path().string()).AfterLast('.').Lower();
|
||||
if (strExtension.CmpNoCase("json") != 0 || loaded_vendors.find(w2s(strVendor)) != loaded_vendors.end())
|
||||
continue;
|
||||
|
||||
LoadProfileFamily(w2s(strVendor), iter->path().string());
|
||||
loaded_vendors.insert(w2s(strVendor));
|
||||
}
|
||||
if (m_destroy)
|
||||
return 0;
|
||||
}
|
||||
|
||||
boost::filesystem::directory_iterator others_endIter;
|
||||
for (boost::filesystem::directory_iterator iter(rsrc_vendor_dir); iter != others_endIter; iter++) {
|
||||
if (!boost::filesystem::is_directory(*iter)) {
|
||||
wxString strVendor = from_u8(iter->path().string()).BeforeLast('.');
|
||||
strVendor = strVendor.AfterLast('\\');
|
||||
strVendor = strVendor.AfterLast('/');
|
||||
wxString strExtension = from_u8(iter->path().string()).AfterLast('.').Lower();
|
||||
if (strExtension.CmpNoCase("json") != 0 || loaded_vendors.find(w2s(strVendor)) != loaded_vendors.end())
|
||||
continue;
|
||||
|
||||
LoadProfileFamily(w2s(strVendor), iter->path().string());
|
||||
loaded_vendors.insert(w2s(strVendor));
|
||||
}
|
||||
if (m_destroy)
|
||||
return 0;
|
||||
}
|
||||
if (m_destroy)
|
||||
return 0;
|
||||
}
|
||||
|
||||
wxGetApp().CallAfter([this] {
|
||||
|
||||
@@ -78,6 +78,7 @@ public:
|
||||
int LoadProfileData();
|
||||
int SaveProfileData();
|
||||
int LoadProfileFamily(std::string strVendor, std::string strFilePath);
|
||||
bool BuildProfileDataFromPresetBundle();
|
||||
int SaveProfile();
|
||||
int GetFilamentInfo( std::string VendorDirectory,json & pFilaList, std::string filepath, std::string &sVendor, std::string &sType);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user