mirror of
https://github.com/OrcaSlicer/OrcaSlicer.git
synced 2026-09-24 09:23:27 +00:00
Compare commits
59
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b205a8559e | ||
|
|
0335d76d30 | ||
|
|
3e56d25f64 | ||
|
|
4c149b69eb | ||
|
|
57cb60e20f | ||
|
|
8ffe84b1dd | ||
|
|
f4268a0eec | ||
|
|
17e2adc283 | ||
|
|
d87f7e462c | ||
|
|
617df79260 | ||
|
|
0a6a42ecb5 | ||
|
|
762e474433 | ||
|
|
115d6dde46 | ||
|
|
9da2975424 | ||
|
|
7b3228d10d | ||
|
|
2de58e557b | ||
|
|
514ab02525 | ||
|
|
d7688a27d0 | ||
|
|
c2965a1336 | ||
|
|
a587859e84 | ||
|
|
3ffb9585d2 | ||
|
|
af854f3242 | ||
|
|
9b60b9cd5d | ||
|
|
927c5efb8c | ||
|
|
81951fddb5 | ||
|
|
4535f19501 | ||
|
|
ee9a796f14 | ||
|
|
3bce4bb197 | ||
|
|
dcee299909 | ||
|
|
9eeb73b68b | ||
|
|
7ab3174f7c | ||
|
|
5a6e31ac5b | ||
|
|
e700113b39 | ||
|
|
5ed8f5ef25 | ||
|
|
454335dba6 | ||
|
|
b0c1887f40 | ||
|
|
e6f917d7c5 | ||
|
|
14d2dfdd4c | ||
|
|
b1e510fc99 | ||
|
|
8c0b89bc8a | ||
|
|
3e2373c86e | ||
|
|
2abfa512ef | ||
|
|
fe0eafc02b | ||
|
|
5fafbb59fc | ||
|
|
abb4eddb9c | ||
|
|
9bcee518f8 | ||
|
|
0c4c04bcaf | ||
|
|
5f8b48473a | ||
|
|
6bb7903b97 | ||
|
|
d641ca598a | ||
|
|
15f330641c | ||
|
|
d07cb462a8 | ||
|
|
93a82af6b4 | ||
|
|
06eefe7c1e | ||
|
|
847807adac | ||
|
|
3be36bdea4 | ||
|
|
a70b903a19 | ||
|
|
6d9ab28e2d | ||
|
|
38ea91a6bb |
@@ -14,6 +14,9 @@ on:
|
||||
- 'resources/**'
|
||||
- ".github/workflows/build_*.yml"
|
||||
- 'scripts/flatpak/**'
|
||||
- 'snap/**'
|
||||
- 'scripts/msix/**'
|
||||
- 'tests/**'
|
||||
|
||||
pull_request:
|
||||
branches:
|
||||
@@ -30,6 +33,9 @@ on:
|
||||
- 'build_release_vs2022.bat'
|
||||
- 'build_release_macos.sh'
|
||||
- 'scripts/flatpak/**'
|
||||
- 'snap/**'
|
||||
- 'scripts/msix/**'
|
||||
- 'tests/**'
|
||||
|
||||
|
||||
schedule:
|
||||
@@ -51,11 +57,23 @@ jobs:
|
||||
build_linux:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
# Build both arches on every event (PRs included), through the same
|
||||
# build_check_cache -> build_deps -> build_orca chain (AppImage + snap).
|
||||
# aarch64 always uses the GitHub-hosted arm runner (there is no arm
|
||||
# self-hosted server). amd64's empty arch is load-bearing: it keeps the
|
||||
# historical 'linux-clang' deps cache key and the unsuffixed asset names.
|
||||
matrix:
|
||||
include:
|
||||
- arch: ""
|
||||
os: ${{ vars.SELF_HOSTED && 'orca-lnx-server' || 'ubuntu-24.04' }}
|
||||
- arch: "aarch64"
|
||||
os: ubuntu-24.04-arm
|
||||
# Don't run scheduled builds on forks:
|
||||
if: ${{ !cancelled() && (github.event_name != 'schedule' || github.repository == 'OrcaSlicer/OrcaSlicer') }}
|
||||
uses: ./.github/workflows/build_check_cache.yml
|
||||
with:
|
||||
os: ${{ vars.SELF_HOSTED && 'orca-lnx-server' || 'ubuntu-24.04' }}
|
||||
os: ${{ matrix.os }}
|
||||
arch: ${{ matrix.arch }}
|
||||
build-deps-only: ${{ inputs.build-deps-only || false }}
|
||||
secrets: inherit
|
||||
build_windows:
|
||||
@@ -95,7 +113,9 @@ jobs:
|
||||
secrets: inherit
|
||||
unit_tests:
|
||||
name: Unit Tests
|
||||
runs-on: ${{ vars.SELF_HOSTED && 'orca-lnx-server' || 'ubuntu-24.04' }}
|
||||
# Tests are built on the aarch64 leg by default (faster GitHub arm runner),
|
||||
# so run them there; self-hosted builds them on the amd64 server instead.
|
||||
runs-on: ${{ vars.SELF_HOSTED && 'orca-lnx-server' || 'ubuntu-24.04-arm' }}
|
||||
needs: build_linux
|
||||
if: ${{ !cancelled() && success() }}
|
||||
steps:
|
||||
@@ -233,4 +253,3 @@ jobs:
|
||||
asset_name: OrcaSlicer-Linux-flatpak_nightly_${{ matrix.variant.arch }}.flatpak
|
||||
asset_content_type: application/octet-stream
|
||||
max_releases: 1 # optional, if there are more releases than this matching the asset_name, the oldest ones are going to be deleted
|
||||
|
||||
|
||||
@@ -33,8 +33,10 @@ jobs:
|
||||
- name: set outputs
|
||||
id: set_outputs
|
||||
env:
|
||||
# Keep macOS cache keys and paths architecture-specific.
|
||||
cache-os: ${{ runner.os == 'macOS' && format('macos-{0}', inputs.arch) || (runner.os == 'Windows' && 'windows' || 'linux-clang') }}
|
||||
# Keep macOS/Linux cache keys architecture-specific. amd64 Linux passes
|
||||
# no arch (key stays 'linux-clang', preserving the existing cache);
|
||||
# aarch64 gets its own 'linux-clang-aarch64' key.
|
||||
cache-os: ${{ runner.os == 'macOS' && format('macos-{0}', inputs.arch) || (runner.os == 'Windows' && 'windows' || format('linux-clang{0}', inputs.arch && format('-{0}', inputs.arch) || '')) }}
|
||||
dep-folder-name: ${{ runner.os == 'macOS' && format('/{0}', inputs.arch) || '/OrcaSlicer_dep' }}
|
||||
output-cmd: ${{ runner.os == 'Windows' && '$env:GITHUB_OUTPUT' || '"$GITHUB_OUTPUT"'}}
|
||||
run: |
|
||||
|
||||
@@ -65,6 +65,15 @@ jobs:
|
||||
echo "ver_pure=$ver_pure" >> $GITHUB_ENV
|
||||
echo "date=$(date +'%Y%m%d')" >> $GITHUB_ENV
|
||||
echo "git_commit_hash=$git_commit_hash" >> $GITHUB_ENV
|
||||
# Per-arch Linux naming, computed once: amd64 keeps the historical
|
||||
# unsuffixed names (arch_suffix empty); snap_arch is snapcraft's token.
|
||||
# Unused on macOS/Windows.
|
||||
if [ '${{ inputs.arch }}' = 'aarch64' ]; then
|
||||
echo "arch_suffix=_aarch64" >> $GITHUB_ENV
|
||||
echo "snap_arch=arm64" >> $GITHUB_ENV
|
||||
else
|
||||
echo "snap_arch=amd64" >> $GITHUB_ENV
|
||||
fi
|
||||
shell: bash
|
||||
|
||||
- name: Get the version and date on Windows
|
||||
@@ -153,14 +162,6 @@ jobs:
|
||||
run: |
|
||||
./build_release_macos.sh -u -x ${{ !vars.SELF_HOSTED && '-1' || '' }} -a universal -t 10.15
|
||||
|
||||
- name: Delete intermediate per-arch artifacts
|
||||
if: runner.os == 'macOS' && inputs.macos-combine-only
|
||||
uses: geekyeggo/delete-artifact@v6
|
||||
with:
|
||||
name: |
|
||||
OrcaSlicer_Mac_bundle_arm64_${{ github.sha }}
|
||||
OrcaSlicer_Mac_bundle_x86_64_${{ github.sha }}
|
||||
|
||||
# Thanks to RaySajuuk, it's working now
|
||||
- name: Sign app and notary
|
||||
if: github.repository == 'OrcaSlicer/OrcaSlicer' && (github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/heads/release/')) && runner.os == 'macOS' && inputs.macos-combine-only
|
||||
@@ -171,6 +172,8 @@ jobs:
|
||||
KEYCHAIN_PASSWORD: ${{ secrets.KEYCHAIN_PASSWORD }}
|
||||
CERTIFICATE_ID: ${{ secrets.MACOS_CERTIFICATE_ID }}
|
||||
run: |
|
||||
# Load the `retry` helper (retries flaky commands such as `hdiutil create`).
|
||||
source ${{ github.workspace }}/scripts/retry.sh
|
||||
CERTIFICATE_PATH=$RUNNER_TEMP/build_certificate.p12
|
||||
KEYCHAIN_PATH=$RUNNER_TEMP/app-signing.keychain-db
|
||||
echo -n "$BUILD_CERTIFICATE_BASE64" | base64 --decode --output $CERTIFICATE_PATH
|
||||
@@ -193,7 +196,7 @@ jobs:
|
||||
rm -rf ${{ github.workspace }}/build/universal/OrcaSlicer_dmg/*
|
||||
cp -R ${{ github.workspace }}/build/universal/OrcaSlicer/OrcaSlicer.app ${{ github.workspace }}/build/universal/OrcaSlicer_dmg/
|
||||
ln -sfn /Applications ${{ github.workspace }}/build/universal/OrcaSlicer_dmg/Applications
|
||||
hdiutil create -volname "OrcaSlicer" -srcfolder ${{ github.workspace }}/build/universal/OrcaSlicer_dmg -ov -format UDZO OrcaSlicer_Mac_universal_${{ env.ver }}.dmg
|
||||
retry hdiutil create -volname "OrcaSlicer" -srcfolder ${{ github.workspace }}/build/universal/OrcaSlicer_dmg -ov -format UDZO OrcaSlicer_Mac_universal_${{ env.ver }}.dmg
|
||||
codesign --deep --force --verbose --options runtime --timestamp --entitlements ${{ github.workspace }}/scripts/disable_validation.entitlements --sign "$CERTIFICATE_ID" OrcaSlicer_Mac_universal_${{ env.ver }}.dmg
|
||||
|
||||
# Create separate OrcaSlicer_profile_validator DMG if the app exists
|
||||
@@ -202,7 +205,7 @@ jobs:
|
||||
rm -rf ${{ github.workspace }}/build/universal/OrcaSlicer_profile_validator_dmg/*
|
||||
cp -R ${{ github.workspace }}/build/universal/OrcaSlicer/OrcaSlicer_profile_validator.app ${{ github.workspace }}/build/universal/OrcaSlicer_profile_validator_dmg/
|
||||
ln -sfn /Applications ${{ github.workspace }}/build/universal/OrcaSlicer_profile_validator_dmg/Applications
|
||||
hdiutil create -volname "OrcaSlicer Profile Validator" -srcfolder ${{ github.workspace }}/build/universal/OrcaSlicer_profile_validator_dmg -ov -format UDZO OrcaSlicer_profile_validator_Mac_universal_${{ env.ver }}.dmg
|
||||
retry hdiutil create -volname "OrcaSlicer Profile Validator" -srcfolder ${{ github.workspace }}/build/universal/OrcaSlicer_profile_validator_dmg -ov -format UDZO OrcaSlicer_profile_validator_Mac_universal_${{ env.ver }}.dmg
|
||||
codesign --deep --force --verbose --options runtime --timestamp --entitlements ${{ github.workspace }}/scripts/disable_validation.entitlements --sign "$CERTIFICATE_ID" OrcaSlicer_profile_validator_Mac_universal_${{ env.ver }}.dmg
|
||||
fi
|
||||
|
||||
@@ -220,11 +223,13 @@ jobs:
|
||||
if: github.ref != 'refs/heads/main' && runner.os == 'macOS' && inputs.macos-combine-only
|
||||
working-directory: ${{ github.workspace }}
|
||||
run: |
|
||||
# Load the `retry` helper (retries flaky commands such as `hdiutil create`).
|
||||
source ${{ github.workspace }}/scripts/retry.sh
|
||||
mkdir -p ${{ github.workspace }}/build/universal/OrcaSlicer_dmg
|
||||
rm -rf ${{ github.workspace }}/build/universal/OrcaSlicer_dmg/*
|
||||
cp -R ${{ github.workspace }}/build/universal/OrcaSlicer/OrcaSlicer.app ${{ github.workspace }}/build/universal/OrcaSlicer_dmg/
|
||||
ln -sfn /Applications ${{ github.workspace }}/build/universal/OrcaSlicer_dmg/Applications
|
||||
hdiutil create -volname "OrcaSlicer" -srcfolder ${{ github.workspace }}/build/universal/OrcaSlicer_dmg -ov -format UDZO OrcaSlicer_Mac_universal_${{ env.ver }}.dmg
|
||||
retry hdiutil create -volname "OrcaSlicer" -srcfolder ${{ github.workspace }}/build/universal/OrcaSlicer_dmg -ov -format UDZO OrcaSlicer_Mac_universal_${{ env.ver }}.dmg
|
||||
|
||||
# Create separate OrcaSlicer_profile_validator DMG if the app exists
|
||||
if [ -f "${{ github.workspace }}/build/universal/OrcaSlicer/OrcaSlicer_profile_validator.app/Contents/MacOS/OrcaSlicer_profile_validator" ]; then
|
||||
@@ -232,9 +237,19 @@ jobs:
|
||||
rm -rf ${{ github.workspace }}/build/universal/OrcaSlicer_profile_validator_dmg/*
|
||||
cp -R ${{ github.workspace }}/build/universal/OrcaSlicer/OrcaSlicer_profile_validator.app ${{ github.workspace }}/build/universal/OrcaSlicer_profile_validator_dmg/
|
||||
ln -sfn /Applications ${{ github.workspace }}/build/universal/OrcaSlicer_profile_validator_dmg/Applications
|
||||
hdiutil create -volname "OrcaSlicer Profile Validator" -srcfolder ${{ github.workspace }}/build/universal/OrcaSlicer_profile_validator_dmg -ov -format UDZO OrcaSlicer_profile_validator_Mac_universal_${{ env.ver }}.dmg
|
||||
retry hdiutil create -volname "OrcaSlicer Profile Validator" -srcfolder ${{ github.workspace }}/build/universal/OrcaSlicer_profile_validator_dmg -ov -format UDZO OrcaSlicer_profile_validator_Mac_universal_${{ env.ver }}.dmg
|
||||
fi
|
||||
|
||||
# Delete the per-arch bundles only after signing/DMG creation succeeded, so a
|
||||
# failed run keeps them available for a re-run instead of forcing a full rebuild.
|
||||
- name: Delete intermediate per-arch artifacts
|
||||
if: success() && runner.os == 'macOS' && inputs.macos-combine-only
|
||||
uses: geekyeggo/delete-artifact@v6
|
||||
with:
|
||||
name: |
|
||||
OrcaSlicer_Mac_bundle_arm64_${{ github.sha }}
|
||||
OrcaSlicer_Mac_bundle_x86_64_${{ github.sha }}
|
||||
|
||||
- name: Upload artifacts mac
|
||||
if: runner.os == 'macOS' && inputs.macos-combine-only
|
||||
uses: actions/upload-artifact@v7
|
||||
@@ -371,6 +386,25 @@ jobs:
|
||||
asset_content_type: application/x-msdownload
|
||||
max_releases: 1
|
||||
|
||||
- name: Build MSIX Store package Win
|
||||
if: runner.os == 'Windows' && !vars.SELF_HOSTED
|
||||
working-directory: ${{ github.workspace }}
|
||||
shell: pwsh
|
||||
run: |
|
||||
./scripts/msix/build_msix.ps1 `
|
||||
-InstallDir "${{ github.workspace }}/build/OrcaSlicer" `
|
||||
-OutputPath "${{ github.workspace }}/build/OrcaSlicer_Windows_MSIX_${{ env.ver }}.msix" `
|
||||
-IdentityName "${{ vars.ORCA_MSIX_IDENTITY_NAME || 'OrcaSlicer.OrcaSlicer' }}" `
|
||||
-Publisher "${{ vars.ORCA_MSIX_PUBLISHER || 'CN=38F7EA55-C73B-4072-B3B2-C8E0EA15BB82' }}" `
|
||||
-PublisherDisplayName "${{ vars.ORCA_MSIX_PUBLISHER_DISPLAY_NAME || 'OrcaSlicer' }}"
|
||||
|
||||
- name: Upload artifacts Win MSIX
|
||||
if: runner.os == 'Windows' && !vars.SELF_HOSTED
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
name: OrcaSlicer_Windows_MSIX_${{ env.ver }}
|
||||
path: ${{ github.workspace }}/build/OrcaSlicer_Windows_MSIX_${{ env.ver }}.msix
|
||||
|
||||
# Ubuntu
|
||||
- name: Apt-Install Dependencies
|
||||
if: runner.os == 'Linux' && !vars.SELF_HOSTED
|
||||
@@ -382,16 +416,54 @@ jobs:
|
||||
if: runner.os == 'Linux'
|
||||
shell: bash
|
||||
run: |
|
||||
./build_linux.sh -istrlL
|
||||
# Build + tar the unit tests (-t) only on the leg that runs them: the
|
||||
# aarch64 leg by default (faster GitHub arm runner), or amd64 when using
|
||||
# self-hosted runners (no arm self-hosted server). unit_tests downloads
|
||||
# this tarball. The profile validator is built with -s, so amd64 keeps it.
|
||||
tests=${{ (!vars.SELF_HOSTED && inputs.arch == 'aarch64') || (vars.SELF_HOSTED && inputs.arch != 'aarch64') }}
|
||||
if $tests; then flags=-istrlL; else flags=-isrlL; fi
|
||||
./build_linux.sh "$flags"
|
||||
./scripts/check_appimage_libs.sh ./build/package ./build/package/bin/orca-slicer
|
||||
mv -n ./build/OrcaSlicer_Linux_V${{ env.ver_pure }}.AppImage ./build/OrcaSlicer_Linux_AppImage${{ env.ubuntu-ver-str }}_${{ env.ver }}.AppImage
|
||||
chmod +x ./build/OrcaSlicer_Linux_AppImage${{ env.ubuntu-ver-str }}_${{ env.ver }}.AppImage
|
||||
tar -cvpf build_tests.tar build/tests
|
||||
appimage=./build/OrcaSlicer_Linux_AppImage${{ env.ubuntu-ver-str }}${{ env.arch_suffix }}_${{ env.ver }}.AppImage
|
||||
mv -n ./build/OrcaSlicer_Linux_V${{ env.ver_pure }}.AppImage "$appimage"
|
||||
chmod +x "$appimage"
|
||||
if $tests; then tar -cvpf build_tests.tar build/tests; fi
|
||||
|
||||
# Build the snap right here, reusing the AppDir we just produced
|
||||
# (build/package): the compiled binary + bundled libs + resources are
|
||||
# repackaged as-is, for both amd64 and aarch64. Skipped on PRs (snapcraft
|
||||
# adds several minutes per arch). The snap is Store-only; it is never
|
||||
# attached to a GitHub release.
|
||||
- name: Free disk space for snap build
|
||||
if: runner.os == 'Linux' && !vars.SELF_HOSTED && github.event_name != 'pull_request'
|
||||
run: sudo rm -rf /usr/local/lib/android /usr/share/dotnet /opt/ghc /opt/hostedtoolcache || true
|
||||
- name: Build snap
|
||||
if: runner.os == 'Linux' && !vars.SELF_HOSTED && github.event_name != 'pull_request'
|
||||
id: snapbuild
|
||||
uses: snapcore/action-build@v1
|
||||
- name: Upload snap artifact
|
||||
if: ${{ ! env.ACT && runner.os == 'Linux' && !vars.SELF_HOSTED && github.event_name != 'pull_request' }}
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
name: OrcaSlicer-Linux-snap_${{ env.ver }}_${{ env.snap_arch }}.snap
|
||||
path: ${{ steps.snapbuild.outputs.snap }}
|
||||
retention-days: 5
|
||||
if-no-files-found: error
|
||||
# Nightly -> Snap Store 'edge'. NOTE: classic confinement means the Store
|
||||
# holds uploads for manual review until classic is granted (snap/README.md).
|
||||
- name: Publish snap to edge (nightly)
|
||||
if: github.repository == 'OrcaSlicer/OrcaSlicer' && github.ref == 'refs/heads/main' && runner.os == 'Linux' && !vars.SELF_HOSTED
|
||||
uses: snapcore/action-publish@v1
|
||||
env:
|
||||
SNAPCRAFT_STORE_CREDENTIALS: ${{ secrets.SNAPCRAFT_STORE_CREDENTIALS }}
|
||||
with:
|
||||
snap: ${{ steps.snapbuild.outputs.snap }}
|
||||
release: edge
|
||||
|
||||
# Use tar because upload-artifacts won't always preserve directory structure
|
||||
# and doesn't preserve file permissions
|
||||
- name: Upload Test Artifact
|
||||
if: runner.os == 'Linux'
|
||||
if: runner.os == 'Linux' && ((!vars.SELF_HOSTED && inputs.arch == 'aarch64') || (vars.SELF_HOSTED && inputs.arch != 'aarch64'))
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
name: ${{ github.sha }}-tests
|
||||
@@ -401,7 +473,7 @@ jobs:
|
||||
if-no-files-found: error
|
||||
|
||||
- name: Run external slicer regression tests
|
||||
if: runner.os == 'Linux'
|
||||
if: runner.os == 'Linux' && inputs.arch != 'aarch64'
|
||||
timeout-minutes: 20
|
||||
shell: bash
|
||||
run: |
|
||||
@@ -411,7 +483,7 @@ jobs:
|
||||
python3 "$test_repo_dir/run_test.py" "${{ github.workspace }}/build/package/bin/orca-slicer"
|
||||
|
||||
- name: Build orca_custom_preset_tests
|
||||
if: github.ref == 'refs/heads/main' && runner.os == 'Linux' && !vars.SELF_HOSTED
|
||||
if: github.ref == 'refs/heads/main' && runner.os == 'Linux' && !vars.SELF_HOSTED && inputs.arch != 'aarch64'
|
||||
working-directory: ${{ github.workspace }}/build/src/Release
|
||||
shell: bash
|
||||
run: |
|
||||
@@ -423,11 +495,11 @@ jobs:
|
||||
if: ${{ ! env.ACT && runner.os == 'Linux' }}
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
name: OrcaSlicer_Linux_ubuntu_${{ env.ubuntu-ver }}_${{ env.ver }}
|
||||
path: './build/OrcaSlicer_Linux_AppImage${{ env.ubuntu-ver-str }}_${{ env.ver }}.AppImage'
|
||||
name: OrcaSlicer_Linux_ubuntu_${{ env.ubuntu-ver }}${{ env.arch_suffix }}_${{ env.ver }}
|
||||
path: "./build/OrcaSlicer_Linux_AppImage${{ env.ubuntu-ver-str }}${{ env.arch_suffix }}_${{ env.ver }}.AppImage"
|
||||
|
||||
- name: Upload OrcaSlicer_profile_validator Ubuntu
|
||||
if: ${{ ! env.ACT && runner.os == 'Linux' && !vars.SELF_HOSTED }}
|
||||
if: ${{ ! env.ACT && runner.os == 'Linux' && !vars.SELF_HOSTED && inputs.arch != 'aarch64' }}
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
name: OrcaSlicer_profile_validator_Linux_ubuntu_${{ env.ubuntu-ver }}_${{ env.ver }}
|
||||
@@ -439,12 +511,12 @@ jobs:
|
||||
with:
|
||||
upload_url: https://uploads.github.com/repos/OrcaSlicer/OrcaSlicer/releases/137995723/assets{?name,label}
|
||||
release_id: 137995723
|
||||
asset_path: ./build/OrcaSlicer_Linux_AppImage${{ env.ubuntu-ver-str }}_${{ env.ver }}.AppImage
|
||||
asset_name: OrcaSlicer_Linux_AppImage${{ env.ubuntu-ver-str }}_nightly.AppImage
|
||||
asset_path: ./build/OrcaSlicer_Linux_AppImage${{ env.ubuntu-ver-str }}${{ env.arch_suffix }}_${{ env.ver }}.AppImage
|
||||
asset_name: OrcaSlicer_Linux_AppImage${{ env.ubuntu-ver-str }}${{ env.arch_suffix }}_nightly.AppImage
|
||||
asset_content_type: application/octet-stream
|
||||
max_releases: 1 # optional, if there are more releases than this matching the asset_name, the oldest ones are going to be deleted
|
||||
- name: Deploy Ubuntu release
|
||||
if: ${{ github.repository == 'OrcaSlicer/OrcaSlicer' && ! env.ACT && github.ref == 'refs/heads/main' && runner.os == 'Linux' && !vars.SELF_HOSTED }}
|
||||
if: ${{ github.repository == 'OrcaSlicer/OrcaSlicer' && ! env.ACT && github.ref == 'refs/heads/main' && runner.os == 'Linux' && !vars.SELF_HOSTED && inputs.arch != 'aarch64' }}
|
||||
uses: rickstaa/action-create-tag@v1
|
||||
with:
|
||||
tag: "nightly-builds"
|
||||
@@ -453,7 +525,7 @@ jobs:
|
||||
message: "nightly-builds"
|
||||
|
||||
- name: Deploy Ubuntu OrcaSlicer_profile_validator release
|
||||
if: ${{ github.repository == 'OrcaSlicer/OrcaSlicer' && ! env.ACT && github.ref == 'refs/heads/main' && runner.os == 'Linux' && !vars.SELF_HOSTED }}
|
||||
if: ${{ github.repository == 'OrcaSlicer/OrcaSlicer' && ! env.ACT && github.ref == 'refs/heads/main' && runner.os == 'Linux' && !vars.SELF_HOSTED && inputs.arch != 'aarch64' }}
|
||||
uses: WebFreak001/deploy-nightly@v3.2.0
|
||||
with:
|
||||
upload_url: https://uploads.github.com/repos/OrcaSlicer/OrcaSlicer/releases/137995723/assets{?name,label}
|
||||
@@ -464,7 +536,7 @@ jobs:
|
||||
max_releases: 1
|
||||
|
||||
- name: Deploy orca_custom_preset_tests
|
||||
if: ${{ github.repository == 'OrcaSlicer/OrcaSlicer' && ! env.ACT && github.ref == 'refs/heads/main' && runner.os == 'Linux' && !vars.SELF_HOSTED }}
|
||||
if: ${{ github.repository == 'OrcaSlicer/OrcaSlicer' && ! env.ACT && github.ref == 'refs/heads/main' && runner.os == 'Linux' && !vars.SELF_HOSTED && inputs.arch != 'aarch64' }}
|
||||
uses: WebFreak001/deploy-nightly@v3.2.0
|
||||
with:
|
||||
upload_url: https://uploads.github.com/repos/OrcaSlicer/OrcaSlicer/releases/137995723/assets{?name,label}
|
||||
|
||||
@@ -73,11 +73,13 @@ jobs:
|
||||
|
||||
- name: Download release artifacts from build run
|
||||
run: |
|
||||
# Windows_V* (not Windows_*) keeps the MSIX Store artifact out: it goes to Partner Center, not GitHub releases.
|
||||
gh run download "$RUN_ID" --repo "$GITHUB_REPOSITORY" --dir artifacts \
|
||||
-p 'OrcaSlicer_Windows_*' \
|
||||
-p 'OrcaSlicer_Windows_V*' \
|
||||
-p 'OrcaSlicer_Mac_universal_*' \
|
||||
-p 'OrcaSlicer_Linux_ubuntu_*' \
|
||||
-p 'OrcaSlicer-Linux-flatpak_*' \
|
||||
-p 'OrcaSlicer-Linux-snap_*' \
|
||||
-p 'PDB'
|
||||
echo "Downloaded artifact folders:"
|
||||
ls -1 artifacts
|
||||
@@ -100,6 +102,9 @@ jobs:
|
||||
find artifacts -type f -name '*.AppImage' -exec cp -v {} upload/ \;
|
||||
# Flatpak bundles (x86_64 + aarch64).
|
||||
find artifacts -type f -name '*.flatpak' -exec cp -v {} upload/ \;
|
||||
# Snaps are intentionally NOT copied here: they go to the Snap Store
|
||||
# only (see the "Publish snaps to the Snap Store" step), not to the
|
||||
# GitHub release.
|
||||
# Windows debug symbols (PDB archive, for developers).
|
||||
find artifacts -type f -name 'Debug_PDB_*.7z' -exec cp -v {} upload/ \;
|
||||
|
||||
@@ -125,3 +130,30 @@ jobs:
|
||||
gh release upload "$TAG" upload/* --repo "$GITHUB_REPOSITORY" --clobber
|
||||
echo "Uploaded to draft release: $TAG"
|
||||
gh release view "$TAG" --repo "$GITHUB_REPOSITORY" --json assets --jq '.assets[].name'
|
||||
|
||||
- name: Publish snaps to the Snap Store
|
||||
# Snaps ship to the Store only (not as release assets). Channel comes from
|
||||
# the tag suffix; nightly -> edge is handled in the build workflows.
|
||||
# NOTE: classic confinement means the Store holds uploads for manual
|
||||
# review until classic is granted (see snap/README.md).
|
||||
env:
|
||||
SNAPCRAFT_STORE_CREDENTIALS: ${{ secrets.SNAPCRAFT_STORE_CREDENTIALS }}
|
||||
run: |
|
||||
set -euo pipefail
|
||||
mapfile -t snaps < <(find artifacts -type f -name '*.snap')
|
||||
if [ ${#snaps[@]} -eq 0 ]; then
|
||||
echo "::warning::No .snap artifacts in run $RUN_ID; skipping Snap Store publish."
|
||||
exit 0
|
||||
fi
|
||||
case "$TAG" in
|
||||
*-rc*) channel=candidate ;;
|
||||
*-beta*|*-alpha*) channel=beta ;;
|
||||
*) channel=stable ;;
|
||||
esac
|
||||
echo "Releasing $TAG to Snap Store channel: $channel"
|
||||
sudo snap install snapcraft --classic
|
||||
for s in "${snaps[@]}"; do
|
||||
echo "::group::snapcraft upload $s --release=$channel"
|
||||
snapcraft upload "$s" --release="$channel"
|
||||
echo "::endgroup::"
|
||||
done
|
||||
|
||||
+1
-2
@@ -45,5 +45,4 @@ test.js
|
||||
.clangd
|
||||
internal_docs/
|
||||
*.flatpak
|
||||
/flatpak-repo/
|
||||
*.pyc
|
||||
/flatpak-repo/
|
||||
@@ -159,8 +159,32 @@ flatpak run com.orcaslicer.OrcaSlicer
|
||||
|
||||
It can also be installed through graphical software managers (KDE Discover, GNOME Software, etc.) when Flathub is enabled. Search for **OrcaSlicer** in your software center.
|
||||
|
||||
### Snap Store
|
||||
|
||||
OrcaSlicer is available from the Snap Store:
|
||||
|
||||
[](https://snapcraft.io/orcaslicer)
|
||||
|
||||
```shell
|
||||
sudo snap install orcaslicer --classic
|
||||
```
|
||||
|
||||
Use a different channel for pre-releases or bleeding-edge builds:
|
||||
|
||||
```shell
|
||||
sudo snap install orcaslicer --classic --candidate # release candidates (vX.Y.Z-rc)
|
||||
sudo snap install orcaslicer --classic --beta # alpha / beta pre-releases
|
||||
sudo snap install orcaslicer --classic --edge # nightly builds
|
||||
```
|
||||
|
||||
The snap uses classic confinement, so it has full hardware and filesystem access just like the
|
||||
AppImage — including USB/serial printers, removable media, network shares, and 3D mice
|
||||
(3Dconnexion SpaceMouse via `spacenavd`). No extra setup is required after install.
|
||||
|
||||
### AppImage
|
||||
|
||||
AppImages are published for both **x86_64** and **aarch64** (ARM64). Pick the file matching your CPU — the ARM64 build has `aarch64` in its name (e.g. `OrcaSlicer_Linux_AppImage_Ubuntu2404_aarch64_*.AppImage`).
|
||||
|
||||
1. Download App image from the [releases page](https://github.com/OrcaSlicer/OrcaSlicer/releases).
|
||||
2. Double click the downloaded file to run it.
|
||||
|
||||
|
||||
+17
-6
@@ -504,13 +504,24 @@ if [[ -n "${USE_LLD}" ]] ; then
|
||||
fi
|
||||
fi
|
||||
|
||||
# Auto-detect ccache for faster rebuilds
|
||||
export CMAKE_CCACHE_ARGS=()
|
||||
if command -v ccache >/dev/null 2>&1 ; then
|
||||
echo "ccache found at $(command -v ccache), enabling compiler caching..."
|
||||
export CMAKE_CCACHE_ARGS=(-DCMAKE_C_COMPILER_LAUNCHER=ccache -DCMAKE_CXX_COMPILER_LAUNCHER=ccache)
|
||||
CMAKE_CCACHE=${CMAKE_CCACHE:-}
|
||||
if [ -n "$CMAKE_CCACHE" ]; then
|
||||
echo "Checking ${CMAKE_CCACHE} environment variable for compiler cache program..."
|
||||
CMAKE_CCACHE=$(command -v "${CMAKE_CCACHE}") || {
|
||||
echo "CMAKE_CCACHE environment variable is set to '${CMAKE_CCACHE}' but it was not found in PATH."
|
||||
CMAKE_CCACHE=""
|
||||
}
|
||||
elif command -v sccache >/dev/null 2>&1 ; then
|
||||
CMAKE_CCACHE=$(command -v sccache)
|
||||
elif command -v ccache >/dev/null 2>&1 ; then
|
||||
CMAKE_CCACHE=$(command -v ccache)
|
||||
fi
|
||||
if [ -n "${CMAKE_CCACHE}" ] ; then
|
||||
echo "${CMAKE_CCACHE} found, enabling compiler caching..."
|
||||
export CMAKE_CCACHE_ARGS=(-DCMAKE_C_COMPILER_LAUNCHER="${CMAKE_CCACHE}" -DCMAKE_CXX_COMPILER_LAUNCHER="${CMAKE_CCACHE}")
|
||||
else
|
||||
echo "Note: ccache not found. Install ccache for faster rebuilds."
|
||||
echo "Note: ccache or sccache are not found. Install either of them for faster rebuilds."
|
||||
fi
|
||||
|
||||
if [[ -n "${BUILD_DEPS}" ]] ; then
|
||||
@@ -525,7 +536,7 @@ if [[ -n "${BUILD_DEPS}" ]] ; then
|
||||
BUILD_ARGS+=(-DCMAKE_BUILD_TYPE="${BUILD_CONFIG}")
|
||||
fi
|
||||
|
||||
print_and_run cmake -S deps -B deps/$BUILD_DIR "${CMAKE_C_CXX_COMPILER_CLANG[@]}" "${CMAKE_LLD_LINKER_ARGS[@]}" -G Ninja "${COLORED_OUTPUT}" "${BUILD_ARGS[@]}"
|
||||
print_and_run cmake -S deps -B deps/$BUILD_DIR "${CMAKE_C_CXX_COMPILER_CLANG[@]}" "${CMAKE_LLD_LINKER_ARGS[@]}" "${CMAKE_CCACHE_ARGS[@]}" -G Ninja "${COLORED_OUTPUT}" "${BUILD_ARGS[@]}"
|
||||
print_and_run cmake --build deps/$BUILD_DIR -j1
|
||||
fi
|
||||
|
||||
|
||||
Vendored
+6
@@ -10,7 +10,13 @@ if (APPLE AND CMAKE_OSX_ARCHITECTURES)
|
||||
set(_context_arch_line "-DBOOST_CONTEXT_ARCHITECTURE:STRING=${CMAKE_OSX_ARCHITECTURES}")
|
||||
endif ()
|
||||
|
||||
set(_options "")
|
||||
if (MSVC AND DEP_DEBUG)
|
||||
set(_options "FORWARD_CONFIG")
|
||||
endif ()
|
||||
|
||||
orcaslicer_add_cmake_project(Boost
|
||||
${_options}
|
||||
URL "https://github.com/boostorg/boost/releases/download/boost-1.84.0/boost-1.84.0.tar.gz"
|
||||
URL_HASH SHA256=4d27e9efed0f6f152dc28db6430b9d3dfb40c0345da7342eaa5a987dde57bd95
|
||||
LIST_SEPARATOR |
|
||||
|
||||
Vendored
+18
-6
@@ -155,17 +155,25 @@ if (NOT _is_multi AND NOT CMAKE_BUILD_TYPE)
|
||||
endif ()
|
||||
|
||||
function(orcaslicer_add_cmake_project projectname)
|
||||
cmake_parse_arguments(P_ARGS "" "INSTALL_DIR;BUILD_COMMAND;INSTALL_COMMAND" "CMAKE_ARGS" ${ARGN})
|
||||
cmake_parse_arguments(P_ARGS "FORWARD_CONFIG" "INSTALL_DIR;BUILD_COMMAND;INSTALL_COMMAND" "CMAKE_ARGS" ${ARGN})
|
||||
|
||||
set(_configs_line -DCMAKE_BUILD_TYPE:STRING=${CMAKE_BUILD_TYPE})
|
||||
if (_is_multi OR MSVC)
|
||||
if (ORCA_INCLUDE_DEBUG_INFO AND NOT DEP_DEBUG)
|
||||
if (P_ARGS_FORWARD_CONFIG)
|
||||
set(_configs_line -DCMAKE_BUILD_TYPE:STRING=${CMAKE_BUILD_TYPE})
|
||||
elseif (ORCA_INCLUDE_DEBUG_INFO AND NOT DEP_DEBUG)
|
||||
set(_configs_line "-DCMAKE_C_FLAGS_RELEASE:STRING=${CMAKE_C_FLAGS_RELWITHDEBINFO} -DCMAKE_CXX_FLAGS_RELEASE:STRING=${CMAKE_CXX_FLAGS_RELWITHDEBINFO}")
|
||||
else ()
|
||||
set(_configs_line "")
|
||||
endif ()
|
||||
endif ()
|
||||
|
||||
if (P_ARGS_FORWARD_CONFIG)
|
||||
set(_target_config "$<CONFIG>")
|
||||
else()
|
||||
set(_target_config "Release")
|
||||
endif()
|
||||
|
||||
if (MSVC)
|
||||
set(_gen CMAKE_GENERATOR "${DEP_MSVC_GEN}" CMAKE_GENERATOR_PLATFORM "${DEP_PLATFORM}")
|
||||
else()
|
||||
@@ -196,6 +204,8 @@ if (NOT IS_CROSS_COMPILE OR NOT APPLE)
|
||||
-DCMAKE_DEBUG_POSTFIX:STRING=d
|
||||
-DCMAKE_C_COMPILER:STRING=${CMAKE_C_COMPILER}
|
||||
-DCMAKE_CXX_COMPILER:STRING=${CMAKE_CXX_COMPILER}
|
||||
-DCMAKE_C_COMPILER_LAUNCHER:STRING=${CMAKE_C_COMPILER_LAUNCHER}
|
||||
-DCMAKE_CXX_COMPILER_LAUNCHER:STRING=${CMAKE_CXX_COMPILER_LAUNCHER}
|
||||
-DCMAKE_TOOLCHAIN_FILE:STRING=${CMAKE_TOOLCHAIN_FILE}
|
||||
-DCMAKE_EXE_LINKER_FLAGS:STRING=${CMAKE_EXE_LINKER_FLAGS}
|
||||
-DCMAKE_SHARED_LINKER_FLAGS:STRING=${CMAKE_SHARED_LINKER_FLAGS}
|
||||
@@ -206,8 +216,8 @@ if (NOT IS_CROSS_COMPILE OR NOT APPLE)
|
||||
${DEP_CMAKE_OPTS}
|
||||
${P_ARGS_CMAKE_ARGS}
|
||||
${P_ARGS_UNPARSED_ARGUMENTS}
|
||||
BUILD_COMMAND ${CMAKE_COMMAND} --build . --config Release -- ${_build_j}
|
||||
INSTALL_COMMAND ${CMAKE_COMMAND} --build . --target install --config Release
|
||||
BUILD_COMMAND ${CMAKE_COMMAND} --build . --config ${_target_config} -- ${_build_j}
|
||||
INSTALL_COMMAND ${CMAKE_COMMAND} --build . --target install --config ${_target_config}
|
||||
)
|
||||
|
||||
if (FLATPAK)
|
||||
@@ -241,14 +251,16 @@ else()
|
||||
-DCMAKE_INSTALL_PREFIX:STRING=${DESTDIR}
|
||||
-DCMAKE_PREFIX_PATH:STRING=${DESTDIR}
|
||||
-DCMAKE_IGNORE_PREFIX_PATH:STRING=${CMAKE_IGNORE_PREFIX_PATH}
|
||||
-DCMAKE_C_COMPILER_LAUNCHER:STRING=${CMAKE_C_COMPILER_LAUNCHER}
|
||||
-DCMAKE_CXX_COMPILER_LAUNCHER:STRING=${CMAKE_CXX_COMPILER_LAUNCHER}
|
||||
-DBUILD_SHARED_LIBS:BOOL=OFF
|
||||
${_cmake_osx_arch}
|
||||
"${_configs_line}"
|
||||
${DEP_CMAKE_OPTS}
|
||||
${P_ARGS_CMAKE_ARGS}
|
||||
${P_ARGS_UNPARSED_ARGUMENTS}
|
||||
BUILD_COMMAND ${CMAKE_COMMAND} --build . --config Release -- ${_build_j}
|
||||
INSTALL_COMMAND ${CMAKE_COMMAND} --build . --target install --config Release
|
||||
BUILD_COMMAND ${CMAKE_COMMAND} --build . --config ${_target_config} -- ${_build_j}
|
||||
INSTALL_COMMAND ${CMAKE_COMMAND} --build . --target install --config ${_target_config}
|
||||
)
|
||||
|
||||
endif()
|
||||
|
||||
Vendored
+6
@@ -1,4 +1,10 @@
|
||||
set(_options "")
|
||||
if (MSVC AND DEP_DEBUG)
|
||||
set(_options "FORWARD_CONFIG")
|
||||
endif ()
|
||||
|
||||
orcaslicer_add_cmake_project(Draco
|
||||
${_options}
|
||||
URL https://github.com/google/draco/archive/refs/tags/1.5.7.zip
|
||||
URL_HASH SHA256=27b72ba2d5ff3d0a9814ad40d4cb88f8dc89a35491c0866d952473f8f9416b77
|
||||
)
|
||||
Vendored
+5
@@ -1,7 +1,11 @@
|
||||
if (MSVC)
|
||||
set(_use_IPP "-DWITH_IPP=ON")
|
||||
if (DEP_DEBUG)
|
||||
set(_options "FORWARD_CONFIG")
|
||||
endif ()
|
||||
else ()
|
||||
set(_use_IPP "-DWITH_IPP=OFF")
|
||||
set(_options "")
|
||||
endif ()
|
||||
|
||||
if (IN_GIT_REPO)
|
||||
@@ -9,6 +13,7 @@ if (IN_GIT_REPO)
|
||||
endif ()
|
||||
|
||||
orcaslicer_add_cmake_project(OpenCV
|
||||
${_options}
|
||||
URL https://github.com/opencv/opencv/archive/refs/tags/4.6.0.tar.gz
|
||||
URL_HASH SHA256=1ec1cba65f9f20fe5a41fda1586e01c70ea0c9a6d7b67c9e13edf0cfe2239277
|
||||
PATCH_COMMAND git apply ${OpenCV_DIRECTORY_FLAG} --verbose --ignore-space-change --whitespace=fix ${CMAKE_CURRENT_LIST_DIR}/0001-vs.patch ${CMAKE_CURRENT_LIST_DIR}/0002-clang19-macos.patch
|
||||
|
||||
@@ -1123,18 +1123,17 @@ private:
|
||||
|
||||
std::vector<RawShape> objs,excludes;
|
||||
for (const Item &item : items_) {
|
||||
if (item.isFixed()) continue;
|
||||
objs.push_back(item.transformedShape());
|
||||
if (item.isFixed())
|
||||
excludes.push_back(item.transformedShape());
|
||||
else
|
||||
objs.push_back(item.transformedShape());
|
||||
}
|
||||
if (objs.empty())
|
||||
return;
|
||||
// Without fixed items this inner-fit NFP can exceed clipper's range and crash MSVC.
|
||||
if (!excludes.empty())
|
||||
{ // find a best position inside NFP of fixed items (excluded regions), so the center of pile is cloest to bed center
|
||||
RawShape objs_convex_hull = sl::convexHull(objs);
|
||||
for (const Item &item : items_) {
|
||||
if (item.isFixed()) {
|
||||
excludes.push_back(item.transformedShape());
|
||||
}
|
||||
}
|
||||
|
||||
auto nfps = calcnfp(objs_convex_hull, excludes, bbin, Lvl<MaxNfpLevel::value>());
|
||||
if (nfps.empty()) {
|
||||
|
||||
@@ -1,668 +0,0 @@
|
||||
# OrcaSlicer UI Automation Protocol (v1.0.0)
|
||||
|
||||
OrcaSlicer ships an **opt-in, localhost-only JSON-RPC server** that lets external
|
||||
scripts introspect, drive, and screenshot the running OrcaSlicer GUI. It is built
|
||||
for end-to-end testing and automation: a script can enumerate the live widget
|
||||
tree, click buttons, type text, send keyboard shortcuts, wait for UI state, query
|
||||
high-level application state, load models/projects into the running instance,
|
||||
switch the active view/tab, and capture window images (the on-screen capture
|
||||
includes the 3D viewport).
|
||||
|
||||
This document is the protocol reference. It describes activation, the transport,
|
||||
the JSON-RPC envelope, every method, the unified node shape, the target/locator
|
||||
model, error codes, the set of instrumented automation ids, ImGui specifics,
|
||||
platform caveats, a quick-start snippet, and planned future work.
|
||||
|
||||
---
|
||||
|
||||
## 1. Overview & activation
|
||||
|
||||
The automation server is **OFF by default**. It is enabled with two
|
||||
command-line flags:
|
||||
|
||||
| Flag | Meaning |
|
||||
|---|---|
|
||||
| `--automation-server` | Enable the automation server. |
|
||||
| `--automation-server-port=PORT` | Override the listening port. Optional; default is **13619**. |
|
||||
|
||||
Example:
|
||||
|
||||
```bash
|
||||
OrcaSlicer --automation-server --automation-server-port=13619 model.stl
|
||||
```
|
||||
|
||||
The server binds to **`127.0.0.1` only** (the loopback interface). It is never
|
||||
exposed on an external network interface.
|
||||
|
||||
**Security note (v1):** there is **no authentication token** in v1. The localhost
|
||||
bind is the *only* security boundary. Any process able to run code on the machine
|
||||
can connect to the port and drive the GUI — including injecting mouse and keyboard
|
||||
input — while the server is enabled. The feature is intended for testing and
|
||||
automation environments, not for production or shared/multi-user machines.
|
||||
|
||||
When the server is enabled, OrcaSlicer emits a `warning`-level log line at startup
|
||||
to make the active input-injection surface obvious in logs, for example:
|
||||
|
||||
```
|
||||
UI automation server ENABLED ... input injection is active
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 2. Transport
|
||||
|
||||
The server speaks **HTTP/1.1** over the loopback TCP socket:
|
||||
|
||||
| Request | Response |
|
||||
|---|---|
|
||||
| `POST /jsonrpc` with a JSON-RPC 2.0 request body | A JSON-RPC 2.0 response with `Content-Type: application/json`. |
|
||||
| `GET /` | A plain-text health page: `OrcaSlicer automation server v1.0.0` (`Content-Type: text/plain`). |
|
||||
| Anything else | HTTP `404 Not Found`. |
|
||||
|
||||
The server is **single-client / serialized** in v1: it handles one request at a
|
||||
time on its own dedicated I/O thread. Connections are not kept alive; each request
|
||||
is answered and the socket is closed. Clients should issue requests sequentially.
|
||||
|
||||
---
|
||||
|
||||
## 3. JSON-RPC envelope
|
||||
|
||||
The protocol follows **JSON-RPC 2.0**.
|
||||
|
||||
**Request:**
|
||||
|
||||
```json
|
||||
{ "jsonrpc": "2.0", "id": <id>, "method": "<method>", "params": { ... } }
|
||||
```
|
||||
|
||||
- `params` may be omitted; the server treats a missing `params` as an empty object.
|
||||
|
||||
**Success response:**
|
||||
|
||||
```json
|
||||
{ "jsonrpc": "2.0", "id": <id>, "result": { ... } }
|
||||
```
|
||||
|
||||
**Error response:**
|
||||
|
||||
```json
|
||||
{ "jsonrpc": "2.0", "id": <id>, "error": { "code": <int>, "message": "<string>" } }
|
||||
```
|
||||
|
||||
The request `id` is echoed back in the response. When the request has no `id`, or
|
||||
when the request body cannot be parsed as JSON, the response `id` is `null`.
|
||||
|
||||
---
|
||||
|
||||
## 4. Methods
|
||||
|
||||
There are 12 methods. Capabilities advertised by `automation.version` list the 11
|
||||
callable feature methods (every method except `automation.version` itself).
|
||||
|
||||
### `automation.version`
|
||||
|
||||
Returns server identity and the list of supported methods. Takes no parameters.
|
||||
|
||||
**Result:**
|
||||
|
||||
```json
|
||||
{
|
||||
"version": "1.0.0",
|
||||
"protocol": "2.0",
|
||||
"capabilities": [
|
||||
"tree.dump", "tree.find", "widget.get", "input.click", "input.type",
|
||||
"input.key", "sync.wait_for", "app.state", "screenshot.window", "file.open",
|
||||
"view.select"
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
### `tree.dump`
|
||||
|
||||
Snapshot the live UI tree as a single root node with nested children.
|
||||
|
||||
**Params (all optional):**
|
||||
|
||||
| Param | Type | Default | Meaning |
|
||||
|---|---|---|---|
|
||||
| `root` | string (id or path) | full tree | Root the dump at the node with this id/path. |
|
||||
| `max_depth` | int | `-1` | Maximum depth to descend. `-1` = unlimited. |
|
||||
| `visible_only` | bool | `false` | When true, omit non-visible nodes. |
|
||||
| `include_imgui` | bool | `true` | When true, include ImGui items. |
|
||||
|
||||
**Result:** the serialized root [node](#5-unified-node-shape), with `children`
|
||||
included.
|
||||
|
||||
### `tree.find`
|
||||
|
||||
Find all nodes matching a [target predicate](#6-target--locator).
|
||||
|
||||
**Params:** a target predicate — any combination of `name`, `class`, `label`,
|
||||
`value`, `backend` (provided fields are ANDed). The params object is the target
|
||||
itself (it is *not* wrapped in a `target` key for this method).
|
||||
|
||||
**Result:** a **flat JSON array** of matching nodes. The nodes in this array are
|
||||
returned **without** their `children` (use `widget.get`/`tree.dump` to descend).
|
||||
|
||||
### `widget.get`
|
||||
|
||||
Fetch a single node by [target](#6-target--locator).
|
||||
|
||||
**Params:**
|
||||
|
||||
| Param | Type | Required | Meaning |
|
||||
|---|---|---|---|
|
||||
| `target` | object | yes | Target spec (id / path / predicate). |
|
||||
|
||||
**Result:** a single [node](#5-unified-node-shape), with its `children` included.
|
||||
|
||||
**Errors:** `1001` if the target is **not found** *or* **ambiguous** (more than one
|
||||
match).
|
||||
|
||||
### `input.click`
|
||||
|
||||
Click a resolved, actionable node.
|
||||
|
||||
**Params:**
|
||||
|
||||
| Param | Type | Default | Meaning |
|
||||
|---|---|---|---|
|
||||
| `target` | object | required | Target spec; must resolve to exactly one node. |
|
||||
| `button` | string | `"left"` | `"left"`, `"right"`, or `"middle"`. |
|
||||
| `double` | bool | `false` | Double-click when true. |
|
||||
| `modifiers` | array of string | `[]` | Held modifiers: any of `"ctrl"`, `"shift"`, `"alt"`, `"cmd"` (`"meta"` is accepted as an alias of `"cmd"`). |
|
||||
|
||||
**Result:** `{ "ok": true }`.
|
||||
|
||||
**Errors:** `1001` not found / ambiguous; `1002` if the target is disabled or
|
||||
hidden (not actionable). The click path raises and focuses the target's top-level
|
||||
window before injecting the click.
|
||||
|
||||
### `input.type`
|
||||
|
||||
Type text into the currently focused control.
|
||||
|
||||
**Params:**
|
||||
|
||||
| Param | Type | Required | Meaning |
|
||||
|---|---|---|---|
|
||||
| `text` | string | yes | The text to type. |
|
||||
| `target` | object | no | If given, this node is clicked first (to focus it) before typing. |
|
||||
|
||||
**Result:** `{ "ok": true }`.
|
||||
|
||||
**Errors:** if `target` is supplied, the same actionability errors as
|
||||
`input.click` apply (`1001` / `1002`).
|
||||
|
||||
### `input.key`
|
||||
|
||||
Send a key chord (a key plus optional modifiers) to the focused window.
|
||||
|
||||
**Params:**
|
||||
|
||||
| Param | Type | Required | Meaning |
|
||||
|---|---|---|---|
|
||||
| `keys` | string or array | yes | Either a `"+"`-joined string like `"ctrl+s"`, or an array like `["ctrl", "s"]`. The last token is the key; earlier tokens are modifiers. |
|
||||
|
||||
**Result:** `{ "ok": true }`.
|
||||
|
||||
**Key names must be lowercase.** Recognized key names include `"enter"`, `"tab"`,
|
||||
`"esc"`, `"space"`, `"delete"`, `"backspace"`, `"f5"` (and other function keys),
|
||||
and single characters (e.g. `"s"`, `"a"`). Recognized modifiers are `"ctrl"`,
|
||||
`"shift"`, `"alt"`, `"cmd"` (with `"meta"` as an alias for `"cmd"`).
|
||||
**Unrecognized or uppercase key names are silently ignored** — no error is
|
||||
returned, the key simply does not fire. Use lowercase names exclusively.
|
||||
|
||||
### `sync.wait_for`
|
||||
|
||||
Poll the UI until a target node reaches a desired state, or time out. This is the
|
||||
preferred way to synchronize with asynchronous UI changes (it replaces fragile
|
||||
fixed sleeps). Internally it repeatedly refreshes and dumps the tree, re-resolves
|
||||
the target, and evaluates the requested state until it is satisfied.
|
||||
|
||||
**Params:**
|
||||
|
||||
| Param | Type | Default | Meaning |
|
||||
|---|---|---|---|
|
||||
| `target` | object | required | Target spec. |
|
||||
| `state` | string | required | One of `"exists"`, `"visible"`, `"enabled"`, `"value"`. |
|
||||
| `value` | string | — | Required when `state` is `"value"`; the expected value to match. |
|
||||
| `timeout_ms` | int | `5000` | Maximum time to wait, in milliseconds. |
|
||||
| `poll_ms` | int | `100` | Poll interval, in milliseconds (minimum 1). |
|
||||
|
||||
State semantics:
|
||||
|
||||
- `exists` — the target resolves to a node.
|
||||
- `visible` — the node exists and is visible.
|
||||
- `enabled` — the node exists and is **both enabled and visible**.
|
||||
- `value` — the node has a value and that value equals the supplied `value`.
|
||||
|
||||
**Result:** `{ "ok": true, "elapsed_ms": <int> }`.
|
||||
|
||||
**Errors:** `1003` on timeout (the state was not reached within `timeout_ms`).
|
||||
|
||||
### `app.state`
|
||||
|
||||
Return a high-level application-state snapshot. Takes no parameters.
|
||||
|
||||
**Result:**
|
||||
|
||||
```json
|
||||
{
|
||||
"active_tab": "<string>",
|
||||
"project_loaded": <bool>,
|
||||
"slicing": <bool>,
|
||||
"slice_progress": <int>,
|
||||
"foreground": <bool>,
|
||||
"modal_dialog": "<string>"
|
||||
}
|
||||
```
|
||||
|
||||
| Field | Meaning |
|
||||
|---|---|
|
||||
| `active_tab` | The active top-level tab/page. |
|
||||
| `project_loaded` | Whether a project/model is currently loaded. |
|
||||
| `slicing` | Whether slicing is currently in progress. |
|
||||
| `slice_progress` | Slicing progress (`-1` when unknown). |
|
||||
| `foreground` | Whether the main window is in the foreground. |
|
||||
| `modal_dialog` | Present only when a modal dialog is active; identifies it. Omitted otherwise. |
|
||||
|
||||
### `screenshot.window`
|
||||
|
||||
Capture a window as a PNG, exactly as it appears on screen.
|
||||
|
||||
**Params:**
|
||||
|
||||
| Param | Type | Default | Meaning |
|
||||
|---|---|---|---|
|
||||
| `target` | object | main frame | If given, capture this window; otherwise capture the main frame. |
|
||||
|
||||
**Result:** `{ "png_base64": "<base64 PNG>", "width": <int>, "height": <int> }`.
|
||||
|
||||
**Errors:** `1005` on screenshot failure; `1001` if a supplied `target` is not
|
||||
found or ambiguous.
|
||||
|
||||
**How it works:** the window's on-screen rectangle is read back from the
|
||||
DWM-composited desktop framebuffer (`wxScreenDC`), so the capture includes every
|
||||
native child control, the OpenGL 3D viewport, and ImGui overlays — it is a faithful
|
||||
image of what the user sees. (Capturing the parent window's own client DC instead
|
||||
would clip out child HWNDs and the GL surface, leaving them black; that is why this
|
||||
method reads from the screen.)
|
||||
|
||||
**Caveats:**
|
||||
|
||||
- The window must be **visible and unobscured**. Because the source is the on-screen
|
||||
framebuffer, any overlapping window occludes the captured region. The backend
|
||||
raises the target window before capturing.
|
||||
- **HiDPI:** the reported `width`/`height` come from the window's logical client size,
|
||||
while the screen framebuffer is in physical pixels. On per-monitor-DPI displays the
|
||||
two can differ; the capture may be cropped or scaled relative to the logical size.
|
||||
- Because the capture is the live on-screen image, the 3D content reflects the
|
||||
**current view**: the model in the 3D editor, or the gcode toolpaths in Preview
|
||||
after a slice. There is no separate offscreen 3D-render method — the window
|
||||
capture already includes whatever the GL canvas is showing.
|
||||
|
||||
### `file.open`
|
||||
|
||||
Load one or more files into the **already-running** instance at runtime, by calling
|
||||
`Plater::load_files(...)` directly on the GUI thread. This is the supported way to add
|
||||
or swap a model without relaunching the process. Loading is **synchronous**: when the
|
||||
call returns `ok: true`, `app.state().project_loaded` is already `true` (no polling
|
||||
race).
|
||||
|
||||
**Params:**
|
||||
|
||||
| Param | Type | Required | Meaning |
|
||||
|---|---|---|---|
|
||||
| `paths` | string or array of strings | yes | One or more **absolute** file paths. A bare string is accepted and treated as a one-element list. Paths are read from the **host (server) filesystem** — client and server are localhost-only. |
|
||||
|
||||
`.3mf` files are routed as projects and meshes as models automatically, based on file
|
||||
content (the same default strategy as drag-drop); there is no `as_project` flag in v1.
|
||||
|
||||
**Result:** `{ "ok": true, "loaded": <int> }`, where `loaded` is the number of objects
|
||||
added to the scene (`load_files(...).size()`).
|
||||
|
||||
**Errors:**
|
||||
|
||||
- `-32602` (invalid params) — `paths` is missing, is not a string/array, contains a
|
||||
non-string entry, or yields no non-empty path.
|
||||
- `1007` (load failed) — `load_files` returned empty or threw (file not found, parse
|
||||
error, or unsupported format).
|
||||
- `1004` (GUI busy) — the GUI-thread marshal timed out. An extremely large model can
|
||||
exceed the marshal timeout and surface here; documented, not mitigated in v1.
|
||||
|
||||
### `view.select`
|
||||
|
||||
Switch the main window to a top-level view/tab at runtime. Useful to put the UI in a
|
||||
known state before other actions — e.g. switch to **Prepare** before loading a model,
|
||||
or to **Preview** after slicing.
|
||||
|
||||
**Params:**
|
||||
|
||||
| Param | Type | Required | Meaning |
|
||||
|---|---|---|---|
|
||||
| `view` | string | yes | The target view. One of: `home`, `prepare` (3D editor), `preview` (sliced G-code), `device`, `multi_device`, `project`, `calibration`. |
|
||||
|
||||
**Result:** `{ "ok": true, "view": <string>, "index": <int> }`, where `index` is the
|
||||
resulting tab index (it can vary with layout, since some tabs — e.g. `multi_device` —
|
||||
are only present in certain configurations).
|
||||
|
||||
**Errors:**
|
||||
|
||||
- `-32602` (invalid params) — `view` is missing, is not a string, or is empty.
|
||||
- `1001` (not found) — the view name is unknown, or that view is not available in the
|
||||
current layout (for example `prepare`/`preview` in G-code-viewer mode, or
|
||||
`multi_device` when multi-device is disabled).
|
||||
- `1004` (GUI busy) — the GUI-thread marshal timed out.
|
||||
|
||||
---
|
||||
|
||||
## 5. Unified node shape
|
||||
|
||||
Both wx widgets and ImGui items are reported with the same node schema:
|
||||
|
||||
```json
|
||||
{
|
||||
"backend": "wx" | "imgui",
|
||||
"id": "<string>",
|
||||
"path": "<string>",
|
||||
"class": "<string>",
|
||||
"label": "<string>",
|
||||
"rect": { "x": <int>, "y": <int>, "w": <int>, "h": <int> },
|
||||
"enabled": <bool>,
|
||||
"visible": <bool>,
|
||||
"value": "<string>",
|
||||
"children": [ <node>, ... ]
|
||||
}
|
||||
```
|
||||
|
||||
| Field | Meaning |
|
||||
|---|---|
|
||||
| `backend` | `"wx"` for native wxWidgets controls, `"imgui"` for immediate-mode ImGui items. |
|
||||
| `id` | The automation id when one is set, otherwise a derived id. For ImGui items the `path` doubles as the `id`. |
|
||||
| `path` | Positional path, e.g. `"MainFrame/Panel[2]/Button[0]"`. For ImGui items: `"ImGui/<window>/<label>"`. |
|
||||
| `class` | wx class name, or the ImGui item type. |
|
||||
| `label` | The control's label/caption. May include an ImGui `##`-id suffix for ImGui items. |
|
||||
| `rect` | Bounding rectangle in **screen coordinates**. |
|
||||
| `enabled` | Whether the control is enabled. |
|
||||
| `visible` | Whether the control is visible. |
|
||||
| `value` | The control's value (text/choice/check/slider, etc.). **Omitted entirely** when the control has no applicable value. |
|
||||
| `children` | Child nodes. **wx only**, and present only when children are included (e.g. `tree.dump`, `widget.get`). ImGui items are flat (no children) and are listed under their window. |
|
||||
|
||||
Notes:
|
||||
|
||||
- The `value` key is **omitted** (not `null`) when the control has no value.
|
||||
- `children` is present only for wx nodes when children are requested; ImGui nodes
|
||||
never carry `children`.
|
||||
|
||||
---
|
||||
|
||||
## 6. Target / locator
|
||||
|
||||
Most methods accept a **target** object that identifies one or more nodes. A
|
||||
target may specify:
|
||||
|
||||
| Field | Meaning |
|
||||
|---|---|
|
||||
| `id` | Exact automation id. |
|
||||
| `path` | Exact positional path. |
|
||||
| `name` | Predicate: matches either the node's `id` **or** its `label`. |
|
||||
| `class` | Predicate: exact class name. |
|
||||
| `label` | Predicate: exact label. |
|
||||
| `value` | Predicate: node has a value and it equals this string. |
|
||||
| `backend` | Predicate: `"wx"` or `"imgui"`. |
|
||||
|
||||
**Resolution order:** **`id` → `path` → predicate.**
|
||||
|
||||
- If `id` is present, only `id` is used (exact match).
|
||||
- Else if `path` is present, only `path` is used (exact match).
|
||||
- Else the predicate fields (`name`, `class`, `label`, `value`, `backend`) are
|
||||
used, and all provided predicate fields are **ANDed** together.
|
||||
|
||||
Action methods (`input.click`, `input.type` with a target, `widget.get`, and
|
||||
single-target `screenshot.window`) require a **unique** match. If the target
|
||||
resolves to zero matches or more than one match, the call fails with error `1001`
|
||||
(not found / ambiguous). `tree.find` is the exception: it returns *all* matches as
|
||||
an array and never errors on ambiguity.
|
||||
|
||||
---
|
||||
|
||||
## 7. Error codes
|
||||
|
||||
Standard JSON-RPC codes:
|
||||
|
||||
| Code | Meaning |
|
||||
|---|---|
|
||||
| `-32700` | Parse error — the request body was not valid JSON. |
|
||||
| `-32600` | Invalid request — missing/invalid `method`. |
|
||||
| `-32601` | Method not found — unknown method name. |
|
||||
| `-32602` | Invalid params — missing/invalid parameters for the method. |
|
||||
|
||||
Application-specific codes:
|
||||
|
||||
| Code | Meaning |
|
||||
|---|---|
|
||||
| `1001` | Widget/target not found **or** ambiguous (more than one match). |
|
||||
| `1002` | Not actionable — the target is disabled or hidden. |
|
||||
| `1003` | Wait timeout — `sync.wait_for` did not reach the requested state in time. |
|
||||
| `1004` | GUI thread busy / timeout — a backend call could not be marshaled onto the GUI thread in time (wedged GUI). |
|
||||
| `1005` | Screenshot failed. |
|
||||
| `1006` | Disabled. |
|
||||
| `1007` | Load failed — `file.open`'s `load_files` returned empty or threw (not found, parse error, unsupported format). |
|
||||
|
||||
---
|
||||
|
||||
## 8. Automation-id naming conventions & instrumented ids
|
||||
|
||||
Stable automation ids follow these prefix conventions:
|
||||
|
||||
| Prefix | Used for |
|
||||
|---|---|
|
||||
| `btn_` | Buttons |
|
||||
| `combo_` | Preset combo boxes |
|
||||
| `tab_` | Tabs |
|
||||
| `canvas_` | Canvases |
|
||||
| `dlg_` | Dialog buttons |
|
||||
|
||||
### Instrumented ids (as-built in v1)
|
||||
|
||||
The following controls currently carry stable automation ids:
|
||||
|
||||
| id | Control | Note |
|
||||
|---|---|---|
|
||||
| `btn_slice` | Slice-plate button | |
|
||||
| `btn_export` | Print / Export button | Multi-purpose: the action (Print plate / Export G-code / Send) depends on the current mode. |
|
||||
| `tab_device` | Device / Monitor tab (`MonitorPanel`) | |
|
||||
| `combo_printer` | Printer preset combo (sidebar) | |
|
||||
| `combo_filament` | Filament preset combo (sidebar) | First filament row only; extra multi-material rows are not instrumented. |
|
||||
| `canvas_3d` | 3D editor GL canvas | |
|
||||
|
||||
### Controls NOT instrumented in v1
|
||||
|
||||
Several controls are intentionally **not** instrumented in v1 because they have no
|
||||
stable `wxWindow` target to attach an id to:
|
||||
|
||||
- **`combo_process`** — process settings are not a sidebar combo box in the current
|
||||
OrcaSlicer layout, so there is no combo control to instrument.
|
||||
- **`btn_add`** — the add/import-object control is a `GLToolbar` item rendered
|
||||
*inside* the GL canvas, not a `wxWindow`.
|
||||
- **`tab_prepare` / `tab_preview`** — the Prepare and Preview notebook pages are
|
||||
both backed by the **same** window, and the per-tab buttons are private; there is
|
||||
no distinct stable window to target.
|
||||
|
||||
For controls that are not instrumented, scripts should fall back to class / label /
|
||||
path lookup (for wx controls) or ImGui-item lookup (for ImGui controls).
|
||||
|
||||
---
|
||||
|
||||
## 9. ImGui notes
|
||||
|
||||
ImGui is **immediate-mode**: an item is addressable only while it is being drawn in
|
||||
the current frame. The automation backend records ImGui items each frame, and a
|
||||
`refresh_ui` is forced before every read or action so that the latest frame's items
|
||||
are captured.
|
||||
|
||||
Consequences and conventions:
|
||||
|
||||
- Use [`sync.wait_for`](#syncwait_for) to wait for a transient gizmo or panel item
|
||||
to appear before acting on it.
|
||||
- ImGui items are reported with `backend: "imgui"`, a `path` of the form
|
||||
`ImGui/<window>/<label>`, and that **path doubles as the item's `id`** in v1.
|
||||
- ImGui items are **flat** — they have no `children` and are listed under their
|
||||
window.
|
||||
- Labels may include ImGui `##`-id suffixes (the part after `##` that ImGui uses to
|
||||
disambiguate identically labeled widgets).
|
||||
- Raw `ImGui::` gizmos that are *not* routed through the instrumented
|
||||
`ImGuiWrapper` widgets (for example some Emboss / SVG / Text gizmo controls) are
|
||||
only covered at the **window level** in v1; their individual sub-items are not
|
||||
enumerated.
|
||||
|
||||
---
|
||||
|
||||
## 10. Platform & display caveats
|
||||
|
||||
- **Input requires a focused, visible window.** OS-level input injection uses
|
||||
`wxUIActionSimulator`, which requires a focused, visible window. The click path
|
||||
raises and focuses the target's top-level window first.
|
||||
- **Linux CI needs a display.** There must be an X display available; wrap test
|
||||
runs with `xvfb-run` (for example, `xvfb-run -a python example_slice.py ...`).
|
||||
- **Input is asynchronous.** Do **not** rely on fixed sleeps. Use
|
||||
[`sync.wait_for`](#syncwait_for) — for example, wait for `btn_export` to become
|
||||
`enabled` after slicing completes — rather than sleeping for a guessed duration.
|
||||
- **`screenshot.window` reads the screen.** It captures the on-screen, DWM-composited
|
||||
framebuffer, so the target window must be visible and unobscured, and the result is
|
||||
in physical pixels (see HiDPI caveat under [`screenshot.window`](#screenshotwindow)).
|
||||
The capture includes the GL 3D viewport as currently shown (model or toolpaths).
|
||||
- **Single-client / serialized.** v1 handles one request at a time; issue requests
|
||||
sequentially from a single client.
|
||||
- **GUI-thread marshaling.** Every backend call is marshaled onto the GUI thread
|
||||
with a timeout. A wedged or unresponsive GUI returns error `1004`.
|
||||
|
||||
---
|
||||
|
||||
## 11. Quick start
|
||||
|
||||
Using the reference client in `tools/automation/orca_automation.py`:
|
||||
|
||||
```python
|
||||
from orca_automation import OrcaClient
|
||||
|
||||
orca = OrcaClient(port=13619)
|
||||
print(orca.version()) # {'version': '1.0.0', ...}
|
||||
|
||||
orca.select_view("prepare") # switch to the 3D editor
|
||||
orca.open(r"C:\models\part.stl") # load a model at runtime (synchronous)
|
||||
|
||||
orca.click({"id": "btn_slice"}) # start slicing the plate
|
||||
orca.wait_for({"id": "btn_export"}, # wait until slicing finishes
|
||||
state="enabled", timeout_ms=180000)
|
||||
|
||||
orca.select_view("preview") # switch to the sliced G-code preview
|
||||
png = orca.screenshot() # on-screen capture (incl. 3D view)
|
||||
with open("window.png", "wb") as f:
|
||||
f.write(png)
|
||||
```
|
||||
|
||||
For a full, runnable end-to-end example — launching OrcaSlicer with the automation
|
||||
flags, loading a model, slicing, waiting for completion, and saving a window PNG —
|
||||
see `tools/automation/example_slice.py`.
|
||||
|
||||
---
|
||||
|
||||
## 12. Future work
|
||||
|
||||
Planned enhancements beyond v1:
|
||||
|
||||
- **Authentication token** plus a Preferences toggle to enable/disable the server
|
||||
from the GUI.
|
||||
- **WebSocket push events** for real-time UI/state notifications (instead of
|
||||
polling).
|
||||
- **Per-item ImGui gizmo instrumentation** so individual gizmo sub-controls (Emboss
|
||||
/ SVG / Text, etc.) are addressable, not just at the window level.
|
||||
- **More widget ids** — the process combo, the add/import button, and the
|
||||
Prepare/Preview tabs once they expose stable windows.
|
||||
- An **MCP wrapper** to expose the automation surface to model-context tooling.
|
||||
|
||||
---
|
||||
|
||||
## Verification (v1)
|
||||
|
||||
This section records the final regression gate for the v1 feature: confirmation
|
||||
that the protocol core is covered by unit tests, that the existing test suites
|
||||
are unaffected, and that the **disabled path (automation OFF, the default) is a
|
||||
true no-op** — zero new threads, zero socket binds, zero allocations, and zero
|
||||
behavior change.
|
||||
|
||||
### Unit-suite results (Release, Windows / MSVC, Ninja Multi-Config)
|
||||
|
||||
| Suite | Result |
|
||||
|---|---|
|
||||
| `automation` (protocol core) | **32 / 32 passed** |
|
||||
| `libslic3r` (most affected by the additive `PrintConfig.cpp` CLI options) | **99 / 99 passed** |
|
||||
| `fff_print` | **14 / 14 passed** |
|
||||
| `libnest2d` | **14 / 14 passed** |
|
||||
| `sla_print` | **21 / 21 passed** |
|
||||
| `slic3rutils` | 3 / 5 passed — 2 pre-existing `[OrcaCloudServiceAgent]` SEGFAULTs, **unrelated to automation** (see note) |
|
||||
|
||||
> The two `slic3rutils` failures are `Orca cloud flat/nested session resolves
|
||||
> display name consistently`. They exercise `Slic3r::OrcaCloudServiceAgent`, which
|
||||
> the automation branch does **not** touch (verified via `git diff --stat
|
||||
> main...HEAD` — no change to `src/slic3r/Utils/OrcaCloudServiceAgent.*` or
|
||||
> `tests/slic3rutils/*`). They are pre-existing and not a regression introduced by
|
||||
> this feature.
|
||||
|
||||
### Static disabled-path audit (the core regression guarantee)
|
||||
|
||||
Verified by code reading that with no `--automation-server` flag:
|
||||
|
||||
- **Flag defaults off.** `m_automation_port` defaults to `0`
|
||||
(`src/slic3r/GUI/GUI_App.hpp:249`); `is_automation_enabled()` returns
|
||||
`m_automation_port > 0` (`GUI_App.hpp:386`) → `false` by default.
|
||||
- **No server / thread / socket.** `post_init()` calls
|
||||
`start_automation_server()` **only** when
|
||||
`init_params->automation_port > 0` (`src/slic3r/GUI/GUI_App.cpp:737-740`), and
|
||||
`start_automation_server()` itself early-returns when `m_automation_port <= 0`
|
||||
(`GUI_App.cpp:7097`). The backend / dispatcher / beast server objects are
|
||||
constructed nowhere else → no `orca_automation` thread and no localhost bind
|
||||
when the flag is absent.
|
||||
- **Recording hooks short-circuit.** `ImGuiWrapper::automation_record_last_item`
|
||||
has as its **first statement** `if (!wxGetApp().is_automation_enabled())
|
||||
return;` (`src/slic3r/GUI/ImGuiWrapper.cpp:576-577`) — a single bool check, no
|
||||
`ImGuiItemRecord` allocation and no `ImGuiItemTable` access on the disabled
|
||||
path. In `ImGuiWrapper::render()` the window-enumeration loop and
|
||||
`swap_frame()` are fully wrapped in `if (wxGetApp().is_automation_enabled())`
|
||||
(`ImGuiWrapper.cpp:599-611`); when off, `render()` is its original
|
||||
`ImGui::Render()` + `render_draw_data()` plus one bool check.
|
||||
- **Instrumentation is inert.** The ~7 `set_automation_id(...)` calls
|
||||
(`MainFrame.cpp:1330,1389,1841,1842`; `Plater.cpp:1772,2172,5068`) only store a
|
||||
pointer into a static registry and bind a `wxEVT_DESTROY` pruning handler
|
||||
(`src/slic3r/GUI/Automation/AutomationRegistry.cpp:24-36`). The registry is
|
||||
**read** only via `window_for_automation_id` / `automation_id_of`, which are
|
||||
called solely by the backend while the server is running → harmless when off.
|
||||
- **CLI options are purely additive.** `automation_server` (coBool, default
|
||||
`false`) and `automation_server_port` (coInt, default `13619`) are new `add()`
|
||||
entries appended after `enable_timelapse`
|
||||
(`src/libslic3r/PrintConfig.cpp:10794-10805`); no existing option is changed.
|
||||
`GUI_InitParams::automation_port` defaults to `0`
|
||||
(`src/slic3r/GUI/GUI_Init.hpp:37`) and is set only when `--automation-server`
|
||||
is supplied (`src/OrcaSlicer.cpp:1345-1348`).
|
||||
|
||||
**Conclusion:** with automation OFF (the default), the feature allocates nothing
|
||||
and changes nothing — the only added cost on any hot path is a single boolean
|
||||
comparison.
|
||||
|
||||
### Deferred manual runtime checks (require a display / Xvfb)
|
||||
|
||||
These need a live GUI and cannot be run headlessly in CI; they are the manual
|
||||
acceptance steps:
|
||||
|
||||
1. Launch **without** `--automation-server` → `curl http://127.0.0.1:13619/`
|
||||
fails to connect (no listener); no `orca_automation` thread exists.
|
||||
2. Launch **with** `--automation-server --automation-server-port=13619` →
|
||||
`GET /` returns the health text; `POST /jsonrpc {"method":"automation.version"}`
|
||||
returns version / protocol / capabilities; `widget.get {"target":{"id":"btn_slice"}}`
|
||||
returns a node with a sensible screen rect.
|
||||
3. Interactive sanity: open a gizmo / move sliders with automation OFF → no
|
||||
visual or behavior change.
|
||||
|
||||
See `tools/automation/example_slice.py` for the runnable end-to-end path.
|
||||
@@ -1,608 +0,0 @@
|
||||
# `file.open` Automation Method Implementation Plan
|
||||
|
||||
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
|
||||
|
||||
**Goal:** Add a `file.open` JSON-RPC automation method that loads one or more files into an already-running OrcaSlicer instance by calling `Plater::load_files(...)` synchronously on the GUI thread.
|
||||
|
||||
**Architecture:** Follows the existing `screenshot.window` / `app.state` method pattern. A new pure-virtual `open_files(paths)` is added to the wx-free `IUiBackend` interface; `WxUiBackend` implements it via the existing `run_on_gui(...)` GUI-thread marshal calling `Plater::load_files`; the `JsonRpcDispatcher` gains a `file.open` route, a param-parsing helper, and a new `kErrLoadFailed = 1007` error code. The unit-testable surface (dispatcher + param validation + routing) is driven against `MockUiBackend`.
|
||||
|
||||
**Tech Stack:** C++17, nlohmann::json, Catch2 v2 (`catch_all.hpp` / `Catch2WithMain`), wxWidgets, CMake + Ninja Multi-Config. Python 3 reference client (stdlib only).
|
||||
|
||||
---
|
||||
|
||||
## Design-spec note (resolve before coding)
|
||||
|
||||
The design spec's error table reads `1002 | kInvalidParams | paths missing/empty…`, but in the codebase `kInvalidParams` is the standard JSON-RPC code **`-32602`**, while `1002` is `kErrNotActionable`. The spec's **Constant column (`kInvalidParams`) is authoritative** and matches every other param-validation path in the dispatcher (e.g. `m_input_type` throws `kInvalidParams` for a bad `text`). This plan therefore validates `file.open` params with **`kInvalidParams` (-32602)**, exactly like the existing handlers, and the tests assert `== kInvalidParams`. The literal "1002" in the spec is a typo; do not emit code 1002 for param errors.
|
||||
|
||||
## File Structure
|
||||
|
||||
| File | Change | Responsibility |
|
||||
|---|---|---|
|
||||
| `src/slic3r/GUI/Automation/IUiBackend.hpp` | modify | Add pure-virtual `int open_files(paths)` to the backend abstraction (stays wx-free). |
|
||||
| `src/slic3r/GUI/Automation/JsonRpcDispatcher.hpp` | modify | Add `kErrLoadFailed = 1007` constant + `m_file_open` declaration. |
|
||||
| `src/slic3r/GUI/Automation/JsonRpcDispatcher.cpp` | modify | Add `parse_paths` helper, `m_file_open` body, dispatch route, capabilities entry. |
|
||||
| `src/slic3r/GUI/Automation/WxUiBackend.hpp` | modify | Declare `open_files` override. |
|
||||
| `src/slic3r/GUI/Automation/WxUiBackend.cpp` | modify | Implement `open_files` via `run_on_gui` → `Plater::load_files`. |
|
||||
| `tests/automation/MockUiBackend.hpp` | modify | `open_files` override: record paths + return-count + fail knob. |
|
||||
| `tests/automation/test_dispatcher.cpp` | modify | Catch2 tests for routing, string/array, validation, failure, capabilities. |
|
||||
| `tools/automation/orca_automation.py` | modify | `open(self, paths)` client wrapper. |
|
||||
| `tools/automation/example_slice.py` | modify | Launch without a model arg, then `orca.open([model])`. |
|
||||
| `doc/automation.md` | modify | Document the method, capabilities, error `1007`. |
|
||||
|
||||
**Build/test layout:** Ninja Multi-Config in `build/`. The unit suite target is `automation_tests`; its sources (`tests/automation/CMakeLists.txt`) compile `JsonRpcDispatcher.cpp` + `MockUiBackend` but **not** `WxUiBackend.cpp`. So dispatcher/mock changes are fully unit-testable headlessly; `WxUiBackend.cpp` is verified by the full app build only.
|
||||
|
||||
---
|
||||
|
||||
## Task 1: Extend the backend abstraction (interface + mock + error code)
|
||||
|
||||
Adds the `open_files` contract so tests can be written. Adding a pure virtual to `IUiBackend` forces every implementation to provide it — in the unit-test target that is only `MockUiBackend`, so this task keeps the `automation_tests` build green.
|
||||
|
||||
**Files:**
|
||||
- Modify: `src/slic3r/GUI/Automation/IUiBackend.hpp`
|
||||
- Modify: `src/slic3r/GUI/Automation/JsonRpcDispatcher.hpp:19`
|
||||
- Modify: `tests/automation/MockUiBackend.hpp`
|
||||
|
||||
- [ ] **Step 1: Add the error constant**
|
||||
|
||||
In `src/slic3r/GUI/Automation/JsonRpcDispatcher.hpp`, after the existing `kErrDisabled` line (currently line 19), add:
|
||||
|
||||
```cpp
|
||||
constexpr int kErrDisabled = 1006;
|
||||
constexpr int kErrLoadFailed = 1007; // file.open: load_files returned empty / threw
|
||||
```
|
||||
|
||||
- [ ] **Step 2: Add the pure-virtual `open_files` to the interface**
|
||||
|
||||
In `src/slic3r/GUI/Automation/IUiBackend.hpp`, inside `class IUiBackend`, immediately after the `screenshot_window` pure virtual (currently line 97), add:
|
||||
|
||||
```cpp
|
||||
// Load one or more files (absolute paths) into the running instance on the GUI
|
||||
// thread. Returns the number of objects added to the scene (load_files(...).size()).
|
||||
// Throws AutomationError(kErrLoadFailed) when nothing loads. Header stays wx-free:
|
||||
// the concrete LoadStrategy is chosen inside WxUiBackend, not exposed here.
|
||||
virtual int open_files(const std::vector<std::string>& paths) = 0;
|
||||
```
|
||||
|
||||
- [ ] **Step 3: Implement `open_files` in the mock with record + knobs**
|
||||
|
||||
In `tests/automation/MockUiBackend.hpp`: add an include for the error constant near the top (after the `IUiBackend.hpp` include on line 2):
|
||||
|
||||
```cpp
|
||||
#include "slic3r/GUI/Automation/IUiBackend.hpp"
|
||||
#include "slic3r/GUI/Automation/JsonRpcDispatcher.hpp" // kErrLoadFailed
|
||||
```
|
||||
|
||||
Add recorded-call + canned-output members. After the `screenshot_window_count` recorded field (line 20) add:
|
||||
|
||||
```cpp
|
||||
int screenshot_window_count = 0;
|
||||
std::vector<std::vector<std::string>> opened_paths; // paths of each open_files()
|
||||
```
|
||||
|
||||
After the `click_result` canned field (line 26) add:
|
||||
|
||||
```cpp
|
||||
bool click_result = true;
|
||||
int open_return_count = 0; // value open_files() returns
|
||||
bool open_should_fail = false; // when true, open_files() throws kErrLoadFailed
|
||||
```
|
||||
|
||||
Add the override next to the other overrides, after `screenshot_window` (lines 49-51):
|
||||
|
||||
```cpp
|
||||
PngImage screenshot_window(const UiNode*) override {
|
||||
++screenshot_window_count; return canned_png;
|
||||
}
|
||||
int open_files(const std::vector<std::string>& paths) override {
|
||||
opened_paths.push_back(paths);
|
||||
if (open_should_fail)
|
||||
throw AutomationError(kErrLoadFailed, "mock load failed");
|
||||
return open_return_count;
|
||||
}
|
||||
```
|
||||
|
||||
- [ ] **Step 4: Build the unit-test target to confirm it still compiles**
|
||||
|
||||
Run: `cmake --build build --config RelWithDebInfo --target automation_tests`
|
||||
Expected: build succeeds (the new pure virtual is satisfied by the mock; no behavior change yet).
|
||||
|
||||
- [ ] **Step 5: Commit**
|
||||
|
||||
```bash
|
||||
git add src/slic3r/GUI/Automation/IUiBackend.hpp src/slic3r/GUI/Automation/JsonRpcDispatcher.hpp tests/automation/MockUiBackend.hpp
|
||||
git commit -m "feat(automation): add open_files to backend interface + kErrLoadFailed (1007)"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Task 2: `file.open` dispatcher handler (parse, route, validate, fail)
|
||||
|
||||
Implements the full JSON-RPC handler against the mock: param parsing (string or array), validation, routing to `open_files`, and `kErrLoadFailed` propagation.
|
||||
|
||||
**Files:**
|
||||
- Modify: `src/slic3r/GUI/Automation/JsonRpcDispatcher.hpp:49` (declaration)
|
||||
- Modify: `src/slic3r/GUI/Automation/JsonRpcDispatcher.cpp`
|
||||
- Test: `tests/automation/test_dispatcher.cpp`
|
||||
|
||||
- [ ] **Step 1: Write the failing happy-path test (array of paths)**
|
||||
|
||||
Append to `tests/automation/test_dispatcher.cpp`:
|
||||
|
||||
```cpp
|
||||
TEST_CASE("file.open with an array of paths routes to backend", "[automation][rpc]") {
|
||||
MockUiBackend mock;
|
||||
mock.open_return_count = 3;
|
||||
JsonRpcDispatcher d(mock);
|
||||
const json resp = d.dispatch({{"jsonrpc","2.0"},{"id",1},{"method","file.open"},
|
||||
{"params",{{"paths", json::array({"C:/abs/a.stl","C:/abs/b.stl"})}}}});
|
||||
CHECK(resp.at("result").at("ok") == true);
|
||||
CHECK(resp.at("result").at("loaded") == 3);
|
||||
REQUIRE(mock.opened_paths.size() == 1);
|
||||
REQUIRE(mock.opened_paths[0].size() == 2);
|
||||
CHECK(mock.opened_paths[0][0] == "C:/abs/a.stl");
|
||||
CHECK(mock.opened_paths[0][1] == "C:/abs/b.stl");
|
||||
}
|
||||
```
|
||||
|
||||
- [ ] **Step 2: Run the test to verify it fails**
|
||||
|
||||
Run: `cmake --build build --config RelWithDebInfo --target automation_tests && build/tests/automation/RelWithDebInfo/automation_tests.exe "file.open with an array of paths routes to backend"`
|
||||
Expected: FAIL — `file.open` is an unknown method, so the response carries `error.code == -32601` and has no `result` (the `resp.at("result")` access throws). (If the exe path differs on your machine, locate it with `find build -iname automation_tests.exe`.)
|
||||
|
||||
- [ ] **Step 3: Declare the handler**
|
||||
|
||||
In `src/slic3r/GUI/Automation/JsonRpcDispatcher.hpp`, after the `m_screenshot_window` declaration (currently line 49) add:
|
||||
|
||||
```cpp
|
||||
nlohmann::json m_screenshot_window(const nlohmann::json& params);
|
||||
nlohmann::json m_file_open(const nlohmann::json& params);
|
||||
```
|
||||
|
||||
- [ ] **Step 4: Add the `parse_paths` helper and `m_file_open` body**
|
||||
|
||||
In `src/slic3r/GUI/Automation/JsonRpcDispatcher.cpp`, add a `parse_paths` helper. Place it in the anonymous namespace that also holds `parse_keys` — insert it right before that namespace's closing `} // namespace` (currently line 130):
|
||||
|
||||
```cpp
|
||||
// "paths" may be a single string ("C:/a.stl") or an array of strings. Returns the
|
||||
// non-empty absolute paths; throws kInvalidParams when paths is missing, not a
|
||||
// string/array, contains a non-string entry, or yields no non-empty path.
|
||||
std::vector<std::string> parse_paths(const nlohmann::json& params) {
|
||||
if (!params.is_object() || !params.contains("paths"))
|
||||
throw AutomationError(kInvalidParams, "file.open requires 'paths'");
|
||||
const auto& p = params.at("paths");
|
||||
std::vector<std::string> out;
|
||||
if (p.is_string()) {
|
||||
out.push_back(p.get<std::string>());
|
||||
} else if (p.is_array()) {
|
||||
for (const auto& e : p) {
|
||||
if (!e.is_string())
|
||||
throw AutomationError(kInvalidParams, "'paths' entries must be strings");
|
||||
out.push_back(e.get<std::string>());
|
||||
}
|
||||
} else {
|
||||
throw AutomationError(kInvalidParams, "'paths' must be a string or array");
|
||||
}
|
||||
out.erase(std::remove_if(out.begin(), out.end(),
|
||||
[](const std::string& s) { return s.empty(); }),
|
||||
out.end());
|
||||
if (out.empty())
|
||||
throw AutomationError(kInvalidParams, "'paths' is empty");
|
||||
return out;
|
||||
}
|
||||
```
|
||||
|
||||
(`<algorithm>` for `std::remove_if` is already included at the top of the file, line 4.)
|
||||
|
||||
Add the handler body next to the other handlers. After `m_screenshot_window` (currently ends line 343, just before the final `}}} // namespace`), add:
|
||||
|
||||
```cpp
|
||||
nlohmann::json JsonRpcDispatcher::m_file_open(const nlohmann::json& params) {
|
||||
const std::vector<std::string> paths = parse_paths(params);
|
||||
const int loaded = m_backend.open_files(paths);
|
||||
return { {"ok", true}, {"loaded", loaded} };
|
||||
}
|
||||
```
|
||||
|
||||
- [ ] **Step 5: Add the dispatch route**
|
||||
|
||||
In `JsonRpcDispatcher::dispatch`, after the `screenshot.window` route (currently line 195) add:
|
||||
|
||||
```cpp
|
||||
if (method == "screenshot.window") return make_result(id, m_screenshot_window(params));
|
||||
if (method == "file.open") return make_result(id, m_file_open(params));
|
||||
```
|
||||
|
||||
- [ ] **Step 6: Run the happy-path test to verify it passes**
|
||||
|
||||
Run: `cmake --build build --config RelWithDebInfo --target automation_tests && build/tests/automation/RelWithDebInfo/automation_tests.exe "file.open with an array of paths routes to backend"`
|
||||
Expected: PASS — 1 test case, all assertions passed.
|
||||
|
||||
- [ ] **Step 7: Add the remaining handler tests (string, validation, failure)**
|
||||
|
||||
Append to `tests/automation/test_dispatcher.cpp`:
|
||||
|
||||
```cpp
|
||||
TEST_CASE("file.open accepts a bare string path", "[automation][rpc]") {
|
||||
MockUiBackend mock;
|
||||
mock.open_return_count = 1;
|
||||
JsonRpcDispatcher d(mock);
|
||||
const json resp = d.dispatch({{"jsonrpc","2.0"},{"id",2},{"method","file.open"},
|
||||
{"params",{{"paths","C:/abs/a.stl"}}}});
|
||||
CHECK(resp.at("result").at("loaded") == 1);
|
||||
REQUIRE(mock.opened_paths.size() == 1);
|
||||
REQUIRE(mock.opened_paths[0].size() == 1);
|
||||
CHECK(mock.opened_paths[0][0] == "C:/abs/a.stl");
|
||||
}
|
||||
|
||||
TEST_CASE("file.open with missing paths -> invalid params", "[automation][rpc]") {
|
||||
MockUiBackend mock;
|
||||
JsonRpcDispatcher d(mock);
|
||||
const json resp = d.dispatch({{"jsonrpc","2.0"},{"id",3},{"method","file.open"},
|
||||
{"params", json::object()}});
|
||||
CHECK(resp.at("error").at("code") == kInvalidParams);
|
||||
CHECK(mock.opened_paths.empty());
|
||||
}
|
||||
|
||||
TEST_CASE("file.open with empty paths array -> invalid params", "[automation][rpc]") {
|
||||
MockUiBackend mock;
|
||||
JsonRpcDispatcher d(mock);
|
||||
const json resp = d.dispatch({{"jsonrpc","2.0"},{"id",4},{"method","file.open"},
|
||||
{"params",{{"paths", json::array()}}}});
|
||||
CHECK(resp.at("error").at("code") == kInvalidParams);
|
||||
CHECK(mock.opened_paths.empty());
|
||||
}
|
||||
|
||||
TEST_CASE("file.open with a non-string entry -> invalid params", "[automation][rpc]") {
|
||||
MockUiBackend mock;
|
||||
JsonRpcDispatcher d(mock);
|
||||
const json resp = d.dispatch({{"jsonrpc","2.0"},{"id",5},{"method","file.open"},
|
||||
{"params",{{"paths", json::array({"C:/a.stl", 42})}}}});
|
||||
CHECK(resp.at("error").at("code") == kInvalidParams);
|
||||
CHECK(mock.opened_paths.empty());
|
||||
}
|
||||
|
||||
TEST_CASE("file.open backend load failure -> 1007", "[automation][rpc]") {
|
||||
MockUiBackend mock;
|
||||
mock.open_should_fail = true;
|
||||
JsonRpcDispatcher d(mock);
|
||||
const json resp = d.dispatch({{"jsonrpc","2.0"},{"id",6},{"method","file.open"},
|
||||
{"params",{{"paths","C:/abs/a.stl"}}}});
|
||||
CHECK(resp.at("error").at("code") == kErrLoadFailed);
|
||||
}
|
||||
```
|
||||
|
||||
- [ ] **Step 8: Run all file.open tests to verify they pass**
|
||||
|
||||
Run: `cmake --build build --config RelWithDebInfo --target automation_tests && build/tests/automation/RelWithDebInfo/automation_tests.exe "file.open*"`
|
||||
Expected: PASS — 6 test cases, all assertions passed.
|
||||
|
||||
- [ ] **Step 9: Commit**
|
||||
|
||||
```bash
|
||||
git add src/slic3r/GUI/Automation/JsonRpcDispatcher.hpp src/slic3r/GUI/Automation/JsonRpcDispatcher.cpp tests/automation/test_dispatcher.cpp
|
||||
git commit -m "feat(automation): add file.open dispatcher handler with validation + tests"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Task 3: Advertise `file.open` in `automation.version` capabilities
|
||||
|
||||
**Files:**
|
||||
- Modify: `src/slic3r/GUI/Automation/JsonRpcDispatcher.cpp:166-172`
|
||||
- Test: `tests/automation/test_dispatcher.cpp`
|
||||
|
||||
- [ ] **Step 1: Write the failing capabilities test**
|
||||
|
||||
Append to `tests/automation/test_dispatcher.cpp`:
|
||||
|
||||
```cpp
|
||||
TEST_CASE("automation.version capabilities include file.open", "[automation][rpc]") {
|
||||
MockUiBackend mock;
|
||||
JsonRpcDispatcher d(mock);
|
||||
const json resp = d.dispatch({{"jsonrpc","2.0"},{"id",1},{"method","automation.version"}});
|
||||
const auto& caps = resp.at("result").at("capabilities");
|
||||
bool found = false;
|
||||
for (const auto& c : caps) if (c == "file.open") found = true;
|
||||
CHECK(found);
|
||||
}
|
||||
```
|
||||
|
||||
- [ ] **Step 2: Run the test to verify it fails**
|
||||
|
||||
Run: `cmake --build build --config RelWithDebInfo --target automation_tests && build/tests/automation/RelWithDebInfo/automation_tests.exe "automation.version capabilities include file.open"`
|
||||
Expected: FAIL — `CHECK(found)` is false; `file.open` is not yet in the capabilities array.
|
||||
|
||||
- [ ] **Step 3: Add `file.open` to the capabilities array**
|
||||
|
||||
In `JsonRpcDispatcher::m_version` (currently lines 166-172), add `"file.open"` to the array:
|
||||
|
||||
```cpp
|
||||
nlohmann::json JsonRpcDispatcher::m_version(const nlohmann::json&) {
|
||||
return { {"version", kAutomationVersion},
|
||||
{"protocol", "2.0"},
|
||||
{"capabilities", nlohmann::json::array({
|
||||
"tree.dump","tree.find","widget.get","input.click","input.type",
|
||||
"input.key","sync.wait_for","app.state","screenshot.window",
|
||||
"file.open" })} };
|
||||
}
|
||||
```
|
||||
|
||||
- [ ] **Step 4: Run the test to verify it passes**
|
||||
|
||||
Run: `cmake --build build --config RelWithDebInfo --target automation_tests && build/tests/automation/RelWithDebInfo/automation_tests.exe "automation.version capabilities include file.open"`
|
||||
Expected: PASS.
|
||||
|
||||
- [ ] **Step 5: Run the whole automation suite to confirm no regressions**
|
||||
|
||||
Run: `cmake --build build --config RelWithDebInfo --target automation_tests && build/tests/automation/RelWithDebInfo/automation_tests.exe --order rand --warn NoAssertions`
|
||||
Expected: PASS — all cases green (the pre-existing ~32 plus the 7 new `file.open`/capabilities cases ≈ 39).
|
||||
|
||||
- [ ] **Step 6: Commit**
|
||||
|
||||
```bash
|
||||
git add src/slic3r/GUI/Automation/JsonRpcDispatcher.cpp tests/automation/test_dispatcher.cpp
|
||||
git commit -m "feat(automation): advertise file.open in automation.version capabilities"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Task 4: Implement `WxUiBackend::open_files` (real GUI-thread load)
|
||||
|
||||
Not covered by the headless unit suite (`WxUiBackend.cpp` is excluded from `automation_tests`); verified by the full app build + the manual runtime check in Task 8.
|
||||
|
||||
**Files:**
|
||||
- Modify: `src/slic3r/GUI/Automation/WxUiBackend.hpp:21`
|
||||
- Modify: `src/slic3r/GUI/Automation/WxUiBackend.cpp`
|
||||
|
||||
- [ ] **Step 1: Declare the override**
|
||||
|
||||
In `src/slic3r/GUI/Automation/WxUiBackend.hpp`, after the `screenshot_window` declaration (line 21) add:
|
||||
|
||||
```cpp
|
||||
PngImage screenshot_window(const UiNode* target) override;
|
||||
int open_files(const std::vector<std::string>& paths) override;
|
||||
```
|
||||
|
||||
- [ ] **Step 2: Implement `open_files`**
|
||||
|
||||
In `src/slic3r/GUI/Automation/WxUiBackend.cpp`, add the implementation just before the final `}}} // namespace Slic3r::GUI::Automation` (currently line 306):
|
||||
|
||||
```cpp
|
||||
int WxUiBackend::open_files(const std::vector<std::string>& paths) {
|
||||
return run_on_gui(m_gui_timeout_ms, [&]() -> int {
|
||||
Plater* plater = wxGetApp().plater();
|
||||
if (plater == nullptr)
|
||||
throw AutomationError(kErrLoadFailed, "no plater to load into");
|
||||
// Default strategy matches drag-drop / Plater::load_files's own default: it
|
||||
// routes .3mf as a project and meshes as models based on file content, so no
|
||||
// as_project flag is needed in v1. ask_multi=false: never prompt.
|
||||
const LoadStrategy strategy = LoadStrategy::LoadModel | LoadStrategy::LoadConfig;
|
||||
std::vector<size_t> loaded;
|
||||
try {
|
||||
loaded = plater->load_files(paths, strategy, /*ask_multi=*/false);
|
||||
} catch (const std::exception& e) {
|
||||
throw AutomationError(kErrLoadFailed,
|
||||
std::string("load_files failed: ") + e.what());
|
||||
}
|
||||
if (loaded.empty())
|
||||
throw AutomationError(kErrLoadFailed, "load_files loaded nothing");
|
||||
return static_cast<int>(loaded.size());
|
||||
});
|
||||
}
|
||||
```
|
||||
|
||||
Notes for the implementer:
|
||||
- `LoadStrategy` and its `operator|` (namespace `Slic3r`, from `libslic3r/Format/bbs_3mf.hpp`) are already in scope: `WxUiBackend.cpp` includes `Plater.hpp` (line 7), which transitively pulls in the enum, and this translation unit lives in `Slic3r::GUI::Automation` so unqualified `LoadStrategy` resolves via the enclosing `Slic3r` namespace. No new include is required.
|
||||
- `Plater::load_files(const std::vector<std::string>&, LoadStrategy, bool)` is the existing string overload (`Plater.hpp:379`) — no `boost::filesystem::path` conversion needed.
|
||||
- `kErrLoadFailed` comes from `JsonRpcDispatcher.hpp`, already included at line 4.
|
||||
- An `AutomationError` thrown inside the `run_on_gui` lambda is captured by the helper's `set_exception` and rethrown from `fut.get()`, so the 1007 code propagates to the dispatcher unchanged.
|
||||
|
||||
- [ ] **Step 3: Build the full app to verify it compiles and links**
|
||||
|
||||
Run: `cmake --build build --config RelWithDebInfo --target OrcaSlicer`
|
||||
Expected: build succeeds (no missing-symbol / pure-virtual errors; `WxUiBackend` is now concrete).
|
||||
|
||||
- [ ] **Step 4: Commit**
|
||||
|
||||
```bash
|
||||
git add src/slic3r/GUI/Automation/WxUiBackend.hpp src/slic3r/GUI/Automation/WxUiBackend.cpp
|
||||
git commit -m "feat(automation): implement WxUiBackend::open_files via Plater::load_files"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Task 5: Python client wrapper `OrcaClient.open`
|
||||
|
||||
**Files:**
|
||||
- Modify: `tools/automation/orca_automation.py:80-82`
|
||||
|
||||
- [ ] **Step 1: Add the `open` method**
|
||||
|
||||
In `tools/automation/orca_automation.py`, after the `key` method (ends line 82), add:
|
||||
|
||||
```python
|
||||
def key(self, keys) -> dict:
|
||||
# keys: "ctrl+s" or ["ctrl", "s"]
|
||||
return self._call("input.key", {"keys": keys})
|
||||
|
||||
def open(self, paths) -> dict:
|
||||
"""Load one or more files into the running instance at runtime.
|
||||
|
||||
`paths` is a single absolute path string or a list of them. Paths are read
|
||||
from the host filesystem by the server (localhost-only). Returns
|
||||
{"ok": True, "loaded": <count>}. Raises OrcaError 1007 on load failure."""
|
||||
if isinstance(paths, str):
|
||||
paths = [paths]
|
||||
return self._call("file.open", {"paths": list(paths)})
|
||||
```
|
||||
|
||||
- [ ] **Step 2: Smoke-test the wrapper's normalization offline (no server needed)**
|
||||
|
||||
Run:
|
||||
```bash
|
||||
python -c "import sys; sys.path.insert(0, 'tools/automation'); import orca_automation as m; c = m.OrcaClient.__new__(m.OrcaClient); c._call = lambda meth, params=None: (meth, params); print(c.open('C:/a.stl')); print(c.open(['C:/a.stl','C:/b.stl']))"
|
||||
```
|
||||
Expected output:
|
||||
```
|
||||
('file.open', {'paths': ['C:/a.stl']})
|
||||
('file.open', {'paths': ['C:/a.stl', 'C:/b.stl']})
|
||||
```
|
||||
|
||||
- [ ] **Step 3: Commit**
|
||||
|
||||
```bash
|
||||
git add tools/automation/orca_automation.py
|
||||
git commit -m "feat(automation): add OrcaClient.open() wrapper for file.open"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Task 6: Update `example_slice.py` to load at runtime via `file.open`
|
||||
|
||||
**Files:**
|
||||
- Modify: `tools/automation/example_slice.py:26-52`
|
||||
|
||||
- [ ] **Step 1: Launch without the model arg, then call `open`**
|
||||
|
||||
In `tools/automation/example_slice.py`, change the `subprocess.Popen` call (lines 26-31) to drop the trailing model positional:
|
||||
|
||||
```python
|
||||
proc = subprocess.Popen([
|
||||
args.orca,
|
||||
"--automation-server",
|
||||
f"--automation-server-port={args.port}",
|
||||
])
|
||||
```
|
||||
|
||||
Then replace the project-load wait block (currently lines 46-51) so the model is loaded at runtime via `file.open` instead of relying on a launch-time positional:
|
||||
|
||||
```python
|
||||
# Load the model into the already-running instance, then wait until the
|
||||
# project reports loaded. file.open is synchronous, so project_loaded is
|
||||
# already true on return; the wait is a belt-and-suspenders guard.
|
||||
orca.open([args.model])
|
||||
deadline = time.time() + 30
|
||||
while time.time() < deadline:
|
||||
if orca.app_state().get("project_loaded"):
|
||||
break
|
||||
time.sleep(0.5)
|
||||
```
|
||||
|
||||
- [ ] **Step 2: Byte-compile the script to confirm no syntax errors**
|
||||
|
||||
Run: `python -m py_compile tools/automation/example_slice.py`
|
||||
Expected: no output, exit code 0.
|
||||
|
||||
- [ ] **Step 3: Commit**
|
||||
|
||||
```bash
|
||||
git add tools/automation/example_slice.py
|
||||
git commit -m "docs(automation): example_slice.py loads model at runtime via file.open"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Task 7: Document `file.open` in `doc/automation.md`
|
||||
|
||||
**Files:**
|
||||
- Modify: `doc/automation.md` (capabilities example §4 line 111-114; new method subsection after `screenshot.window`; error table §7)
|
||||
|
||||
- [ ] **Step 1: Add `file.open` to the capabilities example**
|
||||
|
||||
In `doc/automation.md`, update the `automation.version` result example (lines 111-114) to include `file.open`:
|
||||
|
||||
```json
|
||||
"capabilities": [
|
||||
"tree.dump", "tree.find", "widget.get", "input.click", "input.type",
|
||||
"input.key", "sync.wait_for", "app.state", "screenshot.window", "file.open"
|
||||
]
|
||||
```
|
||||
|
||||
The §4 prose count is already written for this: "There are 11 methods … the 10 callable feature methods" now matches exactly (10 capability entries + `automation.version` = 11). Leave that sentence unchanged.
|
||||
|
||||
- [ ] **Step 2: Add the `file.open` method subsection**
|
||||
|
||||
In `doc/automation.md`, immediately after the `screenshot.window` method subsection (it ends just before the `---` on line 303) and before that `---`, insert:
|
||||
|
||||
```markdown
|
||||
### `file.open`
|
||||
|
||||
Load one or more files into the **already-running** instance at runtime, by calling
|
||||
`Plater::load_files(...)` directly on the GUI thread. This is the supported way to add
|
||||
or swap a model without relaunching the process. Loading is **synchronous**: when the
|
||||
call returns `ok: true`, `app.state().project_loaded` is already `true` (no polling
|
||||
race).
|
||||
|
||||
**Params:**
|
||||
|
||||
| Param | Type | Required | Meaning |
|
||||
|---|---|---|---|
|
||||
| `paths` | string or array of strings | yes | One or more **absolute** file paths. A bare string is accepted and treated as a one-element list. Paths are read from the **host (server) filesystem** — client and server are localhost-only. |
|
||||
|
||||
`.3mf` files are routed as projects and meshes as models automatically, based on file
|
||||
content (the same default strategy as drag-drop); there is no `as_project` flag in v1.
|
||||
|
||||
**Result:** `{ "ok": true, "loaded": <int> }`, where `loaded` is the number of objects
|
||||
added to the scene (`load_files(...).size()`).
|
||||
|
||||
**Errors:**
|
||||
|
||||
- `-32602` (invalid params) — `paths` is missing, is not a string/array, contains a
|
||||
non-string entry, or yields no non-empty path.
|
||||
- `1007` (load failed) — `load_files` returned empty or threw (file not found, parse
|
||||
error, or unsupported format).
|
||||
- `1004` (GUI busy) — the GUI-thread marshal timed out. An extremely large model can
|
||||
exceed the marshal timeout and surface here; documented, not mitigated in v1.
|
||||
```
|
||||
|
||||
- [ ] **Step 3: Add the `1007` row to the error-code table**
|
||||
|
||||
In `doc/automation.md` §7, in the application-specific codes table, after the `1006` row (line 395) add:
|
||||
|
||||
```markdown
|
||||
| `1006` | Disabled. |
|
||||
| `1007` | Load failed — `file.open`'s `load_files` returned empty or threw (not found, parse error, unsupported format). |
|
||||
```
|
||||
|
||||
- [ ] **Step 4: Commit**
|
||||
|
||||
```bash
|
||||
git add doc/automation.md
|
||||
git commit -m "docs(automation): document file.open method and error 1007"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Final verification
|
||||
|
||||
- [ ] **Step 1: Full automation unit suite green**
|
||||
|
||||
Run: `cmake --build build --config RelWithDebInfo --target automation_tests && build/tests/automation/RelWithDebInfo/automation_tests.exe --order rand --warn NoAssertions`
|
||||
Expected: PASS — all cases (pre-existing ~32 + 7 new) green, no `NoAssertions` warnings on the new cases.
|
||||
|
||||
- [ ] **Step 2: Full app builds**
|
||||
|
||||
Run: `cmake --build build --config RelWithDebInfo --target ALL_BUILD -- -m`
|
||||
Expected: build succeeds.
|
||||
|
||||
- [ ] **Step 3: Manual runtime check (requires a display)**
|
||||
|
||||
Launch with `--automation-server` and **no** model arg, then from a Python shell:
|
||||
```python
|
||||
from orca_automation import OrcaClient
|
||||
orca = OrcaClient(port=13619)
|
||||
print(orca.open(["C:/abs/path/cube.stl"])) # -> {'ok': True, 'loaded': 1}
|
||||
print(orca.app_state()["project_loaded"]) # -> True
|
||||
open("window.png","wb").write(orca.screenshot()) # PNG shows the loaded model
|
||||
```
|
||||
Expected: `loaded >= 1`, `project_loaded == True`, screenshot shows the model.
|
||||
|
||||
- [ ] **Step 4: Gating check (automation OFF is a no-op)**
|
||||
|
||||
Confirm by reading: with no `--automation-server` flag, the server/backend/dispatcher are never constructed (`GUI_App.cpp` `start_automation_server()` early-return), so `file.open` is unreachable. No new hot-path cost beyond the existing single bool check. (See `doc/automation.md` §Verification — disabled-path audit; this feature adds no new gating surface.)
|
||||
|
||||
---
|
||||
|
||||
## Backward compatibility
|
||||
|
||||
Additive only: one new method (`file.open`), one new error code (`1007`), one new capabilities entry, and one new backend interface method. No existing method, profile, project-file handling, or default behavior changes. The method is reachable only when `--automation-server` is passed.
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,117 +0,0 @@
|
||||
# Design — `file.open` automation method (runtime model loading)
|
||||
|
||||
**Date:** 2026-06-03
|
||||
**Branch:** `feature/automation`
|
||||
**Status:** Approved for implementation
|
||||
|
||||
---
|
||||
|
||||
## Problem
|
||||
|
||||
Today a model can be loaded into OrcaSlicer **only at process launch**: the model path
|
||||
is passed as a CLI positional arg and OrcaSlicer's normal startup file-loading ingests
|
||||
it. The JSON-RPC automation protocol has **no** load method, so swapping or adding a
|
||||
model in an already-running instance requires a fresh process launch.
|
||||
|
||||
Driving the native File→Import dialog via `input.click` is not a viable substitute: the
|
||||
OS file picker is not a `wxWindow`, so it never appears in the `tree.dump` hierarchy
|
||||
(`WxUiBackend::dump_tree` walks `wxGetApp().mainframe` children only), and `input.click`
|
||||
can only target nodes resolved from that tree (no raw-coordinate click). Blind typing via
|
||||
`input.type` is mechanically possible but unobservable: the native picker is not a
|
||||
`wxDialog`, so `app.state().modal_dialog` and `sync.wait_for` cannot gate on it, leaving
|
||||
only sleep-and-hope timing. A direct API method is the clean fix.
|
||||
|
||||
## Goal
|
||||
|
||||
Add a `file.open` JSON-RPC method that loads one or more files into a running instance by
|
||||
calling `Plater::load_files(...)` directly on the GUI thread. Out of scope: any
|
||||
dialog-driving mechanism (intercept hook or true OS-level drive) — explicitly deferred.
|
||||
|
||||
---
|
||||
|
||||
## Protocol
|
||||
|
||||
- **Method:** `file.open`
|
||||
- **Params:** `{ "paths": ["C:/abs/a.stl", ...] }`
|
||||
- A bare string is also accepted: `{ "paths": "C:/abs/a.stl" }`.
|
||||
- Paths must be **absolute**. The server reads them from the host filesystem
|
||||
(client/server are localhost-only).
|
||||
- **Result:** `{ "ok": true, "loaded": <count> }`
|
||||
- `count` is `load_files(...).size()` — the number of objects added to the scene.
|
||||
- **Errors:**
|
||||
| Code | Constant | Condition |
|
||||
|------|----------|-----------|
|
||||
| -32602 | `kInvalidParams` | `paths` missing/empty, a non-string entry, or no non-empty path |
|
||||
| 1004 | `kErrGuiBusy` | GUI-thread marshal timed out (`m_gui_timeout_ms`) |
|
||||
| 1007 | `kErrLoadFailed` | `load_files` returned empty / threw (not found, parse error, unsupported format) — **new code** |
|
||||
|
||||
## Semantics — synchronous
|
||||
|
||||
`Plater::load_files` runs and completes on the GUI thread. The backend marshals via the
|
||||
existing `run_on_gui(m_gui_timeout_ms, …)` helper and returns only after the load
|
||||
finishes. Consequently, when `file.open` returns `ok:true`, `app.state().project_loaded`
|
||||
is already `true` — there is no polling race.
|
||||
|
||||
Rejected alternative — async "fire-and-poll-`project_loaded`": adds client complexity and
|
||||
loses a definitive per-call error result, with no benefit since loading is synchronous.
|
||||
|
||||
**Caveat:** an extremely large model could exceed `m_gui_timeout_ms` and surface as
|
||||
`1004 kErrGuiBusy`. Documented; not mitigated in v1.
|
||||
|
||||
## Load strategy (v1 minimal)
|
||||
|
||||
Pass the default `LoadStrategy::LoadModel | LoadStrategy::LoadConfig` (identical to
|
||||
drag-drop / `Plater::load_files`'s default) with `ask_multi = false`. This already routes
|
||||
`.3mf` files as projects and meshes as models based on file content, so **no `as_project`
|
||||
flag is needed in v1**. A future `{ "as_project": bool }` flag remains possible but is not
|
||||
implemented now.
|
||||
|
||||
---
|
||||
|
||||
## Components / files to touch
|
||||
|
||||
Follows the existing `screenshot_window` / `app_state` method pattern.
|
||||
|
||||
1. **`src/slic3r/GUI/Automation/IUiBackend.hpp`** — add pure-virtual
|
||||
`int open_files(const std::vector<std::string>& paths)` returning the loaded count,
|
||||
throwing `AutomationError` on failure. Header stays wx-free (no `LoadStrategy` leak).
|
||||
2. **`src/slic3r/GUI/Automation/WxUiBackend.{hpp,cpp}`** — implement `open_files`:
|
||||
`run_on_gui(m_gui_timeout_ms, …)` → `wxGetApp().plater()->load_files(paths, default_strategy, false)`;
|
||||
throw `kErrLoadFailed` if the returned vector is empty.
|
||||
3. **`src/slic3r/GUI/Automation/JsonRpcDispatcher.{hpp,cpp}`** —
|
||||
- add `constexpr int kErrLoadFailed = 1007;`
|
||||
- declare + define `m_file_open(params)` (param parsing/validation; accept string or
|
||||
array; require ≥1 non-empty string path)
|
||||
- add dispatch route `if (method == "file.open") return make_result(id, m_file_open(params));`
|
||||
- add `"file.open"` to the capabilities array in `m_version`.
|
||||
4. **`tests/automation/MockUiBackend.hpp`** — `open_files` override recording the paths
|
||||
vector + a configurable return-count (and a throw/fail knob).
|
||||
5. **`tests/automation/test_dispatcher.cpp`** — Catch2 v2 tests:
|
||||
- array of paths → routes to backend, returns `loaded` count
|
||||
- bare-string path → normalized to one path
|
||||
- missing/empty `paths` → `-32602` (`kInvalidParams`)
|
||||
- backend load failure → `1007`
|
||||
- `automation.version` capabilities array includes `"file.open"`
|
||||
6. **`tools/automation/orca_automation.py`** — `open(self, paths)` wrapper (normalize
|
||||
`str` → `[str]`, send `file.open`).
|
||||
7. **`tools/automation/example_slice.py`** — launch **without** a model arg, then
|
||||
`orca.open([model])`, then wait for `project_loaded`.
|
||||
8. **`doc/automation.md`** — document method (params/result/errors), add to the
|
||||
capabilities list, method index, and error table (`1007`).
|
||||
|
||||
---
|
||||
|
||||
## Testing / verification
|
||||
|
||||
- **Build (Windows):** `cmake --build . --config RelWithDebInfo --target ALL_BUILD -- -m`.
|
||||
- **Unit:** `automation` Catch2 suite green including new tests (≈31 → ≈34 cases).
|
||||
- **Manual:** launch with `--automation-server` and **no** model arg → call `file.open`
|
||||
→ confirm `app.state().project_loaded` flips `true` and `screenshot.window` shows the
|
||||
model.
|
||||
- **Gating:** unchanged — the server only runs under `--automation-server`, so the method
|
||||
is a no-op (unreachable) when automation is disabled.
|
||||
|
||||
## Backward compatibility
|
||||
|
||||
Additive only: a new method, a new error code, and a new capabilities entry. No change to
|
||||
existing methods, profiles, project-file handling, or default behavior.
|
||||
@@ -1,314 +0,0 @@
|
||||
# OrcaSlicer UI Automation — Design Spec
|
||||
|
||||
**Date:** 2026-06-03
|
||||
**Status:** Approved design, pending implementation plan
|
||||
**Topic:** Add an opt-in, externally-controllable UI automation interface to OrcaSlicer for automated GUI testing and future AI-agent control.
|
||||
|
||||
---
|
||||
|
||||
## 1. Overview
|
||||
|
||||
Add a localhost JSON-RPC server to a running OrcaSlicer GUI instance that lets an
|
||||
**external** script (or AI agent) drive and observe the real GUI. "Driving" is done
|
||||
the way a user would — simulated mouse/keyboard via `wxUIActionSimulator` — while
|
||||
"observing" reads the live widget state and captures screenshots.
|
||||
|
||||
The interface must cover **three UI technologies** present in OrcaSlicer:
|
||||
|
||||
1. Native **wxWidgets** widgets (`wxWindow` hierarchy).
|
||||
2. The **OpenGL 3D viewport** (`GLCanvas3D`) — screenshots via the existing
|
||||
framebuffer/thumbnail path.
|
||||
3. **Dear ImGui** immediate-mode controls (gizmo panels, in-canvas overlays,
|
||||
notifications) — recorded as they are drawn, because there is no persistent tree.
|
||||
|
||||
## 2. Goals / Non-Goals
|
||||
|
||||
### Goals
|
||||
- Let an external, language-agnostic client connect to a running instance and:
|
||||
introspect the UI, locate widgets by stable name, perform input actions, wait on
|
||||
conditions, and capture screenshots (including the 3D view as a separate image).
|
||||
- Be safe by default: disabled unless explicitly enabled; bound to `127.0.0.1` only.
|
||||
- Be testable in CI without a display (pure-logic units behind a mock backend).
|
||||
- Ship a reference Python client, a runnable end-to-end example, protocol docs, and
|
||||
C++ unit tests.
|
||||
|
||||
### Non-Goals (v1)
|
||||
- No headless/offscreen automation — OS input injection needs a focused, visible
|
||||
window (Linux CI requires a display, e.g. Xvfb).
|
||||
- No auth token in v1 (documented future hardening; localhost-only is the boundary).
|
||||
- No per-item coverage of raw-`ImGui::` gizmos (Emboss/SVG/Text). They get
|
||||
window-level coverage; per-item is future work.
|
||||
- No new scripting language embedded in the app; control is purely external over JSON-RPC.
|
||||
- We do **not** modify the existing auth `HttpServer`.
|
||||
|
||||
## 3. Background — existing infrastructure (verified)
|
||||
|
||||
- **`wxUIActionSimulator`** is compiled into the wx build and already used
|
||||
(`src/slic3r/GUI/GUI_ObjectList.cpp:211`). Cross-platform simulated input is available.
|
||||
- **`GLCanvas3D::render_thumbnail()`** (`src/slic3r/GUI/GLCanvas3D.cpp:2210+`) →
|
||||
`render_thumbnail_framebuffer()` (`:6352`) renders the 3D scene into a
|
||||
`ThumbnailData` (RGBA) via an FBO + `glReadPixels`. `debug_output_thumbnail()`
|
||||
(`:6099`) shows the `ThumbnailData → wxImage → PNG` conversion. This is the
|
||||
separate-3D-screenshot path. `Plater::generate_thumbnail()`/`generate_thumbnails()`
|
||||
wrap it.
|
||||
- **`ImGuiWrapper`** (`src/slic3r/GUI/ImGuiWrapper.hpp`) is the chokepoint for nearly
|
||||
all ImGui controls: `button`/`bbl_button`, `checkbox`/`bbl_checkbox`, `combo`,
|
||||
`slider_float`, `input_double`, `radio_button`, `menu_item_with_icon`, plus
|
||||
`begin`/`end` for windows. `imgui_internal.h` is in-tree, exposing
|
||||
`ImGui::GetCurrentContext()->Windows`, item rects, and hovered/active id.
|
||||
- **`HttpServer`** (`src/slic3r/GUI/HttpServer.{hpp,cpp}`, boost::beast, port 13618)
|
||||
is used for cloud auth. **It cannot serve a POST body** — `session::read_body()`
|
||||
reads and discards the body and never replies (`HttpServer.cpp:57-65`). It is
|
||||
effectively GET-only. We will **not** reuse or modify it.
|
||||
- **`OtherInstanceMessageHandler`** (`src/slic3r/GUI/InstanceCheck.{hpp,cpp}`) is the
|
||||
template for "start a localhost listener once the MainFrame exists, post events into
|
||||
the GUI." Useful as a structural reference.
|
||||
- **CLI args** are parsed in `src/OrcaSlicer.cpp` (`CLI::setup`/`CLI::run`) and flow
|
||||
into the GUI run params consumed by `GUI_App::OnInit()`.
|
||||
|
||||
## 4. Architecture
|
||||
|
||||
```
|
||||
External script / AI agent ──► Python client (orca_automation.py)
|
||||
│ HTTP POST /jsonrpc (JSON-RPC 2.0) on 127.0.0.1:<port>
|
||||
▼
|
||||
AutomationServer (dedicated boost::beast listener; own thread;
|
||||
│ started only when --automation-server is set)
|
||||
│ parse JSON-RPC envelope
|
||||
▼
|
||||
JsonRpcDispatcher (pure logic — method registry; unit-testable)
|
||||
│ marshal each call to the GUI thread via wxGetApp().CallAfter
|
||||
│ + std::promise/future with a per-request timeout
|
||||
▼
|
||||
IUiBackend (interface) ──► WxUiBackend (real, GUI thread) / MockBackend (tests)
|
||||
├─ Introspection : walk wxWindow tree + read ImGui item table → unified JSON
|
||||
├─ Locator : resolve automation-id / predicate → wx widget or ImGui item
|
||||
├─ Actions : raise window, then wxUIActionSimulator click/type/key
|
||||
├─ Sync : wait_for (poll condition) + app.state snapshot
|
||||
└─ Screenshots : wx widget → wxDC→PNG ; 3D view → render_thumbnail()→PNG
|
||||
```
|
||||
|
||||
### Components (new files unless noted)
|
||||
|
||||
All new code lives under `src/slic3r/GUI/Automation/`.
|
||||
|
||||
| Component | Responsibility |
|
||||
|---|---|
|
||||
| `AutomationServer.{hpp,cpp}` | Dedicated boost::beast HTTP listener with **POST + body** support; one `POST /jsonrpc` endpoint; returns `application/json`. Localhost-only. Own thread. |
|
||||
| `JsonRpcDispatcher.{hpp,cpp}` | Parse JSON-RPC 2.0; route `method` → handler; build result/error. Depends only on `IUiBackend`. No wx/ImGui includes → unit-testable. |
|
||||
| `IUiBackend.hpp` | Abstract interface: `dump_tree`, `find`, `get_widget`, `click`, `type`, `key`, `wait_for`, `app_state`, `screenshot_window`, `screenshot_viewport3d`. Uses plain structs (no wx types) so tests can mock it. |
|
||||
| `WxUiBackend.{hpp,cpp}` | Real implementation. Runs on GUI thread. Walks `wxWindow` tree, reads the ImGui item table, drives `wxUIActionSimulator`, captures screenshots. |
|
||||
| `MockUiBackend.{hpp,cpp}` (tests) | Deterministic fake tree + recorded actions for unit tests. |
|
||||
| `AutomationRegistry.{hpp,cpp}` | Process-wide `wxWindow* → automation_id` map + reverse lookup; `set_automation_id(win, "id")` helper. Header is dependency-light so widget-construction code can call the helper unconditionally (it is a cheap no-op-safe registration). |
|
||||
| `WidgetSerializer.{hpp,cpp}` | `wxWindow` → JSON node (name/id, class, label, screen-rect, enabled, shown, value via RTTI). |
|
||||
| `ImGuiItemTable.{hpp,cpp}` | Per-frame recorder of ImGui items + live-window enumeration. Populated from `ImGuiWrapper`; read on GUI thread. |
|
||||
|
||||
### Touch points in existing files
|
||||
- `src/slic3r/GUI/ImGuiWrapper.cpp` — add recording hooks inside the wrapped widget
|
||||
methods and `begin`/`end`, **guarded by an `is_automation_enabled()` flag** so there
|
||||
is zero overhead and zero behavior change when automation is off.
|
||||
- `src/slic3r/GUI/GUI_App.{hpp,cpp}` — own the `AutomationServer`; start it in
|
||||
`OnInit()` only when the flag is set; stop it on exit. Expose
|
||||
`is_automation_enabled()`.
|
||||
- `src/OrcaSlicer.cpp` — parse `--automation-server[=PORT]`; pass through GUI run params.
|
||||
- A handful of widget-construction sites (Slice/Export buttons, preset combos, main
|
||||
tabs, common dialog OK/Cancel, the 3D canvas) — add `set_automation_id(...)` calls
|
||||
(~15-20 widgets in v1).
|
||||
- CMake: add the new `Automation/` sources to the GUI target; add the unit-test target.
|
||||
|
||||
## 5. Transport & Protocol
|
||||
|
||||
- **Transport:** HTTP/1.1 on `127.0.0.1:<port>` (default **13619**, adjacent to the
|
||||
auth server's 13618). Single endpoint: `POST /jsonrpc`, body is a JSON-RPC 2.0
|
||||
request, response is a JSON-RPC 2.0 result/error. `GET /` returns a tiny health/version page.
|
||||
- **Protocol:** JSON-RPC 2.0. `id`, `method`, `params`. Batch not required in v1.
|
||||
|
||||
### v1 methods
|
||||
|
||||
| Method | Params | Result |
|
||||
|---|---|---|
|
||||
| `automation.version` | — | `{version, protocol, capabilities[]}` |
|
||||
| `tree.dump` | `{root?, max_depth?, visible_only?, include_imgui?}` | tree of nodes (wx + imgui) |
|
||||
| `tree.find` | `{name?, class?, label?, value?, backend?}` | `[node...]` matches |
|
||||
| `widget.get` | `{target}` | single node detail |
|
||||
| `input.click` | `{target, button?=left, double?=false, modifiers?[]}` | `{ok}` |
|
||||
| `input.type` | `{target?, text}` | `{ok}` |
|
||||
| `input.key` | `{keys}` e.g. `"ctrl+s"` or `["ctrl","s"]` | `{ok}` |
|
||||
| `sync.wait_for` | `{target, state: exists\|visible\|enabled\|value, value?, timeout_ms?=5000, poll_ms?=100}` | `{ok, elapsed_ms}` |
|
||||
| `app.state` | — | `{active_tab, project_loaded, slicing, slice_progress, modal_dialog?, foreground}` |
|
||||
| `screenshot.window` | `{target?}` (default main frame) | `{png_base64, width, height}` |
|
||||
| `screenshot.viewport3d` | `{plate?, width?, height?}` | `{png_base64, width, height}` |
|
||||
|
||||
### Node shape (unified for wx and ImGui)
|
||||
```json
|
||||
{
|
||||
"backend": "wx" | "imgui",
|
||||
"id": "btn_slice", // automation id if set, else derived path id
|
||||
"path": "MainFrame/.../btn_slice", // stable-ish positional path
|
||||
"class": "Button", // wx class name or imgui item type
|
||||
"label": "Slice plate",
|
||||
"rect": { "x": 100, "y": 200, "w": 120, "h": 32 }, // screen coords
|
||||
"enabled": true,
|
||||
"visible": true,
|
||||
"value": "PLA", // when applicable (text/choice/check/slider)
|
||||
"children": [ ... ] // wx only; imgui items are flat under their window
|
||||
}
|
||||
```
|
||||
|
||||
### Error model (JSON-RPC `error.code`)
|
||||
- `-32700` parse error, `-32601` method not found, `-32602` invalid params (standard).
|
||||
- Application codes: `1001` widget/target not found, `1002` target not actionable
|
||||
(disabled/hidden), `1003` wait timeout, `1004` GUI thread busy/timeout,
|
||||
`1005` screenshot failed, `1006` automation feature disabled.
|
||||
|
||||
## 6. Threading model
|
||||
|
||||
- `AutomationServer` runs on its own thread and accepts connections; the dispatcher
|
||||
parses on that thread.
|
||||
- **Every** call touching wx/ImGui/GL is marshaled to the GUI thread with
|
||||
`wxGetApp().CallAfter([...]{ ... })`; the server thread blocks on a `std::future`
|
||||
with a per-request timeout (default 5 s; `wait_for` uses its own larger budget).
|
||||
Timeout → error `1004`.
|
||||
- This is mandatory: wx widgets, the ImGui context, and the GL context are not
|
||||
thread-safe and are owned by the GUI thread.
|
||||
- `CallAfter` is serviced even while modal dialogs run (nested event loop), so
|
||||
automation can interact with dialogs.
|
||||
|
||||
## 7. Widget locator & automation IDs (wxWidgets)
|
||||
|
||||
- **Stable IDs:** `set_automation_id(window, "btn_slice")` registers the widget in
|
||||
`AutomationRegistry`. Stored in a side map keyed by `wxWindow*` (not via `SetName`,
|
||||
to avoid any coupling with wx's name-based lookups). Registration is removed on
|
||||
widget destruction (bind to `wxEVT_DESTROY` or prune lazily on lookup).
|
||||
- **Derived IDs:** for un-instrumented widgets, `WidgetSerializer` derives a positional
|
||||
`path` (e.g. `MainFrame/Panel[2]/Button[0]`) so an AI agent can still target anything.
|
||||
Named IDs are the preferred, stable path.
|
||||
- **Locator resolution order:** exact automation id → exact path → predicate match
|
||||
(name/class/label/value). Ambiguous matches return the list via `tree.find`; action
|
||||
methods require a unique match or error `1001`.
|
||||
- **v1 instrumented widgets (~15-20):** Slice/Export buttons, printer & filament preset
|
||||
combos, the main tab buttons (`tp3DEditor`/`tpPreview`/`tpMonitor`/…), Add/Import,
|
||||
common dialog OK/Cancel/Yes/No, the `GLCanvas3D` itself.
|
||||
|
||||
## 8. ImGui coverage (v1 = wrapper items + window introspection)
|
||||
|
||||
- **Item recording:** inside each `ImGuiWrapper` wrapped method, when
|
||||
`is_automation_enabled()` is true, append the just-drawn item to a per-frame
|
||||
`ImGuiItemTable` entry: `{window_name, label/id, type, rect, enabled, value}`.
|
||||
Item rect comes from `ImGui::GetItemRectMin/Max()` (ImGui display coords) mapped to
|
||||
**screen** coords via the `GLCanvas3D` client origin (`ClientToScreen`) and DPI scale.
|
||||
- **Window enumeration:** via `imgui_internal.h`, enumerate `GetCurrentContext()->Windows`
|
||||
for window name, rect, visibility, plus the global hovered/active item id.
|
||||
- **Double-buffering:** the table is swapped at frame end (`ImGuiWrapper::render`) so
|
||||
readers see a complete frame. Reads happen on the GUI thread (after marshaling), same
|
||||
thread as rendering, so a simple front/back swap suffices.
|
||||
- **Freshness:** because items exist only while drawn, before an ImGui tree read or
|
||||
action the backend forces a canvas refresh and flushes events so the latest frame is
|
||||
captured. `sync.wait_for` can poll for an ImGui item to appear (e.g. after opening a
|
||||
gizmo).
|
||||
- **Actions:** an ImGui target resolves to its recorded screen rect; `input.click`/
|
||||
`input.type` use `wxUIActionSimulator` on that rect — identical action path to wx,
|
||||
different rect source. Typing into an ImGui input works because simulated keystrokes
|
||||
flow through the existing `ImGuiWrapper::update_key_data` bridge once the field is
|
||||
focused by a click.
|
||||
- **Limitation (documented):** raw-`ImGui::` gizmos (Emboss, SVG, Text) are covered at
|
||||
the **window** level only in v1; per-item instrumentation is future work.
|
||||
|
||||
## 9. Screenshots
|
||||
|
||||
- **`screenshot.window`:** capture a `wxWindow` (default: main frame) via
|
||||
`wxClientDC`/`wxWindowDC` → `wxBitmap` → `wxImage` → PNG → base64. Works for native
|
||||
widgets but **not** for the GL canvas region (returns black there) — hence the
|
||||
separate 3D method.
|
||||
- **`screenshot.viewport3d`:** reuse `GLCanvas3D::render_thumbnail()` (FBO +
|
||||
`glReadPixels`) → `ThumbnailData` → `wxImage` (per `debug_output_thumbnail`) → PNG →
|
||||
base64. Optional `plate`, `width`, `height` params. Runs on the GUI thread with the GL
|
||||
context current.
|
||||
|
||||
## 10. Activation & security
|
||||
|
||||
- **Off by default.** Enabled by CLI flag `--automation-server[=PORT]` (default port
|
||||
13619). (An app-config/Preferences toggle may be added later; v1 is flag-only.)
|
||||
- **Bind `127.0.0.1` only.** No external interface.
|
||||
- **No token in v1** (per decision); documented as a recommended future hardening,
|
||||
along with an optional `--automation-token`.
|
||||
- When disabled: no listener, no thread, and all `ImGuiWrapper` recording hooks are
|
||||
skipped — **zero** runtime overhead and **zero** behavior change. This satisfies the
|
||||
project's "features gated by options must not affect existing behavior when disabled"
|
||||
constraint.
|
||||
|
||||
## 11. Testability
|
||||
|
||||
- `JsonRpcDispatcher` depends only on `IUiBackend` and has **no** wx/ImGui/GL includes.
|
||||
- **C++ unit tests (Catch2), display-free, run in CI:**
|
||||
- JSON-RPC envelope parse/validate/dispatch (good + malformed input, error codes).
|
||||
- Method routing and param validation for every v1 method against `MockUiBackend`.
|
||||
- `WidgetSerializer` node shape (fed a synthetic node model, not real wx widgets).
|
||||
- Locator resolution: exact id, path, predicate, ambiguity, not-found.
|
||||
- The only piece needing a real GUI is `WxUiBackend`; it is exercised by the manual
|
||||
end-to-end example, not by CI unit tests.
|
||||
|
||||
## 12. Deliverables
|
||||
|
||||
- **C++:** the `Automation/` components, `ImGuiWrapper` recording hooks, widget
|
||||
instrumentation, CLI flag plumbing, `GUI_App` lifecycle, CMake wiring.
|
||||
- **`tools/automation/orca_automation.py`:** reference Python client wrapping the
|
||||
JSON-RPC calls (`connect`, `version`, `dump_tree`, `find`, `click`, `type`, `key`,
|
||||
`wait_for`, `app_state`, `screenshot`, `screenshot_3d`).
|
||||
- **`tools/automation/example_slice.py`:** runnable end-to-end flow — launch OrcaSlicer
|
||||
with the flag, load a model, click Slice, `wait_for` completion, save a 3D-preview PNG.
|
||||
Doubles as a manual smoke test.
|
||||
- **`doc/automation.md`:** protocol reference (methods, params, results, error codes),
|
||||
node shape, automation-id naming conventions, ImGui notes, platform/display caveats.
|
||||
- **`tests/`:** Catch2 unit-test target for the dispatch/serialize/locator logic.
|
||||
|
||||
## 13. New / changed file inventory
|
||||
|
||||
**New**
|
||||
- `src/slic3r/GUI/Automation/AutomationServer.{hpp,cpp}`
|
||||
- `src/slic3r/GUI/Automation/JsonRpcDispatcher.{hpp,cpp}`
|
||||
- `src/slic3r/GUI/Automation/IUiBackend.hpp`
|
||||
- `src/slic3r/GUI/Automation/WxUiBackend.{hpp,cpp}`
|
||||
- `src/slic3r/GUI/Automation/AutomationRegistry.{hpp,cpp}`
|
||||
- `src/slic3r/GUI/Automation/WidgetSerializer.{hpp,cpp}`
|
||||
- `src/slic3r/GUI/Automation/ImGuiItemTable.{hpp,cpp}`
|
||||
- `tools/automation/orca_automation.py`
|
||||
- `tools/automation/example_slice.py`
|
||||
- `doc/automation.md`
|
||||
- `tests/automation/` (Catch2 target) + `MockUiBackend.{hpp,cpp}`
|
||||
|
||||
**Changed**
|
||||
- `src/slic3r/GUI/ImGuiWrapper.cpp` (guarded recording hooks)
|
||||
- `src/slic3r/GUI/GUI_App.{hpp,cpp}` (server lifecycle, `is_automation_enabled()`)
|
||||
- `src/OrcaSlicer.cpp` (CLI flag)
|
||||
- ~15-20 widget-construction sites (`set_automation_id`)
|
||||
- `src/slic3r/GUI/CMakeLists.txt` + `tests/CMakeLists.txt`
|
||||
|
||||
## 14. Known constraints & limitations
|
||||
|
||||
- OS input injection requires the OrcaSlicer window **focused and visible**; the backend
|
||||
raises/focuses the main window before injecting. Linux CI needs a display (Xvfb).
|
||||
- Input is asynchronous at the OS level; correctness relies on `sync.wait_for` rather
|
||||
than fixed sleeps.
|
||||
- ImGui items are only addressable while their host panel is drawn.
|
||||
- Raw-`ImGui::` gizmos: window-level only in v1.
|
||||
- Single-client assumption in v1 (serialized request handling); no concurrent sessions
|
||||
contract.
|
||||
|
||||
## 15. Future work (out of scope for v1)
|
||||
|
||||
- Optional auth token + Preferences toggle.
|
||||
- WebSocket channel for server-push events (slice progress, dialog-appeared).
|
||||
- Per-item instrumentation for raw-`ImGui::` gizmos.
|
||||
- An MCP server wrapping the JSON-RPC client for direct AI-agent integration.
|
||||
- Optional integration of Dear ImGui Test Engine for deterministic ImGui interaction.
|
||||
|
||||
## 16. Verification plan
|
||||
|
||||
- **CI:** Catch2 unit tests (dispatch/serialize/locator) pass with no display.
|
||||
- **Manual / e2e:** run `tools/automation/example_slice.py` against a built OrcaSlicer
|
||||
launched with `--automation-server`; confirm model loads, Slice runs, `wait_for`
|
||||
returns on completion, and both a wx-window PNG and a 3D-viewport PNG are produced.
|
||||
- **Regression:** build and run with automation **off**; confirm no new threads, no
|
||||
listener, and ImGui rendering is byte-for-byte unchanged (hooks compiled out of the
|
||||
hot path via the disabled flag).
|
||||
+1749
-3808
File diff suppressed because it is too large
Load Diff
+3976
-8974
File diff suppressed because it is too large
Load Diff
+4476
-8761
File diff suppressed because it is too large
Load Diff
+4577
-10232
File diff suppressed because it is too large
Load Diff
+2194
-4993
File diff suppressed because it is too large
Load Diff
+4552
-10382
File diff suppressed because it is too large
Load Diff
+4234
-10629
File diff suppressed because it is too large
Load Diff
+3697
-8239
File diff suppressed because it is too large
Load Diff
+4135
-9731
File diff suppressed because it is too large
Load Diff
+3106
-6155
File diff suppressed because it is too large
Load Diff
+3536
-7325
File diff suppressed because it is too large
Load Diff
@@ -48,9 +48,12 @@ src/slic3r/GUI/DeviceTab/uiAmsHumidityPopup.cpp
|
||||
src/slic3r/GUI/DeviceTab/uiDeviceUpdateVersion.h
|
||||
src/slic3r/GUI/DeviceTab/uiDeviceUpdateVersion.cpp
|
||||
src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.cpp
|
||||
src/slic3r/GUI/Gizmos/GLGizmoFdmSupports.hpp
|
||||
src/slic3r/GUI/Gizmos/GLGizmoFlatten.cpp
|
||||
src/slic3r/GUI/Gizmos/GLGizmoMmuSegmentation.cpp
|
||||
src/slic3r/GUI/Gizmos/GLGizmoMmuSegmentation.hpp
|
||||
src/slic3r/GUI/Gizmos/GLGizmoFuzzySkin.cpp
|
||||
src/slic3r/GUI/Gizmos/GLGizmoFuzzySkin.hpp
|
||||
src/slic3r/GUI/Gizmos/GLGizmoMove.cpp
|
||||
src/slic3r/GUI/Gizmos/GLGizmoRotate.cpp
|
||||
src/slic3r/GUI/Gizmos/GLGizmoScale.cpp
|
||||
@@ -58,6 +61,7 @@ src/slic3r/GUI/Gizmos/GLGizmosManager.cpp
|
||||
src/slic3r/GUI/Gizmos/GLGizmoPainterBase.cpp
|
||||
src/slic3r/GUI/Gizmos/GizmoObjectManipulation.cpp
|
||||
src/slic3r/GUI/Gizmos/GLGizmoCut.cpp
|
||||
src/slic3r/GUI/Gizmos/GLGizmoCut.hpp
|
||||
src/slic3r/GUI/Gizmos/GLGizmoSimplify.cpp
|
||||
src/slic3r/GUI/Gizmos/GLGizmoFaceDetector.cpp
|
||||
src/slic3r/GUI/Gizmos/GLGizmoSeam.cpp
|
||||
@@ -67,7 +71,9 @@ src/slic3r/GUI/Gizmos/GLGizmoText.hpp
|
||||
src/slic3r/GUI/Gizmos/GLGizmoEmboss.cpp
|
||||
src/slic3r/GUI/Gizmos/GLGizmoSVG.cpp
|
||||
src/slic3r/GUI/Gizmos/GLGizmoMeasure.cpp
|
||||
src/slic3r/GUI/Gizmos/GLGizmoMeasure.hpp
|
||||
src/slic3r/GUI/Gizmos/GLGizmoAssembly.cpp
|
||||
src/slic3r/GUI/Gizmos/GLGizmoAssembly.hpp
|
||||
src/slic3r/GUI/GUI.cpp
|
||||
src/slic3r/GUI/GUI_App.cpp
|
||||
src/slic3r/GUI/GUI_AuxiliaryList.cpp
|
||||
@@ -89,6 +95,8 @@ src/slic3r/GUI/Widgets/AMSControl.cpp
|
||||
src/slic3r/GUI/Widgets/FanControl.cpp
|
||||
src/slic3r/GUI/Widgets/FilamentLoad.cpp
|
||||
src/slic3r/GUI/Widgets/TempInput.cpp
|
||||
src/slic3r/GUI/Widgets/CheckList.cpp
|
||||
src/slic3r/GUI/Widgets/SwitchButton.cpp
|
||||
src/slic3r/GUI/ImGuiWrapper.cpp
|
||||
src/slic3r/GUI/Jobs/ArrangeJob.cpp
|
||||
src/slic3r/GUI/Jobs/OrientJob.cpp
|
||||
@@ -98,6 +106,7 @@ src/slic3r/GUI/Jobs/BindJob.cpp
|
||||
src/slic3r/GUI/Jobs/PrintJob.cpp
|
||||
src/slic3r/GUI/Jobs/SendJob.cpp
|
||||
src/slic3r/GUI/Jobs/EmbossJob.cpp
|
||||
src/slic3r/GUI/Jobs/PlaterWorker.hpp
|
||||
src/slic3r/GUI/ThermalPreconditioningDialog.cpp
|
||||
src/slic3r/GUI/ThermalPreconditioningDialog.hpp
|
||||
src/slic3r/GUI/Jobs/SLAImportJob.cpp
|
||||
@@ -237,8 +246,12 @@ src/slic3r/GUI/FilamentMapPanel.cpp
|
||||
src/slic3r/Utils/Obico.cpp
|
||||
src/slic3r/Utils/SimplyPrint.cpp
|
||||
src/slic3r/Utils/Flashforge.cpp
|
||||
src/slic3r/Utils/ElegooLink.cpp
|
||||
src/slic3r/Utils/CrealityPrint.cpp
|
||||
src/slic3r/Utils/PrintHost.cpp
|
||||
src/slic3r/GUI/Jobs/OAuthJob.cpp
|
||||
src/slic3r/GUI/Gizmos/GLGizmoBrimEars.cpp
|
||||
src/slic3r/GUI/Gizmos/GLGizmoBrimEars.hpp
|
||||
src/slic3r/GUI/PartSkipDialog.cpp
|
||||
src/slic3r/GUI/PartSkipDialog.hpp
|
||||
src/slic3r/GUI/SkipPartCanvas.cpp
|
||||
@@ -250,4 +263,12 @@ src/slic3r/GUI/NetworkPluginDialog.cpp
|
||||
src/slic3r/GUI/RammingChart.cpp
|
||||
src/slic3r/GUI/StepMeshDialog.cpp
|
||||
src/slic3r/GUI/FilamentPickerDialog.hpp
|
||||
src/slic3r/GUI/PresetBundleDialog.cpp
|
||||
src/slic3r/GUI/ExportPresetBundleDialog.cpp
|
||||
src/slic3r/GUI/DesktopIntegrationDialog.cpp
|
||||
src/slic3r/GUI/Downloader.cpp
|
||||
src/slic3r/GUI/DownloaderFileGet.cpp
|
||||
src/slic3r/GUI/FileArchiveDialog.cpp
|
||||
src/slic3r/GUI/PrinterCloudAuthDialog.cpp
|
||||
src/slic3r/GUI/PrinterWebViewHandler.cpp
|
||||
src/libslic3r/PresetBundle.cpp
|
||||
|
||||
+4238
-9022
File diff suppressed because it is too large
Load Diff
+4759
-9873
File diff suppressed because it is too large
Load Diff
+3998
-8990
File diff suppressed because it is too large
Load Diff
+3924
-9085
File diff suppressed because it is too large
Load Diff
+3800
-8330
File diff suppressed because it is too large
Load Diff
+4857
-9730
File diff suppressed because it is too large
Load Diff
+1546
-973
File diff suppressed because it is too large
Load Diff
+3590
-7767
File diff suppressed because it is too large
Load Diff
+3448
-7268
File diff suppressed because it is too large
Load Diff
+4023
-8091
File diff suppressed because it is too large
Load Diff
+2939
-5432
File diff suppressed because it is too large
Load Diff
+3320
-5651
File diff suppressed because it is too large
Load Diff
Binary file not shown.
@@ -146,16 +146,16 @@ text = Fine-tuning for flow rate\nDid you know that flow rate can be fine-tuned
|
||||
text = Split your prints into plates\nDid you know that you can split a model that has a lot of parts into individual plates ready to print? This will simplify the process of keeping track of all the parts.
|
||||
|
||||
[hint:Speed up your print with Adaptive Layer Height]
|
||||
text = Speed up your print with Adaptive Layer Height\nDid you know that you can print a model even faster, by using the Adaptive Layer Height option? Check it out!
|
||||
text = Speed up your print with Adaptive Layer Height\nDid you know that you can print a model even faster by using the Adaptive Layer Height option? Check it out!
|
||||
|
||||
[hint:Support painting]
|
||||
text = Support painting\nDid you know that you can paint the location of your supports? This feature makes it easy to place the support material only on the sections of the model that actually need it.
|
||||
|
||||
[hint:Different types of supports]
|
||||
text = Different types of supports\nDid you know that you can choose from multiple types of supports? Tree supports work great for organic models, while saving filament and improving print speed. Check them out!
|
||||
text = Different types of supports\nDid you know that you can choose from multiple types of supports? Tree supports work great for organic models while saving filament and improving print speed. Check them out!
|
||||
|
||||
[hint:Printing Silk Filament]
|
||||
text = Printing Silk Filament\nDid you know that Silk filament needs special consideration to print it successfully? Higher temperature and lower speed are always recommended for the best results.
|
||||
text = Printing Silk Filament\nDid you know that Silk filament needs special consideration to print successfully? A higher temperature and lower speed are always recommended for the best results.
|
||||
|
||||
[hint:Brim for better adhesion]
|
||||
text = Brim for better adhesion\nDid you know that when printed models have a small contact interface with the printing surface, it's recommended to use a brim?
|
||||
@@ -167,13 +167,13 @@ text = Set parameters for multiple objects\nDid you know that you can set slicin
|
||||
text = Stack objects\nDid you know that you can stack objects as a whole one?
|
||||
|
||||
[hint:Flush into support/objects/infill]
|
||||
text = Flush into support/objects/infill\nDid you know that you can reduce wasted filament by flushing it into support/objects/infill during filament change?
|
||||
text = Flush into support/objects/infill\nDid you know that you can reduce wasted filament by flushing it into support/objects/infill during filament changes?
|
||||
|
||||
[hint:Improve strength]
|
||||
text = Improve strength\nDid you know that you can use more wall loops and higher sparse infill density to improve the strength of the model?
|
||||
|
||||
[hint:When do you need to print with the printer door opened]
|
||||
text = When do you need to print with the printer door opened?\nDid you know that opening the printer door can reduce the probability of extruder/hotend clogging when printing lower temperature filament with a higher enclosure temperature? More info about this in the Wiki.
|
||||
text = When do you need to print with the printer door opened?\nDid you know that opening the printer door can reduce the probability of extruder/hotend clogging when printing lower temperature filament with a higher enclosure temperature? There is more info about this in the Wiki.
|
||||
|
||||
[hint:Avoid warping]
|
||||
text = Avoid warping\nDid you know that when printing materials that are prone to warping such as ABS, appropriately increasing the heatbed temperature can reduce the probability of warping?
|
||||
|
||||
@@ -45,7 +45,7 @@
|
||||
"0"
|
||||
],
|
||||
"filament_max_volumetric_speed": [
|
||||
"1"
|
||||
"10"
|
||||
],
|
||||
"filament_minimal_purge_on_wipe_tower": [
|
||||
"15"
|
||||
|
||||
@@ -54,7 +54,7 @@
|
||||
"0"
|
||||
],
|
||||
"filament_max_volumetric_speed": [
|
||||
"2"
|
||||
"12"
|
||||
],
|
||||
"filament_minimal_purge_on_wipe_tower": [
|
||||
"15"
|
||||
|
||||
@@ -51,7 +51,7 @@
|
||||
"0"
|
||||
],
|
||||
"filament_max_volumetric_speed": [
|
||||
"1"
|
||||
"12"
|
||||
],
|
||||
"filament_retraction_length": [
|
||||
"0.8"
|
||||
|
||||
@@ -48,7 +48,7 @@
|
||||
"0"
|
||||
],
|
||||
"filament_max_volumetric_speed": [
|
||||
"1"
|
||||
"12"
|
||||
],
|
||||
"filament_retraction_length": [
|
||||
"0.8"
|
||||
|
||||
@@ -48,7 +48,7 @@
|
||||
"0"
|
||||
],
|
||||
"filament_max_volumetric_speed": [
|
||||
"2"
|
||||
"12"
|
||||
],
|
||||
"filament_type": [
|
||||
"PETG"
|
||||
|
||||
@@ -39,7 +39,7 @@
|
||||
"0"
|
||||
],
|
||||
"filament_max_volumetric_speed": [
|
||||
"1"
|
||||
"16"
|
||||
],
|
||||
"filament_minimal_purge_on_wipe_tower": [
|
||||
"15"
|
||||
|
||||
@@ -45,7 +45,7 @@
|
||||
"0"
|
||||
],
|
||||
"filament_max_volumetric_speed": [
|
||||
"1"
|
||||
"16"
|
||||
],
|
||||
"filament_minimal_purge_on_wipe_tower": [
|
||||
"15"
|
||||
|
||||
@@ -45,7 +45,7 @@
|
||||
"0"
|
||||
],
|
||||
"filament_max_volumetric_speed": [
|
||||
"1"
|
||||
"16"
|
||||
],
|
||||
"filament_minimal_purge_on_wipe_tower": [
|
||||
"15"
|
||||
|
||||
@@ -48,7 +48,7 @@
|
||||
"0"
|
||||
],
|
||||
"filament_max_volumetric_speed": [
|
||||
"1"
|
||||
"12"
|
||||
],
|
||||
"filament_minimal_purge_on_wipe_tower": [
|
||||
"15"
|
||||
|
||||
@@ -54,7 +54,7 @@
|
||||
"0"
|
||||
],
|
||||
"filament_max_volumetric_speed": [
|
||||
"2"
|
||||
"12"
|
||||
],
|
||||
"filament_minimal_purge_on_wipe_tower": [
|
||||
"15"
|
||||
|
||||
@@ -48,7 +48,7 @@
|
||||
"0"
|
||||
],
|
||||
"filament_max_volumetric_speed": [
|
||||
"1"
|
||||
"12"
|
||||
],
|
||||
"filament_minimal_purge_on_wipe_tower": [
|
||||
"15"
|
||||
|
||||
@@ -54,7 +54,7 @@
|
||||
"0"
|
||||
],
|
||||
"filament_max_volumetric_speed": [
|
||||
"1.8"
|
||||
"12"
|
||||
],
|
||||
"filament_minimal_purge_on_wipe_tower": [
|
||||
"15"
|
||||
|
||||
@@ -48,7 +48,7 @@
|
||||
"0"
|
||||
],
|
||||
"filament_max_volumetric_speed": [
|
||||
"2"
|
||||
"16"
|
||||
],
|
||||
"filament_minimal_purge_on_wipe_tower": [
|
||||
"15"
|
||||
|
||||
@@ -45,7 +45,7 @@
|
||||
"1"
|
||||
],
|
||||
"filament_max_volumetric_speed": [
|
||||
"3"
|
||||
"6"
|
||||
],
|
||||
"filament_retract_before_wipe": [
|
||||
"100%"
|
||||
|
||||
@@ -54,7 +54,7 @@
|
||||
"0"
|
||||
],
|
||||
"filament_max_volumetric_speed": [
|
||||
"4"
|
||||
"10"
|
||||
],
|
||||
"filament_retract_before_wipe": [
|
||||
"100%"
|
||||
|
||||
@@ -54,7 +54,7 @@
|
||||
"0"
|
||||
],
|
||||
"filament_max_volumetric_speed": [
|
||||
"4"
|
||||
"10"
|
||||
],
|
||||
"filament_retract_before_wipe": [
|
||||
"100%"
|
||||
|
||||
@@ -54,7 +54,7 @@
|
||||
"0"
|
||||
],
|
||||
"filament_max_volumetric_speed": [
|
||||
"4"
|
||||
"10"
|
||||
],
|
||||
"filament_retract_before_wipe": [
|
||||
"100%"
|
||||
|
||||
@@ -54,7 +54,7 @@
|
||||
"0"
|
||||
],
|
||||
"filament_max_volumetric_speed": [
|
||||
"4"
|
||||
"10"
|
||||
],
|
||||
"filament_retract_before_wipe": [
|
||||
"100%"
|
||||
|
||||
@@ -48,7 +48,7 @@
|
||||
"0"
|
||||
],
|
||||
"filament_max_volumetric_speed": [
|
||||
"2"
|
||||
"16"
|
||||
],
|
||||
"filament_minimal_purge_on_wipe_tower": [
|
||||
"15"
|
||||
|
||||
@@ -48,7 +48,7 @@
|
||||
"1"
|
||||
],
|
||||
"filament_max_volumetric_speed": [
|
||||
"3"
|
||||
"6"
|
||||
],
|
||||
"filament_retract_before_wipe": [
|
||||
"100"
|
||||
|
||||
@@ -57,7 +57,7 @@
|
||||
"0"
|
||||
],
|
||||
"filament_max_volumetric_speed": [
|
||||
"1"
|
||||
"10"
|
||||
],
|
||||
"filament_minimal_purge_on_wipe_tower": [
|
||||
"15"
|
||||
|
||||
@@ -51,7 +51,7 @@
|
||||
"0"
|
||||
],
|
||||
"filament_max_volumetric_speed": [
|
||||
"2"
|
||||
"23"
|
||||
],
|
||||
"filament_minimal_purge_on_wipe_tower": [
|
||||
"15"
|
||||
|
||||
@@ -54,7 +54,7 @@
|
||||
"0"
|
||||
],
|
||||
"filament_max_volumetric_speed": [
|
||||
"2"
|
||||
"18"
|
||||
],
|
||||
"filament_minimal_purge_on_wipe_tower": [
|
||||
"15"
|
||||
|
||||
@@ -51,7 +51,7 @@
|
||||
"0"
|
||||
],
|
||||
"filament_max_volumetric_speed": [
|
||||
"2"
|
||||
"18"
|
||||
],
|
||||
"filament_retract_before_wipe": [
|
||||
"100"
|
||||
|
||||
@@ -39,7 +39,7 @@
|
||||
"0"
|
||||
],
|
||||
"filament_max_volumetric_speed": [
|
||||
"2"
|
||||
"21"
|
||||
],
|
||||
"filament_minimal_purge_on_wipe_tower": [
|
||||
"15"
|
||||
|
||||
@@ -45,7 +45,7 @@
|
||||
"0"
|
||||
],
|
||||
"filament_max_volumetric_speed": [
|
||||
"2"
|
||||
"21"
|
||||
],
|
||||
"filament_minimal_purge_on_wipe_tower": [
|
||||
"15"
|
||||
|
||||
@@ -51,7 +51,7 @@
|
||||
"0"
|
||||
],
|
||||
"filament_max_volumetric_speed": [
|
||||
"2"
|
||||
"21"
|
||||
],
|
||||
"filament_minimal_purge_on_wipe_tower": [
|
||||
"15"
|
||||
|
||||
@@ -124,7 +124,7 @@
|
||||
"default_filament_profile": [
|
||||
"Creality HF Generic PLA"
|
||||
],
|
||||
"machine_start_gcode": "; HEADER_BLOCK_START\n; generated by Creality_Print V7.0.1.4212 on [year]-[month]-[day] at [hour]:[minute]:[second]\n; total layer number: [total_layer_count]\n; creality_uuid: <random_uuid>\n; HEADER_BLOCK_END\n; SET PRINT AREA MIN AND MAX COORDINATES TO ENABLE ADAPTIVE PROBING\n; MINX = {first_layer_print_min[0]}\n; MINY = {first_layer_print_min[1]}\n; MAXX = {first_layer_print_max[0]}\n; MAXY = {first_layer_print_max[1]}\nM140 S0\nM104 S0 \nSTART_PRINT EXTRUDER_TEMP=[nozzle_temperature_initial_layer] BED_TEMP=[bed_temperature_initial_layer_single]\nT[initial_no_support_extruder]\nM109 S[nozzle_temperature_initial_layer]\nM204 S2000\nG1 Z3 F600\nM83\nG1 Y150 F12000\nG1 X0 F12000\nG1 Z0.2 F600\nG1 X0 Y150 F6000\nG1 X0 Y0 E15 F6000\nG1 X150 Y0 E15 F6000\nG92 E0\nG1 Z1 F600",
|
||||
"machine_start_gcode": "START_PRINT EXTRUDER_TEMP=[nozzle_temperature_initial_layer] BED_TEMP=[bed_temperature_initial_layer_single]\nT[initial_no_support_extruder]\nM204 S2000\nM104 S[nozzle_temperature_initial_layer]\nG1 Z3 F600\nM83\nG92 E0\nG1 Z1 F600",
|
||||
"machine_end_gcode": "END_PRINT",
|
||||
"scan_first_layer": "0",
|
||||
"thumbnails": [
|
||||
|
||||
@@ -124,7 +124,7 @@
|
||||
"default_filament_profile": [
|
||||
"Creality HF Generic PLA"
|
||||
],
|
||||
"machine_start_gcode": "; HEADER_BLOCK_START\n; generated by Creality_Print V7.0.1.4212 on [year]-[month]-[day] at [hour]:[minute]:[second]\n; total layer number: [total_layer_count]\n; creality_uuid: <random_uuid>\n; HEADER_BLOCK_END\n; SET PRINT AREA MIN AND MAX COORDINATES TO ENABLE ADAPTIVE PROBING\n; MINX = {first_layer_print_min[0]}\n; MINY = {first_layer_print_min[1]}\n; MAXX = {first_layer_print_max[0]}\n; MAXY = {first_layer_print_max[1]}\nM140 S0\nM104 S0 \nSTART_PRINT EXTRUDER_TEMP=[nozzle_temperature_initial_layer] BED_TEMP=[bed_temperature_initial_layer_single]\nT[initial_no_support_extruder]\nM109 S[nozzle_temperature_initial_layer]\nM204 S2000\nG1 Z3 F600\nM83\nG1 Y150 F12000\nG1 X0 F12000\nG1 Z0.2 F600\nG1 X0 Y150 F6000\nG1 X0 Y0 E15 F6000\nG1 X150 Y0 E15 F6000\nG92 E0\nG1 Z1 F600",
|
||||
"machine_start_gcode": "START_PRINT EXTRUDER_TEMP=[nozzle_temperature_initial_layer] BED_TEMP=[bed_temperature_initial_layer_single]\nT[initial_no_support_extruder]\nM204 S2000\nM104 S[nozzle_temperature_initial_layer]\nG1 Z3 F600\nM83\nG92 E0\nG1 Z1 F600",
|
||||
"machine_end_gcode": "END_PRINT",
|
||||
"scan_first_layer": "0",
|
||||
"thumbnails": [
|
||||
|
||||
@@ -124,7 +124,7 @@
|
||||
"default_filament_profile": [
|
||||
"Creality HF Generic PLA"
|
||||
],
|
||||
"machine_start_gcode": "; HEADER_BLOCK_START\n; generated by Creality_Print V7.0.1.4212 on [year]-[month]-[day] at [hour]:[minute]:[second]\n; total layer number: [total_layer_count]\n; creality_uuid: <random_uuid>\n; HEADER_BLOCK_END\n; SET PRINT AREA MIN AND MAX COORDINATES TO ENABLE ADAPTIVE PROBING\n; MINX = {first_layer_print_min[0]}\n; MINY = {first_layer_print_min[1]}\n; MAXX = {first_layer_print_max[0]}\n; MAXY = {first_layer_print_max[1]}\nM140 S0\nM104 S0 \nSTART_PRINT EXTRUDER_TEMP=[nozzle_temperature_initial_layer] BED_TEMP=[bed_temperature_initial_layer_single]\nT[initial_no_support_extruder]\nM109 S[nozzle_temperature_initial_layer]\nM204 S2000\nG1 Z3 F600\nM83\nG1 Y150 F12000\nG1 X0 F12000\nG1 Z0.2 F600\nG1 X0 Y150 F6000\nG1 X0 Y0 E15 F6000\nG1 X150 Y0 E15 F6000\nG92 E0\nG1 Z1 F600",
|
||||
"machine_start_gcode": "START_PRINT EXTRUDER_TEMP=[nozzle_temperature_initial_layer] BED_TEMP=[bed_temperature_initial_layer_single]\nT[initial_no_support_extruder]\nM204 S2000\nM104 S[nozzle_temperature_initial_layer]\nG1 Z3 F600\nM83\nG92 E0\nG1 Z1 F600",
|
||||
"machine_end_gcode": "END_PRINT",
|
||||
"scan_first_layer": "0",
|
||||
"thumbnails": [
|
||||
|
||||
@@ -54,7 +54,7 @@
|
||||
"machine_pause_gcode": "PAUSE",
|
||||
"machine_platform_motion_enable": "1",
|
||||
"machine_ptc_exist": "1",
|
||||
"machine_start_gcode": "; HEADER_BLOCK_START\n; generated by Creality_Print V7.0.1.4212 on [year]-[month]-[day] at [hour]:[minute]:[second]\n; total layer number: [total_layer_count]\n; creality_uuid: <random_uuid>\n; HEADER_BLOCK_END\n; SET PRINT AREA MIN AND MAX COORDINATES TO ENABLE ADAPTIVE PROBING\n; MINX = {first_layer_print_min[0]}\n; MINY = {first_layer_print_min[1]}\n; MAXX = {first_layer_print_max[0]}\n; MAXY = {first_layer_print_max[1]}\nM140 S0\nM104 S0 \nSTART_PRINT EXTRUDER_TEMP=[nozzle_temperature_initial_layer] BED_TEMP=[bed_temperature_initial_layer_single]\nT[initial_no_support_extruder]\nM109 S[nozzle_temperature_initial_layer]\nM204 S2000\nG1 Z3 F600\nM83\nG1 Y150 F12000\nG1 X0 F12000\nG1 Z0.2 F600\nG1 X0 Y150 F6000\nG1 X0 Y0 E15 F6000\nG1 X150 Y0 E15 F6000\nG92 E0\nG1 Z1 F600",
|
||||
"machine_start_gcode": "START_PRINT EXTRUDER_TEMP=[nozzle_temperature_initial_layer] BED_TEMP=[bed_temperature_initial_layer_single]\nT[initial_no_support_extruder]\nM204 S2000\nM104 S[nozzle_temperature_initial_layer]\nG1 Z3 F600\nM83\nG92 E0\nG1 Z1 F600",
|
||||
"machine_unload_filament_time": "0",
|
||||
"manual_filament_change": "0",
|
||||
"nozzle_hrc": "0",
|
||||
|
||||
@@ -123,7 +123,7 @@
|
||||
"default_filament_profile": [
|
||||
"Creality Generic PLA @K1-all"
|
||||
],
|
||||
"machine_start_gcode": "M140 S0\nM104 S0 \nSTART_PRINT EXTRUDER_TEMP=[nozzle_temperature_initial_layer] BED_TEMP=[bed_temperature_initial_layer_single]\nT[initial_no_support_extruder]\nM204 S2000\nG1 Z3 F600\nM83\nG92 E0\nG1 Z1 F600",
|
||||
"machine_start_gcode": "START_PRINT EXTRUDER_TEMP=[nozzle_temperature_initial_layer] BED_TEMP=[bed_temperature_initial_layer_single]\nT[initial_no_support_extruder]\nM204 S2000\nM104 S[nozzle_temperature_initial_layer]\nG1 Z3 F600\nM83\nG92 E0\nG1 Z1 F600",
|
||||
"machine_end_gcode": "END_PRINT",
|
||||
"scan_first_layer": "0",
|
||||
"thumbnails_format": "PNG",
|
||||
|
||||
@@ -53,7 +53,7 @@
|
||||
"machine_min_travel_rate": "0,0",
|
||||
"machine_pause_gcode": "PAUSE",
|
||||
"machine_platform_motion_enable": "1",
|
||||
"machine_start_gcode": "; HEADER_BLOCK_START\n; generated by Creality_Print V7.0.1.4212 on [year]-[month]-[day] at [hour]:[minute]:[second]\n; total layer number: [total_layer_count]\n; creality_uuid: <random_uuid>\n; HEADER_BLOCK_END\n; SET PRINT AREA MIN AND MAX COORDINATES TO ENABLE ADAPTIVE PROBING\n; MINX = {first_layer_print_min[0]}\n; MINY = {first_layer_print_min[1]}\n; MAXX = {first_layer_print_max[0]}\n; MAXY = {first_layer_print_max[1]}\nM140 S0\nM104 S0 \nSTART_PRINT EXTRUDER_TEMP=[nozzle_temperature_initial_layer] BED_TEMP=[bed_temperature_initial_layer_single]\nT[initial_no_support_extruder]\nM109 S[nozzle_temperature_initial_layer]\nM204 S2000\nG1 Z3 F600\nM83\nG1 Y150 F12000\nG1 X0 F12000\nG1 Z0.2 F600\nG1 X0 Y150 F6000\nG1 X0 Y0 E15 F6000\nG1 X150 Y0 E15 F6000\nG92 E0\nG1 Z1 F600",
|
||||
"machine_start_gcode": "START_PRINT EXTRUDER_TEMP=[nozzle_temperature_initial_layer] BED_TEMP=[bed_temperature_initial_layer_single]\nT[initial_no_support_extruder]\nM204 S2000\nM104 S[nozzle_temperature_initial_layer]\nG1 Z3 F600\nM83\nG92 E0\nG1 Z1 F600",
|
||||
"machine_unload_filament_time": "0",
|
||||
"manual_filament_change": "0",
|
||||
"nozzle_hrc": "0",
|
||||
|
||||
@@ -54,7 +54,7 @@
|
||||
"machine_pause_gcode": "PAUSE",
|
||||
"machine_platform_motion_enable": "1",
|
||||
"machine_ptc_exist": "1",
|
||||
"machine_start_gcode": "; HEADER_BLOCK_START\n; generated by Creality_Print V7.0.1.4212 on [year]-[month]-[day] at [hour]:[minute]:[second]\n; total layer number: [total_layer_count]\n; creality_uuid: <random_uuid>\n; HEADER_BLOCK_END\n; SET PRINT AREA MIN AND MAX COORDINATES TO ENABLE ADAPTIVE PROBING\n; MINX = {first_layer_print_min[0]}\n; MINY = {first_layer_print_min[1]}\n; MAXX = {first_layer_print_max[0]}\n; MAXY = {first_layer_print_max[1]}\nM140 S0\nM104 S0 \nSTART_PRINT EXTRUDER_TEMP=[nozzle_temperature_initial_layer] BED_TEMP=[bed_temperature_initial_layer_single]\nT[initial_no_support_extruder]\nM109 S[nozzle_temperature_initial_layer]\nM204 S2000\nG1 Z3 F600\nM83\nG1 Y150 F12000\nG1 X0 F12000\nG1 Z0.2 F600\nG1 X0 Y150 F6000\nG1 X0 Y0 E15 F6000\nG1 X150 Y0 E15 F6000\nG92 E0\nG1 Z1 F600",
|
||||
"machine_start_gcode": "START_PRINT EXTRUDER_TEMP=[nozzle_temperature_initial_layer] BED_TEMP=[bed_temperature_initial_layer_single]\nT[initial_no_support_extruder]\nM204 S2000\nM104 S[nozzle_temperature_initial_layer]\nG1 Z3 F600\nM83\nG92 E0\nG1 Z1 F600",
|
||||
"machine_unload_filament_time": "0",
|
||||
"manual_filament_change": "0",
|
||||
"nozzle_hrc": "0",
|
||||
|
||||
@@ -54,7 +54,7 @@
|
||||
"machine_pause_gcode": "PAUSE",
|
||||
"machine_platform_motion_enable": "1",
|
||||
"machine_ptc_exist": "1",
|
||||
"machine_start_gcode": "; HEADER_BLOCK_START\n; generated by Creality_Print V7.0.1.4212 on [year]-[month]-[day] at [hour]:[minute]:[second]\n; total layer number: [total_layer_count]\n; creality_uuid: <random_uuid>\n; HEADER_BLOCK_END\n; SET PRINT AREA MIN AND MAX COORDINATES TO ENABLE ADAPTIVE PROBING\n; MINX = {first_layer_print_min[0]}\n; MINY = {first_layer_print_min[1]}\n; MAXX = {first_layer_print_max[0]}\n; MAXY = {first_layer_print_max[1]}\nM140 S0\nM104 S0 \nSTART_PRINT EXTRUDER_TEMP=[nozzle_temperature_initial_layer] BED_TEMP=[bed_temperature_initial_layer_single]\nT[initial_no_support_extruder]\nM109 S[nozzle_temperature_initial_layer]\nM204 S2000\nG1 Z3 F600\nM83\nG1 Y150 F12000\nG1 X0 F12000\nG1 Z0.2 F600\nG1 X0 Y150 F6000\nG1 X0 Y0 E15 F6000\nG1 X150 Y0 E15 F6000\nG92 E0\nG1 Z1 F600",
|
||||
"machine_start_gcode": "START_PRINT EXTRUDER_TEMP=[nozzle_temperature_initial_layer] BED_TEMP=[bed_temperature_initial_layer_single]\nT[initial_no_support_extruder]\nM204 S2000\nM104 S[nozzle_temperature_initial_layer]\nG1 Z3 F600\nM83\nG92 E0\nG1 Z1 F600",
|
||||
"machine_unload_filament_time": "0",
|
||||
"manual_filament_change": "0",
|
||||
"nozzle_hrc": "0",
|
||||
|
||||
@@ -88,7 +88,8 @@
|
||||
"5"
|
||||
],
|
||||
"machine_pause_gcode": "PAUSE",
|
||||
"machine_start_gcode": ";SET PRINT AREA MIN AND MAX COORDINATES TO ENABLE ADAPTIVE PROBING\n; MINX = {first_layer_print_min[0]}\n; MINY = {first_layer_print_min[1]}\n; MAXX = {first_layer_print_max[0]}\n; MAXY = {first_layer_print_max[1]}\nM140 S0\nM104 S0 \nSTART_PRINT EXTRUDER_TEMP=[nozzle_temperature_initial_layer] BED_TEMP=[bed_temperature_initial_layer_single]\nT[initial_no_support_extruder]\nM104 S[nozzle_temperature_initial_layer]\nM204 S2000\nG1 Z3 F600\nM83\nG1 Y130 F12000\nG1 X0 F12000\nG1 Z0.2 F600\nG1 X0 Y130 F6000\nG1 E0.8 F300\nG1 X0 Y0 E9 F{filament_max_volumetric_speed[initial_extruder]/0.3*60}\nG1 X130 Y0 E9 F{filament_max_volumetric_speed[initial_extruder]/0.3*60}\nG92 E0\nG1 Z1 F600",
|
||||
"_comment": "the generated by Creality_Print start gcode removes a warning/error message when printing with Orca because it thinks its sliced with CrealityPrint ",
|
||||
"machine_start_gcode": "; HEADER_BLOCK_START\n; generated by Creality_Print V7.0.1.4212 on [year]-[month]-[day] at [hour]:[minute]:[second]\n; total layer number: [total_layer_count]\n; HEADER_BLOCK_END\n; SET PRINT AREA MIN AND MAX COORDINATES TO ENABLE ADAPTIVE PROBING\n; MINX = {first_layer_print_min[0]}\n; MINY = {first_layer_print_min[1]}\n; MAXX = {first_layer_print_max[0]}\n; MAXY = {first_layer_print_max[1]}\nM140 S0\nM104 S0 \nSTART_PRINT EXTRUDER_TEMP=[nozzle_temperature_initial_layer] BED_TEMP=[bed_temperature_initial_layer_single]\nT[initial_no_support_extruder]\nM109 S[nozzle_temperature_initial_layer]\nM204 S2000\nG1 Z3 F600\nM83\nG1 Y150 F12000\nG1 X0 F12000\nG1 Z0.2 F600\nG1 X0 Y150 F6000\nG1 X0 Y0 E15 F6000\nG1 X150 Y0 E15 F6000\nG92 E0\nG1 Z1 F600",
|
||||
"machine_unload_filament_time": "0",
|
||||
"manual_filament_change": "0",
|
||||
"nozzle_type": "hardened_steel",
|
||||
|
||||
@@ -86,8 +86,8 @@
|
||||
"5"
|
||||
],
|
||||
"machine_pause_gcode": "PAUSE",
|
||||
"machine_start_gcode": "M140 S0\nM104 S0 \nSTART_PRINT EXTRUDER_TEMP=[nozzle_temperature_initial_layer] BED_TEMP=[bed_temperature_initial_layer_single]\nT[initial_no_support_extruder]\nM104 S[nozzle_temperature_initial_layer]\nM204 S2000\nG1 Z3 F600\nM83\nG1 Y150 F12000\nG1 X0 F12000\nG1 Z0.2 F600\nG1 X0 Y150 F6000\nG1 E0.8 F300\nG1 X0 Y0 E9 F{filament_max_volumetric_speed[initial_extruder]/0.3*60}\nG1 X150 Y0 E9 F{filament_max_volumetric_speed[initial_extruder]/0.3*60}\nG92 E0\nG1 Z1 F600",
|
||||
"machine_unload_filament_time": "0",
|
||||
"_comment": "the generated by Creality_Print start gcode removes a warning/error message when printing with Orca because it thinks its sliced with CrealityPrint ",
|
||||
"machine_start_gcode": "; HEADER_BLOCK_START\n; generated by Creality_Print V7.0.1.4212 on [year]-[month]-[day] at [hour]:[minute]:[second]\n; total layer number: [total_layer_count]\n; HEADER_BLOCK_END\n; SET PRINT AREA MIN AND MAX COORDINATES TO ENABLE ADAPTIVE PROBING\n; MINX = {first_layer_print_min[0]}\n; MINY = {first_layer_print_min[1]}\n; MAXX = {first_layer_print_max[0]}\n; MAXY = {first_layer_print_max[1]}\nM140 S0\nM104 S0 \nSTART_PRINT EXTRUDER_TEMP=[nozzle_temperature_initial_layer] BED_TEMP=[bed_temperature_initial_layer_single]\nT[initial_no_support_extruder]\nM109 S[nozzle_temperature_initial_layer]\nM204 S2000\nG1 Z3 F600\nM83\nG1 Y150 F12000\nG1 X0 F12000\nG1 Z0.2 F600\nG1 X0 Y150 F6000\nG1 X0 Y0 E15 F6000\nG1 X150 Y0 E15 F6000\nG92 E0\nG1 Z1 F600",
|
||||
"manual_filament_change": "0",
|
||||
"nozzle_type": "hardened_steel",
|
||||
"nozzle_volume": "183",
|
||||
|
||||
@@ -86,7 +86,8 @@
|
||||
"5"
|
||||
],
|
||||
"machine_pause_gcode": "PAUSE",
|
||||
"machine_start_gcode": "M140 S0\nM104 S0 \nSTART_PRINT EXTRUDER_TEMP=[nozzle_temperature_initial_layer] BED_TEMP=[bed_temperature_initial_layer_single]\nT[initial_no_support_extruder]\nM104 S[nozzle_temperature_initial_layer]\nM204 S2000\nG1 Z3 F600\nM83\nG1 Y150 F12000\nG1 X0 F12000\nG1 Z0.2 F600\nG1 X0 Y150 F6000\nG1 E0.8 F300\nG1 X0 Y0 E9 F{filament_max_volumetric_speed[initial_extruder]/0.3*60}\nG1 X150 Y0 E9 F{filament_max_volumetric_speed[initial_extruder]/0.3*60}\nG92 E0\nG1 Z1 F600",
|
||||
"_comment": "the generated by Creality_Print start gcode removes a warning/error message when printing with Orca because it thinks its sliced with CrealityPrint ",
|
||||
"machine_start_gcode": "; HEADER_BLOCK_START\n; generated by Creality_Print V7.0.1.4212 on [year]-[month]-[day] at [hour]:[minute]:[second]\n; total layer number: [total_layer_count]\n; HEADER_BLOCK_END\n; SET PRINT AREA MIN AND MAX COORDINATES TO ENABLE ADAPTIVE PROBING\n; MINX = {first_layer_print_min[0]}\n; MINY = {first_layer_print_min[1]}\n; MAXX = {first_layer_print_max[0]}\n; MAXY = {first_layer_print_max[1]}\nM140 S0\nM104 S0 \nSTART_PRINT EXTRUDER_TEMP=[nozzle_temperature_initial_layer] BED_TEMP=[bed_temperature_initial_layer_single]\nT[initial_no_support_extruder]\nM109 S[nozzle_temperature_initial_layer]\nM204 S2000\nG1 Z3 F600\nM83\nG1 Y150 F12000\nG1 X0 F12000\nG1 Z0.2 F600\nG1 X0 Y150 F6000\nG1 X0 Y0 E15 F6000\nG1 X150 Y0 E15 F6000\nG92 E0\nG1 Z1 F600",
|
||||
"machine_unload_filament_time": "0",
|
||||
"manual_filament_change": "0",
|
||||
"nozzle_type": "hardened_steel",
|
||||
|
||||
@@ -86,7 +86,8 @@
|
||||
"5"
|
||||
],
|
||||
"machine_pause_gcode": "PAUSE",
|
||||
"machine_start_gcode": "M140 S0\nM104 S0 \nSTART_PRINT EXTRUDER_TEMP=[nozzle_temperature_initial_layer] BED_TEMP=[bed_temperature_initial_layer_single]\nT[initial_no_support_extruder]\nM104 S[nozzle_temperature_initial_layer]\nM204 S2000\nG1 Z3 F600\nM83\nG1 Y150 F12000\nG1 X0 F12000\nG1 Z0.2 F600\nG1 X0 Y150 F6000\nG1 E0.8 F300\nG1 X0 Y0 E9 F{filament_max_volumetric_speed[initial_extruder]/0.3*60}\nG1 X150 Y0 E9 F{filament_max_volumetric_speed[initial_extruder]/0.3*60}\nG92 E0\nG1 Z1 F600",
|
||||
"_comment": "the generated by Creality_Print start gcode removes a warning/error message when printing with Orca because it thinks its sliced with CrealityPrint ",
|
||||
"machine_start_gcode": "; HEADER_BLOCK_START\n; generated by Creality_Print V7.0.1.4212 on [year]-[month]-[day] at [hour]:[minute]:[second]\n; total layer number: [total_layer_count]\n; HEADER_BLOCK_END\n; SET PRINT AREA MIN AND MAX COORDINATES TO ENABLE ADAPTIVE PROBING\n; MINX = {first_layer_print_min[0]}\n; MINY = {first_layer_print_min[1]}\n; MAXX = {first_layer_print_max[0]}\n; MAXY = {first_layer_print_max[1]}\nM140 S0\nM104 S0 \nSTART_PRINT EXTRUDER_TEMP=[nozzle_temperature_initial_layer] BED_TEMP=[bed_temperature_initial_layer_single]\nT[initial_no_support_extruder]\nM109 S[nozzle_temperature_initial_layer]\nM204 S2000\nG1 Z3 F600\nM83\nG1 Y150 F12000\nG1 X0 F12000\nG1 Z0.2 F600\nG1 X0 Y150 F6000\nG1 X0 Y0 E15 F6000\nG1 X150 Y0 E15 F6000\nG92 E0\nG1 Z1 F600",
|
||||
"machine_unload_filament_time": "0",
|
||||
"manual_filament_change": "0",
|
||||
"nozzle_type": "hardened_steel",
|
||||
|
||||
@@ -138,7 +138,8 @@
|
||||
"default_filament_profile": [
|
||||
"Creality Generic PLA @K2-all"
|
||||
],
|
||||
"machine_start_gcode": "; HEADER_BLOCK_START\n; generated by Creality_Print V7.0.1.4212 on [year]-[month]-[day] at [hour]:[minute]:[second]\n; total layer number: [total_layer_count]\n; creality_uuid: <random_uuid>\n; HEADER_BLOCK_END\n; SET PRINT AREA MIN AND MAX COORDINATES TO ENABLE ADAPTIVE PROBING\n; MINX = {first_layer_print_min[0]}\n; MINY = {first_layer_print_min[1]}\n; MAXX = {first_layer_print_max[0]}\n; MAXY = {first_layer_print_max[1]}\nM140 S0\nM104 S0 \nSTART_PRINT EXTRUDER_TEMP=[nozzle_temperature_initial_layer] BED_TEMP=[bed_temperature_initial_layer_single]\nT[initial_no_support_extruder]\nM109 S[nozzle_temperature_initial_layer]\nM204 S2000\nG1 Z3 F600\nM83\nG1 Y150 F12000\nG1 X0 F12000\nG1 Z0.2 F600\nG1 X0 Y150 F6000\nG1 X0 Y0 E15 F6000\nG1 X150 Y0 E15 F6000\nG92 E0\nG1 Z1 F600",
|
||||
"_comment": "the generated by Creality_Print start gcode removes a warning/error message when printing with Orca because it thinks its sliced with CrealityPrint ",
|
||||
"machine_start_gcode": "; HEADER_BLOCK_START\n; generated by Creality_Print V7.0.1.4212 on [year]-[month]-[day] at [hour]:[minute]:[second]\n; total layer number: [total_layer_count]\n; HEADER_BLOCK_END\n; SET PRINT AREA MIN AND MAX COORDINATES TO ENABLE ADAPTIVE PROBING\n; MINX = {first_layer_print_min[0]}\n; MINY = {first_layer_print_min[1]}\n; MAXX = {first_layer_print_max[0]}\n; MAXY = {first_layer_print_max[1]}\nM140 S0\nM104 S0 \nSTART_PRINT EXTRUDER_TEMP=[nozzle_temperature_initial_layer] BED_TEMP=[bed_temperature_initial_layer_single]\nT[initial_no_support_extruder]\nM109 S[nozzle_temperature_initial_layer]\nM204 S2000\nG1 Z3 F600\nM83\nG1 Y150 F12000\nG1 X0 F12000\nG1 Z0.2 F600\nG1 X0 Y150 F6000\nG1 X0 Y0 E15 F6000\nG1 X150 Y0 E15 F6000\nG92 E0\nG1 Z1 F600",
|
||||
"machine_end_gcode": "END_PRINT",
|
||||
"machine_pause_gcode": "PAUSE",
|
||||
"change_filament_gcode": "G2 Z{z_after_toolchange + 0.4} I0.86 J0.86 P1 F10000 ; spiral lift a little from second lift\nG1 X0 Y245 F30000\nG1 Z{z_after_toolchange} F600",
|
||||
|
||||
@@ -138,7 +138,8 @@
|
||||
"default_filament_profile": [
|
||||
"Creality Generic PLA @K2-all"
|
||||
],
|
||||
"machine_start_gcode": "; HEADER_BLOCK_START\n; generated by Creality_Print V7.0.1.4212 on [year]-[month]-[day] at [hour]:[minute]:[second]\n; total layer number: [total_layer_count]\n; creality_uuid: <random_uuid>\n; HEADER_BLOCK_END\n; SET PRINT AREA MIN AND MAX COORDINATES TO ENABLE ADAPTIVE PROBING\n; MINX = {first_layer_print_min[0]}\n; MINY = {first_layer_print_min[1]}\n; MAXX = {first_layer_print_max[0]}\n; MAXY = {first_layer_print_max[1]}\nM140 S0\nM104 S0 \nSTART_PRINT EXTRUDER_TEMP=[nozzle_temperature_initial_layer] BED_TEMP=[bed_temperature_initial_layer_single]\nT[initial_no_support_extruder]\nM109 S[nozzle_temperature_initial_layer]\nM204 S2000\nG1 Z3 F600\nM83\nG1 Y150 F12000\nG1 X0 F12000\nG1 Z0.2 F600\nG1 X0 Y150 F6000\nG1 X0 Y0 E15 F6000\nG1 X150 Y0 E15 F6000\nG92 E0\nG1 Z1 F600",
|
||||
"_comment": "the generated by Creality_Print start gcode removes a warning/error message when printing with Orca because it thinks its sliced with CrealityPrint ",
|
||||
"machine_start_gcode": "; HEADER_BLOCK_START\n; generated by Creality_Print V7.0.1.4212 on [year]-[month]-[day] at [hour]:[minute]:[second]\n; total layer number: [total_layer_count]\n; HEADER_BLOCK_END\n; SET PRINT AREA MIN AND MAX COORDINATES TO ENABLE ADAPTIVE PROBING\n; MINX = {first_layer_print_min[0]}\n; MINY = {first_layer_print_min[1]}\n; MAXX = {first_layer_print_max[0]}\n; MAXY = {first_layer_print_max[1]}\nM140 S0\nM104 S0 \nSTART_PRINT EXTRUDER_TEMP=[nozzle_temperature_initial_layer] BED_TEMP=[bed_temperature_initial_layer_single]\nT[initial_no_support_extruder]\nM109 S[nozzle_temperature_initial_layer]\nM204 S2000\nG1 Z3 F600\nM83\nG1 Y150 F12000\nG1 X0 F12000\nG1 Z0.2 F600\nG1 X0 Y150 F6000\nG1 X0 Y0 E15 F6000\nG1 X150 Y0 E15 F6000\nG92 E0\nG1 Z1 F600",
|
||||
"machine_end_gcode": "END_PRINT",
|
||||
"machine_pause_gcode": "PAUSE",
|
||||
"change_filament_gcode": "G2 Z{z_after_toolchange + 0.4} I0.86 J0.86 P1 F10000 ; spiral lift a little from second lift\nG1 X0 Y245 F30000\nG1 Z{z_after_toolchange} F600",
|
||||
|
||||
@@ -138,7 +138,8 @@
|
||||
"default_filament_profile": [
|
||||
"Creality Generic PLA @K2-all"
|
||||
],
|
||||
"machine_start_gcode": "; HEADER_BLOCK_START\n; generated by Creality_Print V7.0.1.4212 on [year]-[month]-[day] at [hour]:[minute]:[second]\n; total layer number: [total_layer_count]\n; creality_uuid: <random_uuid>\n; HEADER_BLOCK_END\n; SET PRINT AREA MIN AND MAX COORDINATES TO ENABLE ADAPTIVE PROBING\n; MINX = {first_layer_print_min[0]}\n; MINY = {first_layer_print_min[1]}\n; MAXX = {first_layer_print_max[0]}\n; MAXY = {first_layer_print_max[1]}\nM140 S0\nM104 S0 \nSTART_PRINT EXTRUDER_TEMP=[nozzle_temperature_initial_layer] BED_TEMP=[bed_temperature_initial_layer_single]\nT[initial_no_support_extruder]\nM109 S[nozzle_temperature_initial_layer]\nM204 S2000\nG1 Z3 F600\nM83\nG1 Y150 F12000\nG1 X0 F12000\nG1 Z0.2 F600\nG1 X0 Y150 F6000\nG1 X0 Y0 E15 F6000\nG1 X150 Y0 E15 F6000\nG92 E0\nG1 Z1 F600",
|
||||
"_comment": "the generated by Creality_Print start gcode removes a warning/error message when printing with Orca because it thinks its sliced with CrealityPrint ",
|
||||
"machine_start_gcode": "; HEADER_BLOCK_START\n; generated by Creality_Print V7.0.1.4212 on [year]-[month]-[day] at [hour]:[minute]:[second]\n; total layer number: [total_layer_count]\n; HEADER_BLOCK_END\n; SET PRINT AREA MIN AND MAX COORDINATES TO ENABLE ADAPTIVE PROBING\n; MINX = {first_layer_print_min[0]}\n; MINY = {first_layer_print_min[1]}\n; MAXX = {first_layer_print_max[0]}\n; MAXY = {first_layer_print_max[1]}\nM140 S0\nM104 S0 \nSTART_PRINT EXTRUDER_TEMP=[nozzle_temperature_initial_layer] BED_TEMP=[bed_temperature_initial_layer_single]\nT[initial_no_support_extruder]\nM109 S[nozzle_temperature_initial_layer]\nM204 S2000\nG1 Z3 F600\nM83\nG1 Y150 F12000\nG1 X0 F12000\nG1 Z0.2 F600\nG1 X0 Y150 F6000\nG1 X0 Y0 E15 F6000\nG1 X150 Y0 E15 F6000\nG92 E0\nG1 Z1 F600",
|
||||
"machine_end_gcode": "END_PRINT",
|
||||
"machine_pause_gcode": "PAUSE",
|
||||
"change_filament_gcode": "G2 Z{z_after_toolchange + 0.4} I0.86 J0.86 P1 F10000 ; spiral lift a little from second lift\nG1 X0 Y245 F30000\nG1 Z{z_after_toolchange} F600",
|
||||
|
||||
@@ -138,7 +138,8 @@
|
||||
"default_filament_profile": [
|
||||
"Creality Generic PLA @K2-all"
|
||||
],
|
||||
"machine_start_gcode": "; HEADER_BLOCK_START\n; generated by Creality_Print V7.0.1.4212 on [year]-[month]-[day] at [hour]:[minute]:[second]\n; total layer number: [total_layer_count]\n; creality_uuid: <random_uuid>\n; HEADER_BLOCK_END\n; SET PRINT AREA MIN AND MAX COORDINATES TO ENABLE ADAPTIVE PROBING\n; MINX = {first_layer_print_min[0]}\n; MINY = {first_layer_print_min[1]}\n; MAXX = {first_layer_print_max[0]}\n; MAXY = {first_layer_print_max[1]}\nM140 S0\nM104 S0 \nSTART_PRINT EXTRUDER_TEMP=[nozzle_temperature_initial_layer] BED_TEMP=[bed_temperature_initial_layer_single]\nT[initial_no_support_extruder]\nM109 S[nozzle_temperature_initial_layer]\nM204 S2000\nG1 Z3 F600\nM83\nG1 Y150 F12000\nG1 X0 F12000\nG1 Z0.2 F600\nG1 X0 Y150 F6000\nG1 X0 Y0 E15 F6000\nG1 X150 Y0 E15 F6000\nG92 E0\nG1 Z1 F600",
|
||||
"_comment": "the generated by Creality_Print start gcode removes a warning/error message when printing with Orca because it thinks its sliced with CrealityPrint ",
|
||||
"machine_start_gcode": "; HEADER_BLOCK_START\n; generated by Creality_Print V7.0.1.4212 on [year]-[month]-[day] at [hour]:[minute]:[second]\n; total layer number: [total_layer_count]\n; HEADER_BLOCK_END\n; SET PRINT AREA MIN AND MAX COORDINATES TO ENABLE ADAPTIVE PROBING\n; MINX = {first_layer_print_min[0]}\n; MINY = {first_layer_print_min[1]}\n; MAXX = {first_layer_print_max[0]}\n; MAXY = {first_layer_print_max[1]}\nM140 S0\nM104 S0 \nSTART_PRINT EXTRUDER_TEMP=[nozzle_temperature_initial_layer] BED_TEMP=[bed_temperature_initial_layer_single]\nT[initial_no_support_extruder]\nM109 S[nozzle_temperature_initial_layer]\nM204 S2000\nG1 Z3 F600\nM83\nG1 Y150 F12000\nG1 X0 F12000\nG1 Z0.2 F600\nG1 X0 Y150 F6000\nG1 X0 Y0 E15 F6000\nG1 X150 Y0 E15 F6000\nG92 E0\nG1 Z1 F600",
|
||||
"machine_end_gcode": "END_PRINT",
|
||||
"machine_pause_gcode": "PAUSE",
|
||||
"change_filament_gcode": "G2 Z{z_after_toolchange + 0.4} I0.86 J0.86 P1 F10000 ; spiral lift a little from second lift\nG1 X0 Y245 F30000\nG1 Z{z_after_toolchange} F600",
|
||||
|
||||
@@ -138,8 +138,8 @@
|
||||
"default_filament_profile": [
|
||||
"Creality Generic PLA @K2-all"
|
||||
],
|
||||
"machine_start_gcode": ";SET PRINT AREA MIN AND MAX COORDINATES TO ENABLE ADAPTIVE PROBING\n; MINX = {first_layer_print_min[0]}\n; MINY = {first_layer_print_min[1]}\n; MAXX = {first_layer_print_max[0]}\n; MAXY = {first_layer_print_max[1]}\n\nM140 S0\nM104 S0 \nSTART_PRINT EXTRUDER_TEMP=[nozzle_temperature_initial_layer] BED_TEMP=[bed_temperature_initial_layer_single]\nT[initial_no_support_extruder]\nM104 S[nozzle_temperature_initial_layer]\nM204 S2000\nG1 Z3 F600\nM83\nG1 Y150 F12000\nG1 X0 F12000\nG1 Z0.2 F600\nG1 X0 Y150 F6000\nG1 E0.8 F300\nG1 X0 Y0 E9 F{filament_max_volumetric_speed[initial_extruder]/0.3*60}\nG1 X150 Y0 E9 F{filament_max_volumetric_speed[initial_extruder]/0.3*60}\nG92 E0\nG1 Z1 F600",
|
||||
"machine_end_gcode": "END_PRINT",
|
||||
"_comment": "the generated by Creality_Print start gcode removes a warning/error message when printing with Orca because it thinks its sliced with CrealityPrint ",
|
||||
"machine_start_gcode": "; HEADER_BLOCK_START\n; generated by Creality_Print V7.0.1.4212 on [year]-[month]-[day] at [hour]:[minute]:[second]\n; total layer number: [total_layer_count]\n; HEADER_BLOCK_END\n; SET PRINT AREA MIN AND MAX COORDINATES TO ENABLE ADAPTIVE PROBING\n; MINX = {first_layer_print_min[0]}\n; MINY = {first_layer_print_min[1]}\n; MAXX = {first_layer_print_max[0]}\n; MAXY = {first_layer_print_max[1]}\nM140 S0\nM104 S0 \nSTART_PRINT EXTRUDER_TEMP=[nozzle_temperature_initial_layer] BED_TEMP=[bed_temperature_initial_layer_single]\nT[initial_no_support_extruder]\nM109 S[nozzle_temperature_initial_layer]\nM204 S2000\nG1 Z3 F600\nM83\nG1 Y150 F12000\nG1 X0 F12000\nG1 Z0.2 F600\nG1 X0 Y150 F6000\nG1 X0 Y0 E15 F6000\nG1 X150 Y0 E15 F6000\nG92 E0\nG1 Z1 F600",
|
||||
"machine_pause_gcode": "PAUSE",
|
||||
"change_filament_gcode": "G2 Z{z_after_toolchange + 0.4} I0.86 J0.86 P1 F10000 ; spiral lift a little from second lift\nG1 X0 Y245 F30000\nG1 Z{z_after_toolchange} F600",
|
||||
"before_layer_change_gcode": ";BEFORE_LAYER_CHANGE\n;[layer_z]\nG92 E0\n",
|
||||
|
||||
@@ -136,7 +136,8 @@
|
||||
"default_filament_profile": [
|
||||
"Creality Generic PLA @K2-all"
|
||||
],
|
||||
"machine_start_gcode": "M140 S0\nM104 S0 \nSTART_PRINT EXTRUDER_TEMP=[nozzle_temperature_initial_layer] BED_TEMP=[bed_temperature_initial_layer_single]\nT[initial_no_support_extruder]\nM104 S[nozzle_temperature_initial_layer]\nM204 S2000\nG1 Z3 F600\nM83\nG1 Y150 F12000\nG1 X0 F12000\nG1 Z0.2 F600\nG1 X0 Y150 F6000\nG1 E0.8 F300\nG1 X0 Y0 E9 F{filament_max_volumetric_speed[initial_extruder]/0.3*60}\nG1 X150 Y0 E9 F{filament_max_volumetric_speed[initial_extruder]/0.3*60}\nG92 E0\nG1 Z1 F600",
|
||||
"_comment": "the generated by Creality_Print start gcode removes a warning/error message when printing with Orca because it thinks its sliced with CrealityPrint ",
|
||||
"machine_start_gcode": "; HEADER_BLOCK_START\n; generated by Creality_Print V7.0.1.4212 on [year]-[month]-[day] at [hour]:[minute]:[second]\n; total layer number: [total_layer_count]\n; HEADER_BLOCK_END\n; SET PRINT AREA MIN AND MAX COORDINATES TO ENABLE ADAPTIVE PROBING\n; MINX = {first_layer_print_min[0]}\n; MINY = {first_layer_print_min[1]}\n; MAXX = {first_layer_print_max[0]}\n; MAXY = {first_layer_print_max[1]}\nM140 S0\nM104 S0 \nSTART_PRINT EXTRUDER_TEMP=[nozzle_temperature_initial_layer] BED_TEMP=[bed_temperature_initial_layer_single]\nT[initial_no_support_extruder]\nM109 S[nozzle_temperature_initial_layer]\nM204 S2000\nG1 Z3 F600\nM83\nG1 Y150 F12000\nG1 X0 F12000\nG1 Z0.2 F600\nG1 X0 Y150 F6000\nG1 X0 Y0 E15 F6000\nG1 X150 Y0 E15 F6000\nG92 E0\nG1 Z1 F600",
|
||||
"machine_end_gcode": "{if print_sequence == \"by object\"}\nG91\nG1 X2 Y2 Z1 F24000\nG90\nG1 Z{max_layer_z+2} F600\n{endif}\nEND_PRINT",
|
||||
"machine_pause_gcode": "PAUSE",
|
||||
"change_filament_gcode": "G2 Z{z_after_toolchange + 0.4} I0.86 J0.86 P1 F10000 ; spiral lift a little from second lift\n{if print_sequence == \"by object\"}\nG0 Z{max_layer_z + 0.8} F900\n{endif}\nG1 X0 Y160 F30000\nG1 Z{z_after_toolchange} F600",
|
||||
|
||||
@@ -136,7 +136,8 @@
|
||||
"default_filament_profile": [
|
||||
"Creality Generic PLA @K2-all"
|
||||
],
|
||||
"machine_start_gcode": "M140 S0\nM104 S0 \nSTART_PRINT EXTRUDER_TEMP=[nozzle_temperature_initial_layer] BED_TEMP=[bed_temperature_initial_layer_single]\nT[initial_no_support_extruder]\nM104 S[nozzle_temperature_initial_layer]\nM204 S2000\nG1 Z3 F600\nM83\nG1 Y150 F12000\nG1 X0 F12000\nG1 Z0.2 F600\nG1 X0 Y150 F6000\nG1 E0.8 F300\nG1 X0 Y0 E9 F{filament_max_volumetric_speed[initial_extruder]/0.3*60}\nG1 X150 Y0 E9 F{filament_max_volumetric_speed[initial_extruder]/0.3*60}\nG92 E0\nG1 Z1 F600",
|
||||
"_comment": "the generated by Creality_Print start gcode removes a warning/error message when printing with Orca because it thinks its sliced with CrealityPrint ",
|
||||
"machine_start_gcode": "; HEADER_BLOCK_START\n; generated by Creality_Print V7.0.1.4212 on [year]-[month]-[day] at [hour]:[minute]:[second]\n; total layer number: [total_layer_count]\n; HEADER_BLOCK_END\n; SET PRINT AREA MIN AND MAX COORDINATES TO ENABLE ADAPTIVE PROBING\n; MINX = {first_layer_print_min[0]}\n; MINY = {first_layer_print_min[1]}\n; MAXX = {first_layer_print_max[0]}\n; MAXY = {first_layer_print_max[1]}\nM140 S0\nM104 S0 \nSTART_PRINT EXTRUDER_TEMP=[nozzle_temperature_initial_layer] BED_TEMP=[bed_temperature_initial_layer_single]\nT[initial_no_support_extruder]\nM109 S[nozzle_temperature_initial_layer]\nM204 S2000\nG1 Z3 F600\nM83\nG1 Y150 F12000\nG1 X0 F12000\nG1 Z0.2 F600\nG1 X0 Y150 F6000\nG1 X0 Y0 E15 F6000\nG1 X150 Y0 E15 F6000\nG92 E0\nG1 Z1 F600",
|
||||
"machine_end_gcode": "{if print_sequence == \"by object\"}\nG91\nG1 X2 Y2 Z1 F24000\nG90\nG1 Z{max_layer_z+2} F600\n{endif}\nEND_PRINT",
|
||||
"machine_pause_gcode": "PAUSE",
|
||||
"change_filament_gcode": "G2 Z{z_after_toolchange + 0.4} I0.86 J0.86 P1 F10000 ; spiral lift a little from second lift\n{if print_sequence == \"by object\"}\nG0 Z{max_layer_z + 0.8} F900\n{endif}\nG1 X0 Y160 F30000\nG1 Z{z_after_toolchange} F600",
|
||||
|
||||
@@ -136,7 +136,8 @@
|
||||
"default_filament_profile": [
|
||||
"Creality Generic PLA @K2-all"
|
||||
],
|
||||
"machine_start_gcode": "M140 S0\nM104 S0 \nSTART_PRINT EXTRUDER_TEMP=[nozzle_temperature_initial_layer] BED_TEMP=[bed_temperature_initial_layer_single]\nT[initial_no_support_extruder]\nM104 S[nozzle_temperature_initial_layer]\nM204 S2000\nG1 Z3 F600\nM83\nG1 Y150 F12000\nG1 X0 F12000\nG1 Z0.2 F600\nG1 X0 Y150 F6000\nG1 E0.8 F300\nG1 X0 Y0 E9 F{filament_max_volumetric_speed[initial_extruder]/0.3*60}\nG1 X150 Y0 E9 F{filament_max_volumetric_speed[initial_extruder]/0.3*60}\nG92 E0\nG1 Z1 F600",
|
||||
"_comment": "the generated by Creality_Print start gcode removes a warning/error message when printing with Orca because it thinks its sliced with CrealityPrint ",
|
||||
"machine_start_gcode": "; HEADER_BLOCK_START\n; generated by Creality_Print V7.0.1.4212 on [year]-[month]-[day] at [hour]:[minute]:[second]\n; total layer number: [total_layer_count]\n; HEADER_BLOCK_END\n; SET PRINT AREA MIN AND MAX COORDINATES TO ENABLE ADAPTIVE PROBING\n; MINX = {first_layer_print_min[0]}\n; MINY = {first_layer_print_min[1]}\n; MAXX = {first_layer_print_max[0]}\n; MAXY = {first_layer_print_max[1]}\nM140 S0\nM104 S0 \nSTART_PRINT EXTRUDER_TEMP=[nozzle_temperature_initial_layer] BED_TEMP=[bed_temperature_initial_layer_single]\nT[initial_no_support_extruder]\nM109 S[nozzle_temperature_initial_layer]\nM204 S2000\nG1 Z3 F600\nM83\nG1 Y150 F12000\nG1 X0 F12000\nG1 Z0.2 F600\nG1 X0 Y150 F6000\nG1 X0 Y0 E15 F6000\nG1 X150 Y0 E15 F6000\nG92 E0\nG1 Z1 F600",
|
||||
"machine_end_gcode": "{if print_sequence == \"by object\"}\nG91\nG1 X2 Y2 Z1 F24000\nG90\nG1 Z{max_layer_z+2} F600\n{endif}\nEND_PRINT",
|
||||
"machine_pause_gcode": "PAUSE",
|
||||
"change_filament_gcode": "G2 Z{z_after_toolchange + 0.4} I0.86 J0.86 P1 F10000 ; spiral lift a little from second lift\n{if print_sequence == \"by object\"}\nG0 Z{max_layer_z + 0.8} F900\n{endif}\nG1 X0 Y160 F30000\nG1 Z{z_after_toolchange} F600",
|
||||
|
||||
@@ -53,7 +53,8 @@
|
||||
"machine_min_travel_rate": "0,0",
|
||||
"machine_pause_gcode": "PAUSE",
|
||||
"machine_platform_motion_enable": "1",
|
||||
"machine_start_gcode": "M140 S0\nM104 S0 \nSTART_PRINT EXTRUDER_TEMP=[nozzle_temperature_initial_layer] BED_TEMP=[bed_temperature_initial_layer_single]\nT[initial_no_support_extruder]\nM104 S[nozzle_temperature_initial_layer]\nM204 S2000\nG1 Z3 F600\nM83\nG1 Y150 F12000\nG1 X0 F12000\nG1 Z0.2 F600\nG1 X0 Y150 F6000\nG1 E0.8 F300\nG1 X0 Y0 E9 F{filament_max_volumetric_speed[initial_extruder]/0.3*60}\nG1 X150 Y0 E9 F{filament_max_volumetric_speed[initial_extruder]/0.3*60}\nG92 E0\nG1 Z1 F600",
|
||||
"_comment": "the generated by Creality_Print start gcode removes a warning/error message when printing with Orca because it thinks its sliced with CrealityPrint ",
|
||||
"machine_start_gcode": "; HEADER_BLOCK_START\n; generated by Creality_Print V7.0.1.4212 on [year]-[month]-[day] at [hour]:[minute]:[second]\n; total layer number: [total_layer_count]\n; HEADER_BLOCK_END\n; SET PRINT AREA MIN AND MAX COORDINATES TO ENABLE ADAPTIVE PROBING\n; MINX = {first_layer_print_min[0]}\n; MINY = {first_layer_print_min[1]}\n; MAXX = {first_layer_print_max[0]}\n; MAXY = {first_layer_print_max[1]}\nM140 S0\nM104 S0 \nSTART_PRINT EXTRUDER_TEMP=[nozzle_temperature_initial_layer] BED_TEMP=[bed_temperature_initial_layer_single]\nT[initial_no_support_extruder]\nM109 S[nozzle_temperature_initial_layer]\nM204 S2000\nG1 Z3 F600\nM83\nG1 Y150 F12000\nG1 X0 F12000\nG1 Z0.2 F600\nG1 X0 Y150 F6000\nG1 X0 Y0 E15 F6000\nG1 X150 Y0 E15 F6000\nG92 E0\nG1 Z1 F600",
|
||||
"machine_unload_filament_time": "0",
|
||||
"manual_filament_change": "0",
|
||||
"nozzle_hrc": "0",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "Elegoo",
|
||||
"version": "02.04.00.02",
|
||||
"version": "02.04.00.04",
|
||||
"force_update": "0",
|
||||
"description": "Elegoo configurations",
|
||||
"machine_model_list": [
|
||||
|
||||
@@ -62,7 +62,7 @@
|
||||
"change_filament_gcode": "",
|
||||
"machine_pause_gcode": "M600",
|
||||
"support_multi_filament": "0",
|
||||
"layer_change_gcode": "SET_PRINT_STATS_INFO CURRENT_LAYER={layer_num + 1}",
|
||||
"machine_start_gcode": ";;===== date: 20251011 =====================\n;printer_model:[printer_model]\n;initial_filament:{filament_type[initial_extruder]}\n;curr_bed_type:{curr_bed_type}\n;bed_level_temp:[bed_temperature_initial_layer_single]\nM400 ; wait for buffer to clear\nM220 S100 ;Set the feed speed to 100%\nM221 S100 ;Set the flow rate to 100%\nM104 S140\nM140 S[bed_temperature_initial_layer_single]\nG90\nG28 ;home\nM729 ;Clean Nozzle\nM190 S[bed_temperature_initial_layer_single]\n\n\n;=============turn on fans to prevent PLA jamming=================\n{if filament_type[initial_no_support_extruder]==\"PLA\"}\n {if (bed_temperature[initial_no_support_extruder] >50)||(bed_temperature_initial_layer[initial_no_support_extruder] >50)}\n M106 P3 S255\n {elsif (bed_temperature[initial_no_support_extruder] >45)||(bed_temperature_initial_layer[initial_no_support_extruder] >45)}\n M106 P3 S180\n {endif};Prevent PLA from jamming\n{endif}\n\n;enable_pressure_advance:{enable_pressure_advance[initial_extruder]}\n;This value is called if pressure advance is enabled\n{if enable_pressure_advance[initial_extruder] == \"true\"}\nSET_PRESSURE_ADVANCE ADVANCE=[pressure_advance] ;\nM400\n{endif}\nM204 S{min(20000,max(1000,outer_wall_acceleration))} ;Call exterior wall print acceleration\n\n\nG1 X{print_bed_max[0]*0.5} Y-1.2 F20000\nG1 Z0.3 F900\nM109 S[nozzle_temperature_initial_layer]\nM83\nG92 E0 ;Reset Extruder\nG1 F{min(6000, max(900, filament_max_volumetric_speed[initial_no_support_extruder]/0.5/0.3*60))} \nG1 X-1.2 E10.156 ;Draw the first line\nG1 Y98.8 E7.934\nG1 X-0.5 Y100 E0.1\nG1 Y-0.3 E7.934\nG1 X{print_bed_max[0]*0.5-50} E6.284\nG1 F{0.2*min(12000, max(1200, filament_max_volumetric_speed[initial_no_support_extruder]/0.5/0.3*60))} \nG1 X{print_bed_max[0]*0.5-30} E2\nG1 F{min(12000, max(1200, filament_max_volumetric_speed[initial_no_support_extruder]/0.5/0.3*60))} \nG1 X{print_bed_max[0]*0.5-10} E2\nG1 F{0.2*min(12000, max(1200, filament_max_volumetric_speed[initial_no_support_extruder]/0.5/0.3*60))} \nG1 X{print_bed_max[0]*0.5+10} E2\nG1 F{min(12000, max(1200, filament_max_volumetric_speed[initial_no_support_extruder]/0.5/0.3*60))} \nG1 X{print_bed_max[0]*0.5+30} E2\nG1 F{min(12000, max(1200, filament_max_volumetric_speed[initial_no_support_extruder]/0.5/0.3*60))} \nG1 X{print_bed_max[0]*0.5+50} E2\n;End PA test.\n\n\nG3 I-1 J0 Z0.6 F1200.0 ;Move to side a little\nG1 F20000\nG92 E0 ;Reset Extruder\nSET_PRINT_STATS_INFO TOTAL_LAYER=[total_layer_count]\n;LAYER_COUNT:[total_layer_count]\n;LAYER:0",
|
||||
"layer_change_gcode": "SET_PRINT_STATS_INFO TOTAL_LAYER=[total_layer_count] CURRENT_LAYER={layer_num+1}",
|
||||
"machine_start_gcode": ";;===== date: 20251011 =====================\n;printer_model:[printer_model]\n;initial_filament:{filament_type[initial_extruder]}\n;curr_bed_type:{curr_bed_type}\n;bed_level_temp:[bed_temperature_initial_layer_single]\nM400 ; wait for buffer to clear\nM220 S100 ;Set the feed speed to 100%\nM221 S100 ;Set the flow rate to 100%\nM104 S140\nM140 S[bed_temperature_initial_layer_single]\nG90\nG28 ;home\nM729 ;Clean Nozzle\nM190 S[bed_temperature_initial_layer_single]\n\n\n;=============turn on fans to prevent PLA jamming=================\n{if filament_type[initial_no_support_extruder]==\"PLA\"}\n {if (bed_temperature[initial_no_support_extruder] >50)||(bed_temperature_initial_layer[initial_no_support_extruder] >50)}\n M106 P3 S255\n {elsif (bed_temperature[initial_no_support_extruder] >45)||(bed_temperature_initial_layer[initial_no_support_extruder] >45)}\n M106 P3 S180\n {endif};Prevent PLA from jamming\n{endif}\n\n;enable_pressure_advance:{enable_pressure_advance[initial_extruder]}\n;This value is called if pressure advance is enabled\n{if enable_pressure_advance[initial_extruder] == \"true\"}\nSET_PRESSURE_ADVANCE ADVANCE=[pressure_advance] ;\nM400\n{endif}\nM204 S{min(20000,max(1000,outer_wall_acceleration))} ;Call exterior wall print acceleration\n\n\nG1 X{print_bed_max[0]*0.5} Y-1.2 F20000\nG1 Z0.3 F900\nM109 S[nozzle_temperature_initial_layer]\nM83\nG92 E0 ;Reset Extruder\nG1 F{min(6000, max(900, filament_max_volumetric_speed[initial_no_support_extruder]/0.5/0.3*60))} \nG1 X-1.2 E10.156 ;Draw the first line\nG1 Y98.8 E7.934\nG1 X-0.5 Y100 E0.1\nG1 Y-0.3 E7.934\nG1 X{print_bed_max[0]*0.5-50} E6.284\nG1 F{0.2*min(12000, max(1200, filament_max_volumetric_speed[initial_no_support_extruder]/0.5/0.3*60))} \nG1 X{print_bed_max[0]*0.5-30} E2\nG1 F{min(12000, max(1200, filament_max_volumetric_speed[initial_no_support_extruder]/0.5/0.3*60))} \nG1 X{print_bed_max[0]*0.5-10} E2\nG1 F{0.2*min(12000, max(1200, filament_max_volumetric_speed[initial_no_support_extruder]/0.5/0.3*60))} \nG1 X{print_bed_max[0]*0.5+10} E2\nG1 F{min(12000, max(1200, filament_max_volumetric_speed[initial_no_support_extruder]/0.5/0.3*60))} \nG1 X{print_bed_max[0]*0.5+30} E2\nG1 F{min(12000, max(1200, filament_max_volumetric_speed[initial_no_support_extruder]/0.5/0.3*60))} \nG1 X{print_bed_max[0]*0.5+50} E2\n;End PA test.\n\n\nG3 I-1 J0 Z0.6 F1200.0 ;Move to side a little\nG1 F20000\nG92 E0 ;Reset Extruder\nSET_PRINT_STATS_INFO TOTAL_LAYER=[total_layer_count] CURRENT_LAYER=0\n;LAYER_COUNT:[total_layer_count]\n;LAYER:0",
|
||||
"machine_end_gcode": ";===== date: 20250603 =====================\nM400 ; wait for buffer to clear\nM140 S0 ;Turn-off bed\nM106 S255 ;Cooling nozzle\nM83\nG92 E0 ; zero the extruder\nG1 E-0.8 F1800\nG2 I1 J0 Z{max_layer_z+0.5} F3000 ; lower z a little\nG90\n{if max_layer_z > 100}G1 Z{min(max_layer_z+50, printable_height+0.5)} F20000{else}G1 Z100 F20000 {endif}; Move print head up \nM204 S5000\nM400\nM83\nG1 X202 F20000\nM400\nG1 Y250 F20000\nG1 Y264.5 F1200\nM400\nG92 E0\nM104 S0 ;Turn-off hotend\nM140 S0 ;Turn-off bed\nM106 S0 ; turn off fan\nM106 P2 S0 ; turn off remote part cooling fan\nM106 P3 S0 ; turn off chamber cooling fan\nM84 ;Disable all steppers"
|
||||
}
|
||||
|
||||
@@ -66,7 +66,7 @@
|
||||
"change_filament_gcode": "\n;==========CC_CHANGE_FILAMENT_GCODE_CCB==========\n;===== date: 2025-12-04-001 =====================\nM106 S0\nM106 P2 S0\nG1 Z{min(max_layer_z+3, printable_height+0.5)} F1200\nM6211 T[next_extruder] L[flush_length] M{old_filament_e_feedrate} N{new_filament_e_feedrate} Q[old_filament_temp] R[nozzle_temperature_range_high] S[new_filament_temp]\nT[next_extruder]\n;==========CC_CHANGE_FILAMENT_GCODE_CCE==========",
|
||||
"machine_pause_gcode": "M600",
|
||||
"support_multi_filament": "1",
|
||||
"layer_change_gcode": "SET_PRINT_STATS_INFO CURRENT_LAYER={layer_num + 1}",
|
||||
"machine_start_gcode": ";===== CC_START_GCODE ================\n;===== date: 2026-04-28-001 =====================\n;printer_model:[printer_model]\n;initial_filament:{filament_type[initial_extruder]}\n;curr_bed_type:{curr_bed_type}\n;bed_level_temp:[bed_temperature_initial_layer_single]\nM400 ; wait for buffer to clear\nM220 S100 ;Set the feed speed to 100%\nM221 S100 ;Set the flow rate to 100%\nM104 S140\nM140 S[bed_temperature_initial_layer_single]\nG90\nM83\nG28 ;home\nM729 ;Clean Nozzle\nM190 S[bed_temperature_initial_layer_single]\n\nM109 S[nozzle_temperature_initial_layer]\nM6211 A1 L200 T[initial_no_support_extruder] Q[nozzle_temperature_initial_layer] R[nozzle_temperature_initial_layer] S[nozzle_temperature_initial_layer]\nT[initial_no_support_extruder]\n\n;=============turn on fans to prevent PLA jamming=================\n{if filament_type[initial_no_support_extruder]==\"PLA\"}\n {if (bed_temperature[initial_no_support_extruder] >50)||(bed_temperature_initial_layer[initial_no_support_extruder] >50)}\n M106 P3 S255\n {elsif (bed_temperature[initial_no_support_extruder] >45)||(bed_temperature_initial_layer[initial_no_support_extruder] >45)}\n M106 P3 S180\n {endif};Prevent PLA from jamming\n{endif}\n\n;enable_pressure_advance:{enable_pressure_advance[initial_extruder]}\n;This value is called if pressure advance is enabled\n{if enable_pressure_advance[initial_extruder] == \"true\"}\nSET_PRESSURE_ADVANCE ADVANCE=[pressure_advance] ;\nM400\n{endif}\nM204 S{min(20000,max(1000,outer_wall_acceleration))} ;Call exterior wall print acceleration\n\n\nG1 X{print_bed_max[0]*0.5} Y-1.2 F20000\nG1 Z0.3 F900\n\n\nG90\nM83\nG92 E0 ;Reset Extruder\nG1 F{min(6000, max(900, filament_max_volumetric_speed[initial_no_support_extruder]/0.5/0.3*60))} \nG1 X0 E10.156 ;Draw the first line\nG1 Y98.8 E7.934\nG1 X0.9 Y100 E0.1\nG1 Y-0.3 E7.934\nG1 X{print_bed_max[0]*0.5-50} E6.284\nG1 F{0.2*min(12000, max(1200, filament_max_volumetric_speed[initial_no_support_extruder]/0.5/0.3*60))} \nG1 X{print_bed_max[0]*0.5-30} E2\nG1 F{min(12000, max(1200, filament_max_volumetric_speed[initial_no_support_extruder]/0.5/0.3*60))} \nG1 X{print_bed_max[0]*0.5-10} E2\nG1 F{0.2*min(12000, max(1200, filament_max_volumetric_speed[initial_no_support_extruder]/0.5/0.3*60))} \nG1 X{print_bed_max[0]*0.5+10} E2\nG1 F{min(12000, max(1200, filament_max_volumetric_speed[initial_no_support_extruder]/0.5/0.3*60))} \nG1 X{print_bed_max[0]*0.5+30} E2\nG1 F{min(12000, max(1200, filament_max_volumetric_speed[initial_no_support_extruder]/0.5/0.3*60))} \nG1 X{print_bed_max[0]*0.5+50} E2\n;End PA test.\n\n\nG3 I-1 J0 Z0.6 F1200.0 ;Move to side a little\nG1 F20000\nG92 E0 ;Reset Extruder\nSET_PRINT_STATS_INFO TOTAL_LAYER=[total_layer_count]\n;LAYER_COUNT:[total_layer_count]\n;LAYER:0",
|
||||
"layer_change_gcode": "SET_PRINT_STATS_INFO TOTAL_LAYER=[total_layer_count] CURRENT_LAYER={layer_num+1}",
|
||||
"machine_start_gcode": ";===== CC_START_GCODE ================\n;===== date: 2026-04-28-001 =====================\n;printer_model:[printer_model]\n;initial_filament:{filament_type[initial_extruder]}\n;curr_bed_type:{curr_bed_type}\n;bed_level_temp:[bed_temperature_initial_layer_single]\nM400 ; wait for buffer to clear\nM220 S100 ;Set the feed speed to 100%\nM221 S100 ;Set the flow rate to 100%\nM104 S140\nM140 S[bed_temperature_initial_layer_single]\nG90\nM83\nG28 ;home\nM729 ;Clean Nozzle\nM190 S[bed_temperature_initial_layer_single]\n\nM109 S[nozzle_temperature_initial_layer]\nM6211 A1 L200 T[initial_no_support_extruder] Q[nozzle_temperature_initial_layer] R[nozzle_temperature_initial_layer] S[nozzle_temperature_initial_layer]\nT[initial_no_support_extruder]\n\n;=============turn on fans to prevent PLA jamming=================\n{if filament_type[initial_no_support_extruder]==\"PLA\"}\n {if (bed_temperature[initial_no_support_extruder] >50)||(bed_temperature_initial_layer[initial_no_support_extruder] >50)}\n M106 P3 S255\n {elsif (bed_temperature[initial_no_support_extruder] >45)||(bed_temperature_initial_layer[initial_no_support_extruder] >45)}\n M106 P3 S180\n {endif};Prevent PLA from jamming\n{endif}\n\n;enable_pressure_advance:{enable_pressure_advance[initial_extruder]}\n;This value is called if pressure advance is enabled\n{if enable_pressure_advance[initial_extruder] == \"true\"}\nSET_PRESSURE_ADVANCE ADVANCE=[pressure_advance] ;\nM400\n{endif}\nM204 S{min(20000,max(1000,outer_wall_acceleration))} ;Call exterior wall print acceleration\n\n\nG1 X{print_bed_max[0]*0.5} Y-1.2 F20000\nG1 Z0.3 F900\n\n\nG90\nM83\nG92 E0 ;Reset Extruder\nG1 F{min(6000, max(900, filament_max_volumetric_speed[initial_no_support_extruder]/0.5/0.3*60))} \nG1 X0 E10.156 ;Draw the first line\nG1 Y98.8 E7.934\nG1 X0.9 Y100 E0.1\nG1 Y-0.3 E7.934\nG1 X{print_bed_max[0]*0.5-50} E6.284\nG1 F{0.2*min(12000, max(1200, filament_max_volumetric_speed[initial_no_support_extruder]/0.5/0.3*60))} \nG1 X{print_bed_max[0]*0.5-30} E2\nG1 F{min(12000, max(1200, filament_max_volumetric_speed[initial_no_support_extruder]/0.5/0.3*60))} \nG1 X{print_bed_max[0]*0.5-10} E2\nG1 F{0.2*min(12000, max(1200, filament_max_volumetric_speed[initial_no_support_extruder]/0.5/0.3*60))} \nG1 X{print_bed_max[0]*0.5+10} E2\nG1 F{min(12000, max(1200, filament_max_volumetric_speed[initial_no_support_extruder]/0.5/0.3*60))} \nG1 X{print_bed_max[0]*0.5+30} E2\nG1 F{min(12000, max(1200, filament_max_volumetric_speed[initial_no_support_extruder]/0.5/0.3*60))} \nG1 X{print_bed_max[0]*0.5+50} E2\n;End PA test.\n\n\nG3 I-1 J0 Z0.6 F1200.0 ;Move to side a little\nG1 F20000\nG92 E0 ;Reset Extruder\nSET_PRINT_STATS_INFO TOTAL_LAYER=[total_layer_count] CURRENT_LAYER=0\n;LAYER_COUNT:[total_layer_count]\n;LAYER:0",
|
||||
"machine_end_gcode": ";===== CC_END_GCODE ================\n;===== date: 2025-12-04-001 =====================\nM400 ; wait for buffer to clear\nM140 S0 ;Turn-off bed\nM106 S255 ;Cooling nozzle\nM83\nG92 E0 ; zero the extruder\nG1 E-0.8 F1800\nG2 I1 J0 Z{max_layer_z+0.5} F3000 ; lower z a little\nG90\n{if max_layer_z > 100}G1 Z{min(max_layer_z+50, printable_height+0.5)} F20000{else}G1 Z100 F20000 {endif}; Move print head up \nM204 S5000\nM749\nM204 S5000\nM400\nM83\nG1 X202 F20000\nM400\nG1 Y250 F20000\nG1 Y264.5 F1200\nM400\nG92 E0\nM104 S0 ;Turn-off hotend\nM140 S0 ;Turn-off bed\nM106 S0 ; turn off fan\nM106 P2 S0 ; turn off remote part cooling fan\nM106 P3 S0 ; turn off chamber cooling fan\nM84 ;Disable all steppers"
|
||||
}
|
||||
|
||||
@@ -86,9 +86,9 @@
|
||||
"9"
|
||||
],
|
||||
"change_filament_gcode": "\n;==========CC2_CHANGE_FILAMENT_GCODE==========\n;===== date: 2026-01-16-001 =====================\nM106 S0\nM106 P2 S0\nG1 Z{min(max_layer_z+3, printable_height+0.5)} F1200\nM6211 T[next_extruder] L[flush_length] M{old_filament_e_feedrate} N{new_filament_e_feedrate} Q[old_filament_temp] R[nozzle_temperature_range_high] S[new_filament_temp]\nT[next_extruder]\n",
|
||||
"layer_change_gcode": "M73 L{layer_num+1}\n;LAYER:{layer_num+1}\nSET_PRINT_STATS_INFO CURRENT_LAYER={layer_num + 1}",
|
||||
"layer_change_gcode": "M73 L{layer_num+1}\n;LAYER:{layer_num+1}\nSET_PRINT_STATS_INFO TOTAL_LAYER=[total_layer_count] CURRENT_LAYER={layer_num+1}",
|
||||
"machine_end_gcode": ";===== CC2_END_GCODE ================\n;===== date: 2026-01-16-001 =====================\n\n\nM140 S0 ;Turn-off bed\nM83\nG92 E0 ; zero the extruder\nG1 E-1.5 F1800\nG2 I0 J1 Z{max_layer_z+0.5} F3000 ; lower z a little\nM106 S0\nM106 P2 S0\nG90\n{if max_layer_z > 75}G1 Z{min(max_layer_z+5, printable_height+0.5)} F20000{else}G1 Z80 F20000 {endif}; Move print head up \nG180 S9\nM104 S0\nM84\n",
|
||||
"machine_start_gcode": ";===== CC2_START_GCODE ================\n;===== date: 2026-01-16-001 =====================\n\nG90\nM104 S140\nM140 S[bed_temperature_initial_layer_single]\nM190 S[bed_temperature_initial_layer_single] A\nM106 S0\nBED_MESH_CALIBRATE mesh_min={adaptive_bed_mesh_min[0]},{adaptive_bed_mesh_min[1]} mesh_max={adaptive_bed_mesh_max[0]},{adaptive_bed_mesh_max[1]} ALGORITHM=[bed_mesh_algo] PROBE_COUNT={bed_mesh_probe_count[0]},{bed_mesh_probe_count[1]} ADAPTIVE=0 ADAPTIVE_MARGIN=0 FROM_SLICER=1\nM204 S{min(20000,max(1000,outer_wall_acceleration))} ;Call exterior wall print acceleration\nG28\nM109 S[nozzle_temperature_initial_layer]\nM6211 A1 L200 T[initial_no_support_extruder] Q[nozzle_temperature_initial_layer] R[nozzle_temperature_initial_layer] S[nozzle_temperature_initial_layer]\nT[initial_no_support_extruder]\n\n{if first_layer_print_min[1] > 0.5}\nG180 S7\nG1 X{print_bed_max[0]*0.5-1} Y-1.2 F20000\nG1 Z0.5 F900\nM109 S[nozzle_temperature_initial_layer]\nM83\nG92 E0 ;Reset Extruder\nG1 E6 F{min(1200, max(120, filament_max_volumetric_speed[initial_no_support_extruder]*60/2/2.5043))} \nM106 S200\nG1 X{print_bed_max[0]*0.5-41} E20 F{min(12000, max(1200, filament_max_volumetric_speed[initial_no_support_extruder]*60/2/2.5043))} \nG1 F6000\nG1 X{print_bed_max[0]*0.5-46} E0.8\n{else}\nG1 E30 F{min(1200, max(120, filament_max_volumetric_speed[initial_no_support_extruder]*60/2/2.5043))}\n{endif}\nM106 S0\nG180 S8\nG1 F20000\nG92 E0 ;Reset Extruder\nSET_PRINT_STATS_INFO TOTAL_LAYER=[total_layer_count]\n;LAYER_COUNT:[total_layer_count]\n;LAYER:0",
|
||||
"machine_start_gcode": ";===== CC2_START_GCODE ================\n;===== date: 2026-01-16-001 =====================\n\nG90\nM104 S140\nM140 S[bed_temperature_initial_layer_single]\nM190 S[bed_temperature_initial_layer_single] A\nM106 S0\nBED_MESH_CALIBRATE mesh_min={adaptive_bed_mesh_min[0]},{adaptive_bed_mesh_min[1]} mesh_max={adaptive_bed_mesh_max[0]},{adaptive_bed_mesh_max[1]} ALGORITHM=[bed_mesh_algo] PROBE_COUNT={bed_mesh_probe_count[0]},{bed_mesh_probe_count[1]} ADAPTIVE=0 ADAPTIVE_MARGIN=0 FROM_SLICER=1\nM204 S{min(20000,max(1000,outer_wall_acceleration))} ;Call exterior wall print acceleration\nG28\nM109 S[nozzle_temperature_initial_layer]\nM6211 A1 L200 T[initial_no_support_extruder] Q[nozzle_temperature_initial_layer] R[nozzle_temperature_initial_layer] S[nozzle_temperature_initial_layer]\nT[initial_no_support_extruder]\n\n{if first_layer_print_min[1] > 0.5}\nG180 S7\nG1 X{print_bed_max[0]*0.5-1} Y-1.2 F20000\nG1 Z0.5 F900\nM109 S[nozzle_temperature_initial_layer]\nM83\nG92 E0 ;Reset Extruder\nG1 E6 F{min(1200, max(120, filament_max_volumetric_speed[initial_no_support_extruder]*60/2/2.5043))} \nM106 S200\nG1 X{print_bed_max[0]*0.5-41} E20 F{min(12000, max(1200, filament_max_volumetric_speed[initial_no_support_extruder]*60/2/2.5043))} \nG1 F6000\nG1 X{print_bed_max[0]*0.5-46} E0.8\n{else}\nG1 E30 F{min(1200, max(120, filament_max_volumetric_speed[initial_no_support_extruder]*60/2/2.5043))}\n{endif}\nM106 S0\nG180 S8\nG1 F20000\nG92 E0 ;Reset Extruder\nSET_PRINT_STATS_INFO TOTAL_LAYER=[total_layer_count] CURRENT_LAYER=0\n;LAYER_COUNT:[total_layer_count]\n;LAYER:0",
|
||||
"retract_restart_extra_toolchange": [
|
||||
"0.5"
|
||||
]
|
||||
|
||||
@@ -2,5 +2,6 @@
|
||||
"inherits": "0.20mm Standard @Elegoo Giga 0.4 nozzle",
|
||||
"layer_height": "0.16",
|
||||
"instantiation": "true",
|
||||
"renamed_from": "0.16mm Optimal @EOS Giga 0.4 nozzle",
|
||||
"name": "0.16mm Optimal @Elegoo Giga 0.4 nozzle"
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
"inherits": "0.30mm Standard @Elegoo Giga 0.6 nozzle",
|
||||
"layer_height": "0.18",
|
||||
"instantiation": "true",
|
||||
"renamed_from": "0.18mm Fine @EOS Giga 0.6 nozzle",
|
||||
"name": "0.18mm Fine @Elegoo Giga 0.6 nozzle",
|
||||
"compatible_printers": [
|
||||
"Elegoo OrangeStorm Giga 0.6 nozzle"
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
"from": "system",
|
||||
"setting_id": "PEOSG04020",
|
||||
"instantiation": "true",
|
||||
"renamed_from": "0.20mm Standard @EOS Giga 0.4 nozzle",
|
||||
"default_acceleration": "3000",
|
||||
"filename_format": "EOGiga1_{nozzle_diameter[0]}_{input_filename_base}_{filament_name}_{layer_height}_{print_time}.gcode",
|
||||
"initial_layer_print_height": "0.25",
|
||||
|
||||
@@ -4,5 +4,6 @@
|
||||
"wall_sequence": "inner-outer-inner wall",
|
||||
"reduce_crossing_wall": "1",
|
||||
"instantiation": "true",
|
||||
"renamed_from": "0.20mm Strength @EOS Giga 0.4 nozzle",
|
||||
"wall_loops": "6"
|
||||
}
|
||||
|
||||
@@ -2,5 +2,6 @@
|
||||
"inherits": "0.20mm Standard @Elegoo Giga 0.4 nozzle",
|
||||
"layer_height": "0.24",
|
||||
"instantiation": "true",
|
||||
"renamed_from": "0.24mm Draft @EOS Giga 0.4 nozzle",
|
||||
"name": "0.24mm Draft @Elegoo Giga 0.4 nozzle"
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user