Compare commits

..
Author SHA1 Message Date
Hanif Koh 35dc1ca6ef Lock Config and Preset Files Across Instances and Write Them Atomically
Every running instance shares one OrcaSlicer.conf and one user preset
tree, and nothing kept their writers apart. Two instances saving at the
same moment, or the cloud preset sync thread writing while the GUI thread
saved, could interleave, and a reader in another instance could open a
preset JSON or .info file between truncate and close and get a partial
file, dropping that preset for the session with a parse error.

Add InstanceLock, a scoped guard that serialises the threads of one
process through a recursive mutex and other processes through an advisory
OS file lock: flock on POSIX, held on the guard's own descriptor so no
other close in the process can drop it, and LockFileEx on Windows. The
outermost guard opens the lock file and closes it on release, so nothing
stays open between saves and a data dir can be removed once nothing is
saving into it; the file itself is kept, since deleting it would let a
third instance lock a fresh file while the second still holds the old
one. It is best effort: when the lock file cannot be opened or locked, or
another instance still holds it after a second, the guard logs once and
lets the write proceed, then leaves the file alone for ten seconds, so a
hung instance never blocks every other one and a holder stuck in a
debugger does not cost a stall per save. The guard sits at the leaf
readers and writers: set_sync_info_and_save() calls save_info() under the
preset collection mutex, so a batch lock around save_user_presets() would
invert the order against the sync thread. The user preset scan reads its
files on worker threads without the guard, since the mutex would
serialise them, and takes it per file in the serial commit step, so a
save never waits for the whole scan. Each read keeps the bytes of the
preset and its .info as they were before parsing; commit compares them
with the disk under the guard and reads a file that changed again, so it
never deletes or writes back over another instance's newer save, nor
installs a .json and .info from two different saves; a preset another
instance removed in the meantime is not installed. Without the guard, in
a cool-down, the scan still loads the presets but leaves their files
alone: an unreadable file stays for the next scan, and a derived
compatible printer is not written back. Read-only scans, which is what
the CLI does, take no lock and create no lock file.

AppConfig holds OrcaSlicer.conf.lock in load() and save(); load is
included because the Windows path restores from the .bak copy. Every
user preset writer and reader holds user.lock: Preset::save(), which
writes no .info when the preset itself could not be written, since an
.info without its preset reads as a cloud deletion request, save_info(),
reload() and remove_files(), each preset the scan commits, the
bundle metadata reads and write, the .info removal after a cloud-confirmed
delete, the orphaned-.info scan on the sync thread, the bundle folder
removal on unsubscribe and the physical printer writers and delete
paths. A bundle import extracts under cache/ into a folder per process
and per import, where no scan reads.

Preset JSON, .info, bundle metadata, physical printer and config files,
and the caches and state files that already used a temporary by hand,
now go through write_file_atomically(), which writes <file>.<pid>.<n>.tmp
beside the target and renames it over, so a reader that never waits sees
a complete old or new file. A symlink is followed; a target that is not
a regular file is written in place; and when no temporary can be created
beside an existing target, or the rename itself is refused, by a Windows
reader holding the file open or a mount that cannot replace in one step,
the helper writes in place as before, since losing the save is worse
than a torn read. On POSIX the rename replaces the
target atomically where the old code removed it first and left a window
with no file at all; only a mount that refuses a one-step replace gets
the old remove-then-rename. A crash between temporary and rename leaves
the temporary behind, which no scan reads. Preset::save() returns
whether it wrote the preset, so the scan counts a compatible printer it
could not write back as an error. A failed config write keeps
the config dirty, and the idle handler waits ten seconds before retrying
while an explicit save always tries.
2026-09-28 16:21:31 +08:00
raistlin7447 7f30eb8a49 libslic3r: merge the loaded vendors' presets in one pass
load_vendors merged each vendor's bundle into the startup bundle in turn,
inserting every preset at its sorted place in a deque, so each insert shifted
every preset after it.

PresetBundle::merge_presets now takes every loaded bundle at once, and each
collection merges its sorted presets with theirs in one pass. A preset name this
bundle or an earlier one of the list already has is still left out and reported
under the vendor that repeats it, in the same order as before.
2026-09-27 08:42:38 -05:00
raistlin7447 b5d1866445 libslic3r: read the other vendors while the filament library loads
Installing a vendor's filaments looks up the filament library, so load_vendors
loads the library first. Reading a vendor, from its cache or its JSONs, needs
nothing from the library, yet every other vendor waited for it before starting.

load_vendor_configs_from_json splits into read_vendor and install_vendor_read.
load_vendors reads the other vendors in parallel while the library loads, and
installs each against it as soon as both its read and the library are done. A
cache the library can no longer install is still replaced by a parse of the
vendor's JSONs, now when it installs; a canceled load is not mistaken for such
a cache. Vendors start costliest first, and a cache older than the profile
beside it is ordered as a parse.

load_vendors also names the vendors that failed, the library included, which
keeps what it installed before failing and so stays among the bundle's vendors.
The setup wizard falls back to its own scan on that list rather than on which
vendors the bundle holds.
2026-09-27 08:42:37 -05:00
raistlin7447 3b1a5c0630 libslic3r: copy the configs a vendor keeps while resolving
A vendor keeps a copy of each config its other entries inherit, and the filament
library keeps every one of them for the vendors that inherit from it. The copy
was made in commit, one entry at a time on the calling thread while the workers
waited, and the library alone makes 960 of them.

Whether an entry's config is kept depends only on names known before any entry
resolves, so resolve_vendor_preset makes the copy alongside the rest of its work
and commit moves it in.
2026-09-27 08:42:36 -05:00
raistlin7447 18867094fa libslic3r: load the setup wizard's vendors the way startup does
When the wizard's profile-data cache is stale, which any vendor's version change
makes it, the printer and filament selection dialogs rebuild it by loading every
shipped vendor, 66 of them here, one after another. Startup loads its vendors in
parallel once the filament library is in.

That step moves out of load_system_presets_from_json into
PresetBundle::load_vendors, which takes each vendor with the directory it is
installed in, and the wizard calls it too. A vendor that fails to load still
sends the wizard to its hand-written JSON scan, as the exception it used to throw
did, now once the others have loaded and with the error logged. merge_presets
goes private, since the wizard no longer merges bundles itself.

Opening either dialog with a stale cache goes from 3.38-3.55 s to 2.33-2.47 s on
a 16-core desktop, and the vendor rebuild inside it from 2.34-2.48 s to
1.27-1.41 s. The catalog it writes is byte-identical to the one main writes, and
a dump of every preset startup loads is unchanged.
2026-09-27 08:42:36 -05:00
raistlin7447 5ae88ae5c7 libslic3r: install parsed and cached presets through one routine
A cache load installed the entries others inherit from or include first and
resolved the rest together, while the JSON parse installed each entry straight
after parsing it. The two orders only agree while every preset is listed after
what it depends on. An include listed after the preset naming it was missing for
one and found by the other, and every new kind of reference between presets
would have needed the same care in two places.

Both loads now go through install_vendor, which installs processes, filaments and
printers in listing order. Entries resolve together in runs and commit one at a
time, and a run ends before an entry that inherits or includes one already in
it. No entry resolves against a registration from its own run, so the result is
what installing one at a time gives, in any listing order.

The JSON parse reads every sub-file first, under one numeric locale setter, and
stops at the first that fails. The ones before it are installed before the
failure is raised, as they were one at a time, and the filament library's maps
are published only from a complete load. Each entry's parse errors are logged and
counted when it installs, so the log and a load that fails partway come out as
one-at-a-time loading leaves them.

Every preset already in the collection counts as taken for a new one's name.

resolve_vendor_preset and commit_vendor_preset take a VendorInstall with the
collection's shared state in place of a dozen parameters, const for resolve.

The first launch after an update, which parses every profile, goes from
1510-1533 ms to 806-849 ms on a 16-core desktop, and a cached launch from
342-357 ms to 328-334 ms.
2026-09-27 08:42:35 -05:00
raistlin7447 ea3a40d073 libslic3r: move the filament library's config map instead of copying it
Every preset of the filament library keeps its config for other vendors to
inherit from, and publishing that map copied all 1281 of them. The map it is
built in is cleared by the next install before anything reads it again, so the
copy was of no use to anyone.

Loading the library goes from 145 to 86 ms, and the whole preset load from 360
to 300 ms.
2026-09-27 08:42:35 -05:00
raistlin7447 806568287c libslic3r: append a vendor's presets and sort once
Installing a preset placed it at its position in the sorted collection, which
moves every preset after it, so a vendor with 2528 filaments paid an O(n)
insertion 2528 times. That is serial work at the end of a load whose per-preset
part now runs across threads, so it is what the load waits on.

The entries are appended and the collection sorted once they are all in, the way
user presets are already loaded. The sort is on a scope guard, so it also runs
for an entry that cannot be installed and throws past the end of the loop. A
repeated name was caught by find_preset against the half-filled collection,
which an unsorted one cannot answer, so the names are kept in a set, seeded with
the default presets that are in the collection before the first entry arrives.
2026-09-27 08:42:34 -05:00
raistlin7447 0342ddbe26 libslic3r: check only the keys an entry added for validity
A preset's config is a copy of the one it inherits with the entry's diff applied
over it, so the only keys it can hold that the parent does not define are the
ones in that diff. Scanning all of them instead walked 141 keys per filament
preset where 23 would do, and built a vector of every key's name to do it.

The keys the caller knows were added are checked where it has them, and the scan
of everything stays for the callers that do not. An entry that includes a
template is checked the same way, since an include diff holds only keys of the
collection default. The check also moves above Preset::normalize, which derives
keys from the ones it finds. A key derived from one the profile should not have
carried is in neither the diff nor the parent, so after normalize nothing would
name it.
2026-09-27 08:42:34 -05:00
raistlin7447 33af0651b7 libslic3r: resolve user presets together
A user preset is read from its own file and flattened against the system preset
it inherits. It cannot inherit another file of the same pass, because the presets
being read only join the collection once they have all been read, so the files
are independent of each other.

load_presets splits the way the vendor cache load already does:
resolve_user_preset reads and flattens one file and touches nothing shared, and
commit_user_preset installs it, counts its errors, and does the file work a load
can trigger (removing an unreadable preset, writing back a filament preset that
named no compatible printer). Both callers now share resolve_then_commit, which
holds the two-phase shape, works through the items in batches so what is held at
once does not grow with how many there are, and gives each piece of a batch one
CNumericLocalesSetter rather than one per file. It throws rather than commit a
batch that a canceled task group of the caller left unresolved.

A setter nested in another on the same thread does nothing while the locale is
still "C", so one setter per piece of a batch covers every file in it, and each
batch runs isolated, so a thread waiting on it picks up none of the caller's
other work. A filament preset whose derived compatible printer cannot be written
back still loads, with the failure counted.

Loading 328 user presets goes from 105 to 34 ms on a 16-core desktop.
2026-09-27 08:42:33 -05:00
raistlin7447 781a5d8516 libslic3r: compare preset json keys without the locale
Reading one preset file tries a dozen key names against every key in it with
boost::iequals, which compares through std::locale(). Constructing that takes a
lock the whole process shares in the MSVC runtime, so it is both the bulk of the
work in a preset file's parse and the reason two threads cannot do it at once.

Comparing the bytes instead takes loading 328 user presets from 129 to 105 ms
one at a time, and the same files spread across threads from 132 to 27 ms. The
first launch after an update, when the system profiles are parsed from JSON
rather than served from their cache, goes through the same function.
2026-09-27 08:42:32 -05:00
raistlin7447 38446ab77a libslic3r: resolve a vendor's cached presets together
Installing one entry is a config copy, the entry's diff applied over it,
normalization and an invalid-key scan. For the largest vendor that is around
2000 filaments at about 115 us each, and all of it reads state that does not
change while the collection loads.

load_vendor_preset splits into resolve_vendor_preset, which flattens an entry
and touches nothing shared, and commit_vendor_preset, which installs it. A
cache load installs the entries others inherit from first, since what those
register is what the rest resolve against, then resolves the rest together and
commits them one at a time. The JSON parse calls the two back to back as before.

On a 16-core desktop with 4263 system presets installed, preset loading goes
from 644-652 ms to 370-376 ms, and a bundle loaded either way comes out
identical, preset for preset and option for option.
2026-09-27 08:42:32 -05:00
raistlin7447 9080269f6f libslic3r: split the system preset load across threads reliably
The parallel_for over the vendors used an auto_partitioner, which splits its
range only while a worker is asking for work. At six vendors that often did not
happen at all. In two runs out of four on this machine every vendor in that step
ran on the calling thread, which the log shows as one thread id for all of them.

A grain of one splits the range up front. The vendors also start slowest first,
so the vendor that decides when the step ends is not the one left until last. A
vendor with no cache is parsed from its JSONs, which costs far more than any
cache load, so those go before cached ones, and within each group the bigger
file goes first, the cache or the profile index that names the vendor's presets.
2026-09-27 08:42:31 -05:00
259 changed files with 4949 additions and 7131 deletions
+45 -105
View File
@@ -1,7 +1,5 @@
name: Daily OFL OTA Update
run-name: Daily OFL OTA Update [OFL barrier]
# This workflow is intended for creating and publishing the OrcaFilamentLibrary (OFL) OPC package to
# https://github.com/OrcaSlicer/orcaslicer-profiles, which generates an OTA update.
# This cronjob runs daily at 00:00 UTC every day and scans main plus every release/vX.Y.Z branch for
@@ -14,9 +12,9 @@ run-name: Daily OFL OTA Update [OFL barrier]
# vendor-dispatch path is also what makes post_merge_profiles.yml call the OTA auto-publish API after
# uploading - see post_merge_profiles.yml for both sides of that contract.
#
# Each run captures a timestamp, dispatches the needed OFL publishers, waits for
# all of them to finish, then clears the pending-publish table once. Changes merged
# after that timestamp remain pending for the next run.
# At the start of each run, the pending-publish table is cleared up to a captured
# timestamp (POST /api/v1/ota/ofl/pending/clear?timestamp=...). Changes merged after
# that timestamp remain pending for the next run.
on:
schedule:
@@ -36,14 +34,32 @@ jobs:
if: ${{ github.repository == 'OrcaSlicer/OrcaSlicer' }}
runs-on: ubuntu-24.04
steps:
- name: Capture start timestamp
- name: Capture start timestamp and clear OFL pending queue
id: start
shell: bash
env:
OTA_API_BASE_URL: ${{ vars.OTA_API_BASE_URL }}
OTA_API_KEY: ${{ secrets.OFL_OTA_PUBLISH_KEY }}
run: |
set -euo pipefail
[ -n "$OTA_API_BASE_URL" ] || { echo "::error::vars.OTA_API_BASE_URL is not set"; exit 1; }
[ -n "$OTA_API_KEY" ] || { echo "::error::secrets.OFL_OTA_PUBLISH_KEY is not set"; exit 1; }
timestamp="$(date -u +%s)"
echo "timestamp=$timestamp" >> "$GITHUB_OUTPUT"
resp_file="$RUNNER_TEMP/ota-pending-clear-response.json"
status="$(curl -sS -o "$resp_file" -w '%{http_code}' -X POST \
"${OTA_API_BASE_URL%/}/api/v1/ota/ofl/pending/clear?timestamp=$timestamp" \
-H "Authorization: Bearer $OTA_API_KEY")"
body="$(cat "$resp_file")"
echo "$body"
if [ "$status" != "200" ]; then
echo "::error::OTA pending-clear call failed with HTTP $status"
exit 1
fi
- name: Checkout repository
uses: actions/checkout@v7
with:
@@ -68,21 +84,27 @@ jobs:
| grep -E '^(main|release/v[0-9]+\.[0-9]+\.[0-9]+)$' | sort -u
)
# The cron run is the checkpoint: a successful run means every
# dispatched branch publisher completed and the pending queue was
# cleared. Manual or push-triggered post_merge_profiles runs are not
# checkpoints for this scan.
successful_cron_runs="$(gh api --method GET \
"repos/${{ github.repository }}/actions/workflows/ofl-ota-cronjob.yml/runs" \
-f status=success -f branch=main -f per_page=100 --paginate \
--jq '.workflow_runs[] | select((.display_title // "") | contains("[OFL barrier]"))')"
since="$(jq -rs 'sort_by(.run_started_at) | last.run_started_at // empty' <<< "$successful_cron_runs")"
for branch in "${branches[@]}"; do
echo "::group::$branch"
# post_merge_profiles.yml's own run history, not this workflow's: this
# workflow only ever runs against main (schedule, or workflow_dispatch
# --ref main), so its head branch never varies - filtering ITS history
# by $branch would never match anything except main. post_merge_profiles.yml
# genuinely runs per-branch (this dispatch below sets --ref "$branch"),
# so its history is the real per-branch checkpoint. It also means a
# failed publish naturally gets retried tomorrow: the checkpoint only
# advances on a run that actually succeeded.
# --method GET is required, not cosmetic: gh api defaults to POST
# whenever -f fields are present unless a method is given
# explicitly, and POST on this list-runs endpoint 404s - confirmed
# on real Actions infrastructure, not just reasoned about.
since="$(gh api --method GET "repos/${{ github.repository }}/actions/workflows/post_merge_profiles.yml/runs" \
-f status=success -f branch="$branch" -f per_page=1 \
--jq '.workflow_runs[0].run_started_at // empty')"
if [ -z "$since" ]; then
echo "No prior successful OFL cron run; checking $branch through $SCAN_UNTIL."
echo "No prior successful run for $branch; checking OFL changes up to $SCAN_UNTIL."
changed_files="$(git log --until="$SCAN_UNTIL" --name-only --pretty=format: "origin/$branch" -- \
resources/profiles/OrcaFilamentLibrary resources/profiles/OrcaFilamentLibrary.json \
| sed '/^$/d')"
@@ -102,98 +124,16 @@ jobs:
fi
if [ "$changed" = true ]; then
dispatch_id="${GITHUB_RUN_ID}-${branch//\//-}"
# Record successful dispatches for the barrier step below.
# Branches whose workflow predates workflow_dispatch are skipped
# with a warning, as they were before the barrier was added.
if gh workflow run post_merge_profiles.yml \
# Tolerate a per-branch failure (e.g. a pre-existing release branch
# whose post_merge_profiles.yml predates the vendor/auto_publish
# inputs) rather than aborting the whole scan under set -e.
if ! gh workflow run post_merge_profiles.yml \
--repo "${{ github.repository }}" \
--ref "$branch" \
-f vendor="$VENDOR" -f auto_publish=true \
-f ofl_cron_dispatch_id="$dispatch_id"; then
printf '%s\t%s\n' "$branch" "$dispatch_id" >> "$RUNNER_TEMP/ofl-dispatches.tsv"
else
echo "::warning::skipping $branch because post_merge_profiles.yml could not be dispatched at that ref"
-f vendor="$VENDOR" -f auto_publish=true; then
echo "::warning::failed to dispatch post_merge_profiles.yml for $branch - its post_merge_profiles.yml at this ref may predate the vendor/auto_publish inputs"
fi
fi
echo "::endgroup::"
done
- name: Wait for OFL publishers
id: wait
shell: bash
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
DISPATCHES_FILE: ${{ runner.temp }}/ofl-dispatches.tsv
run: |
set -euo pipefail
if [ ! -s "$DISPATCHES_FILE" ]; then
echo "No OFL publisher workflows were dispatched; pending queue will not be cleared."
echo "publishers_dispatched=false" >> "$GITHUB_OUTPUT"
exit 0
fi
: > "$RUNNER_TEMP/ofl-run-ids.tsv"
while IFS=$'\t' read -r branch dispatch_id; do
[ -n "$branch" ] || continue
echo "Waiting for OFL publisher on $branch ($dispatch_id)"
run_id=""
for _ in {1..120}; do
runs_json="$(gh api --method GET \
"repos/${{ github.repository }}/actions/workflows/post_merge_profiles.yml/runs" \
-f branch="$branch" -f event=workflow_dispatch -f per_page=100)"
run_id="$(jq -r --arg marker "[OFL cron $dispatch_id]" \
'[.workflow_runs[] | select((.display_title // "") | contains($marker))] \
| sort_by(.created_at) | last | .id // empty' <<< "$runs_json")"
[ -n "$run_id" ] && break
sleep 5
done
if [ -z "$run_id" ]; then
echo "::error::could not find dispatched post_merge_profiles run for $branch ($dispatch_id)"
exit 1
fi
printf '%s\t%s\n' "$branch" "$run_id" >> "$RUNNER_TEMP/ofl-run-ids.tsv"
done < "$DISPATCHES_FILE"
all_success=true
while IFS=$'\t' read -r branch run_id; do
[ -n "$run_id" ] || continue
echo "Watching OFL publisher run $run_id for $branch"
if ! gh run watch "$run_id" --repo "${{ github.repository }}" --exit-status; then
all_success=false
fi
done < "$RUNNER_TEMP/ofl-run-ids.tsv"
if [ "$all_success" != true ]; then
echo "::error::one or more OFL publisher workflows failed; pending queue will not be cleared"
exit 1
fi
echo "publishers_dispatched=true" >> "$GITHUB_OUTPUT"
- name: Clear OFL pending queue
if: steps.wait.outputs.publishers_dispatched == 'true'
shell: bash
env:
OTA_API_BASE_URL: ${{ vars.OTA_API_BASE_URL }}
OTA_API_KEY: ${{ secrets.OFL_OTA_PUBLISH_KEY }}
TIMESTAMP: ${{ steps.start.outputs.timestamp }}
run: |
set -euo pipefail
[ -n "$OTA_API_BASE_URL" ] || { echo "::error::vars.OTA_API_BASE_URL is not set"; exit 1; }
[ -n "$OTA_API_KEY" ] || { echo "::error::secrets.OFL_OTA_PUBLISH_KEY is not set"; exit 1; }
resp_file="$RUNNER_TEMP/ota-pending-clear-response.json"
status="$(curl -sS -o "$resp_file" -w '%{http_code}' -X POST \
"${OTA_API_BASE_URL%/}/api/v1/ota/ofl/pending/clear?timestamp=$TIMESTAMP" \
-H "Authorization: Bearer $OTA_API_KEY")"
body="$(cat "$resp_file")"
echo "$body"
if [ "$status" != "200" ]; then
echo "::error::OTA pending-clear call failed with HTTP $status"
exit 1
fi
-15
View File
@@ -1,14 +1,5 @@
name: Post-merge profiles
run-name: >-
Post-merge profiles${{
inputs.ofl_cron_dispatch_id != '' &&
inputs.vendor == 'OrcaFilamentLibrary' &&
(inputs.auto_publish == true || inputs.auto_publish == 'true') &&
format(' [OFL cron {0}]', inputs.ofl_cron_dispatch_id) ||
''
}}
# Push-triggered counterpart to check_profiles.yml (which only gates PRs). When a
# profile change lands on main or a release branch, rebuild the affected vendors'
# binary preset caches (<vendor>.opc) and publish each as a versioned ZIP asset on
@@ -68,12 +59,6 @@ on:
required: false
type: boolean
default: false
ofl_cron_dispatch_id:
description: >-
Unique marker supplied by the trusted OFL daily cron so it can find
and wait for this dispatched workflow run.
required: false
type: string
permissions:
contents: read
+1 -42
View File
@@ -834,37 +834,6 @@ find_package(OpenSSL REQUIRED)
find_package(CURL REQUIRED)
find_package(Freetype REQUIRED)
if (SLIC3R_GUI)
# LibDataChannel's installed export references its bundled dependencies,
# but does not install their CMake targets. Recreate those targets from
# the same dependency prefix before loading the LibDataChannel config.
if (NOT TARGET Usrsctp::usrsctp)
find_library(_ORCA_USRSCTP_LIBRARY NAMES usrsctp
PATHS "${CMAKE_PREFIX_PATH}/lib" NO_DEFAULT_PATH)
if (_ORCA_USRSCTP_LIBRARY)
add_library(Usrsctp::usrsctp UNKNOWN IMPORTED GLOBAL)
set_target_properties(Usrsctp::usrsctp PROPERTIES
IMPORTED_LOCATION "${_ORCA_USRSCTP_LIBRARY}"
IMPORTED_LINK_INTERFACE_LANGUAGES C
INTERFACE_LINK_LIBRARIES "Threads::Threads")
endif()
endif()
if (NOT TARGET LibJuice::LibJuice)
find_library(_ORCA_LIBJUICE_LIBRARY NAMES juice
PATHS "${CMAKE_PREFIX_PATH}/lib" NO_DEFAULT_PATH)
if (_ORCA_LIBJUICE_LIBRARY)
add_library(LibJuice::LibJuice UNKNOWN IMPORTED GLOBAL)
set_target_properties(LibJuice::LibJuice PROPERTIES
IMPORTED_LOCATION "${_ORCA_LIBJUICE_LIBRARY}"
IMPORTED_LINK_INTERFACE_LANGUAGES C
INTERFACE_LINK_LIBRARIES "Threads::Threads")
endif()
endif()
find_package(LibDataChannel CONFIG REQUIRED)
endif()
add_library(libcurl INTERFACE)
target_link_libraries(libcurl INTERFACE CURL::libcurl)
@@ -1145,7 +1114,6 @@ function(orcaslicer_copy_dlls target config postfix output_dlls)
endif ()
file(COPY ${_occt_dlls}
${CMAKE_PREFIX_PATH}/bin/freetype.dll
${CMAKE_PREFIX_PATH}/bin/avformat-61.dll
${CMAKE_PREFIX_PATH}/bin/avcodec-61.dll
${CMAKE_PREFIX_PATH}/bin/swresample-5.dll
${CMAKE_PREFIX_PATH}/bin/swscale-8.dll
@@ -1158,7 +1126,6 @@ function(orcaslicer_copy_dlls target config postfix output_dlls)
${_out_dir}/WebView2Loader.dll
${_out_dir}/freetype.dll
${_out_dir}/avformat-61.dll
${_out_dir}/avcodec-61.dll
${_out_dir}/swresample-5.dll
${_out_dir}/swscale-8.dll
@@ -1182,10 +1149,7 @@ function(orcaslicer_copy_sos target config postfix output_sos)
set(_out_dir "${CMAKE_CURRENT_BINARY_DIR}")
endif ()
file(COPY ${CMAKE_PREFIX_PATH}/lib/libavformat.so
${CMAKE_PREFIX_PATH}/lib/libavformat.so.61
${CMAKE_PREFIX_PATH}/lib/libavformat.so.61.1.100
${CMAKE_PREFIX_PATH}/lib/libavcodec.so
file(COPY ${CMAKE_PREFIX_PATH}/lib/libavcodec.so
${CMAKE_PREFIX_PATH}/lib/libavcodec.so.61
${CMAKE_PREFIX_PATH}/lib/libavcodec.so.61.3.100
${CMAKE_PREFIX_PATH}/lib/libavutil.so
@@ -1200,9 +1164,6 @@ function(orcaslicer_copy_sos target config postfix output_sos)
DESTINATION ${_out_dir})
set(${output_sos}
${_out_dir}/libavformat.so
${_out_dir}/libavformat.so.61
${_out_dir}/libavformat.so.61.1.100
${_out_dir}/libavcodec.so
${_out_dir}/libavcodec.so.61
${_out_dir}/libavcodec.so.61.3.100
@@ -1332,8 +1293,6 @@ endif ()
if (CMAKE_SYSTEM_NAME STREQUAL "Linux")
set(LIBRARY_FILES
${LIBDIR_BIN}/libavformat.so.61
${LIBDIR_BIN}/libavformat.so.61.1.100
${LIBDIR_BIN}/libavcodec.so.61
${LIBDIR_BIN}/libavcodec.so.61.3.100
${LIBDIR_BIN}/libavutil.so.59
+11 -23
View File
@@ -164,7 +164,7 @@ if (NOT _is_multi AND NOT CMAKE_BUILD_TYPE)
endif ()
function(orcaslicer_add_cmake_project projectname)
cmake_parse_arguments(P_ARGS "FORWARD_CONFIG" "INSTALL_DIR;BUILD_COMMAND;INSTALL_COMMAND;SOURCE_DIR" "CMAKE_ARGS" ${ARGN})
cmake_parse_arguments(P_ARGS "FORWARD_CONFIG" "INSTALL_DIR;BUILD_COMMAND;INSTALL_COMMAND" "CMAKE_ARGS" ${ARGN})
# MSVC is true for clang-cl as well, so the sub-build toolchain has to key on the
# generator. A non-Visual-Studio superbuild passes its own generator down, and with
@@ -210,18 +210,12 @@ function(orcaslicer_add_cmake_project projectname)
set(_build_j "-j${NPROC}")
endif ()
set(_source_dir_arg "")
if (P_ARGS_SOURCE_DIR)
set(_source_dir_arg SOURCE_DIR ${P_ARGS_SOURCE_DIR})
endif ()
if (NOT IS_CROSS_COMPILE OR NOT APPLE)
ExternalProject_Add(
dep_${projectname}
EXCLUDE_FROM_ALL ON
INSTALL_DIR ${DESTDIR}
DOWNLOAD_DIR ${DEP_DOWNLOAD_DIR}/${projectname}
${_source_dir_arg}
${_gen}
CMAKE_ARGS
-DCMAKE_POLICY_VERSION_MINIMUM=3.5
@@ -255,14 +249,12 @@ if (NOT IS_CROSS_COMPILE OR NOT APPLE)
# note for future devs: shared libs may actually create a size reduction
# but orcaslicer_deps tends to get really funny regarding linking after that (notably boost)
# so, as much as I would like to use that, it's not happening
if (NOT P_ARGS_SOURCE_DIR)
ExternalProject_Add_Step(dep_${projectname} free_download_space
DEPENDEES download # do after download
COMMENT "Freeing Space: Removing source archive"
WORKING_DIRECTORY ${DEP_DOWNLOAD_DIR}
COMMAND ${CMAKE_COMMAND} -E rm -rf ${projectname}
)
endif ()
ExternalProject_Add_Step(dep_${projectname} free_download_space
DEPENDEES download # do after download
COMMENT "Freeing Space: Removing source archive"
WORKING_DIRECTORY ${DEP_DOWNLOAD_DIR}
COMMAND ${CMAKE_COMMAND} -E rm -r ${projectname}
)
ExternalProject_Add_Step(dep_${projectname} free_build_space
DEPENDEES install # do after install
COMMENT "Freeing Space: Removing source and build files"
@@ -276,7 +268,6 @@ else()
EXCLUDE_FROM_ALL ON
INSTALL_DIR ${DESTDIR}
DOWNLOAD_DIR ${DEP_DOWNLOAD_DIR}/${projectname}
${_source_dir_arg}
${_gen}
CMAKE_ARGS
-DCMAKE_POLICY_VERSION_MINIMUM=3.5
@@ -405,6 +396,10 @@ include(libnoise/libnoise.cmake)
include(Draco/Draco.cmake)
include(FFMPEG/FFMPEG.cmake)
include(Assimp/Assimp.cmake)
# I *think* 1.1 is used for *just* md5 hashing?
# 3.1 has everything in the right place, but the md5 funcs used are deprecated
# a grep across the repo shows it is used for other things
@@ -415,12 +410,6 @@ if(NOT OPENSSL_FOUND)
set(OPENSSL_PKG dep_OpenSSL)
endif()
include(FFMPEG/FFMPEG.cmake)
include(Assimp/Assimp.cmake)
include(DataChannel/DataChannel.cmake)
set(DATACHANNEL_PKG dep_DataChannel)
# we don't want to load a "wrong" openssl when loading curl
# so, just don't even bother
# ...i think this is how it works? change if wrong
@@ -494,7 +483,6 @@ set(_dep_list
dep_wxInspector
dep_FFMPEG
dep_Assimp
${DATACHANNEL_PKG}
)
if (MSVC)
-37
View File
@@ -1,37 +0,0 @@
# libdatachannel is the native ICE/DTLS/SCTP implementation used by the
# GUI WebRTC camera controller. Keep the source revision fixed: the signaling
# protocol is evolving independently of this transport dependency.
#
# It vendors plog, usrsctp and libjuice as git submodules, which a plain
# GitHub tag tarball does not include. The flatpak sandbox has no network
# access during the build, so there the manifest itself clones the repo
# (submodules and all) into the dependency download directory before the
# sandbox closes. ExternalProject_Add is pointed at that existing checkout
# instead of being given its own network-dependent download method.
if (FLATPAK)
set(_datachannel_source
SOURCE_DIR ${DEP_DOWNLOAD_DIR}/DataChannel
)
else()
set(_datachannel_source
GIT_REPOSITORY https://github.com/paullouisageneau/libdatachannel.git
GIT_TAG v0.24.5
GIT_SHALLOW ON
GIT_SUBMODULES_RECURSE ON
)
endif()
orcaslicer_add_cmake_project(DataChannel
DEPENDS ${OPENSSL_PKG}
CMAKE_ARGS
-DNO_EXAMPLES=ON
-DNO_TESTS=ON
-DNO_WEBSOCKET=ON
-DNO_MEDIA=ON
-DUSE_NICE=OFF
-DUSE_SYSTEM_JUICE=OFF
-DUSE_SYSTEM_USRSCTP=OFF
-DOPENSSL_ROOT_DIR:PATH=${DESTDIR}
-DOPENSSL_USE_STATIC_LIBS=ON
${_datachannel_source}
)
+7 -23
View File
@@ -1,26 +1,14 @@
set(_conf_cmd ./configure)
set(_ffmpeg_depends)
set(_ffmpeg_configure_command ${_conf_cmd})
if (TARGET dep_OpenSSL)
set(_ffmpeg_depends DEPENDS dep_OpenSSL)
set(_ffmpeg_configure_command
${CMAKE_COMMAND} -E env
"PKG_CONFIG_PATH=${DESTDIR}/lib/pkgconfig:$ENV{PKG_CONFIG_PATH}"
${_conf_cmd}
)
endif()
if (MSVC)
set(_source_dir "${CMAKE_BINARY_DIR}/dep_FFMPEG-prefix/src/dep_FFMPEG")
set(PREBUILD_URL_arm64 "https://github.com/Noisyfox/FFmpeg-Builds-Orca/releases/download/autobuild-2026-09-18-16-50/ffmpeg-n7.0.3-33-g887d4b4919-winarm64-orca-shared-7.0.zip")
set(PREBUILD_HASH_arm64 "da480cbb39680056de824c57ec4dc3bd577b479ebbc310ff1f9dc55cf014b4c1")
set(PREBUILD_URL_x64 "https://github.com/Noisyfox/FFmpeg-Builds-Orca/releases/download/autobuild-2026-09-18-16-50/ffmpeg-n7.0.3-33-g887d4b4919-win64-orca-shared-7.0.zip")
set(PREBUILD_HASH_x64 "85da19daf198f5548259d8aabb349db84997a3f6e6886d8d7764114add9c6dae")
set(PREBUILD_URL_arm64 "https://github.com/Noisyfox/FFmpeg-Builds-Orca/releases/download/autobuild-2026-07-17-14-28/ffmpeg-n7.0.3-31-g9b6ffd74b5-winarm64-orca-shared-7.0.zip")
set(PREBUILD_HASH_arm64 "12f4140279f2f8469885e1b5b2e8be9d788882914c21523cacd56989f3548054")
set(PREBUILD_URL_x64 "https://github.com/Noisyfox/FFmpeg-Builds-Orca/releases/download/autobuild-2026-07-17-14-28/ffmpeg-n7.0.3-31-g9b6ffd74b5-win64-orca-shared-7.0.zip")
set(PREBUILD_HASH_x64 "e65916020ddb9ef84b2666dfbcbfc9b1d67f69d15b4a66db53754637bf2d498c")
ExternalProject_Add(dep_FFMPEG
${_ffmpeg_depends}
URL ${PREBUILD_URL_${DEPS_ARCH}}
URL_HASH SHA256=${PREBUILD_HASH_${DEPS_ARCH}}
DOWNLOAD_DIR ${DEP_DOWNLOAD_DIR}/FFMPEG
@@ -33,8 +21,6 @@ if (MSVC)
)
else ()
set(_openssl_cmd --enable-openssl)
if (APPLE)
set(_minos_cmd
"--extra-cflags=-mmacosx-version-min=${DEP_OSX_TARGET}"
@@ -66,11 +52,10 @@ else ()
endif()
ExternalProject_Add(dep_FFMPEG
${_ffmpeg_depends}
URL https://github.com/FFmpeg/FFmpeg/archive/refs/tags/n7.0.3.tar.gz
URL_HASH SHA256=DEEDCABE339165214A3637DF4C86A507AEF0D793CF8774FF68735F4737E8DDBC
DOWNLOAD_DIR ${DEP_DOWNLOAD_DIR}/FFMPEG
CONFIGURE_COMMAND ${_ffmpeg_configure_command}
CONFIGURE_COMMAND ${_conf_cmd}
${_cross_cmd}
${_pic_cmd}
${_arch_cmd}
@@ -78,21 +63,20 @@ else ()
"--prefix=${DESTDIR}"
${_link_cmd}
${_minos_cmd}
${_openssl_cmd}
--disable-doc
--enable-small
--disable-outdevs
--disable-filters
--enable-filter=*null*,afade,*fifo,*format,*resample,aeval,allrgb,allyuv,atempo,pan,*bars,color,*key,crop,draw*,eq*,framerate,*_qsv,*_vaapi,*v4l2*,hw*,scale,volume,test*
--disable-protocols
--enable-protocol=file,fd,pipe,http,https,rtp,tcp,udp
--enable-protocol=file,fd,pipe,rtp,udp
--disable-muxers
--enable-muxer=rtp
--disable-encoders
--disable-decoders
--enable-decoder=*aac*,h264*,mp3*,mjpeg,rv*
--disable-demuxers
--enable-demuxer=h264,mp3,mov,mpjpeg,rtsp,sdp
--enable-demuxer=h264,mp3,mov
--disable-zlib
--disable-avdevice
BUILD_IN_SOURCE ON
+45 -19
View File
@@ -28,8 +28,8 @@ Per-vendor granularity is what makes the system practical:
- A vendor whose profile is bumped invalidates only its own cache. The other 60-odd
vendors keep theirs — even when the bumped vendor is the shared Orca filament
library everyone else inherits from.
- The setup wizard, which loads vendors one at a time, gets the same speedup as
startup without a second code path.
- The setup wizard loads its vendors through the same routine as startup, so it
gets the same speedup without a second code path.
- A vendor with no cache, or a broken one, costs only that vendor a parse.
A cache holds *system* presets only. User presets, project settings and modified
@@ -162,15 +162,20 @@ cache nothing can invalidate is worse than no cache.
Vendors load in a fixed order, because filament inheritance crosses exactly one
boundary: any vendor's filament may inherit from the shared Orca filament library,
and nothing else reaches across vendors — an `include` is always vendor-local. The
library therefore goes first, alone; every other vendor follows in parallel, resolving
against it; and the results are merged in a stable order:
and nothing else reaches across vendors — an `include` is always vendor-local. Only
installing a vendor's presets crosses it; reading the vendor, from its cache or its
JSONs, needs nothing from the library. So every other vendor is read while the library
loads, each is installed against it as soon as both are done, and the results are
merged in a stable order:
```mermaid
flowchart LR
lib["1 · OrcaFilamentLibrary<br/>loaded first, synchronously"] --> par["2 · every other vendor in parallel,<br/>each into its own bundle, filaments<br/>resolving against the loaded library"] --> merge["3 · bundles merged into one,<br/>sequentially, in stable vendor order"]
lib["1 · OrcaFilamentLibrary loaded;<br/>meanwhile every other vendor read<br/>from its cache or its JSONs"] --> par["2 · every other vendor installed<br/>in parallel, each into its own bundle,<br/>filaments resolving against the library"] --> merge["3 · bundles merged into one,<br/>in one pass per collection,<br/>in stable vendor order"]
```
`PresetBundle::load_vendors` runs these steps for startup and for the setup wizard,
which hand it the vendors to load and the directory each is installed in.
Whether a vendor comes from its cache or from a parse changes nothing in that
order — both produce the same bundle, so cached and parsed vendors mix freely in
one startup.
@@ -211,11 +216,12 @@ shipped cache answered first, so the profile in `<data_dir>/system/` was never p
and its cache was never written back.
Serving from a cache is not a memory-image restore. The entries are deserialized and
then installed one by one — inheritance resolved against the presets installed before
them and the currently loaded filament library, includes layered in, configs flattened
onto the collection defaults, validated and registered — by the same function the JSON
path calls straight after parsing a sub-file. An `include` layers what the included
base states, between the parent and the preset's own keys: the base's diff against the
then installed by `install_vendor`, the routine the JSON path hands the vendor's entries
to once it has parsed the sub-files: inheritance resolved against the presets installed
before them and the currently loaded filament library, includes layered in, configs
flattened onto the collection defaults, validated and registered. An `include` layers
what the included base states, between the parent and the preset's own keys: the base's
diff against the
default, taken when the base itself was installed and before the per-variant padding
`inherits` sees, so only what a template sets reaches the presets including it. The two
paths share everything below the parse, which is what makes a cache-loaded bundle
@@ -223,6 +229,16 @@ indistinguishable from a JSON-loaded one by construction rather than by test cov
Installation also rebuilds each preset's file path from the local data directory, so a
shipped cache never carries the generating machine's paths.
Installing an entry is split in two. `resolve_vendor_preset` flattens it, reading only
what is registered under the names it inherits and includes, and `commit_vendor_preset`
registers it, the only step that writes anything shared. Entries resolve across threads
in runs and commit in the order the vendor lists them. A run ends before an entry that
inherits or includes one already in it, since that one's commit registers what the
entry resolves against, so no entry in a run reads what another in it registers. An
entry's parse messages are held until it commits. The bundle, the log's parse and
install messages and the error count therefore come out as parsing and installing one
entry at a time would leave them, whatever the listing order.
App upgrades work because a cache normally survives one. Only a deliberate
`CACHE_VERSION` bump makes an installed cache unreadable, and that is handled at
install time rather than at load: a vendor whose cache this build cannot read counts
@@ -254,17 +270,20 @@ the wizard caches the *derived JSON*, not another form of the inputs:
open, the wizard computes the current stamps (one version peek per vendor) and, when
they match, serves the catalog from the file — no bundle built, no preset installed.
Caching bundle inputs instead was tried and measured: rebuilding the bundle from
per-vendor caches costs ~2 s of preset installation whatever feeds it, so only
skipping the rebuild entirely wins.
per-vendor caches costs over a second of preset installation whatever feeds it, so
only skipping the rebuild entirely wins.
Any change to the set — a vendor added, removed or updated, or its cache-only
`.opc` replaced by a newer one — changes the stamps and retires the whole file;
the wizard then rebuilds the bundle vendor by vendor (per-vendor caches serving where
they cover) and writes the catalog back. Selections, region and per-open decorations
are applied downstream of the cache either way, so a served catalog is
indistinguishable from a rebuilt one. Nothing ships this file and the updater never
touches it; it is a locally written artifact, re-derived whenever stale, written
through a temp file and rename so half a cache is never readable.
the wizard then rebuilds the bundle with `PresetBundle::load_vendors`, the load
startup uses (per-vendor caches serving where they cover), and writes the catalog
back. When a vendor fails to load, the filament library included, that open falls
back to the wizard's own scan of the vendor JSONs, as when no bundle can be built,
and writes nothing. Selections, region and per-open decorations are applied
downstream of the cache either way, so a served catalog is indistinguishable from a
rebuilt one. Nothing ships this file and the updater never touches it; it is a
locally written artifact, re-derived whenever stale, written through a temp file and
rename so half a cache is never readable.
The cache lives under `<data_dir>/cache/`, not beside the vendors: everything that
scans `<data_dir>/system/` treats any `.opc` there as a vendor, so a non-vendor
@@ -378,6 +397,13 @@ enumerates only `*.json` will find no vendors at all in a packaged build.
the `CachedPreset` field list — written and read by `visit_entry` in
`PresetCacheFormat.cpp`, one list for the save, the load and the name peek alike — or
the cache's own layout or stamps, requires bumping `CACHE_VERSION` by hand.
- **Adding a kind of reference between presets**, as `inherits` and `include` are:
parse the names into `CachedPreset` (a field change, so `CACHE_VERSION` is bumped),
have `install_vendor_entries` end a run before an entry that names one already in it
and retain what the names point at, look them up only in `resolve_vendor_preset`, and
register what they point at only in `commit_vendor_preset`. The listing-order test in
`test_vendor_cache.cpp` fails for a kind the runs do not check once its fixture uses
it.
- **The dictionary indexes with a `uint16`**, so `print_config_def` may hold at most
65535 options and one cache at most 65535 distinct enum value names.
`CacheDictionary::save` throws past that, which surfaces when CI generates the
-240
View File
@@ -1,240 +0,0 @@
# Printer agents
Printer agents isolate printer-specific communication from the rest of OrcaSlicer. The GUI and
`DeviceManager` operate on a shared set of printer operations and device state; a selected printer
agent implements those operations for a particular printer ecosystem. The agent boundary allows
Bambu, Moonraker-based printers, built-in integrations, and Python-provided integrations to use the
same application workflow without making the GUI understand every printer protocol.
The current boundary is an adapter boundary around the existing application contract. In particular,
some request fields and message payloads still use the Bambu-shaped representation that existing
`MachineObject` and `DeviceManager` code consumes. The printer agent is responsible for translating
that representation into the protocol spoken by its printer. This is an intentional compatibility
constraint of the current design; the interface is not yet a neutral printer protocol.
The v1 dialect migration path is deliberately narrow. `DeviceManager` currently speaks the Bambu JSON
dialect because that is the payload shape already used throughout the command and state workflow. The
v1 `OrcaPrinterAgent` also accepts that Bambu dialect. Its transport path places the small translation
needed for the target printer at `deliver_to_sink`, keeping the compatibility code at the edge rather
than spreading it through `DeviceManager` or the agent interface.
The eventual direction is for `DeviceManager` to produce an Orca JSON dialect. The Bambu agent will then
own the translation from Orca JSON to Bambu's protocol, while `OrcaPrinterAgent` can forward the Orca
payload directly to its sink. The v1 translation at `deliver_to_sink` can then be removed without
changing `DeviceManager`, the command callers, or the rest of the agent workflow.
## Components
The system has four relevant layers:
```text
GUI / DeviceManager / MachineObject
|
NetworkAgent
/ \
IPrinterAgent ICloudServiceAgent
| |
printer protocol authentication and cloud services
```
### `DeviceManager` and `MachineObject`
`DeviceManager` owns the application-facing printer workflow. It maintains `MachineObject` instances,
updates their state, filters devices for the active printer agent, and initiates operations such as
homing, temperature changes, printing, subscriptions, and camera playback.
`MachineObject` remains the shared state model used by the GUI. It does not contain the implementation
of a printer protocol. When a device is discovered or returned by a cloud query, the device is tagged
with the active `printer_agent_id`. Device lists and selected-machine operations use that tag to avoid
sending an operation through an agent that does not own the device.
### `NetworkAgent`
`NetworkAgent` is the façade used by the GUI and `DeviceManager`. It owns:
- the currently selected `IPrinterAgent`;
- the registered cloud-service instances, indexed by provider;
- callbacks shared by the active printer agent and the application;
- the forwarding methods for printer commands and cloud operations.
There is one active printer agent for the currently selected printer preset. Switching the preset
increments the machine-list generation, disconnects the old printer agent, removes its callbacks, and
installs the newly selected agent. The façade then forwards printer operations to that agent.
Cloud operations are selected separately using a provider key. `NetworkAgent` forwards a cloud request
to the matching `ICloudServiceAgent`, and forwards cloud camera operations with a device ID. The
printer agent receives a cloud-agent pointer through `set_cloud_agent()` when it is created, allowing
printer communication to obtain cloud tokens without depending on a concrete cloud implementation.
### `IPrinterAgent`
`IPrinterAgent` is the printer-facing contract. It covers:
- cloud-relay and direct-LAN message delivery;
- LAN connection, discovery, binding, and certificates;
- printer subscriptions and callbacks;
- print operations;
- filament synchronization;
- camera capability and local camera URL reporting;
- printer command methods.
Concrete built-in implementations include the Bambu wrapper, the native Orca/Moonraker path, and
other printer-agent implementations registered by the application. A printer agent may use either
the cloud agent, a direct LAN connection, or both.
### `ICloudServiceAgent`
`ICloudServiceAgent` owns authentication and services provided by a cloud backend. It covers login
state, tokens, user and printer lists, settings synchronization, model services, cloud messages, and
cloud camera operations.
Cloud camera operations are device-scoped:
- `get_camera_url(dev_id, callback)` obtains a stream URL for one device;
- `create_camera_signaling_channel(dev_id)` creates signaling for one device where the provider
supports it.
This is separate from the local camera URL exposed by `IPrinterAgent`, which is currently scoped to
the active printer agent because a normal LAN agent represents one physical printer connection.
## Agent registration and selection
`NetworkAgentFactory` maintains the printer-agent registry. Each registry entry contains an agent ID,
a display name, and a factory function. Built-in agents register during application initialization.
Python printer-agent capabilities register dynamically and contribute an agent ID and factory entry.
The selected printer preset contains the printer-agent choice. If no explicit choice is stored, the
application preserves the existing default behavior: Bambu presets select the Bambu agent and other
presets select the native Orca agent. When a preset is changed, `GUI_App` resolves the effective agent
ID, obtains the corresponding cloud agent, creates the printer agent through the registry, and installs
it in `NetworkAgent`.
The registry rejects conflicting agent IDs. This matters for Python plugins because an agent ID is the
stable identity used by presets and device ownership; two enabled plugin capabilities must not claim
the same ID.
## Message and command flow
There are two low-level message paths:
- `send_message()` publishes a command through the printer's cloud relay;
- `send_message_to_printer()` sends a command directly to the printer over the LAN path.
Both paths accept a JSON string, quality-of-service and flag values, and return the existing network
status code domain. The agent owns the conversion from that JSON contract to its native transport.
The typed `command_*` methods are the application-facing convenience layer. The five generic defaults
currently implemented by `IPrinterAgent` construct the existing JSON dialect and route through the
same message path:
| Method | Default operation |
| --- | --- |
| `command_xyz_abs()` | Send `G90` for absolute positioning |
| `command_auto_leveling()` | Send `G29` for bed leveling |
| `command_go_home()` | Use the supported homing operation or send `G28` |
| `command_set_bed()` | Use the supported bed control or send `M140` |
| `command_set_nozzle()` | Send `M104` for nozzle temperature |
These are compatibility defaults for common printer workflows, not a guarantee that every firmware
implements every command identically. An agent can override a method when its protocol needs another
operation. For example, a Klipper configuration may use `BED_MESH_CALIBRATE` instead of `G29`.
The remaining common command methods default to `ORCA_NETWORK_ERR_CMD_NOT_SUPPORTED` because their
existing behavior is vendor-specific or has no portable implementation:
- AMS RFID refresh;
- AMS calibration;
- AMS tray selection;
- camera start;
- axis control.
The methods remain on the common interface so an agent that supports them can override them explicitly.
`sequence_id` remains part of the command contract because `DeviceManager` creates and tracks it as
the command ID.
## Device ownership and stale responses
Printer-agent ownership is represented by `printer_agent_id` on device records and `MachineObject`
instances. The active agent ID is attached when a device is discovered, returned by a cloud list, or
reused after a preset switch. Local-machine configuration also persists the agent ID so a saved LAN
device is not silently reused by an unrelated agent.
Cloud printer-list responses carry three pieces of request context added by `NetworkAgent`:
```text
provider cloud provider used for the request
agent_id active printer agent when the request was made
generation machine-list generation when the request was made
```
`DeviceManager` accepts the response only when those values still match the current provider, active
agent, and generation. This prevents a slow response from the previous preset or provider from
repopulating the current device list.
The provider mapping is currently selected by `GUI_App`: the Bambu agent maps to the Bambu cloud
provider and other agents map to the Orca cloud provider. The generation check protects that existing
selection from races; it does not make cloud-provider ownership intrinsic to an agent. Cloud-printer
ownership and the broader Orca cloud services are therefore still separate architectural concerns.
## Python printer agents
`PrinterAgentPluginCapability` implements `IPrinterAgent` directly. The live capability object is
registered with `NetworkAgentFactory` and handed out as the printer agent when its agent ID is selected.
The plugin receives the selected `ICloudServiceAgent` through `set_cloud_agent()` just like a built-in
printer agent.
Python plugins must implement the core communication and lifecycle methods required by the interface,
including agent metadata, printer connection, discovery callbacks, and the two message-send methods.
Methods that are meaningful only to a particular printer are optional overrides where the C++ base
class provides a default.
All ten `command_*` methods are available in the Python binding and in the trampoline. Their override
status is intentionally optional:
- the five generic commands use the C++ default when Python does not override them;
- the five vendor-specific commands return `NOT_SUPPORTED` unless Python supplies an implementation;
- a Python implementation can replace either behavior for its own protocol.
The Python camera binding exposes HTTP, HTTPS, RTSP, and HTTP-snapshot modes. WebRTC remains a
built-in C++ camera mode, but is not exposed as a Python mode because the current Python capability
does not provide the corresponding cloud signaling-channel contract.
## Camera playback boundary
The camera stream mode describes how a stream is obtained; it does not by itself define ownership of
the wxWidgets view that renders it. `MediaPlayCtrl` selects and tears down the active backend, while
the wx parent owns the child window or renderer. This is important because a web view, native media
control, and frame-based/WebRTC renderer have different wx window-lifetime requirements.
Cloud URL and signaling requests are routed through `NetworkAgent` to the cloud provider selected for
the device. Local URL requests are routed to the active printer agent. The distinction keeps cloud
account services device-scoped while preserving the current one-LAN-agent/one-printer model.
## Compatibility constraints
The printer-agent boundary intentionally preserves several existing application contracts:
- Bambu-shaped JSON is still the shared command representation;
- existing network status codes are reused, with Orca-specific unsupported/capability errors added
in the Orca-reserved range;
- `MachineObject` remains the shared device-state model;
- preset and local-machine data retain compatibility with the existing agent-selection behavior;
- Python plugins use the existing capability and pybind11 registration system.
The agent abstraction is therefore responsible for containing vendor differences, not for pretending
that all vendor protocols are identical. The planned Orca JSON dialect is the protocol-neutral command
model for the `DeviceManager`/agent boundary. Once it is introduced, Bambu-specific translation remains
inside the Bambu agent and the Orca agent's v1 sink adapter can be removed as a self-contained cleanup.
## Main implementation locations
- [`IPrinterAgent`](../../src/slic3r/Utils/IPrinterAgent.hpp) — printer-agent contract and generic command defaults
- [`ICloudServiceAgent`](../../src/slic3r/Utils/ICloudServiceAgent.hpp) — cloud service and per-device
cloud camera contract
- [`NetworkAgent`](../../src/slic3r/Utils/NetworkAgent.hpp) — façade and dispatch between active agents
- [`NetworkAgentFactory`](../../src/slic3r/Utils/NetworkAgentFactory.hpp) — built-in and Python agent registry
- [`DeviceManager`](../../src/slic3r/GUI/DeviceCore/DevManager.cpp) — device ownership, filtering, and
stale-response checks
- [`PrinterAgentPluginCapability`](../../src/slic3r/plugin/pluginTypes/printerAgent/PrinterAgentPluginCapability.cpp)
— Python bindings
- [`MediaPlayCtrl`](../../src/slic3r/GUI/MediaPlayCtrl.cpp) — camera backend selection and playback lifecycle
+55 -155
View File
@@ -1,6 +1,6 @@
{
"name": "Blocks",
"version": "02.04.00.13",
"version": "02.04.00.04",
"force_update": "0",
"description": "Blocks configurations",
"machine_model_list": [
@@ -43,8 +43,8 @@
"sub_path": "process/0.16mm Optimal 0.4 nozzle @Blocks_RF50.json"
},
{
"name": "0.20mm Standard 0.4 nozzle @Blocks_Pro_S100",
"sub_path": "process/0.20mm Standard 0.4 nozzle @Blocks_Pro_S100.json"
"name": "0.20mm Standard 0.4 nozzle @Blocks",
"sub_path": "process/0.20mm Standard 0.4 nozzle @Blocks.json"
},
{
"name": "0.20mm Standard 0.4 nozzle @Blocks_RD50_V2",
@@ -55,8 +55,8 @@
"sub_path": "process/0.20mm Standard 0.4 nozzle @Blocks_RF50.json"
},
{
"name": "0.24mm Draft 0.4 nozzle @Blocks_Pro_S100",
"sub_path": "process/0.24mm Draft 0.4 nozzle @Blocks_Pro_S100.json"
"name": "0.24mm Draft 0.4 nozzle @Blocks",
"sub_path": "process/0.24mm Draft 0.4 nozzle @Blocks.json"
},
{
"name": "0.24mm Draft 0.4 nozzle @Blocks_RD50_V2",
@@ -75,8 +75,8 @@
"sub_path": "process/0.28mm Extra Draft 0.4 nozzle @Blocks_RF50.json"
},
{
"name": "0.30mm Extra Draft 0.4 nozzle @Blocks_Pro_S100",
"sub_path": "process/0.30mm Extra Draft 0.4 nozzle @Blocks_Pro_S100.json"
"name": "0.30mm Extra Draft 0.4 nozzle @Blocks",
"sub_path": "process/0.30mm Extra Draft 0.4 nozzle @Blocks.json"
},
{
"name": "fdm_process_common 0.6 nozzle",
@@ -111,8 +111,8 @@
"sub_path": "process/0.26mm Standard 0.6 nozzle @Blocks_RF50.json"
},
{
"name": "0.30mm Standard 0.6 nozzle @Blocks_Pro_S100",
"sub_path": "process/0.30mm Standard 0.6 nozzle @Blocks_Pro_S100.json"
"name": "0.30mm Standard 0.6 nozzle @Blocks",
"sub_path": "process/0.30mm Standard 0.6 nozzle @Blocks.json"
},
{
"name": "0.32mm Draft 0.6 nozzle @Blocks_RD50_V2",
@@ -131,12 +131,12 @@
"sub_path": "process/0.38mm Extra Draft 0.6 nozzle @Blocks_RF50.json"
},
{
"name": "0.40mm Draft 0.6 nozzle @Blocks_Pro_S100",
"sub_path": "process/0.40mm Draft 0.6 nozzle @Blocks_Pro_S100.json"
"name": "0.40mm Draft 0.6 nozzle @Blocks",
"sub_path": "process/0.40mm Draft 0.6 nozzle @Blocks.json"
},
{
"name": "0.30mm Optimal 0.8 nozzle @Blocks_Pro_S100",
"sub_path": "process/0.30mm Optimal 0.8 nozzle @Blocks_Pro_S100.json"
"name": "0.30mm Optimal 0.8 nozzle @Blocks",
"sub_path": "process/0.30mm Optimal 0.8 nozzle @Blocks.json"
},
{
"name": "0.30mm Optimal 0.8 nozzle @Blocks_RD50_V2",
@@ -155,8 +155,8 @@
"sub_path": "process/0.38mm Standard 0.8 nozzle @Blocks_RF50.json"
},
{
"name": "0.40mm Standard 0.8 nozzle @Blocks_Pro_S100",
"sub_path": "process/0.40mm Standard 0.8 nozzle @Blocks_Pro_S100.json"
"name": "0.40mm Standard 0.8 nozzle @Blocks",
"sub_path": "process/0.40mm Standard 0.8 nozzle @Blocks.json"
},
{
"name": "0.46mm Draft 0.8 nozzle @Blocks_RD50_V2",
@@ -167,8 +167,8 @@
"sub_path": "process/0.46mm Draft 0.8 nozzle @Blocks_RF50.json"
},
{
"name": "0.50mm Draft 0.8 nozzle @Blocks_Pro_S100",
"sub_path": "process/0.50mm Draft 0.8 nozzle @Blocks_Pro_S100.json"
"name": "0.50mm Draft 0.8 nozzle @Blocks",
"sub_path": "process/0.50mm Draft 0.8 nozzle @Blocks.json"
},
{
"name": "0.54mm Extra Draft 0.8 nozzle @Blocks_RD50_V2",
@@ -179,36 +179,36 @@
"sub_path": "process/0.54mm Extra Draft 0.8 nozzle @Blocks_RF50.json"
},
{
"name": "0.30mm Optimal 1.0 nozzle @Blocks_Pro_S100",
"sub_path": "process/0.30mm Optimal 1.0 nozzle @Blocks_Pro_S100.json"
"name": "0.30mm Optimal 1.0 nozzle @Blocks",
"sub_path": "process/0.30mm Optimal 1.0 nozzle @Blocks.json"
},
{
"name": "0.50mm Standard 1.0 nozzle @Blocks_Pro_S100",
"sub_path": "process/0.50mm Standard 1.0 nozzle @Blocks_Pro_S100.json"
"name": "0.50mm Standard 1.0 nozzle @Blocks",
"sub_path": "process/0.50mm Standard 1.0 nozzle @Blocks.json"
},
{
"name": "0.60mm Draft 1.0 nozzle @Blocks_Pro_S100",
"sub_path": "process/0.60mm Draft 1.0 nozzle @Blocks_Pro_S100.json"
"name": "0.60mm Draft 1.0 nozzle @Blocks",
"sub_path": "process/0.60mm Draft 1.0 nozzle @Blocks.json"
},
{
"name": "0.70mm Extra Draft 1.0 nozzle @Blocks_Pro_S100",
"sub_path": "process/0.70mm Extra Draft 1.0 nozzle @Blocks_Pro_S100.json"
"name": "0.70mm Extra Draft 1.0 nozzle @Blocks",
"sub_path": "process/0.70mm Extra Draft 1.0 nozzle @Blocks.json"
},
{
"name": "0.50mm Optimal 1.2 nozzle @Blocks_Pro_S100",
"sub_path": "process/0.50mm Optimal 1.2 nozzle @Blocks_Pro_S100.json"
"name": "0.50mm Optimal 1.2 nozzle @Blocks",
"sub_path": "process/0.50mm Optimal 1.2 nozzle @Blocks.json"
},
{
"name": "0.60mm Standard 1.2 nozzle @Blocks_Pro_S100",
"sub_path": "process/0.60mm Standard 1.2 nozzle @Blocks_Pro_S100.json"
"name": "0.60mm Standard 1.2 nozzle @Blocks",
"sub_path": "process/0.60mm Standard 1.2 nozzle @Blocks.json"
},
{
"name": "0.70mm Draft 1.2 nozzle @Blocks_Pro_S100",
"sub_path": "process/0.70mm Draft 1.2 nozzle @Blocks_Pro_S100.json"
"name": "0.70mm Draft 1.2 nozzle @Blocks",
"sub_path": "process/0.70mm Draft 1.2 nozzle @Blocks.json"
},
{
"name": "0.80mm Extra Draft 1.2 nozzle @Blocks_Pro_S100",
"sub_path": "process/0.80mm Extra Draft 1.2 nozzle @Blocks_Pro_S100.json"
"name": "0.80mm Extra Draft 1.2 nozzle @Blocks",
"sub_path": "process/0.80mm Extra Draft 1.2 nozzle @Blocks.json"
}
],
"filament_list": [
@@ -249,148 +249,48 @@
"sub_path": "filament/fdm_filament_tpu.json"
},
{
"name": "Blocks ABS @base",
"sub_path": "filament/Blocks ABS @base.json"
"name": "Generic ABS @Blocks",
"sub_path": "filament/Generic ABS @Blocks.json"
},
{
"name": "Blocks ASA @base",
"sub_path": "filament/Blocks ASA @base.json"
"name": "Generic ASA @Blocks",
"sub_path": "filament/Generic ASA @Blocks.json"
},
{
"name": "Blocks ASA-CF @base",
"sub_path": "filament/Blocks ASA-CF @base.json"
"name": "Generic ASA-CF @Blocks",
"sub_path": "filament/Generic ASA-CF @Blocks.json"
},
{
"name": "Blocks PA @base",
"sub_path": "filament/Blocks PA @base.json"
"name": "Generic PA @Blocks",
"sub_path": "filament/Generic PA @Blocks.json"
},
{
"name": "Blocks PA-CF @base",
"sub_path": "filament/Blocks PA-CF @base.json"
"name": "Generic PA-CF @Blocks",
"sub_path": "filament/Generic PA-CF @Blocks.json"
},
{
"name": "Blocks PC @base",
"sub_path": "filament/Blocks PC @base.json"
"name": "Generic PC @Blocks",
"sub_path": "filament/Generic PC @Blocks.json"
},
{
"name": "Blocks PETG @base",
"sub_path": "filament/Blocks PETG @base.json"
"name": "Generic PETG @Blocks",
"sub_path": "filament/Generic PETG @Blocks.json"
},
{
"name": "Blocks PLA @base",
"sub_path": "filament/Blocks PLA @base.json"
"name": "Generic PLA @Blocks",
"sub_path": "filament/Generic PLA @Blocks.json"
},
{
"name": "Blocks PLA-CF @base",
"sub_path": "filament/Blocks PLA-CF @base.json"
"name": "Generic PLA-CF @Blocks",
"sub_path": "filament/Generic PLA-CF @Blocks.json"
},
{
"name": "Blocks PVA @base",
"sub_path": "filament/Blocks PVA @base.json"
"name": "Generic PVA @Blocks",
"sub_path": "filament/Generic PVA @Blocks.json"
},
{
"name": "Blocks TPU @base",
"sub_path": "filament/Blocks TPU @base.json"
},
{
"name": "Blocks ABS @Blocks_RD50_V2",
"sub_path": "filament/Blocks ABS @Blocks_RD50_V2.json"
},
{
"name": "Blocks ABS @Blocks_RF50",
"sub_path": "filament/Blocks ABS @Blocks_RF50.json"
},
{
"name": "Blocks ASA @Blocks_RD50_V2",
"sub_path": "filament/Blocks ASA @Blocks_RD50_V2.json"
},
{
"name": "Blocks ASA @Blocks_RF50",
"sub_path": "filament/Blocks ASA @Blocks_RF50.json"
},
{
"name": "Blocks ASA-CF @Blocks_RD50_V2",
"sub_path": "filament/Blocks ASA-CF @Blocks_RD50_V2.json"
},
{
"name": "Blocks ASA-CF @Blocks_RF50",
"sub_path": "filament/Blocks ASA-CF @Blocks_RF50.json"
},
{
"name": "Blocks PA @Blocks_RD50_V2",
"sub_path": "filament/Blocks PA @Blocks_RD50_V2.json"
},
{
"name": "Blocks PA @Blocks_RF50",
"sub_path": "filament/Blocks PA @Blocks_RF50.json"
},
{
"name": "Blocks PA-CF @Blocks_RD50_V2",
"sub_path": "filament/Blocks PA-CF @Blocks_RD50_V2.json"
},
{
"name": "Blocks PA-CF @Blocks_RF50",
"sub_path": "filament/Blocks PA-CF @Blocks_RF50.json"
},
{
"name": "Blocks PC @Blocks_RD50_V2",
"sub_path": "filament/Blocks PC @Blocks_RD50_V2.json"
},
{
"name": "Blocks PC @Blocks_RF50",
"sub_path": "filament/Blocks PC @Blocks_RF50.json"
},
{
"name": "Blocks PETG @Blocks_Pro_S100",
"sub_path": "filament/Blocks PETG @Blocks_Pro_S100.json"
},
{
"name": "Blocks PETG @Blocks_RD50_V2",
"sub_path": "filament/Blocks PETG @Blocks_RD50_V2.json"
},
{
"name": "Blocks PETG @Blocks_RF50",
"sub_path": "filament/Blocks PETG @Blocks_RF50.json"
},
{
"name": "Blocks PLA @Blocks_Pro_S100",
"sub_path": "filament/Blocks PLA @Blocks_Pro_S100.json"
},
{
"name": "Blocks PLA @Blocks_RD50_V2",
"sub_path": "filament/Blocks PLA @Blocks_RD50_V2.json"
},
{
"name": "Blocks PLA @Blocks_RF50",
"sub_path": "filament/Blocks PLA @Blocks_RF50.json"
},
{
"name": "Blocks PLA-CF @Blocks_RD50_V2",
"sub_path": "filament/Blocks PLA-CF @Blocks_RD50_V2.json"
},
{
"name": "Blocks PLA-CF @Blocks_RF50",
"sub_path": "filament/Blocks PLA-CF @Blocks_RF50.json"
},
{
"name": "Blocks PVA @Blocks_RD50_V2",
"sub_path": "filament/Blocks PVA @Blocks_RD50_V2.json"
},
{
"name": "Blocks PVA @Blocks_RF50",
"sub_path": "filament/Blocks PVA @Blocks_RF50.json"
},
{
"name": "Blocks TPU @Blocks_Pro_S100",
"sub_path": "filament/Blocks TPU @Blocks_Pro_S100.json"
},
{
"name": "Blocks TPU @Blocks_RD50_V2",
"sub_path": "filament/Blocks TPU @Blocks_RD50_V2.json"
},
{
"name": "Blocks TPU @Blocks_RF50",
"sub_path": "filament/Blocks TPU @Blocks_RF50.json"
"name": "Generic TPU @Blocks",
"sub_path": "filament/Generic TPU @Blocks.json"
}
],
"machine_list": [
@@ -1,14 +0,0 @@
{
"type": "filament",
"name": "Blocks ABS @Blocks_RD50_V2",
"inherits": "Blocks ABS @base",
"from": "system",
"setting_id": "qhQ0qQDGJgsueCAF",
"instantiation": "true",
"compatible_printers": [
"BLOCKS RD50 V2 0.4 nozzle",
"BLOCKS RD50 V2 0.6 nozzle",
"BLOCKS RD50 V2 0.8 nozzle"
],
"renamed_from": "Blocks Generic ABS;Generic ABS @Blocks"
}
@@ -1,13 +0,0 @@
{
"type": "filament",
"name": "Blocks ABS @Blocks_RF50",
"inherits": "Blocks ABS @base",
"from": "system",
"setting_id": "bGYMFFdKiROKuSiY",
"instantiation": "true",
"compatible_printers": [
"BLOCKS RF50 0.4 nozzle",
"BLOCKS RF50 0.6 nozzle",
"BLOCKS RF50 0.8 nozzle"
]
}
@@ -1,14 +0,0 @@
{
"type": "filament",
"name": "Blocks ASA @Blocks_RD50_V2",
"inherits": "Blocks ASA @base",
"from": "system",
"setting_id": "HW7xDXThvpVFbMq5",
"instantiation": "true",
"compatible_printers": [
"BLOCKS RD50 V2 0.4 nozzle",
"BLOCKS RD50 V2 0.6 nozzle",
"BLOCKS RD50 V2 0.8 nozzle"
],
"renamed_from": "Blocks Generic ASA;Generic ASA @Blocks"
}
@@ -1,13 +0,0 @@
{
"type": "filament",
"name": "Blocks ASA @Blocks_RF50",
"inherits": "Blocks ASA @base",
"from": "system",
"setting_id": "lBW7SCsvawi4icNU",
"instantiation": "true",
"compatible_printers": [
"BLOCKS RF50 0.4 nozzle",
"BLOCKS RF50 0.6 nozzle",
"BLOCKS RF50 0.8 nozzle"
]
}
@@ -1,14 +0,0 @@
{
"type": "filament",
"name": "Blocks ASA-CF @Blocks_RD50_V2",
"inherits": "Blocks ASA-CF @base",
"from": "system",
"setting_id": "x5ksnGLGGy1PgDns",
"instantiation": "true",
"compatible_printers": [
"BLOCKS RD50 V2 0.4 nozzle",
"BLOCKS RD50 V2 0.6 nozzle",
"BLOCKS RD50 V2 0.8 nozzle"
],
"renamed_from": "Blocks Generic ASA-CF;Generic ASA-CF @Blocks"
}
@@ -1,13 +0,0 @@
{
"type": "filament",
"name": "Blocks ASA-CF @Blocks_RF50",
"inherits": "Blocks ASA-CF @base",
"from": "system",
"setting_id": "pcbd5qFiMZAvDPg1",
"instantiation": "true",
"compatible_printers": [
"BLOCKS RF50 0.4 nozzle",
"BLOCKS RF50 0.6 nozzle",
"BLOCKS RF50 0.8 nozzle"
]
}
@@ -1,14 +0,0 @@
{
"type": "filament",
"name": "Blocks PA @Blocks_RD50_V2",
"inherits": "Blocks PA @base",
"from": "system",
"setting_id": "gxD81n2jjrReLD29",
"instantiation": "true",
"compatible_printers": [
"BLOCKS RD50 V2 0.4 nozzle",
"BLOCKS RD50 V2 0.6 nozzle",
"BLOCKS RD50 V2 0.8 nozzle"
],
"renamed_from": "Blocks Generic PA;Generic PA @Blocks"
}
@@ -1,13 +0,0 @@
{
"type": "filament",
"name": "Blocks PA @Blocks_RF50",
"inherits": "Blocks PA @base",
"from": "system",
"setting_id": "PzKxRMDnQUzVWRer",
"instantiation": "true",
"compatible_printers": [
"BLOCKS RF50 0.4 nozzle",
"BLOCKS RF50 0.6 nozzle",
"BLOCKS RF50 0.8 nozzle"
]
}
@@ -1,17 +0,0 @@
{
"type": "filament",
"name": "Blocks PA @base",
"inherits": "fdm_filament_pa",
"from": "system",
"filament_id": "OFidOgK4",
"instantiation": "false",
"filament_vendor": [
"Blocks"
],
"filament_flow_ratio": [
"0.98"
],
"filament_max_volumetric_speed": [
"10"
]
}
@@ -1,14 +0,0 @@
{
"type": "filament",
"name": "Blocks PA-CF @Blocks_RD50_V2",
"inherits": "Blocks PA-CF @base",
"from": "system",
"setting_id": "EtbeKx3rbhUo1TtM",
"instantiation": "true",
"compatible_printers": [
"BLOCKS RD50 V2 0.4 nozzle",
"BLOCKS RD50 V2 0.6 nozzle",
"BLOCKS RD50 V2 0.8 nozzle"
],
"renamed_from": "Blocks Generic PA-CF;Generic PA-CF @Blocks"
}
@@ -1,13 +0,0 @@
{
"type": "filament",
"name": "Blocks PA-CF @Blocks_RF50",
"inherits": "Blocks PA-CF @base",
"from": "system",
"setting_id": "udI8Q4X7HaPIaPt4",
"instantiation": "true",
"compatible_printers": [
"BLOCKS RF50 0.4 nozzle",
"BLOCKS RF50 0.6 nozzle",
"BLOCKS RF50 0.8 nozzle"
]
}
@@ -1,14 +0,0 @@
{
"type": "filament",
"name": "Blocks PC @Blocks_RD50_V2",
"inherits": "Blocks PC @base",
"from": "system",
"setting_id": "HLc0shWnKwJzSFyW",
"instantiation": "true",
"compatible_printers": [
"BLOCKS RD50 V2 0.4 nozzle",
"BLOCKS RD50 V2 0.6 nozzle",
"BLOCKS RD50 V2 0.8 nozzle"
],
"renamed_from": "Blocks Generic PC;Generic PC @Blocks"
}
@@ -1,13 +0,0 @@
{
"type": "filament",
"name": "Blocks PC @Blocks_RF50",
"inherits": "Blocks PC @base",
"from": "system",
"setting_id": "9a6bccZopMTeNHYu",
"instantiation": "true",
"compatible_printers": [
"BLOCKS RF50 0.4 nozzle",
"BLOCKS RF50 0.6 nozzle",
"BLOCKS RF50 0.8 nozzle"
]
}
@@ -1,17 +0,0 @@
{
"type": "filament",
"name": "Blocks PC @base",
"inherits": "fdm_filament_pc",
"from": "system",
"filament_id": "OFlGY0MN",
"instantiation": "false",
"filament_vendor": [
"Blocks"
],
"filament_flow_ratio": [
"0.94"
],
"filament_max_volumetric_speed": [
"10"
]
}
@@ -1,16 +0,0 @@
{
"type": "filament",
"name": "Blocks PETG @Blocks_Pro_S100",
"inherits": "Blocks PETG @base",
"from": "system",
"setting_id": "AhMR49fcFqtrQ6hI",
"instantiation": "true",
"compatible_printers": [
"BLOCKS Pro S100 0.4 nozzle",
"BLOCKS Pro S100 0.6 nozzle",
"BLOCKS Pro S100 0.8 nozzle",
"BLOCKS Pro S100 1.0 nozzle",
"BLOCKS Pro S100 1.2 nozzle"
],
"renamed_from": "Blocks Generic PETG;Generic PETG @Blocks"
}
@@ -1,13 +0,0 @@
{
"type": "filament",
"name": "Blocks PETG @Blocks_RD50_V2",
"inherits": "Blocks PETG @base",
"from": "system",
"setting_id": "3v7ah1zVG8MERPSJ",
"instantiation": "true",
"compatible_printers": [
"BLOCKS RD50 V2 0.4 nozzle",
"BLOCKS RD50 V2 0.6 nozzle",
"BLOCKS RD50 V2 0.8 nozzle"
]
}
@@ -1,13 +0,0 @@
{
"type": "filament",
"name": "Blocks PETG @Blocks_RF50",
"inherits": "Blocks PETG @base",
"from": "system",
"setting_id": "S7weR19wo9meJ6Yz",
"instantiation": "true",
"compatible_printers": [
"BLOCKS RF50 0.4 nozzle",
"BLOCKS RF50 0.6 nozzle",
"BLOCKS RF50 0.8 nozzle"
]
}
@@ -1,16 +0,0 @@
{
"type": "filament",
"name": "Blocks PLA @Blocks_Pro_S100",
"inherits": "Blocks PLA @base",
"from": "system",
"setting_id": "vWojuLYIfduLGFLL",
"instantiation": "true",
"compatible_printers": [
"BLOCKS Pro S100 0.4 nozzle",
"BLOCKS Pro S100 0.6 nozzle",
"BLOCKS Pro S100 0.8 nozzle",
"BLOCKS Pro S100 1.0 nozzle",
"BLOCKS Pro S100 1.2 nozzle"
],
"renamed_from": "Blocks Generic PLA;Generic PLA @Blocks"
}
@@ -1,13 +0,0 @@
{
"type": "filament",
"name": "Blocks PLA @Blocks_RD50_V2",
"inherits": "Blocks PLA @base",
"from": "system",
"setting_id": "y7y8NcXNdp17sJUs",
"instantiation": "true",
"compatible_printers": [
"BLOCKS RD50 V2 0.4 nozzle",
"BLOCKS RD50 V2 0.6 nozzle",
"BLOCKS RD50 V2 0.8 nozzle"
]
}
@@ -1,13 +0,0 @@
{
"type": "filament",
"name": "Blocks PLA @Blocks_RF50",
"inherits": "Blocks PLA @base",
"from": "system",
"setting_id": "jt1MJF4eZzkkscEJ",
"instantiation": "true",
"compatible_printers": [
"BLOCKS RF50 0.4 nozzle",
"BLOCKS RF50 0.6 nozzle",
"BLOCKS RF50 0.8 nozzle"
]
}
@@ -1,14 +0,0 @@
{
"type": "filament",
"name": "Blocks PLA-CF @Blocks_RD50_V2",
"inherits": "Blocks PLA-CF @base",
"from": "system",
"setting_id": "lhBtMKP1DmOB3HOI",
"instantiation": "true",
"compatible_printers": [
"BLOCKS RD50 V2 0.4 nozzle",
"BLOCKS RD50 V2 0.6 nozzle",
"BLOCKS RD50 V2 0.8 nozzle"
],
"renamed_from": "Blocks Generic PLA-CF;Generic PLA-CF @Blocks"
}
@@ -1,13 +0,0 @@
{
"type": "filament",
"name": "Blocks PLA-CF @Blocks_RF50",
"inherits": "Blocks PLA-CF @base",
"from": "system",
"setting_id": "6CU83mAyhcZ4FCPI",
"instantiation": "true",
"compatible_printers": [
"BLOCKS RF50 0.4 nozzle",
"BLOCKS RF50 0.6 nozzle",
"BLOCKS RF50 0.8 nozzle"
]
}
@@ -1,23 +0,0 @@
{
"type": "filament",
"name": "Blocks PLA-CF @base",
"inherits": "fdm_filament_pla",
"from": "system",
"filament_id": "OF6FfwOP",
"instantiation": "false",
"filament_vendor": [
"Blocks"
],
"filament_flow_ratio": [
"0.95"
],
"filament_max_volumetric_speed": [
"20"
],
"filament_type": [
"PLA-CF"
],
"slow_down_layer_time": [
"7"
]
}
@@ -1,14 +0,0 @@
{
"type": "filament",
"name": "Blocks PVA @Blocks_RD50_V2",
"inherits": "Blocks PVA @base",
"from": "system",
"setting_id": "hREDqwKF6qyKkakp",
"instantiation": "true",
"compatible_printers": [
"BLOCKS RD50 V2 0.4 nozzle",
"BLOCKS RD50 V2 0.6 nozzle",
"BLOCKS RD50 V2 0.8 nozzle"
],
"renamed_from": "Blocks Generic PVA;Generic PVA @Blocks"
}
@@ -1,13 +0,0 @@
{
"type": "filament",
"name": "Blocks PVA @Blocks_RF50",
"inherits": "Blocks PVA @base",
"from": "system",
"setting_id": "q6pKqHY7Iz4OZaAC",
"instantiation": "true",
"compatible_printers": [
"BLOCKS RF50 0.4 nozzle",
"BLOCKS RF50 0.6 nozzle",
"BLOCKS RF50 0.8 nozzle"
]
}
@@ -1,17 +0,0 @@
{
"type": "filament",
"name": "Blocks PVA @base",
"inherits": "fdm_filament_pva",
"from": "system",
"filament_id": "OFqcNuE1",
"instantiation": "false",
"filament_vendor": [
"Blocks"
],
"filament_flow_ratio": [
"0.98"
],
"filament_max_volumetric_speed": [
"20"
]
}
@@ -1,16 +0,0 @@
{
"type": "filament",
"name": "Blocks TPU @Blocks_Pro_S100",
"inherits": "Blocks TPU @base",
"from": "system",
"setting_id": "6Rkz2je91JRTRoTi",
"instantiation": "true",
"compatible_printers": [
"BLOCKS Pro S100 0.4 nozzle",
"BLOCKS Pro S100 0.6 nozzle",
"BLOCKS Pro S100 0.8 nozzle",
"BLOCKS Pro S100 1.0 nozzle",
"BLOCKS Pro S100 1.2 nozzle"
],
"renamed_from": "Blocks Generic TPU;Generic TPU @Blocks"
}
@@ -1,13 +0,0 @@
{
"type": "filament",
"name": "Blocks TPU @Blocks_RD50_V2",
"inherits": "Blocks TPU @base",
"from": "system",
"setting_id": "mkCqRQTtXldzoEUg",
"instantiation": "true",
"compatible_printers": [
"BLOCKS RD50 V2 0.4 nozzle",
"BLOCKS RD50 V2 0.6 nozzle",
"BLOCKS RD50 V2 0.8 nozzle"
]
}
@@ -1,13 +0,0 @@
{
"type": "filament",
"name": "Blocks TPU @Blocks_RF50",
"inherits": "Blocks TPU @base",
"from": "system",
"setting_id": "bCCvu5Ow1JkwIrRR",
"instantiation": "true",
"compatible_printers": [
"BLOCKS RF50 0.4 nozzle",
"BLOCKS RF50 0.6 nozzle",
"BLOCKS RF50 0.8 nozzle"
]
}
@@ -1,13 +1,12 @@
{
"type": "filament",
"name": "Blocks ABS @base",
"name": "Generic ABS @Blocks",
"inherits": "fdm_filament_abs",
"renamed_from": "Blocks Generic ABS",
"from": "system",
"filament_id": "OFBBhiX8",
"instantiation": "false",
"filament_vendor": [
"Blocks"
],
"setting_id": "TL1Q1kA4t36GGObG",
"filament_id": "OFY9muEs",
"instantiation": "true",
"filament_flow_ratio": [
"0.95"
],
@@ -82,5 +81,13 @@
],
"during_print_exhaust_fan_speed": [
"100"
],
"compatible_printers": [
"BLOCKS RD50 V2 0.4 nozzle",
"BLOCKS RD50 V2 0.6 nozzle",
"BLOCKS RD50 V2 0.8 nozzle",
"BLOCKS RF50 0.4 nozzle",
"BLOCKS RF50 0.6 nozzle",
"BLOCKS RF50 0.8 nozzle"
]
}
@@ -1,13 +1,12 @@
{
"type": "filament",
"name": "Blocks ASA @base",
"name": "Generic ASA @Blocks",
"inherits": "fdm_filament_asa",
"renamed_from": "Blocks Generic ASA",
"from": "system",
"filament_id": "OFTz2YXa",
"instantiation": "false",
"filament_vendor": [
"Blocks"
],
"setting_id": "xP7cB4RHgbm4cYDD",
"filament_id": "OFLPAxz3",
"instantiation": "true",
"filament_flow_ratio": [
"0.95"
],
@@ -82,5 +81,13 @@
],
"during_print_exhaust_fan_speed": [
"100"
],
"compatible_printers": [
"BLOCKS RD50 V2 0.4 nozzle",
"BLOCKS RD50 V2 0.6 nozzle",
"BLOCKS RD50 V2 0.8 nozzle",
"BLOCKS RF50 0.4 nozzle",
"BLOCKS RF50 0.6 nozzle",
"BLOCKS RF50 0.8 nozzle"
]
}
@@ -1,13 +1,12 @@
{
"type": "filament",
"name": "Blocks ASA-CF @base",
"name": "Generic ASA-CF @Blocks",
"inherits": "fdm_filament_asa",
"renamed_from": "Blocks Generic ASA-CF",
"from": "system",
"filament_id": "OF8b9I7o",
"instantiation": "false",
"filament_vendor": [
"Blocks"
],
"setting_id": "ju4ue7yJoz3ujny6",
"filament_id": "OF53eLVD",
"instantiation": "true",
"filament_type": [
"ASA-CF"
],
@@ -85,5 +84,13 @@
],
"during_print_exhaust_fan_speed": [
"100"
],
"compatible_printers": [
"BLOCKS RD50 V2 0.4 nozzle",
"BLOCKS RD50 V2 0.6 nozzle",
"BLOCKS RD50 V2 0.8 nozzle",
"BLOCKS RF50 0.4 nozzle",
"BLOCKS RF50 0.6 nozzle",
"BLOCKS RF50 0.8 nozzle"
]
}
@@ -0,0 +1,24 @@
{
"type": "filament",
"name": "Generic PA @Blocks",
"inherits": "fdm_filament_pa",
"renamed_from": "Blocks Generic PA",
"from": "system",
"setting_id": "hL3p8gAeciwDkjNK",
"filament_id": "OFg8ndtj",
"instantiation": "true",
"filament_flow_ratio": [
"0.98"
],
"filament_max_volumetric_speed": [
"10"
],
"compatible_printers": [
"BLOCKS RD50 V2 0.4 nozzle",
"BLOCKS RD50 V2 0.6 nozzle",
"BLOCKS RD50 V2 0.8 nozzle",
"BLOCKS RF50 0.4 nozzle",
"BLOCKS RF50 0.6 nozzle",
"BLOCKS RF50 0.8 nozzle"
]
}
@@ -1,13 +1,12 @@
{
"type": "filament",
"name": "Blocks PA-CF @base",
"name": "Generic PA-CF @Blocks",
"inherits": "fdm_filament_pa",
"renamed_from": "Blocks Generic PA-CF",
"from": "system",
"filament_id": "OFdWrXpL",
"instantiation": "false",
"filament_vendor": [
"Blocks"
],
"setting_id": "T6SO0WRdnG6wcyG8",
"filament_id": "OFQLcbps",
"instantiation": "true",
"filament_type": [
"PA-CF"
],
@@ -88,5 +87,13 @@
],
"during_print_exhaust_fan_speed": [
"80"
],
"compatible_printers": [
"BLOCKS RD50 V2 0.4 nozzle",
"BLOCKS RD50 V2 0.6 nozzle",
"BLOCKS RD50 V2 0.8 nozzle",
"BLOCKS RF50 0.4 nozzle",
"BLOCKS RF50 0.6 nozzle",
"BLOCKS RF50 0.8 nozzle"
]
}
@@ -0,0 +1,24 @@
{
"type": "filament",
"name": "Generic PC @Blocks",
"inherits": "fdm_filament_pc",
"renamed_from": "Blocks Generic PC",
"from": "system",
"setting_id": "Xhs8PjtmwwVj0ksE",
"filament_id": "OFLakOUI",
"instantiation": "true",
"filament_flow_ratio": [
"0.94"
],
"filament_max_volumetric_speed": [
"10"
],
"compatible_printers": [
"BLOCKS RD50 V2 0.4 nozzle",
"BLOCKS RD50 V2 0.6 nozzle",
"BLOCKS RD50 V2 0.8 nozzle",
"BLOCKS RF50 0.4 nozzle",
"BLOCKS RF50 0.6 nozzle",
"BLOCKS RF50 0.8 nozzle"
]
}
@@ -1,13 +1,12 @@
{
"type": "filament",
"name": "Blocks PETG @base",
"name": "Generic PETG @Blocks",
"inherits": "fdm_filament_petg",
"renamed_from": "Blocks Generic PETG",
"from": "system",
"filament_id": "OF9t1Ga0",
"instantiation": "false",
"filament_vendor": [
"Blocks"
],
"setting_id": "NgzPlYItKHbmGgv3",
"filament_id": "OFYPdQJh",
"instantiation": "true",
"filament_flow_ratio": [
"0.94"
],
@@ -82,5 +81,18 @@
],
"slow_down_layer_time": [
"5"
],
"compatible_printers": [
"BLOCKS Pro S100 0.4 nozzle",
"BLOCKS Pro S100 0.6 nozzle",
"BLOCKS Pro S100 0.8 nozzle",
"BLOCKS Pro S100 1.0 nozzle",
"BLOCKS Pro S100 1.2 nozzle",
"BLOCKS RD50 V2 0.4 nozzle",
"BLOCKS RD50 V2 0.6 nozzle",
"BLOCKS RD50 V2 0.8 nozzle",
"BLOCKS RF50 0.4 nozzle",
"BLOCKS RF50 0.6 nozzle",
"BLOCKS RF50 0.8 nozzle"
]
}
@@ -1,13 +1,12 @@
{
"type": "filament",
"name": "Blocks PLA @base",
"name": "Generic PLA @Blocks",
"inherits": "fdm_filament_pla",
"renamed_from": "Blocks Generic PLA",
"from": "system",
"filament_id": "OFVGAC8g",
"instantiation": "false",
"filament_vendor": [
"Blocks"
],
"setting_id": "kjhuttx4T8OtTNmV",
"filament_id": "OFDSrzZ8",
"instantiation": "true",
"filament_flow_ratio": [
"0.95"
],
@@ -40,5 +39,18 @@
],
"during_print_exhaust_fan_speed": [
"100"
],
"compatible_printers": [
"BLOCKS Pro S100 0.4 nozzle",
"BLOCKS Pro S100 0.6 nozzle",
"BLOCKS Pro S100 0.8 nozzle",
"BLOCKS Pro S100 1.0 nozzle",
"BLOCKS Pro S100 1.2 nozzle",
"BLOCKS RD50 V2 0.4 nozzle",
"BLOCKS RD50 V2 0.6 nozzle",
"BLOCKS RD50 V2 0.8 nozzle",
"BLOCKS RF50 0.4 nozzle",
"BLOCKS RF50 0.6 nozzle",
"BLOCKS RF50 0.8 nozzle"
]
}
@@ -0,0 +1,30 @@
{
"type": "filament",
"name": "Generic PLA-CF @Blocks",
"inherits": "fdm_filament_pla",
"renamed_from": "Blocks Generic PLA-CF",
"from": "system",
"setting_id": "oKUqzwAWMrpiyGLf",
"filament_id": "OFWbdGsC",
"instantiation": "true",
"filament_flow_ratio": [
"0.95"
],
"filament_max_volumetric_speed": [
"20"
],
"filament_type": [
"PLA-CF"
],
"slow_down_layer_time": [
"7"
],
"compatible_printers": [
"BLOCKS RD50 V2 0.4 nozzle",
"BLOCKS RD50 V2 0.6 nozzle",
"BLOCKS RD50 V2 0.8 nozzle",
"BLOCKS RF50 0.4 nozzle",
"BLOCKS RF50 0.6 nozzle",
"BLOCKS RF50 0.8 nozzle"
]
}
@@ -0,0 +1,24 @@
{
"type": "filament",
"name": "Generic PVA @Blocks",
"inherits": "fdm_filament_pva",
"renamed_from": "Blocks Generic PVA",
"from": "system",
"setting_id": "tNw3oe7VovLkgUbB",
"filament_id": "OFDvXujf",
"instantiation": "true",
"filament_flow_ratio": [
"0.98"
],
"filament_max_volumetric_speed": [
"20"
],
"compatible_printers": [
"BLOCKS RD50 V2 0.4 nozzle",
"BLOCKS RD50 V2 0.6 nozzle",
"BLOCKS RD50 V2 0.8 nozzle",
"BLOCKS RF50 0.4 nozzle",
"BLOCKS RF50 0.6 nozzle",
"BLOCKS RF50 0.8 nozzle"
]
}
@@ -1,13 +1,12 @@
{
"type": "filament",
"name": "Blocks TPU @base",
"name": "Generic TPU @Blocks",
"inherits": "fdm_filament_tpu",
"renamed_from": "Blocks Generic TPU",
"from": "system",
"filament_id": "OFYnedOZ",
"instantiation": "false",
"filament_vendor": [
"Blocks"
],
"setting_id": "6iVYOCZo8ugs4JA0",
"filament_id": "OFgbpcy9",
"instantiation": "true",
"filament_max_volumetric_speed": [
"4"
],
@@ -55,5 +54,18 @@
],
"during_print_exhaust_fan_speed": [
"80"
],
"compatible_printers": [
"BLOCKS Pro S100 0.4 nozzle",
"BLOCKS Pro S100 0.6 nozzle",
"BLOCKS Pro S100 0.8 nozzle",
"BLOCKS Pro S100 1.0 nozzle",
"BLOCKS Pro S100 1.2 nozzle",
"BLOCKS RD50 V2 0.4 nozzle",
"BLOCKS RD50 V2 0.6 nozzle",
"BLOCKS RD50 V2 0.8 nozzle",
"BLOCKS RF50 0.4 nozzle",
"BLOCKS RF50 0.6 nozzle",
"BLOCKS RF50 0.8 nozzle"
]
}
@@ -6,7 +6,7 @@
"setting_id": "G7HQgaspHXduDM8O",
"instantiation": "true",
"printer_model": "BLOCKS Pro S100",
"default_print_profile": "0.20mm Standard 0.4 nozzle @Blocks_Pro_S100",
"default_print_profile": "0.20mm Standard 0.4 nozzle @Blocks",
"max_layer_height": [
"0.30"
],
@@ -25,8 +25,5 @@
"printable_height": "900",
"upward_compatible_machine": [
"BLOCKS Pro S100"
],
"default_filament_profile": [
"Blocks PLA @Blocks_Pro_S100"
]
}
@@ -6,7 +6,7 @@
"setting_id": "XkWdS210oUMQN17N",
"instantiation": "true",
"printer_model": "BLOCKS Pro S100",
"default_print_profile": "0.30mm Standard 0.6 nozzle @Blocks_Pro_S100",
"default_print_profile": "0.30mm Standard 0.6 nozzle @Blocks",
"max_layer_height": [
"0.50"
],
@@ -26,8 +26,5 @@
"upward_compatible_machine": [
"BLOCKS Pro S100"
],
"printer_variant": "0.6",
"default_filament_profile": [
"Blocks PLA @Blocks_Pro_S100"
]
"printer_variant": "0.6"
}
@@ -6,7 +6,7 @@
"setting_id": "I6PdeCS7SbvIlkZf",
"instantiation": "true",
"printer_model": "BLOCKS Pro S100",
"default_print_profile": "0.40mm Standard 0.8 nozzle @Blocks_Pro_S100",
"default_print_profile": "0.40mm Standard 0.8 nozzle @Blocks",
"max_layer_height": [
"0.56"
],
@@ -26,8 +26,5 @@
"upward_compatible_machine": [
"BLOCKS Pro S100"
],
"printer_variant": "0.8",
"default_filament_profile": [
"Blocks PLA @Blocks_Pro_S100"
]
"printer_variant": "0.8"
}
@@ -6,7 +6,7 @@
"setting_id": "elLdUyy9RhUoa37o",
"instantiation": "true",
"printer_model": "BLOCKS Pro S100",
"default_print_profile": "0.50mm Standard 1.0 nozzle @Blocks_Pro_S100",
"default_print_profile": "0.50mm Standard 1.0 nozzle @Blocks",
"max_layer_height": [
"0.7"
],
@@ -26,8 +26,5 @@
"upward_compatible_machine": [
"BLOCKS Pro S100"
],
"printer_variant": "1.0",
"default_filament_profile": [
"Blocks PLA @Blocks_Pro_S100"
]
"printer_variant": "1.0"
}
@@ -6,7 +6,7 @@
"setting_id": "xtsGSJ4JkbHTPi44",
"instantiation": "true",
"printer_model": "BLOCKS Pro S100",
"default_print_profile": "0.60mm Standard 1.2 nozzle @Blocks_Pro_S100",
"default_print_profile": "0.60mm Standard 1.2 nozzle @Blocks",
"max_layer_height": [
"0.8"
],
@@ -26,8 +26,5 @@
"upward_compatible_machine": [
"BLOCKS Pro S100"
],
"printer_variant": "1.2",
"default_filament_profile": [
"Blocks PLA @Blocks_Pro_S100"
]
"printer_variant": "1.2"
}
@@ -8,5 +8,5 @@
"bed_model": "PRO S100 HotBed model.stl",
"bed_texture": "PRO S100 HotBed texture.svg",
"hotend_model": "BLOCKS PrintCore.stl",
"default_materials": "Blocks PLA @Blocks_Pro_S100;Blocks PETG @Blocks_Pro_S100;Blocks TPU @Blocks_Pro_S100"
"default_materials": "Generic PLA @Blocks;Generic PETG @Blocks;Generic TPU @Blocks"
}
@@ -34,9 +34,5 @@
"printable_height": "500",
"upward_compatible_machine": [
"BLOCKS RD50 V2"
],
"printer_variant": "0.4",
"default_filament_profile": [
"Blocks PLA @Blocks_RD50_V2"
]
}
@@ -35,8 +35,5 @@
"deretraction_speed": [
"80"
],
"printer_variant": "0.6",
"default_filament_profile": [
"Blocks PLA @Blocks_RD50_V2"
]
"printer_variant": "0.6"
}
@@ -35,8 +35,5 @@
"deretraction_speed": [
"80"
],
"printer_variant": "0.8",
"default_filament_profile": [
"Blocks PLA @Blocks_RD50_V2"
]
"printer_variant": "0.8"
}
@@ -8,5 +8,5 @@
"bed_model": "RD50 V2 HotBed model.stl",
"bed_texture": "RD50 V2 HotBed texture.svg",
"hotend_model": "BLOCKS PrintCore.stl",
"default_materials": "Blocks PLA @Blocks_RD50_V2;Blocks PETG @Blocks_RD50_V2;Blocks TPU @Blocks_RD50_V2"
"default_materials": "Generic PLA @Blocks;Generic PETG @Blocks;Generic TPU @Blocks"
}
@@ -51,7 +51,7 @@
"12"
],
"retraction_length": [
"1.2"
"1"
],
"retraction_speed": [
"60"
@@ -79,10 +79,5 @@
],
"retract_before_wipe": [
"0"
],
"default_filament_profile": [
"Blocks PLA @Blocks_RF50"
],
"support_multi_bed_types": "1",
"default_bed_type": "Textured PEI Plate"
]
}
@@ -51,7 +51,7 @@
"12"
],
"retraction_length": [
"1.2"
"1"
],
"retraction_speed": [
"60"
@@ -75,8 +75,8 @@
"BLOCKS RF50"
],
"machine_start_gcode": [
"PRINT_START EXTRUDER=[nozzle_temperature_initial_layer] BED=[bed_temperature_initial_layer]\n",
"SET_PRESSURE_ADVANCE ADVANCE=0.046\n"
"PRINT_START EXTRUDER=[nozzle_temperature_initial_layer] BED=[bed_temperature_initial_layer]",
"SET_PRESSURE_ADVANCE ADVANCE=0.046"
],
"printer_variant": "0.6",
"wipe_distance": [
@@ -84,10 +84,5 @@
],
"retract_before_wipe": [
"0"
],
"default_filament_profile": [
"Blocks PLA @Blocks_RF50"
],
"support_multi_bed_types": "1",
"default_bed_type": "Textured PEI Plate"
]
}
@@ -51,7 +51,7 @@
"12"
],
"retraction_length": [
"1.2"
"1"
],
"retraction_speed": [
"60"
@@ -80,10 +80,5 @@
],
"retract_before_wipe": [
"0"
],
"default_filament_profile": [
"Blocks PLA @Blocks_RF50"
],
"support_multi_bed_types": "1",
"default_bed_type": "Textured PEI Plate"
]
}
@@ -8,6 +8,5 @@
"bed_model": "RF50 HotBed model.stl",
"bed_texture": "RF50 HotBed texture.svg",
"hotend_model": "BLOCKS PrintCore.stl",
"default_materials": "Blocks PLA @Blocks_RF50;Blocks PETG @Blocks_RF50;Blocks TPU @Blocks_RF50",
"not_support_bed_type": "Smooth Cool Plate;Engineering Plate;Textured Cool Plate;Cool Plate (SuperTack)"
"default_materials": "Generic PLA @Blocks;Generic PETG @Blocks;Generic TPU @Blocks"
}
@@ -123,6 +123,9 @@
"wipe": [
"1"
],
"default_filament_profile": [
"Generic PLA @Blocks"
],
"bed_exclude_area": [
"0x0"
],
@@ -21,7 +21,7 @@
"top_solid_infill_flow_ratio": "0.97",
"bottom_solid_infill_flow_ratio": "1.08",
"seam_position": "aligned",
"sparse_infill_pattern": "grid",
"sparse_infill_patter": "grid",
"initial_layer_speed": "60",
"initial_layer_infill_speed": "80",
"initial_travel_speed": "100%",
@@ -57,7 +57,5 @@
"accel_to_decel_enable": "1",
"accel_to_decel_factor": "50%",
"elefant_foot_compensation": "0.25",
"elefant_foot_compensation_layers": "1",
"skirt_loops": "2",
"skirt_type": "perobject"
"elefant_foot_compensation_layers": "1"
}
@@ -21,7 +21,7 @@
"top_solid_infill_flow_ratio": "0.97",
"bottom_solid_infill_flow_ratio": "1.08",
"seam_position": "aligned",
"sparse_infill_pattern": "grid",
"sparse_infill_patter": "grid",
"initial_layer_speed": "60",
"initial_layer_infill_speed": "80",
"initial_travel_speed": "100%",
@@ -57,7 +57,5 @@
"accel_to_decel_enable": "1",
"accel_to_decel_factor": "50%",
"elefant_foot_compensation": "0.25",
"elefant_foot_compensation_layers": "1",
"skirt_loops": "2",
"skirt_type": "perobject"
"elefant_foot_compensation_layers": "1"
}
@@ -21,7 +21,7 @@
"top_solid_infill_flow_ratio": "0.97",
"bottom_solid_infill_flow_ratio": "1.08",
"seam_position": "aligned",
"sparse_infill_pattern": "grid",
"sparse_infill_patter": "grid",
"initial_layer_speed": "60",
"initial_layer_infill_speed": "80",
"initial_travel_speed": "100%",
@@ -57,7 +57,5 @@
"accel_to_decel_enable": "1",
"accel_to_decel_factor": "50%",
"elefant_foot_compensation": "0.25",
"elefant_foot_compensation_layers": "1",
"skirt_loops": "2",
"skirt_type": "perobject"
"elefant_foot_compensation_layers": "1"
}
@@ -1,15 +1,14 @@
{
"type": "process",
"name": "0.20mm Standard 0.4 nozzle @Blocks_Pro_S100",
"name": "0.20mm Standard 0.4 nozzle @Blocks",
"inherits": "fdm_process_blocks_common",
"from": "system",
"setting_id": "QSEguMMChNFZsK5H",
"setting_id": "47aDtVL6E5DVr7Mt",
"instantiation": "true",
"layer_height": "0.2",
"bottom_shell_layers": "4",
"top_shell_layers": "5",
"compatible_printers": [
"BLOCKS Pro S100 0.4 nozzle"
],
"renamed_from": "0.20mm Standard 0.4 nozzle @Blocks"
]
}
@@ -25,7 +25,7 @@
"top_solid_infill_flow_ratio": "0.97",
"bottom_solid_infill_flow_ratio": "1.08",
"seam_position": "aligned",
"sparse_infill_pattern": "grid",
"sparse_infill_patter": "grid",
"initial_layer_speed": "60",
"initial_layer_infill_speed": "80",
"initial_travel_speed": "100%",
@@ -61,7 +61,5 @@
"accel_to_decel_enable": "1",
"accel_to_decel_factor": "50%",
"elefant_foot_compensation": "0.25",
"elefant_foot_compensation_layers": "1",
"skirt_loops": "2",
"skirt_type": "perobject"
"elefant_foot_compensation_layers": "1"
}
@@ -1,15 +1,14 @@
{
"type": "process",
"name": "0.24mm Draft 0.4 nozzle @Blocks_Pro_S100",
"name": "0.24mm Draft 0.4 nozzle @Blocks",
"inherits": "fdm_process_blocks_common",
"from": "system",
"setting_id": "uALfEFHGmup6vBYI",
"setting_id": "ETADjxV6ViJQdnHA",
"instantiation": "true",
"layer_height": "0.24",
"bottom_shell_layers": "4",
"top_shell_layers": "5",
"compatible_printers": [
"BLOCKS Pro S100 0.4 nozzle"
],
"renamed_from": "0.24mm Draft 0.4 nozzle @Blocks"
]
}
@@ -21,7 +21,7 @@
"top_solid_infill_flow_ratio": "0.97",
"bottom_solid_infill_flow_ratio": "1.08",
"seam_position": "aligned",
"sparse_infill_pattern": "grid",
"sparse_infill_patter": "grid",
"initial_layer_speed": "60",
"initial_layer_infill_speed": "80",
"initial_travel_speed": "100%",
@@ -57,7 +57,5 @@
"accel_to_decel_enable": "1",
"accel_to_decel_factor": "50%",
"elefant_foot_compensation": "0.25",
"elefant_foot_compensation_layers": "1",
"skirt_loops": "2",
"skirt_type": "perobject"
"elefant_foot_compensation_layers": "1"
}
@@ -21,7 +21,7 @@
"top_solid_infill_flow_ratio": "0.97",
"bottom_solid_infill_flow_ratio": "1.08",
"seam_position": "aligned",
"sparse_infill_pattern": "grid",
"sparse_infill_patter": "grid",
"initial_layer_speed": "60",
"initial_layer_infill_speed": "80",
"initial_travel_speed": "100%",
@@ -57,7 +57,5 @@
"accel_to_decel_enable": "1",
"accel_to_decel_factor": "50%",
"elefant_foot_compensation": "0.25",
"elefant_foot_compensation_layers": "1",
"skirt_loops": "2",
"skirt_type": "perobject"
"elefant_foot_compensation_layers": "1"
}
@@ -22,7 +22,7 @@
"top_solid_infill_flow_ratio": "0.97",
"bottom_solid_infill_flow_ratio": "1.08",
"seam_position": "aligned",
"sparse_infill_pattern": "grid",
"sparse_infill_patter": "grid",
"initial_layer_speed": "60",
"initial_layer_infill_speed": "80",
"initial_travel_speed": "100%",
@@ -57,7 +57,5 @@
"accel_to_decel_enable": "1",
"accel_to_decel_factor": "50%",
"elefant_foot_compensation": "0.25",
"elefant_foot_compensation_layers": "1",
"skirt_loops": "2",
"skirt_type": "perobject"
"elefant_foot_compensation_layers": "1"
}
@@ -1,15 +1,14 @@
{
"type": "process",
"name": "0.30mm Extra Draft 0.4 nozzle @Blocks_Pro_S100",
"name": "0.30mm Extra Draft 0.4 nozzle @Blocks",
"inherits": "fdm_process_blocks_common",
"from": "system",
"setting_id": "HYKF5l2OGvpugSQy",
"setting_id": "UbuICsmppKDsaaZ4",
"instantiation": "true",
"bottom_shell_layers": "3",
"top_shell_layers": "4",
"layer_height": "0.30",
"compatible_printers": [
"BLOCKS Pro S100 0.4 nozzle"
],
"renamed_from": "0.30mm Extra Draft 0.4 nozzle @Blocks"
]
}
@@ -1,15 +1,14 @@
{
"type": "process",
"name": "0.30mm Optimal 0.8 nozzle @Blocks_Pro_S100",
"name": "0.30mm Optimal 0.8 nozzle @Blocks",
"inherits": "fdm_process_common 0.8 nozzle",
"from": "system",
"setting_id": "OUfZvizM5MGnvHfp",
"setting_id": "YqkYzp5zdH4R5upm",
"instantiation": "true",
"bottom_shell_layers": "3",
"top_shell_layers": "3",
"layer_height": "0.3",
"compatible_printers": [
"BLOCKS Pro S100 0.8 nozzle"
],
"renamed_from": "0.30mm Optimal 0.8 nozzle @Blocks"
]
}
@@ -21,7 +21,7 @@
"top_solid_infill_flow_ratio": "0.97",
"bottom_solid_infill_flow_ratio": "1.08",
"seam_position": "aligned",
"sparse_infill_pattern": "grid",
"sparse_infill_patter": "grid",
"initial_layer_speed": "60",
"initial_layer_infill_speed": "80",
"initial_travel_speed": "100%",
@@ -57,7 +57,5 @@
"accel_to_decel_enable": "1",
"accel_to_decel_factor": "50%",
"elefant_foot_compensation": "0.25",
"elefant_foot_compensation_layers": "1",
"skirt_loops": "2",
"skirt_type": "perobject"
"elefant_foot_compensation_layers": "1"
}
@@ -1,15 +1,14 @@
{
"type": "process",
"name": "0.30mm Optimal 1.0 nozzle @Blocks_Pro_S100",
"name": "0.30mm Optimal 1.0 nozzle @Blocks",
"inherits": "fdm_process_common 1.0 nozzle",
"from": "system",
"setting_id": "HGG9WxKVr89qNFDx",
"setting_id": "8pBawPScHkyQu0SW",
"instantiation": "true",
"bottom_shell_layers": "4",
"top_shell_layers": "4",
"layer_height": "0.30",
"compatible_printers": [
"BLOCKS Pro S100 1.0 nozzle"
],
"renamed_from": "0.30mm Optimal 1.0 nozzle @Blocks"
]
}
@@ -1,15 +1,14 @@
{
"type": "process",
"name": "0.30mm Standard 0.6 nozzle @Blocks_Pro_S100",
"name": "0.30mm Standard 0.6 nozzle @Blocks",
"inherits": "fdm_process_common 0.6 nozzle",
"from": "system",
"setting_id": "gO2I1YjBl4nf8yzr",
"setting_id": "EPI3PAVK9LpJOZWO",
"instantiation": "true",
"bottom_shell_layers": "3",
"top_shell_layers": "3",
"layer_height": "0.30",
"compatible_printers": [
"BLOCKS Pro S100 0.6 nozzle"
],
"renamed_from": "0.30mm Standard 0.6 nozzle @Blocks"
]
}
@@ -22,7 +22,7 @@
"top_solid_infill_flow_ratio": "0.97",
"bottom_solid_infill_flow_ratio": "1.08",
"seam_position": "aligned",
"sparse_infill_pattern": "grid",
"sparse_infill_patter": "grid",
"initial_layer_speed": "60",
"initial_layer_infill_speed": "80",
"initial_travel_speed": "100%",
@@ -57,7 +57,5 @@
"accel_to_decel_enable": "1",
"accel_to_decel_factor": "50%",
"elefant_foot_compensation": "0.25",
"elefant_foot_compensation_layers": "1",
"skirt_loops": "2",
"skirt_type": "perobject"
"elefant_foot_compensation_layers": "1"
}
@@ -21,7 +21,7 @@
"top_solid_infill_flow_ratio": "0.97",
"bottom_solid_infill_flow_ratio": "1.08",
"seam_position": "aligned",
"sparse_infill_pattern": "grid",
"sparse_infill_patter": "grid",
"initial_layer_speed": "60",
"initial_layer_infill_speed": "80",
"initial_travel_speed": "100%",
@@ -57,7 +57,5 @@
"accel_to_decel_enable": "1",
"accel_to_decel_factor": "50%",
"elefant_foot_compensation": "0.25",
"elefant_foot_compensation_layers": "1",
"skirt_loops": "2",
"skirt_type": "perobject"
"elefant_foot_compensation_layers": "1"
}
@@ -22,7 +22,7 @@
"top_solid_infill_flow_ratio": "0.97",
"bottom_solid_infill_flow_ratio": "1.08",
"seam_position": "aligned",
"sparse_infill_pattern": "grid",
"sparse_infill_patter": "grid",
"initial_layer_speed": "60",
"initial_layer_infill_speed": "80",
"initial_travel_speed": "100%",
@@ -57,7 +57,5 @@
"accel_to_decel_enable": "1",
"accel_to_decel_factor": "50%",
"elefant_foot_compensation": "0.25",
"elefant_foot_compensation_layers": "1",
"skirt_loops": "2",
"skirt_type": "perobject"
"elefant_foot_compensation_layers": "1"
}
@@ -1,15 +1,14 @@
{
"type": "process",
"name": "0.40mm Draft 0.6 nozzle @Blocks_Pro_S100",
"name": "0.40mm Draft 0.6 nozzle @Blocks",
"inherits": "fdm_process_common 0.6 nozzle",
"from": "system",
"setting_id": "OjsLUl1heqv1cevs",
"setting_id": "CShVKoGspbMJNPw8",
"instantiation": "true",
"bottom_shell_layers": "2",
"top_shell_layers": "2",
"layer_height": "0.40",
"compatible_printers": [
"BLOCKS Pro S100 0.6 nozzle"
],
"renamed_from": "0.40mm Draft 0.6 nozzle @Blocks"
]
}
@@ -1,15 +1,14 @@
{
"type": "process",
"name": "0.40mm Standard 0.8 nozzle @Blocks_Pro_S100",
"name": "0.40mm Standard 0.8 nozzle @Blocks",
"inherits": "fdm_process_common 0.8 nozzle",
"from": "system",
"setting_id": "laOTioBGySeudJpl",
"setting_id": "TSRjmfWaatY7oLJB",
"instantiation": "true",
"bottom_shell_layers": "3",
"top_shell_layers": "4",
"layer_height": "0.40",
"compatible_printers": [
"BLOCKS Pro S100 0.8 nozzle"
],
"renamed_from": "0.40mm Standard 0.8 nozzle @Blocks"
]
}
@@ -22,7 +22,7 @@
"top_solid_infill_flow_ratio": "0.97",
"bottom_solid_infill_flow_ratio": "1.08",
"seam_position": "aligned",
"sparse_infill_pattern": "grid",
"sparse_infill_patter": "grid",
"initial_layer_speed": "60",
"initial_layer_infill_speed": "80",
"initial_travel_speed": "100%",
@@ -57,7 +57,5 @@
"accel_to_decel_enable": "1",
"accel_to_decel_factor": "50%",
"elefant_foot_compensation": "0.25",
"elefant_foot_compensation_layers": "1",
"skirt_loops": "2",
"skirt_type": "perobject"
"elefant_foot_compensation_layers": "1"
}
@@ -1,15 +1,14 @@
{
"type": "process",
"name": "0.50mm Draft 0.8 nozzle @Blocks_Pro_S100",
"name": "0.50mm Draft 0.8 nozzle @Blocks",
"inherits": "fdm_process_common 0.8 nozzle",
"from": "system",
"setting_id": "oAhgUZdADR7kP87e",
"setting_id": "vOtZHrDS88Shmf2m",
"instantiation": "true",
"bottom_shell_layers": "3",
"top_shell_layers": "4",
"layer_height": "0.50",
"compatible_printers": [
"BLOCKS Pro S100 0.8 nozzle"
],
"renamed_from": "0.50mm Draft 0.8 nozzle @Blocks"
]
}
@@ -1,15 +1,14 @@
{
"type": "process",
"name": "0.50mm Optimal 1.2 nozzle @Blocks_Pro_S100",
"name": "0.50mm Optimal 1.2 nozzle @Blocks",
"inherits": "fdm_process_common 1.2 nozzle",
"from": "system",
"setting_id": "0eqNgX26jemsBfV5",
"setting_id": "BKrEAKqDYzdH43wc",
"instantiation": "true",
"bottom_shell_layers": "2",
"top_shell_layers": "3",
"layer_height": "0.50",
"compatible_printers": [
"BLOCKS Pro S100 1.2 nozzle"
],
"renamed_from": "0.50mm Optimal 1.2 nozzle @Blocks"
]
}
@@ -1,15 +1,14 @@
{
"type": "process",
"name": "0.50mm Standard 1.0 nozzle @Blocks_Pro_S100",
"name": "0.50mm Standard 1.0 nozzle @Blocks",
"inherits": "fdm_process_common 1.0 nozzle",
"from": "system",
"setting_id": "z1qCW1cEMc4QvBbV",
"setting_id": "D40iksDm7mFjhSKn",
"instantiation": "true",
"bottom_shell_layers": "3",
"top_shell_layers": "3",
"layer_height": "0.50",
"compatible_printers": [
"BLOCKS Pro S100 1.0 nozzle"
],
"renamed_from": "0.50mm Standard 1.0 nozzle @Blocks"
]
}
@@ -21,7 +21,7 @@
"top_solid_infill_flow_ratio": "0.97",
"bottom_solid_infill_flow_ratio": "1.08",
"seam_position": "aligned",
"sparse_infill_pattern": "grid",
"sparse_infill_patter": "grid",
"initial_layer_speed": "60",
"initial_layer_infill_speed": "80",
"initial_travel_speed": "100%",
@@ -57,7 +57,5 @@
"accel_to_decel_enable": "1",
"accel_to_decel_factor": "50%",
"elefant_foot_compensation": "0.25",
"elefant_foot_compensation_layers": "1",
"skirt_loops": "2",
"skirt_type": "perobject"
"elefant_foot_compensation_layers": "1"
}
@@ -1,15 +1,14 @@
{
"type": "process",
"name": "0.60mm Draft 1.0 nozzle @Blocks_Pro_S100",
"name": "0.60mm Draft 1.0 nozzle @Blocks",
"inherits": "fdm_process_common 1.0 nozzle",
"from": "system",
"setting_id": "gTTgtfG8qb9My3hH",
"setting_id": "UJUe8XCBzZf8NyCL",
"instantiation": "true",
"bottom_shell_layers": "2",
"top_shell_layers": "3",
"layer_height": "0.60",
"compatible_printers": [
"BLOCKS Pro S100 1.0 nozzle"
],
"renamed_from": "0.60mm Draft 1.0 nozzle @Blocks"
]
}
@@ -1,15 +1,14 @@
{
"type": "process",
"name": "0.60mm Standard 1.2 nozzle @Blocks_Pro_S100",
"name": "0.60mm Standard 1.2 nozzle @Blocks",
"inherits": "fdm_process_common 1.2 nozzle",
"from": "system",
"setting_id": "ApcyToENCIANrI71",
"setting_id": "PSBFrTykpkytPI2Q",
"instantiation": "true",
"bottom_shell_layers": "2",
"top_shell_layers": "3",
"layer_height": "0.60",
"compatible_printers": [
"BLOCKS Pro S100 1.2 nozzle"
],
"renamed_from": "0.60mm Standard 1.2 nozzle @Blocks"
]
}
@@ -1,15 +1,14 @@
{
"type": "process",
"name": "0.70mm Draft 1.2 nozzle @Blocks_Pro_S100",
"name": "0.70mm Draft 1.2 nozzle @Blocks",
"inherits": "fdm_process_common 1.2 nozzle",
"from": "system",
"setting_id": "DEv7X7zaTfxRQmjo",
"setting_id": "0ZQ0USGX9SwAKr29",
"instantiation": "true",
"bottom_shell_layers": "2",
"top_shell_layers": "3",
"layer_height": "0.70",
"compatible_printers": [
"BLOCKS Pro S100 1.2 nozzle"
],
"renamed_from": "0.70mm Draft 1.2 nozzle @Blocks"
]
}
@@ -1,15 +1,14 @@
{
"type": "process",
"name": "0.70mm Extra Draft 1.0 nozzle @Blocks_Pro_S100",
"name": "0.70mm Extra Draft 1.0 nozzle @Blocks",
"inherits": "fdm_process_common 1.0 nozzle",
"from": "system",
"setting_id": "nUW7N9dJmTyQT4AG",
"setting_id": "69PFmjOVFBOIp3Qg",
"instantiation": "true",
"bottom_shell_layers": "2",
"top_shell_layers": "3",
"layer_height": "0.70",
"compatible_printers": [
"BLOCKS Pro S100 1.0 nozzle"
],
"renamed_from": "0.70mm Extra Draft 1.0 nozzle @Blocks"
]
}
@@ -1,15 +1,14 @@
{
"type": "process",
"name": "0.80mm Extra Draft 1.2 nozzle @Blocks_Pro_S100",
"name": "0.80mm Extra Draft 1.2 nozzle @Blocks",
"inherits": "fdm_process_common 1.2 nozzle",
"from": "system",
"setting_id": "JMy62SonRfK0cAdL",
"setting_id": "iBwRI5JJewW5HJSv",
"instantiation": "true",
"bottom_shell_layers": "2",
"top_shell_layers": "2",
"layer_height": "0.80",
"compatible_printers": [
"BLOCKS Pro S100 1.2 nozzle"
],
"renamed_from": "0.80mm Extra Draft 1.2 nozzle @Blocks"
]
}
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "OrcaFilamentLibrary",
"version": "02.04.00.13",
"version": "02.04.00.12",
"force_update": "0",
"description": "Orca Filament Library",
"filament_list": [
@@ -6,7 +6,7 @@
"filament_id": "OFvgE0Zh",
"instantiation": "false",
"fan_cooling_layer_time": [
"81"
"80"
],
"fan_min_speed": [
"50"
+1 -3
View File
@@ -575,7 +575,7 @@ function CapabilityCanRun(plugin, capability) {
}
function IsPluginChecked(plugin) {
return plugin.is_loaded;
return GetStatus(plugin) === "Activated";
}
function HasMixedCapabilityState(plugin) {
@@ -1347,8 +1347,6 @@ function StatusDescription(plugin) {
return "This plugin is still loading.";
case "Error":
return "This plugin is blocked until its error is fixed.";
case "RuntimeError":
return "This plugin is loaded but a capability reported an error.";
case "Inactive":
default:
return "This plugin is inactive. Activate it to install or load it.";
@@ -424,11 +424,6 @@ body.pane-resizing {
font-weight: 600;
}
.status-cell.status-runtimeerror {
color: var(--plugin-status-warn);
font-weight: 600;
}
.status-cell.status-loading {
color: var(--plugin-status-warn);
font-weight: 600;
@@ -685,11 +680,6 @@ body.pane-resizing {
color: var(--plugin-status-danger);
}
.detail-status-chip.status-runtimeerror {
background: var(--plugin-status-warn-bg);
color: var(--plugin-status-warn);
}
.detail-status-chip.status-loading {
background: var(--plugin-status-warn-bg);
color: var(--plugin-status-warn);
@@ -331,15 +331,6 @@ modules:
sha256: deedcabe339165214a3637df4c86a507aef0d793cf8774ff68735f4737e8ddbc
dest: external-packages/FFMPEG
# libdatachannel v0.24.5
# The Git checkout includes the submodules that are missing from the
# GitHub source archive. DataChannel.cmake consumes it as SOURCE_DIR.
- type: git
url: https://github.com/paullouisageneau/libdatachannel.git
tag: v0.24.5
commit: 443f6934d9007eb7076ab7825ba330f355fcbead
dest: external-packages/DataChannel
# ---------------------------------------------------------------
# Fallback archives for deps normally provided by the GNOME SDK.
# These are only used if find_package() fails to locate them.
+48 -66
View File
@@ -5,6 +5,7 @@
//BBS
#include "Preset.hpp"
#include "Exception.hpp"
#include "InstanceLock.hpp"
#include "LocalesUtils.hpp"
#include "Thread.hpp"
#include "format.hpp"
@@ -739,10 +740,13 @@ static bool verify_config_file_checksum(boost::nowide::ifstream &ifs)
#ifdef USE_JSON_CONFIG
std::string AppConfig::load()
std::string AppConfig::load(bool read_only)
{
json j;
// Keep another instance from replacing or restoring the file mid-read.
InstanceLock instance_lock(read_only ? std::string() : lock_path());
// 1) Read the complete config file into a boost::property_tree.
namespace pt = boost::property_tree;
pt::ptree tree;
@@ -992,7 +996,6 @@ void AppConfig::save()
// The config is first written to a file with a PID suffix and then moved
// to avoid race conditions with multiple instances of Slic3r
const auto path = config_path();
std::string path_pid = (boost::format("%1%.%2%") % path % get_current_pid()).str();
json j;
@@ -1122,43 +1125,18 @@ void AppConfig::save()
j["local_machines"][local_machine.first] = m_json;
}
boost::nowide::ofstream c;
c.open(path_pid, std::ios::out | std::ios::trunc);
c << j.dump(1, '\t') << std::endl;
#ifdef WIN32
// WIN32 specific: The final "rename_file()" call is not safe in case of an application crash, there is no atomic "rename file" API
// provided by Windows (sic!). Therefore we save a MD5 checksum to be able to verify file corruption. In addition,
// we save the config file into a backup first before moving it to the final destination.
c << appconfig_md5_hash_line(j.dump(1, '\t'));
#endif
c.close();
if (c.fail()) {
BOOST_LOG_TRIVIAL(error) << "Failed to write new configuration to " << path_pid << "; aborting attempt to overwrite original configuration";
return;
}
#ifdef WIN32
// Make a backup of the configuration file before copying it to the final destination.
std::string error_message;
std::string backup_path = (boost::format("%1%.bak") % path).str();
// Copy configuration file with PID suffix into the configuration file with "bak" suffix.
if (copy_file(path_pid, backup_path, error_message, false) != SUCCESS)
BOOST_LOG_TRIVIAL(error) << "Copying from " << path_pid << " to " << backup_path << " failed. Failed to create a backup configuration.";
#endif
// Rename the config atomically.
// On Windows, the rename is likely NOT atomic, thus it may fail if PrusaSlicer crashes on another thread in the meanwhile.
// To cope with that, we already made a backup of the config on Windows.
rename_file(path_pid, path);
m_dirty = false;
const std::string config_str = j.dump(1, '\t');
if (write_config_file(path, config_str + "\n", config_str))
m_dirty = false;
}
#else
std::string AppConfig::load()
std::string AppConfig::load(bool read_only)
{
// Keep another instance from replacing or restoring the file mid-read.
InstanceLock instance_lock(read_only ? std::string() : lock_path());
// 1) Read the complete config file into a boost::property_tree.
namespace pt = boost::property_tree;
pt::ptree tree;
@@ -1296,7 +1274,6 @@ void AppConfig::save()
// The config is first written to a file with a PID suffix and then moved
// to avoid race conditions with multiple instances of Slic3r
const auto path = config_path();
std::string path_pid = (boost::format("%1%.%2%") % path % get_current_pid()).str();
std::stringstream config_ss;
if (m_mode == EAppMode::Editor)
@@ -1332,39 +1309,39 @@ void AppConfig::save()
// One empty line before the MD5 sum.
config_ss << std::endl;
std::string config_str = config_ss.str();
boost::nowide::ofstream c;
c.open(path_pid, std::ios::out | std::ios::trunc);
c << config_str;
#ifdef WIN32
// WIN32 specific: The final "rename_file()" call is not safe in case of an application crash, there is no atomic "rename file" API
// provided by Windows (sic!). Therefore we save a MD5 checksum to be able to verify file corruption. In addition,
// we save the config file into a backup first before moving it to the final destination.
c << appconfig_md5_hash_line(config_str);
#endif
c.close();
if (c.fail()) {
BOOST_LOG_TRIVIAL(error) << "Failed to write new configuration to " << path_pid << "; aborting attempt to overwrite original configuration";
return;
}
#ifdef WIN32
// Make a backup of the configuration file before copying it to the final destination.
std::string error_message;
std::string backup_path = (boost::format("%1%.bak") % path).str();
// Copy configuration file with PID suffix into the configuration file with "bak" suffix.
if (copy_file(path_pid, backup_path, error_message, false) != SUCCESS)
BOOST_LOG_TRIVIAL(error) << "Copying from " << path_pid << " to " << backup_path << " failed. Failed to create a backup configuration.";
#endif
// Rename the config atomically.
// On Windows, the rename is likely NOT atomic, thus it may fail if PrusaSlicer crashes on another thread in the meanwhile.
// To cope with that, we already made a backup of the config on Windows.
rename_file(path_pid, path);
m_dirty = false;
const std::string config_str = config_ss.str();
if (write_config_file(path, config_str, config_str))
m_dirty = false;
}
#endif
bool AppConfig::write_config_file(const std::string &path, std::string body, const std::string &checksum_source)
{
// Everything before this is assembly; only the writes need the other instances kept out.
InstanceLock instance_lock(lock_path());
#ifdef WIN32
// WIN32 specific: the final replace is not safe in case of an application crash, there is no atomic "rename file" API
// provided by Windows (sic!). Therefore we save a MD5 checksum to be able to verify file corruption. In addition,
// we save the config file into a backup first before moving it to the final destination.
body += appconfig_md5_hash_line(checksum_source);
#endif
// Not flushed to the device: the idle handler saves on the GUI thread after
// any change, and the rename already gives a complete old or new file.
if (const std::error_code ec = write_file_atomically(path, body)) {
BOOST_LOG_TRIVIAL(error) << "Failed to write the configuration " << path << ": " << ec.message() << "; trying again in 10 s";
m_retry_save_at = std::chrono::steady_clock::now() + std::chrono::seconds(10);
return false;
}
m_retry_save_at = {};
#ifdef WIN32
// Written after the config, so the backup never holds a state that was not confirmed written.
const std::string backup_path = (boost::format("%1%.bak") % path).str();
if (const std::error_code ec = write_file_atomically(backup_path, body))
BOOST_LOG_TRIVIAL(error) << "Failed to write the backup configuration " << backup_path << ": " << ec.message();
#endif
return true;
}
bool AppConfig::get_variant(const std::string &vendor, const std::string &model, const std::string &variant) const
{
const auto it_v = m_vendors.find(vendor);
@@ -1853,6 +1830,11 @@ void AppConfig::reset_selections()
}
}
std::string AppConfig::lock_path()
{
return Slic3r::data_dir().empty() ? std::string() : config_path() + ".lock";
}
std::string AppConfig::config_path()
{
#ifdef USE_JSON_CONFIG
@@ -1889,7 +1871,7 @@ bool AppConfig::exists()
std::string AppConfig::load_if_exists()
{
return boost::filesystem::exists(loading_path()) ? load() : std::string();
return boost::filesystem::exists(loading_path()) ? load(/*read_only=*/true) : std::string();
}
}; // namespace Slic3r
+16 -1
View File
@@ -2,6 +2,7 @@
#define slic3r_AppConfig_hpp_
#include <set>
#include <chrono>
#include <map>
#include <string>
#include "nlohmann/json.hpp"
@@ -122,14 +123,18 @@ public:
// Load the slic3r.ini from a user profile directory (or a datadir, if configured).
// Return an error string, or an empty string on success.
std::string load();
std::string load(bool read_only = false);
// Treat a missing config as default state; otherwise load it normally.
// The CLI's load: it never saves, so it takes no lock and creates no lock file.
std::string load_if_exists();
// Store the slic3r.ini into a user profile directory (or a datadir, if configured).
void save();
// Does this config need to be saved?
bool dirty() const { return m_dirty; }
// False for ten seconds after a failed write, so the idle handler does not
// repeat a hopeless attempt on every event; an explicit save() always tries.
bool save_due() const { return std::chrono::steady_clock::now() >= m_retry_save_at; }
void set_dirty() { m_dirty = true; }
@@ -339,6 +344,8 @@ public:
// Get the default config path from Slic3r::data_dir().
std::string config_path();
// Lock file guarding config_path() against other running instances; empty without a data dir.
std::string lock_path();
// Returns true if the user's data directory comes from before Slic3r 1.40.0 (no updating)
bool legacy_datadir() const { return m_legacy_datadir; }
@@ -449,8 +456,16 @@ private:
// Preset for each machine
MachineSettingMap m_printer_settings;
// Writes the assembled config text, and on Windows its checksum and a backup copy; false when the
// config itself could not be written, in which case the caller stays dirty and retries. `checksum_source`
// is the text load() will verify, which for the JSON config ends before the trailing newline.
bool write_config_file(const std::string &path, std::string body, const std::string &checksum_source);
// Has any value been modified since the config.ini has been last saved or loaded?
bool m_dirty;
// After a failed write, save_due() is false for the next ten seconds, so the
// idle handler does not repeat a hopeless write on every event.
std::chrono::steady_clock::time_point m_retry_save_at{};
// Original version found in the ini file before it was overwritten
Semver m_orig_version;
// Whether the existing version is before system profiles & configuration updating

Some files were not shown because too many files have changed in this diff Show More