mirror of
https://github.com/OrcaSlicer/OrcaSlicer.git
synced 2026-09-18 22:42:37 +00:00
Compare commits
19
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ce41b5a3cb | ||
|
|
8fd93907fd | ||
|
|
080f27f602 | ||
|
|
23c77f15cf | ||
|
|
00f78c18cf | ||
|
|
2dd3ef7cff | ||
|
|
2bfab589ee | ||
|
|
4a72a3bba2 | ||
|
|
173706750d | ||
|
|
ffb2946b20 | ||
|
|
e89a05b1e4 | ||
|
|
02a8011b97 | ||
|
|
8056b32450 | ||
|
|
fabd08af1e | ||
|
|
33a8265a11 | ||
|
|
d96682a798 | ||
|
|
64d04a75f3 | ||
|
|
bf22ef2a82 | ||
|
|
dfd3444ae7 |
@@ -685,6 +685,18 @@ jobs:
|
|||||||
name: OrcaSlicer_profile_validator_Linux_ubuntu_${{ env.ubuntu-ver }}_${{ env.ver }}
|
name: OrcaSlicer_profile_validator_Linux_ubuntu_${{ env.ubuntu-ver }}_${{ env.ver }}
|
||||||
path: './build/src/Release/OrcaSlicer_profile_validator'
|
path: './build/src/Release/OrcaSlicer_profile_validator'
|
||||||
|
|
||||||
|
# generate_system_cache is what scripts/build_preset_cache.sh bakes the
|
||||||
|
# <vendor>.opc caches with; it was already built by the "Build system
|
||||||
|
# preset cache (Linux)" step above. The .opc format is 64-bit
|
||||||
|
# little-endian native, i.e. identical across every platform Orca ships,
|
||||||
|
# so only the Linux binary is published.
|
||||||
|
- name: Upload generate_system_cache Ubuntu
|
||||||
|
if: ${{ ! env.ACT && runner.os == 'Linux' && !vars.SELF_HOSTED && inputs.arch != 'aarch64' }}
|
||||||
|
uses: actions/upload-artifact@v7
|
||||||
|
with:
|
||||||
|
name: generate_system_cache_Linux_ubuntu_${{ env.ubuntu-ver }}_${{ env.ver }}
|
||||||
|
path: './build/src/dev-utils/Release/generate_system_cache'
|
||||||
|
|
||||||
- name: Deploy Ubuntu release
|
- name: Deploy Ubuntu release
|
||||||
if: ${{ github.repository == 'OrcaSlicer/OrcaSlicer' && ! env.ACT && env.deploy_nightly == 'true' && runner.os == 'Linux' && !vars.SELF_HOSTED }}
|
if: ${{ github.repository == 'OrcaSlicer/OrcaSlicer' && ! env.ACT && env.deploy_nightly == 'true' && runner.os == 'Linux' && !vars.SELF_HOSTED }}
|
||||||
uses: WebFreak001/deploy-nightly@v3.2.0
|
uses: WebFreak001/deploy-nightly@v3.2.0
|
||||||
@@ -715,6 +727,17 @@ jobs:
|
|||||||
asset_content_type: application/octet-stream
|
asset_content_type: application/octet-stream
|
||||||
max_releases: 1
|
max_releases: 1
|
||||||
|
|
||||||
|
- name: Deploy Ubuntu generate_system_cache release
|
||||||
|
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}
|
||||||
|
release_id: 137995723
|
||||||
|
asset_path: ./build/src/dev-utils/Release/generate_system_cache
|
||||||
|
asset_name: generate_system_cache_Linux${{ env.ubuntu-ver-str }}_nightly
|
||||||
|
asset_content_type: application/octet-stream
|
||||||
|
max_releases: 1
|
||||||
|
|
||||||
- name: Deploy orca_custom_preset_tests
|
- name: Deploy orca_custom_preset_tests
|
||||||
if: ${{ github.repository == 'OrcaSlicer/OrcaSlicer' && ! env.ACT && github.ref == 'refs/heads/main' && runner.os == 'Linux' && !vars.SELF_HOSTED && inputs.arch != 'aarch64' }}
|
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
|
uses: WebFreak001/deploy-nightly@v3.2.0
|
||||||
|
|||||||
@@ -0,0 +1,253 @@
|
|||||||
|
name: Post-merge profiles
|
||||||
|
|
||||||
|
# Push-triggered counterpart to check_profiles.yml (which only gates PRs). When a
|
||||||
|
# profile change lands on main or a release branch, rebuild the affected vendors'
|
||||||
|
# binary preset caches (<vendor>.opc) and publish each as a versioned ZIP asset on
|
||||||
|
# a per-Orca-version release of the profiles repo. From there OrcaCloud's OTA
|
||||||
|
# Manager lists the asset, a maintainer attaches a changelog and hits Publish, and
|
||||||
|
# only then does it become a live OTA update - this workflow does none of that
|
||||||
|
# last part (no changelog, no R2, no webhook).
|
||||||
|
#
|
||||||
|
# Asset contract expected by OrcaCloud's release scanner:
|
||||||
|
# ^(\d+\.\d+\.\d+)_([^_]+)_(\d+(?:\.\d+){3})_(\d{12})\.zip$
|
||||||
|
# <orca_ver>_<vendor>_<profile_version>_<UTC yyyymmddHHMM>.zip (zip root: <vendor>.opc)
|
||||||
|
#
|
||||||
|
# Setup (App + secrets): docs/ota/post-merge-profiles-setup.md
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- main
|
||||||
|
- release/*
|
||||||
|
paths:
|
||||||
|
- 'resources/profiles/**'
|
||||||
|
- '.github/workflows/post_merge_profiles.yml'
|
||||||
|
|
||||||
|
workflow_dispatch:
|
||||||
|
|
||||||
|
permissions:
|
||||||
|
contents: read
|
||||||
|
|
||||||
|
# One run per branch; let a run finish rather than cancel it, since it publishes.
|
||||||
|
concurrency:
|
||||||
|
group: post-merge-profiles-${{ github.ref }}
|
||||||
|
cancel-in-progress: false
|
||||||
|
|
||||||
|
env:
|
||||||
|
# generate_system_cache is published to this repo's own nightly-builds release
|
||||||
|
# by build_orca.yml's Linux leg. The job guard pins github.repository to
|
||||||
|
# OrcaSlicer/OrcaSlicer, so this resolves there.
|
||||||
|
TOOL_REPO: ${{ github.repository }}
|
||||||
|
TOOL_ASSET: generate_system_cache_Linux_Ubuntu2404_nightly
|
||||||
|
# Where per-vendor ZIP assets are published; OrcaCloud's OTA reads this repo.
|
||||||
|
PROFILES_OWNER: OrcaSlicer
|
||||||
|
PROFILES_REPO: orcaslicer-profiles
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
publish_profile_caches:
|
||||||
|
name: Publish profile caches
|
||||||
|
if: ${{ github.repository == 'OrcaSlicer/OrcaSlicer' }}
|
||||||
|
# FOLDER_MERGERS is an environment-scoped variable, shared with the PR
|
||||||
|
# merge bot. Keep this environment free of protection rules so this
|
||||||
|
# push-triggered job does not wait for a reviewer.
|
||||||
|
environment: merge-delegation
|
||||||
|
runs-on: ubuntu-24.04
|
||||||
|
steps:
|
||||||
|
- name: Checkout repository
|
||||||
|
uses: actions/checkout@v7
|
||||||
|
with:
|
||||||
|
# Enough history to reach github.event.before for the changed-vendor
|
||||||
|
# diff on a normal push; deeper pushes fall back to HEAD^..HEAD in the
|
||||||
|
# step below. fetch-depth: 0 would clone all of OrcaSlicer's history.
|
||||||
|
fetch-depth: 50
|
||||||
|
|
||||||
|
- name: Resolve changed vendors
|
||||||
|
id: vendors
|
||||||
|
shell: bash
|
||||||
|
env:
|
||||||
|
FOLDER_MERGERS: ${{ vars.FOLDER_MERGERS }}
|
||||||
|
run: |
|
||||||
|
set -euo pipefail
|
||||||
|
base='${{ github.event.before }}'
|
||||||
|
head='${{ github.sha }}'
|
||||||
|
# Zero SHA (branch created / force push) or manual dispatch: fall back
|
||||||
|
# to this commit's own diff.
|
||||||
|
if [ -z "$base" ] || [ "$base" = "0000000000000000000000000000000000000000" ] || ! git cat-file -e "$base^{commit}" 2>/dev/null; then
|
||||||
|
base="$head^"
|
||||||
|
fi
|
||||||
|
echo "Diffing $base..$head"
|
||||||
|
|
||||||
|
mapfile -t candidates < <(
|
||||||
|
git diff --name-only "$base" "$head" -- resources/profiles \
|
||||||
|
| sed -nE 's#^resources/profiles/([^/]+)/.*#\1#p; s#^resources/profiles/([^/]+)\.json$#\1#p' \
|
||||||
|
| sort -u
|
||||||
|
)
|
||||||
|
|
||||||
|
vendors=()
|
||||||
|
for v in "${candidates[@]:-}"; do
|
||||||
|
[ -n "$v" ] || continue
|
||||||
|
json="resources/profiles/$v.json"
|
||||||
|
# A vendor has a manifest plus either a preset directory or a version
|
||||||
|
# field; this drops non-vendor files such as blacklist.json.
|
||||||
|
if [ -f "$json" ] && { [ -d "resources/profiles/$v" ] || jq -e '.version' "$json" >/dev/null 2>&1; }; then
|
||||||
|
vendors+=("$v")
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
if [ "${#vendors[@]}" -eq 0 ]; then
|
||||||
|
echo "No changed vendor profiles in this push; nothing to publish."
|
||||||
|
echo "vendors=" >> "$GITHUB_OUTPUT"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
# A vendor is eligible only when both the profile directory and its
|
||||||
|
# sibling bundle JSON are covered by at least one FOLDER_MERGERS
|
||||||
|
# grant. The account part is intentionally ignored here: this is a
|
||||||
|
# post-merge safety check, not an authorization check for a command.
|
||||||
|
grants=()
|
||||||
|
while IFS= read -r raw_line; do
|
||||||
|
line="${raw_line#"${raw_line%%[![:space:]]*}"}"
|
||||||
|
line="${line%"${line##*[![:space:]]}"}"
|
||||||
|
[ -n "$line" ] || continue
|
||||||
|
[[ "$line" == \#* ]] && continue
|
||||||
|
[[ "$line" == *:* ]] || continue
|
||||||
|
|
||||||
|
grant="${line#*:}"
|
||||||
|
grant="${grant#"${grant%%[![:space:]]*}"}"
|
||||||
|
grant="${grant%"${grant##*[![:space:]]}"}"
|
||||||
|
while [[ "$grant" == */ ]]; do grant="${grant%/}"; done
|
||||||
|
grants+=("$grant")
|
||||||
|
done <<< "${FOLDER_MERGERS:-}"
|
||||||
|
|
||||||
|
is_granted() {
|
||||||
|
local path="$1"
|
||||||
|
local grant
|
||||||
|
for grant in "${grants[@]:-}"; do
|
||||||
|
if [[ "$path" == "$grant" || "$path" == "$grant/"* ]]; then
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
unauthorized=()
|
||||||
|
for v in "${vendors[@]}"; do
|
||||||
|
if ! is_granted "resources/profiles/$v" || ! is_granted "resources/profiles/$v.json"; then
|
||||||
|
unauthorized+=("$v")
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
if [ "${#unauthorized[@]}" -ne 0 ]; then
|
||||||
|
echo "Changed vendor profiles are not covered by FOLDER_MERGERS: ${unauthorized[*]}"
|
||||||
|
echo "No profile caches will be published for this push."
|
||||||
|
echo "vendors=" >> "$GITHUB_OUTPUT"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
printf 'Changed vendors: %s\n' "${vendors[*]}"
|
||||||
|
echo "vendors=${vendors[*]}" >> "$GITHUB_OUTPUT"
|
||||||
|
|
||||||
|
- name: Resolve Orca version
|
||||||
|
id: orca
|
||||||
|
if: steps.vendors.outputs.vendors != ''
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
set -euo pipefail
|
||||||
|
raw="$(sed -nE 's/^set\(SoftFever_VERSION "([^"]+)".*/\1/p' version.inc | head -1)"
|
||||||
|
[ -n "$raw" ] || { echo "::error::could not read SoftFever_VERSION from version.inc"; exit 1; }
|
||||||
|
orca_ver="${raw%%-*}"
|
||||||
|
if ! [[ "$orca_ver" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
|
||||||
|
echo "::error::Orca version '$orca_ver' (from '$raw') is not X.Y.Z"; exit 1
|
||||||
|
fi
|
||||||
|
# release_tag is what the desktop client sends as orca_version and what
|
||||||
|
# OrcaCloud keys R2 on; orca_ver (X.Y.Z) is the asset-name prefix.
|
||||||
|
echo "release_tag=$raw" >> "$GITHUB_OUTPUT"
|
||||||
|
echo "orca_ver=$orca_ver" >> "$GITHUB_OUTPUT"
|
||||||
|
echo "Orca version: release_tag=$raw asset_prefix=$orca_ver"
|
||||||
|
|
||||||
|
- name: Validate profile versions
|
||||||
|
id: pver
|
||||||
|
if: steps.vendors.outputs.vendors != ''
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
set -euo pipefail
|
||||||
|
: > "$RUNNER_TEMP/pver.tsv"
|
||||||
|
for v in ${{ steps.vendors.outputs.vendors }}; do
|
||||||
|
pv="$(jq -r '.version // empty' "resources/profiles/$v.json")"
|
||||||
|
if ! [[ "$pv" =~ ^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
|
||||||
|
echo "::error::vendor $v version '${pv:-<none>}' must be 4 numeric parts (A.B.C.D) for the OTA asset name; fix resources/profiles/$v.json"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
printf '%s\t%s\n' "$v" "$pv" >> "$RUNNER_TEMP/pver.tsv"
|
||||||
|
echo "$v -> $pv"
|
||||||
|
done
|
||||||
|
|
||||||
|
- name: Download generate_system_cache
|
||||||
|
if: steps.vendors.outputs.vendors != ''
|
||||||
|
shell: bash
|
||||||
|
env:
|
||||||
|
# gh (with the default token) rather than an unauthenticated curl: keeps
|
||||||
|
# working if TOOL_REPO is ever private and avoids anonymous rate limits.
|
||||||
|
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
run: |
|
||||||
|
set -euo pipefail
|
||||||
|
gh release download nightly-builds --repo "$TOOL_REPO" \
|
||||||
|
--pattern "$TOOL_ASSET" --output generate_system_cache --clobber
|
||||||
|
chmod +x generate_system_cache
|
||||||
|
|
||||||
|
- name: Build caches and package assets
|
||||||
|
id: pkg
|
||||||
|
if: steps.vendors.outputs.vendors != ''
|
||||||
|
shell: bash
|
||||||
|
run: |
|
||||||
|
set -euo pipefail
|
||||||
|
# One timestamp for the whole run so a multi-vendor merge groups together.
|
||||||
|
ts="$(date -u +%Y%m%d%H%M)"
|
||||||
|
orca_ver='${{ steps.orca.outputs.orca_ver }}'
|
||||||
|
out="$RUNNER_TEMP/assets"
|
||||||
|
mkdir -p "$out"
|
||||||
|
|
||||||
|
for v in ${{ steps.vendors.outputs.vendors }}; do
|
||||||
|
./generate_system_cache -p "$GITHUB_WORKSPACE/resources/profiles" -v "$v" -l 2
|
||||||
|
opc="resources/profiles/$v.opc"
|
||||||
|
[ -f "$opc" ] || { echo "::error::$opc was not generated"; exit 1; }
|
||||||
|
pv="$(awk -F'\t' -v v="$v" '$1==v{print $2}' "$RUNNER_TEMP/pver.tsv")"
|
||||||
|
name="${orca_ver}_${v}_${pv}_${ts}.zip"
|
||||||
|
( cd resources/profiles && zip -q -j "$out/$name" "$v.opc" )
|
||||||
|
echo "packaged $name"
|
||||||
|
done
|
||||||
|
echo "dir=$out" >> "$GITHUB_OUTPUT"
|
||||||
|
|
||||||
|
- name: Mint profiles-repo token
|
||||||
|
id: token
|
||||||
|
if: steps.vendors.outputs.vendors != ''
|
||||||
|
uses: actions/create-github-app-token@v1
|
||||||
|
with:
|
||||||
|
app-id: ${{ secrets.PROFILES_APP_ID }}
|
||||||
|
private-key: ${{ secrets.PROFILES_APP_PRIVATE_KEY }}
|
||||||
|
owner: ${{ env.PROFILES_OWNER }}
|
||||||
|
repositories: ${{ env.PROFILES_REPO }}
|
||||||
|
|
||||||
|
- name: Publish assets to profiles release
|
||||||
|
if: steps.vendors.outputs.vendors != ''
|
||||||
|
shell: bash
|
||||||
|
env:
|
||||||
|
GH_TOKEN: ${{ steps.token.outputs.token }}
|
||||||
|
RELEASE_TAG: ${{ steps.orca.outputs.release_tag }}
|
||||||
|
ASSET_DIR: ${{ steps.pkg.outputs.dir }}
|
||||||
|
run: |
|
||||||
|
set -euo pipefail
|
||||||
|
repo="$PROFILES_OWNER/$PROFILES_REPO"
|
||||||
|
if ! gh release view "$RELEASE_TAG" --repo "$repo" >/dev/null 2>&1; then
|
||||||
|
echo "Creating release $RELEASE_TAG on $repo"
|
||||||
|
gh release create "$RELEASE_TAG" --repo "$repo" \
|
||||||
|
--title "$RELEASE_TAG" --notes "Profile cache assets for Orca $RELEASE_TAG." \
|
||||||
|
--latest=false
|
||||||
|
fi
|
||||||
|
# Asset names are timestamp-unique; a clash means a bug, so don't --clobber.
|
||||||
|
gh release upload "$RELEASE_TAG" --repo "$repo" "$ASSET_DIR"/*.zip
|
||||||
|
|
||||||
|
{
|
||||||
|
echo "### Published to \`$repo\` release \`$RELEASE_TAG\`"
|
||||||
|
for f in "$ASSET_DIR"/*.zip; do echo "- \`$(basename "$f")\`"; done
|
||||||
|
} >> "$GITHUB_STEP_SUMMARY"
|
||||||
@@ -292,5 +292,6 @@ src/slic3r/GUI/AMSDryControl.cpp
|
|||||||
src/slic3r/GUI/AMSDryControl.hpp
|
src/slic3r/GUI/AMSDryControl.hpp
|
||||||
src/libslic3r/PresetBundle.cpp
|
src/libslic3r/PresetBundle.cpp
|
||||||
src/slic3r/GUI/CAD/DesignPanel.cpp
|
src/slic3r/GUI/CAD/DesignPanel.cpp
|
||||||
|
src/slic3r/GUI/CAD/SketchInlineEditor.cpp
|
||||||
src/slic3r/GUI/Gizmos/GLGizmoPrimitive.cpp
|
src/slic3r/GUI/Gizmos/GLGizmoPrimitive.cpp
|
||||||
src/slic3r/GUI/Gizmos/GLGizmoSketch.cpp
|
src/slic3r/GUI/Gizmos/GLGizmoSketch.cpp
|
||||||
|
|||||||
@@ -23,7 +23,8 @@ int main(int argc, char* argv[])
|
|||||||
#else
|
#else
|
||||||
("path,p", po::value<std::string>()->default_value("../../../resources/profiles"), "Path to profiles directory")
|
("path,p", po::value<std::string>()->default_value("../../../resources/profiles"), "Path to profiles directory")
|
||||||
#endif
|
#endif
|
||||||
("log_level,l", po::value<int>()->default_value(2), "Log level (0=trace, 2=info, 4=error)");
|
("log_level,l", po::value<int>()->default_value(2), "Log level (0=trace, 2=info, 4=error)")
|
||||||
|
("vendor,v", po::value<std::string>()->default_value(""), "Vendor name. Optional; generate the cache for this vendor only (the Orca filament library is always included as the inheritance base). All vendors if not specified.");
|
||||||
// clang-format on
|
// clang-format on
|
||||||
|
|
||||||
po::variables_map vm;
|
po::variables_map vm;
|
||||||
@@ -38,6 +39,7 @@ int main(int argc, char* argv[])
|
|||||||
|
|
||||||
const std::string profiles_path = vm["path"].as<std::string>();
|
const std::string profiles_path = vm["path"].as<std::string>();
|
||||||
const int log_level = vm["log_level"].as<int>();
|
const int log_level = vm["log_level"].as<int>();
|
||||||
|
const std::string vendor = vm["vendor"].as<std::string>();
|
||||||
|
|
||||||
if (!fs::exists(profiles_path) || !fs::is_directory(profiles_path)) {
|
if (!fs::exists(profiles_path) || !fs::is_directory(profiles_path)) {
|
||||||
std::cerr << "Error: '" << profiles_path << "' is not a valid directory\n";
|
std::cerr << "Error: '" << profiles_path << "' is not a valid directory\n";
|
||||||
@@ -59,8 +61,12 @@ int main(int argc, char* argv[])
|
|||||||
preset_bundle->set_is_validation_mode(true);
|
preset_bundle->set_is_validation_mode(true);
|
||||||
preset_bundle->set_default_suppressed(true);
|
preset_bundle->set_default_suppressed(true);
|
||||||
preset_bundle->set_generate_vendor_caches(true);
|
preset_bundle->set_generate_vendor_caches(true);
|
||||||
|
// Empty == every vendor. Otherwise only this vendor (plus the always-loaded
|
||||||
|
// Orca filament library) is parsed, so only its <vendor>.opc is written.
|
||||||
|
preset_bundle->set_vendor_to_validate(vendor);
|
||||||
|
|
||||||
std::cout << "Loading system presets from: " << profiles_path << "\n";
|
std::cout << "Loading system presets from: " << profiles_path
|
||||||
|
<< (vendor.empty() ? "" : " (vendor: " + vendor + ")") << "\n";
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// In validation mode data_dir() is the profiles directory set above, so the
|
// In validation mode data_dir() is the profiles directory set above, so the
|
||||||
@@ -71,6 +77,14 @@ int main(int argc, char* argv[])
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// A specific vendor must have produced its own cache; the always-loaded
|
||||||
|
// filament library alone would otherwise mask a misspelt or removed name.
|
||||||
|
if (!vendor.empty() && !fs::exists(fs::path(profiles_path) / (vendor + ".opc"))) {
|
||||||
|
std::cerr << "No cache was generated for vendor '" << vendor << "' under " << profiles_path
|
||||||
|
<< " - check the vendor name.\n";
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
size_t cache_count = 0;
|
size_t cache_count = 0;
|
||||||
for (auto& entry : fs::directory_iterator(profiles_path))
|
for (auto& entry : fs::directory_iterator(profiles_path))
|
||||||
if (boost::iends_with(entry.path().string(), ".opc"))
|
if (boost::iends_with(entry.path().string(), ".opc"))
|
||||||
|
|||||||
+46
-5
@@ -8,6 +8,7 @@
|
|||||||
#include "I18N.hpp"
|
#include "I18N.hpp"
|
||||||
#include "GCode.hpp"
|
#include "GCode.hpp"
|
||||||
#include "Exception.hpp"
|
#include "Exception.hpp"
|
||||||
|
#include "LifecycleEvents.hpp"
|
||||||
#include "ExtrusionEntity.hpp"
|
#include "ExtrusionEntity.hpp"
|
||||||
#include "EdgeGrid.hpp"
|
#include "EdgeGrid.hpp"
|
||||||
#include "Geometry/ConvexHull.hpp"
|
#include "Geometry/ConvexHull.hpp"
|
||||||
@@ -2476,6 +2477,14 @@ void GCode::do_export(Print* print, const char* path, GCodeProcessorResult* resu
|
|||||||
m_writer.set_is_bbl_machine(print->is_BBL_printer());
|
m_writer.set_is_bbl_machine(print->is_BBL_printer());
|
||||||
print->set_started(psGCodeExport);
|
print->set_started(psGCodeExport);
|
||||||
|
|
||||||
|
{
|
||||||
|
LifecycleEventContext ctx;
|
||||||
|
ctx.name = std::to_string(print->model().id().id);
|
||||||
|
ctx.code = LifecycleEvtCode::Ok;
|
||||||
|
ctx.msg = path;
|
||||||
|
fire_lifecycle_event(LifecycleEvent::GCodeExportStarted, ctx);
|
||||||
|
}
|
||||||
|
|
||||||
// check if any custom gcode contains keywords used by the gcode processor to
|
// check if any custom gcode contains keywords used by the gcode processor to
|
||||||
// produce time estimation and gcode toolpaths
|
// produce time estimation and gcode toolpaths
|
||||||
std::vector<std::pair<std::string, std::string>> validation_res = DoExport::validate_custom_gcode(*print);
|
std::vector<std::pair<std::string, std::string>> validation_res = DoExport::validate_custom_gcode(*print);
|
||||||
@@ -2511,12 +2520,22 @@ void GCode::do_export(Print* print, const char* path, GCodeProcessorResult* resu
|
|||||||
m_processor.set_print(print);
|
m_processor.set_print(print);
|
||||||
GCodeOutputStream file(boost::nowide::fopen(path_tmp.c_str(), "wb"), m_processor);
|
GCodeOutputStream file(boost::nowide::fopen(path_tmp.c_str(), "wb"), m_processor);
|
||||||
if (! file.is_open()) {
|
if (! file.is_open()) {
|
||||||
BOOST_LOG_TRIVIAL(error) << std::string("G-code export to ") + path + " failed.\nCannot open the file for writing.\n" << std::endl;
|
std::string err_msg = std::string("G-code export to ") + path + " failed.\nCannot open the file for writing.\n";
|
||||||
|
BOOST_LOG_TRIVIAL(error) << err_msg << std::endl;
|
||||||
if (!fs::exists(folder)) {
|
if (!fs::exists(folder)) {
|
||||||
//fs::create_directory(folder);
|
//fs::create_directory(folder);
|
||||||
BOOST_LOG_TRIVIAL(error) << "the parent path " + folder.string() +" is not there!!!" << std::endl;
|
std::string add_err_msg = "the parent path " + folder.string() +" is not there!!!";
|
||||||
|
BOOST_LOG_TRIVIAL(error) << add_err_msg << std::endl;
|
||||||
|
err_msg += add_err_msg;
|
||||||
}
|
}
|
||||||
throw Slic3r::RuntimeError(std::string("G-code export to ") + path + " failed.\nCannot open the file for writing.\n");
|
{
|
||||||
|
LifecycleEventContext ctx;
|
||||||
|
ctx.name = std::to_string(print->model().id().id);
|
||||||
|
ctx.code = LifecycleEvtCode::Error;
|
||||||
|
ctx.msg = std::string(path) + "\n" + err_msg;
|
||||||
|
fire_lifecycle_event(LifecycleEvent::GCodeExportFinished, ctx);
|
||||||
|
}
|
||||||
|
throw Slic3r::RuntimeError(err_msg);
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@@ -2527,11 +2546,18 @@ void GCode::do_export(Print* print, const char* path, GCodeProcessorResult* resu
|
|||||||
boost::nowide::remove(path_tmp.c_str());
|
boost::nowide::remove(path_tmp.c_str());
|
||||||
throw Slic3r::RuntimeError(std::string("G-code export to ") + path + " failed\nIs the disk full?\n");
|
throw Slic3r::RuntimeError(std::string("G-code export to ") + path + " failed\nIs the disk full?\n");
|
||||||
}
|
}
|
||||||
} catch (std::exception & /* ex */) {
|
} catch (std::exception &ex) {
|
||||||
// Rethrow on any exception. std::runtime_exception and CanceledException are expected to be thrown.
|
// Rethrow on any exception. std::runtime_exception and CanceledException are expected to be thrown.
|
||||||
// Close and remove the file.
|
// Close and remove the file.
|
||||||
file.close();
|
file.close();
|
||||||
boost::nowide::remove(path_tmp.c_str());
|
boost::nowide::remove(path_tmp.c_str());
|
||||||
|
{
|
||||||
|
LifecycleEventContext ctx;
|
||||||
|
ctx.name = std::to_string(print->model().id().id);
|
||||||
|
ctx.code = LifecycleEvtCode::Error;
|
||||||
|
ctx.msg = std::string(path) + "\n" + ex.what();
|
||||||
|
fire_lifecycle_event(LifecycleEvent::GCodeExportFinished, ctx);
|
||||||
|
}
|
||||||
throw;
|
throw;
|
||||||
}
|
}
|
||||||
file.close();
|
file.close();
|
||||||
@@ -2637,6 +2663,13 @@ void GCode::do_export(Print* print, const char* path, GCodeProcessorResult* resu
|
|||||||
|
|
||||||
std::error_code ret = rename_file(path_tmp, path);
|
std::error_code ret = rename_file(path_tmp, path);
|
||||||
if (ret) {
|
if (ret) {
|
||||||
|
{
|
||||||
|
LifecycleEventContext ctx;
|
||||||
|
ctx.name = std::to_string(print->model().id().id);
|
||||||
|
ctx.code = LifecycleEvtCode::Error;
|
||||||
|
ctx.msg = std::string(path) + "\nFailed to rename the output G-code file: " + ret.message();
|
||||||
|
fire_lifecycle_event(LifecycleEvent::GCodeExportFinished, ctx);
|
||||||
|
}
|
||||||
throw Slic3r::RuntimeError(
|
throw Slic3r::RuntimeError(
|
||||||
std::string("Failed to rename the output G-code file from ") + path_tmp + " to " + path + '\n' + "error code " + ret.message() + '\n' +
|
std::string("Failed to rename the output G-code file from ") + path_tmp + " to " + path + '\n' + "error code " + ret.message() + '\n' +
|
||||||
"Is " + path_tmp + " locked?" + '\n');
|
"Is " + path_tmp + " locked?" + '\n');
|
||||||
@@ -2647,7 +2680,15 @@ void GCode::do_export(Print* print, const char* path, GCodeProcessorResult* resu
|
|||||||
|
|
||||||
BOOST_LOG_TRIVIAL(info) << "Exporting G-code finished" << log_memory_info();
|
BOOST_LOG_TRIVIAL(info) << "Exporting G-code finished" << log_memory_info();
|
||||||
print->set_done(psGCodeExport);
|
print->set_done(psGCodeExport);
|
||||||
|
|
||||||
|
{
|
||||||
|
LifecycleEventContext ctx;
|
||||||
|
ctx.name = std::to_string(print->model().id().id);
|
||||||
|
ctx.code = LifecycleEvtCode::Ok;
|
||||||
|
ctx.msg = path;
|
||||||
|
fire_lifecycle_event(LifecycleEvent::GCodeExportFinished, ctx);
|
||||||
|
}
|
||||||
|
|
||||||
// Orca: label_object_enabled reflects whether objects are labeled in the g-code (EXCLUDE_OBJECT /
|
// Orca: label_object_enabled reflects whether objects are labeled in the g-code (EXCLUDE_OBJECT /
|
||||||
// M486), which is driven by exclude_object for every printer
|
// M486), which is driven by exclude_object for every printer
|
||||||
if(result != nullptr)
|
if(result != nullptr)
|
||||||
|
|||||||
@@ -0,0 +1,181 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
// LifecycleEvents.hpp
|
||||||
|
// --------------------
|
||||||
|
// Application lifecycle events (project, slicing, plate editing, preset, printer connection, and
|
||||||
|
// job activity) that other subsystems -- chiefly the plugin layer above libslic3r -- may want to
|
||||||
|
// observe. Lives in libslic3r rather than the plugin layer because some events fire from inside
|
||||||
|
// the slicing engine itself; see fire_lifecycle_event() below.
|
||||||
|
|
||||||
|
#include <functional>
|
||||||
|
#include <string>
|
||||||
|
#include <utility>
|
||||||
|
|
||||||
|
namespace Slic3r
|
||||||
|
{
|
||||||
|
enum class LifecycleEvent {
|
||||||
|
// Project (3mf)
|
||||||
|
NewProject,
|
||||||
|
ProjectOpened,
|
||||||
|
ProjectBeforeSave,
|
||||||
|
ProjectAfterSave,
|
||||||
|
ProjectClosed,
|
||||||
|
ProjectDirtyChanged,
|
||||||
|
|
||||||
|
// Slicing pipeline
|
||||||
|
SliceStarted,
|
||||||
|
SliceGeometryFinished,
|
||||||
|
GCodeExportStarted,
|
||||||
|
GCodeExportFinished,
|
||||||
|
SlicingJobComplete,
|
||||||
|
|
||||||
|
// Plate/model editing
|
||||||
|
ObjectAdded,
|
||||||
|
ObjectDeleted,
|
||||||
|
ObjectTransformed,
|
||||||
|
ObjectChanged,
|
||||||
|
ObjectRenamed,
|
||||||
|
PlateCreated,
|
||||||
|
PlateDeleted,
|
||||||
|
PlateSelected,
|
||||||
|
PlateRenamed,
|
||||||
|
|
||||||
|
// Preset
|
||||||
|
PresetSelected,
|
||||||
|
PresetSaved,
|
||||||
|
|
||||||
|
// Printer/device
|
||||||
|
PrintStateChanged,
|
||||||
|
DeviceOnlineChanged,
|
||||||
|
DeviceDiscovered,
|
||||||
|
DeviceSelected,
|
||||||
|
DeviceConnected,
|
||||||
|
DeviceDisconnected,
|
||||||
|
UploadStarted,
|
||||||
|
UploadFinished,
|
||||||
|
|
||||||
|
// Print/send jobs
|
||||||
|
PrintJobStarted,
|
||||||
|
PrintJobFinished,
|
||||||
|
SendJobStarted,
|
||||||
|
SendJobFinished,
|
||||||
|
};
|
||||||
|
|
||||||
|
// Scoped so callers must qualify (LifecycleEvtCode::Error, not ERROR) -- ERROR/OK collide with
|
||||||
|
// Windows macros (wingdi.h) as unqualified names.
|
||||||
|
enum class LifecycleEvtCode { Ok, Error, Warn };
|
||||||
|
|
||||||
|
struct LifecycleEventContext
|
||||||
|
{
|
||||||
|
// The primary subject identifier for the event. This is event-specific (for example, a
|
||||||
|
// project/output path, preset name, device id, or object name), must not contain status or
|
||||||
|
// prose, and may be empty when the event has no single subject.
|
||||||
|
std::string name;
|
||||||
|
|
||||||
|
// Outcome of the operation represented by the event. For state-change and start events,
|
||||||
|
// Ok means that the event occurred; it does not imply that a future operation succeeded.
|
||||||
|
LifecycleEvtCode code = LifecycleEvtCode::Ok;
|
||||||
|
|
||||||
|
// Optional human-readable detail or diagnostic text. It is not a stable parsing contract;
|
||||||
|
// machine-readable data should be represented by a dedicated field or event instead.
|
||||||
|
std::string msg;
|
||||||
|
|
||||||
|
// Stable subject/object identifier, when the source model provides one.
|
||||||
|
std::string id;
|
||||||
|
|
||||||
|
// Previous value for rename and other before/after events.
|
||||||
|
std::string previous_name;
|
||||||
|
|
||||||
|
// Device identifier for printer and job events.
|
||||||
|
std::string device_id;
|
||||||
|
|
||||||
|
// Job identifier when the originating queue/task provides one.
|
||||||
|
std::string job_id;
|
||||||
|
|
||||||
|
// Source subsystem or operation detail, suitable for filtering but not guaranteed to be
|
||||||
|
// exhaustive across versions.
|
||||||
|
std::string source;
|
||||||
|
|
||||||
|
// Plate, object, or volume index when the source uses an index rather than a stable id.
|
||||||
|
int index = -1;
|
||||||
|
|
||||||
|
// Aggregate project dirty state for ProjectDirtyChanged.
|
||||||
|
bool dirty = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
inline std::string lifecycle_event_to_string(LifecycleEvent event)
|
||||||
|
{
|
||||||
|
switch (event) {
|
||||||
|
case LifecycleEvent::NewProject: return "NewProject";
|
||||||
|
case LifecycleEvent::ProjectOpened: return "ProjectOpened";
|
||||||
|
case LifecycleEvent::ProjectBeforeSave: return "ProjectBeforeSave";
|
||||||
|
case LifecycleEvent::ProjectAfterSave: return "ProjectAfterSave";
|
||||||
|
case LifecycleEvent::ProjectClosed: return "ProjectClosed";
|
||||||
|
case LifecycleEvent::ProjectDirtyChanged: return "ProjectDirtyChanged";
|
||||||
|
|
||||||
|
case LifecycleEvent::SliceStarted: return "SliceStarted";
|
||||||
|
case LifecycleEvent::SliceGeometryFinished: return "SliceGeometryFinished";
|
||||||
|
case LifecycleEvent::GCodeExportStarted: return "GCodeExportStarted";
|
||||||
|
case LifecycleEvent::GCodeExportFinished: return "GCodeExportFinished";
|
||||||
|
case LifecycleEvent::SlicingJobComplete: return "SlicingJobComplete";
|
||||||
|
|
||||||
|
case LifecycleEvent::ObjectAdded: return "ObjectAdded";
|
||||||
|
case LifecycleEvent::ObjectDeleted: return "ObjectDeleted";
|
||||||
|
case LifecycleEvent::ObjectTransformed: return "ObjectTransformed";
|
||||||
|
case LifecycleEvent::ObjectChanged: return "ObjectChanged";
|
||||||
|
case LifecycleEvent::ObjectRenamed: return "ObjectRenamed";
|
||||||
|
case LifecycleEvent::PlateCreated: return "PlateCreated";
|
||||||
|
case LifecycleEvent::PlateDeleted: return "PlateDeleted";
|
||||||
|
case LifecycleEvent::PlateSelected: return "PlateSelected";
|
||||||
|
case LifecycleEvent::PlateRenamed: return "PlateRenamed";
|
||||||
|
|
||||||
|
case LifecycleEvent::PresetSelected: return "PresetSelected";
|
||||||
|
case LifecycleEvent::PresetSaved: return "PresetSaved";
|
||||||
|
case LifecycleEvent::PrintStateChanged: return "PrintStateChanged";
|
||||||
|
|
||||||
|
case LifecycleEvent::DeviceOnlineChanged: return "DeviceOnlineChanged";
|
||||||
|
case LifecycleEvent::DeviceDiscovered: return "DeviceDiscovered";
|
||||||
|
case LifecycleEvent::DeviceSelected: return "DeviceSelected";
|
||||||
|
case LifecycleEvent::DeviceConnected: return "DeviceConnected";
|
||||||
|
case LifecycleEvent::DeviceDisconnected: return "DeviceDisconnected";
|
||||||
|
|
||||||
|
case LifecycleEvent::UploadStarted: return "UploadStarted";
|
||||||
|
case LifecycleEvent::UploadFinished: return "UploadFinished";
|
||||||
|
case LifecycleEvent::PrintJobStarted: return "PrintJobStarted";
|
||||||
|
case LifecycleEvent::PrintJobFinished: return "PrintJobFinished";
|
||||||
|
case LifecycleEvent::SendJobStarted: return "SendJobStarted";
|
||||||
|
case LifecycleEvent::SendJobFinished: return "SendJobFinished";
|
||||||
|
default: return "Unknown";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
inline std::string lifecycle_evt_code_to_string(LifecycleEvtCode code)
|
||||||
|
{
|
||||||
|
switch (code) {
|
||||||
|
case LifecycleEvtCode::Ok: return "Ok";
|
||||||
|
case LifecycleEvtCode::Error: return "Error";
|
||||||
|
case LifecycleEvtCode::Warn: return "Warn";
|
||||||
|
default: return "Unknown";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Global cross-layer seam (mirrors ConfigBase::set_resolve_capability_fn): any libslic3r code can
|
||||||
|
// fire a lifecycle event without depending on the plugin layer above it, which installs the
|
||||||
|
// dispatcher here at startup. Not tied to Print/GCode specifically, since nothing here should
|
||||||
|
// require callers to hold a Print& just to report an event.
|
||||||
|
using LifecycleHookFn = std::function<void(LifecycleEvent, const LifecycleEventContext&)>;
|
||||||
|
|
||||||
|
inline LifecycleHookFn& lifecycle_hook_fn()
|
||||||
|
{
|
||||||
|
static LifecycleHookFn fn;
|
||||||
|
return fn;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void set_lifecycle_hook_fn(LifecycleHookFn fn) { lifecycle_hook_fn() = std::move(fn); }
|
||||||
|
|
||||||
|
inline void fire_lifecycle_event(LifecycleEvent event, const LifecycleEventContext& ctx)
|
||||||
|
{
|
||||||
|
if (const LifecycleHookFn& fn = lifecycle_hook_fn(); fn)
|
||||||
|
fn(event, ctx);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -47,6 +47,7 @@
|
|||||||
#include <boost/log/trivial.hpp>
|
#include <boost/log/trivial.hpp>
|
||||||
|
|
||||||
#include "libslic3r.h"
|
#include "libslic3r.h"
|
||||||
|
#include "LifecycleEvents.hpp"
|
||||||
#include "Utils.hpp"
|
#include "Utils.hpp"
|
||||||
#include "Time.hpp"
|
#include "Time.hpp"
|
||||||
#include "PlaceholderParser.hpp"
|
#include "PlaceholderParser.hpp"
|
||||||
@@ -2972,6 +2973,7 @@ void PresetCollection::save_current_preset(const std::string &new_name, bool det
|
|||||||
// 1) Find the preset with a new_name or create a new one,
|
// 1) Find the preset with a new_name or create a new one,
|
||||||
// initialize it with the edited config.
|
// initialize it with the edited config.
|
||||||
auto it = this->find_preset_internal(new_name);
|
auto it = this->find_preset_internal(new_name);
|
||||||
|
const bool preset_existed = (it != m_presets.end() && it->name == new_name);
|
||||||
if (it != m_presets.end() && it->name == new_name) {
|
if (it != m_presets.end() && it->name == new_name) {
|
||||||
// Preset with the same name found.
|
// Preset with the same name found.
|
||||||
Preset &preset = *it;
|
Preset &preset = *it;
|
||||||
@@ -3079,6 +3081,14 @@ void PresetCollection::save_current_preset(const std::string &new_name, bool det
|
|||||||
this->get_selected_preset().save(&(parent_preset->config));
|
this->get_selected_preset().save(&(parent_preset->config));
|
||||||
else
|
else
|
||||||
this->get_selected_preset().save(nullptr);
|
this->get_selected_preset().save(nullptr);
|
||||||
|
|
||||||
|
{
|
||||||
|
LifecycleEventContext ctx;
|
||||||
|
ctx.name = new_name;
|
||||||
|
ctx.msg = preset_existed ? "overwrite" : "new";
|
||||||
|
ctx.code = LifecycleEvtCode::Ok;
|
||||||
|
fire_lifecycle_event(LifecycleEvent::PresetSaved, ctx);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// A detached standalone preset for the Full Publish receiver: create a user preset holding
|
// A detached standalone preset for the Full Publish receiver: create a user preset holding
|
||||||
|
|||||||
+39
-1
@@ -14,6 +14,7 @@
|
|||||||
#include "Flow.hpp"
|
#include "Flow.hpp"
|
||||||
#include "Geometry/ConvexHull.hpp"
|
#include "Geometry/ConvexHull.hpp"
|
||||||
#include "I18N.hpp"
|
#include "I18N.hpp"
|
||||||
|
#include "LifecycleEvents.hpp"
|
||||||
#include "ShortestPath.hpp"
|
#include "ShortestPath.hpp"
|
||||||
#include "Thread.hpp"
|
#include "Thread.hpp"
|
||||||
#include "Time.hpp"
|
#include "Time.hpp"
|
||||||
@@ -2694,6 +2695,13 @@ void Print::process(long long *time_cost_with_cache, bool use_cache)
|
|||||||
if (m_objects.empty())
|
if (m_objects.empty())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
{
|
||||||
|
LifecycleEventContext ctx;
|
||||||
|
ctx.name = std::to_string(m_model.id().id);
|
||||||
|
ctx.code = LifecycleEvtCode::Ok;
|
||||||
|
fire_lifecycle_event(LifecycleEvent::SliceStarted, ctx);
|
||||||
|
}
|
||||||
|
|
||||||
for (PrintObject *obj : m_objects)
|
for (PrintObject *obj : m_objects)
|
||||||
obj->clear_shared_object();
|
obj->clear_shared_object();
|
||||||
|
|
||||||
@@ -3312,6 +3320,13 @@ void Print::process(long long *time_cost_with_cache, bool use_cache)
|
|||||||
}
|
}
|
||||||
|
|
||||||
BOOST_LOG_TRIVIAL(info) << "Slicing process finished." << log_memory_info();
|
BOOST_LOG_TRIVIAL(info) << "Slicing process finished." << log_memory_info();
|
||||||
|
|
||||||
|
{
|
||||||
|
LifecycleEventContext ctx;
|
||||||
|
ctx.name = std::to_string(m_model.id().id);
|
||||||
|
ctx.code = LifecycleEvtCode::Ok;
|
||||||
|
fire_lifecycle_event(LifecycleEvent::SliceGeometryFinished, ctx);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// G-code export process, running at a background thread.
|
// G-code export process, running at a background thread.
|
||||||
@@ -4911,6 +4926,14 @@ void Print::set_gcode_file_invalidated()
|
|||||||
//BBS: add gcode file preload logic
|
//BBS: add gcode file preload logic
|
||||||
void Print::export_gcode_from_previous_file(const std::string& file, GCodeProcessorResult* result, ThumbnailsGeneratorCallback thumbnail_cb)
|
void Print::export_gcode_from_previous_file(const std::string& file, GCodeProcessorResult* result, ThumbnailsGeneratorCallback thumbnail_cb)
|
||||||
{
|
{
|
||||||
|
{
|
||||||
|
LifecycleEventContext ctx;
|
||||||
|
ctx.name = std::to_string(m_model.id().id);
|
||||||
|
ctx.code = LifecycleEvtCode::Ok;
|
||||||
|
ctx.msg = file;
|
||||||
|
fire_lifecycle_event(LifecycleEvent::GCodeExportStarted, ctx);
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
GCodeProcessor processor;
|
GCodeProcessor processor;
|
||||||
GCodeProcessor::s_IsBBLPrinter = is_BBL_printer();
|
GCodeProcessor::s_IsBBLPrinter = is_BBL_printer();
|
||||||
@@ -4930,13 +4953,28 @@ void Print::export_gcode_from_previous_file(const std::string& file, GCodeProces
|
|||||||
*result = std::move(processor.extract_result());
|
*result = std::move(processor.extract_result());
|
||||||
result->filament_change_sequence = filament_seq_loaded;
|
result->filament_change_sequence = filament_seq_loaded;
|
||||||
result->nozzle_change_sequence = nozzle_seq_loaded;
|
result->nozzle_change_sequence = nozzle_seq_loaded;
|
||||||
} catch (std::exception & /* ex */) {
|
} catch (std::exception &ex) {
|
||||||
BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << boost::format(": found errors when process gcode file %1%") %file.c_str();
|
BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << boost::format(": found errors when process gcode file %1%") %file.c_str();
|
||||||
|
{
|
||||||
|
LifecycleEventContext ctx;
|
||||||
|
ctx.name = std::to_string(m_model.id().id);
|
||||||
|
ctx.code = LifecycleEvtCode::Error;
|
||||||
|
ctx.msg = file + "\n" + ex.what();
|
||||||
|
fire_lifecycle_event(LifecycleEvent::GCodeExportFinished, ctx);
|
||||||
|
}
|
||||||
throw Slic3r::RuntimeError(
|
throw Slic3r::RuntimeError(
|
||||||
std::string("Failed to process the G-code file ") + file + " from previous 3mf\n");
|
std::string("Failed to process the G-code file ") + file + " from previous 3mf\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(": process the G-code file %1% successfully")%file.c_str();
|
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << boost::format(": process the G-code file %1% successfully")%file.c_str();
|
||||||
|
|
||||||
|
{
|
||||||
|
LifecycleEventContext ctx;
|
||||||
|
ctx.name = std::to_string(m_model.id().id);
|
||||||
|
ctx.code = LifecycleEvtCode::Ok;
|
||||||
|
ctx.msg = file;
|
||||||
|
fire_lifecycle_event(LifecycleEvent::GCodeExportFinished, ctx);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std::tuple<float, float> Print::object_skirt_offset(double margin_height) const
|
std::tuple<float, float> Print::object_skirt_offset(double margin_height) const
|
||||||
|
|||||||
@@ -403,7 +403,7 @@ DesignPanel::DesignPanel(wxWindow* parent)
|
|||||||
// bind to their default mode; the other modes stay in the toolbar flyout. Registered here
|
// bind to their default mode; the other modes stay in the toolbar flyout. Registered here
|
||||||
// where select_tool is in scope; the closures run at key-press time (members are live by then).
|
// where select_tool is in scope; the closures run at key-press time (members are live by then).
|
||||||
auto sk_key = [this, select_tool](int ch, DesignSketchTool::Mode m, const wxString& h) {
|
auto sk_key = [this, select_tool](int ch, DesignSketchTool::Mode m, const wxString& h) {
|
||||||
m_keys_sketch[ch] = [this, select_tool, m, h] { select_tool(m, h); };
|
m_keys_sketch[ch] = [select_tool, m, h] { select_tool(m, h); };
|
||||||
};
|
};
|
||||||
sk_key('L', DesignSketchTool::Mode::Line, _L("Line — click start, then end"));
|
sk_key('L', DesignSketchTool::Mode::Line, _L("Line — click start, then end"));
|
||||||
sk_key('R', DesignSketchTool::Mode::CornerRect, _L("Rectangle — click two opposite corners"));
|
sk_key('R', DesignSketchTool::Mode::CornerRect, _L("Rectangle — click two opposite corners"));
|
||||||
@@ -1445,16 +1445,16 @@ DesignPanel::DesignPanel(wxWindow* parent)
|
|||||||
// Choose FIRST, then open: open_tool() titles the card from m_dressup_type, so setting
|
// Choose FIRST, then open: open_tool() titles the card from m_dressup_type, so setting
|
||||||
// it afterwards left the header reading "Fillet 1" over a chamfer. Nothing in the
|
// it afterwards left the header reading "Fillet 1" over a chamfer. Nothing in the
|
||||||
// opener resets the combo, so this order is safe.
|
// opener resets the combo, so this order is safe.
|
||||||
m_verb_actions["btn:dress#0"] = [this, open_feature, k_dress] {
|
m_verb_actions["btn:dress#0"] = [this, open_feature] {
|
||||||
if (m_dressup_type) m_dressup_type->SetSelection(0); open_feature(k_dress); };
|
if (m_dressup_type) m_dressup_type->SetSelection(0); open_feature(k_dress); };
|
||||||
m_verb_actions["btn:dress#1"] = [this, open_feature, k_dress] {
|
m_verb_actions["btn:dress#1"] = [this, open_feature] {
|
||||||
if (m_dressup_type) m_dressup_type->SetSelection(1); open_feature(k_dress); };
|
if (m_dressup_type) m_dressup_type->SetSelection(1); open_feature(k_dress); };
|
||||||
for (int op = 0; op < 3; ++op)
|
for (int op = 0; op < 3; ++op)
|
||||||
m_verb_actions["btn:bool#" + std::to_string(op)] = [this, open_feature, k_bool, op] {
|
m_verb_actions["btn:bool#" + std::to_string(op)] = [this, open_feature, op] {
|
||||||
open_feature(k_bool);
|
open_feature(k_bool);
|
||||||
if (m_bool_op) { m_bool_op->SetSelection(op); refresh_preview(); } };
|
if (m_bool_op) { m_bool_op->SetSelection(op); refresh_preview(); } };
|
||||||
for (int t = 0; t < 2; ++t)
|
for (int t = 0; t < 2; ++t)
|
||||||
m_verb_actions["btn:pat#" + std::to_string(t)] = [this, open_feature, k_pat, t] {
|
m_verb_actions["btn:pat#" + std::to_string(t)] = [this, open_feature, t] {
|
||||||
open_feature(k_pat); if (m_pattern_type) m_pattern_type->SetSelection(t); };
|
open_feature(k_pat); if (m_pattern_type) m_pattern_type->SetSelection(t); };
|
||||||
|
|
||||||
auto* b_poly = icon_btn("design_polygon", _L("Polygon"));
|
auto* b_poly = icon_btn("design_polygon", _L("Polygon"));
|
||||||
@@ -6625,7 +6625,7 @@ void DesignPanel::show_offer_menu(const wxPoint& screen_pos)
|
|||||||
mate_ghost = false;
|
mate_ghost = false;
|
||||||
};
|
};
|
||||||
menu.Bind(wxEVT_MENU_HIGHLIGHT,
|
menu.Bind(wxEVT_MENU_HIGHLIGHT,
|
||||||
[this, cs_a, cs_b, opts, mate_base, &mate_ghost, drop_ghost](wxMenuEvent& e) {
|
[this, cs_a, cs_b, opts, &mate_ghost, drop_ghost](wxMenuEvent& e) {
|
||||||
const int i = e.GetMenuId() - (mate_base + 1);
|
const int i = e.GetMenuId() - (mate_base + 1);
|
||||||
// Off the palette (a verb row, the header, or nothing) — a stale ghost from the row
|
// Off the palette (a verb row, the header, or nothing) — a stale ghost from the row
|
||||||
// you just left is worse than none, so it goes as soon as the cursor does.
|
// you just left is worse than none, so it goes as soon as the cursor does.
|
||||||
@@ -6635,7 +6635,7 @@ void DesignPanel::show_offer_menu(const wxPoint& screen_pos)
|
|||||||
m_viewport->repaint_now(); // synchronous: the popup owns the loop, a queued repaint is never serviced
|
m_viewport->repaint_now(); // synchronous: the popup owns the loop, a queued repaint is never serviced
|
||||||
});
|
});
|
||||||
|
|
||||||
menu.Bind(wxEVT_MENU, [this, cs_a, cs_b, opts, mate_base, drop_ghost](wxCommandEvent& e) {
|
menu.Bind(wxEVT_MENU, [this, cs_a, cs_b, opts, drop_ghost](wxCommandEvent& e) {
|
||||||
const int i = e.GetId() - (mate_base + 1);
|
const int i = e.GetId() - (mate_base + 1);
|
||||||
if (i < 0 || i >= int(opts.size()) || !opts[i].viable) return;
|
if (i < 0 || i >= int(opts.size()) || !opts[i].viable) return;
|
||||||
drop_ghost(); // the real bodies are about to become the ghost's pose
|
drop_ghost(); // the real bodies are about to become the ghost's pose
|
||||||
@@ -6666,7 +6666,7 @@ void DesignPanel::show_offer_menu(const wxPoint& screen_pos)
|
|||||||
// status message on screen, and clicking one created nothing at all. The whole mate palette
|
// status message on screen, and clicking one created nothing at all. The whole mate palette
|
||||||
// enumerated perfectly and fired nothing. Restricting the range keeps each half to its own ids
|
// enumerated perfectly and fired nothing. Restricting the range keeps each half to its own ids
|
||||||
// regardless of bind order.
|
// regardless of bind order.
|
||||||
menu.Bind(wxEVT_MENU_HIGHLIGHT, [this, &bound, base](wxMenuEvent& e) {
|
menu.Bind(wxEVT_MENU_HIGHLIGHT, [this, &bound](wxMenuEvent& e) {
|
||||||
const int i = e.GetMenuId() - base;
|
const int i = e.GetMenuId() - base;
|
||||||
if (i < 0 || i >= int(bound.size()) || bound[i] == nullptr || bound[i]->hint == nullptr)
|
if (i < 0 || i >= int(bound.size()) || bound[i] == nullptr || bound[i]->hint == nullptr)
|
||||||
return;
|
return;
|
||||||
@@ -6674,7 +6674,7 @@ void DesignPanel::show_offer_menu(const wxPoint& screen_pos)
|
|||||||
set_status(wxGetTranslation(wxString::FromUTF8(bound[i]->hint)));
|
set_status(wxGetTranslation(wxString::FromUTF8(bound[i]->hint)));
|
||||||
m_status->Update(); // the popup owns the loop; without this the line repaints late
|
m_status->Update(); // the popup owns the loop; without this the line repaints late
|
||||||
}, base, base + 499); // 499: the mate section starts at base + 500 (see mate_base)
|
}, base, base + 499); // 499: the mate section starts at base + 500 (see mate_base)
|
||||||
menu.Bind(wxEVT_MENU, [this, &bound, base](wxCommandEvent& e) {
|
menu.Bind(wxEVT_MENU, [this, &bound](wxCommandEvent& e) {
|
||||||
const int i = e.GetId() - base;
|
const int i = e.GetId() - base;
|
||||||
if (i >= 0 && i < int(bound.size()) && bound[i])
|
if (i >= 0 && i < int(bound.size()) && bound[i])
|
||||||
run_offer_action(bound[i]->action);
|
run_offer_action(bound[i]->action);
|
||||||
@@ -7935,7 +7935,7 @@ void DesignPanel::apply_live_constraint(SketchConstraintType type)
|
|||||||
constraint_reject_text(plan.reason, type), int(sel.size())));
|
constraint_reject_text(plan.reason, type), int(sel.size())));
|
||||||
return;
|
return;
|
||||||
case ConstraintPlan::Kind::AskValue:
|
case ConstraintPlan::Kind::AskValue:
|
||||||
m_viewport->open_inline_value(plan.prefill, [this, plan, commit](double v) {
|
m_viewport->open_inline_value(plan.prefill, [plan, commit](double v) {
|
||||||
commit(plan.defs, v);
|
commit(plan.defs, v);
|
||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
|
|||||||
@@ -121,7 +121,7 @@ void SketchInlineEditor::do_commit()
|
|||||||
// just sits there and the user has no idea what it wants. Say so in the title line and
|
// just sits there and the user has no idea what it wants. Say so in the title line and
|
||||||
// keep editing.
|
// keep editing.
|
||||||
ux_trace("refused", m_title, std::string("typed=") + m_buf);
|
ux_trace("refused", m_title, std::string("typed=") + m_buf);
|
||||||
m_err = (m_buf[0] == '\0') ? into_u8(_L("Enter a number")) : into_u8(_L("Not a number"));
|
m_err = (m_buf[0] == '\0') ? _u8L("Enter a number") : _u8L("Not a number");
|
||||||
m_focus_pending = true;
|
m_focus_pending = true;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,6 +10,7 @@
|
|||||||
#include "slic3r/GUI/I18N.hpp"
|
#include "slic3r/GUI/I18N.hpp"
|
||||||
#include "slic3r/GUI/GUI_App.hpp"
|
#include "slic3r/GUI/GUI_App.hpp"
|
||||||
#include "slic3r/GUI/Plater.hpp"
|
#include "slic3r/GUI/Plater.hpp"
|
||||||
|
#include "slic3r/plugin/PluginManager.hpp"
|
||||||
#include "slic3r/Utils/NetworkAgentFactory.hpp"
|
#include "slic3r/Utils/NetworkAgentFactory.hpp"
|
||||||
|
|
||||||
#include "libslic3r/Time.hpp"
|
#include "libslic3r/Time.hpp"
|
||||||
@@ -355,7 +356,10 @@ namespace Slic3r
|
|||||||
obj->bind_state = "free";
|
obj->bind_state = "free";
|
||||||
|
|
||||||
obj->last_alive = Slic3r::Utils::get_current_time_utc();
|
obj->last_alive = Slic3r::Utils::get_current_time_utc();
|
||||||
obj->m_is_online = true;
|
// Route through set_online_state() (rather than writing m_is_online directly) so the
|
||||||
|
// DeviceOnlineChanged lifecycle event fires consistently; same effective value/behavior
|
||||||
|
// here since the object was already online in the common case.
|
||||||
|
obj->set_online_state(true);
|
||||||
obj->set_dev_name(dev_name);
|
obj->set_dev_name(dev_name);
|
||||||
/* if (!obj->dev_ip.empty()) {
|
/* if (!obj->dev_ip.empty()) {
|
||||||
Slic3r::GUI::wxGetApp().app_config->set_str("ip_address", obj->dev_id, obj->dev_ip);
|
Slic3r::GUI::wxGetApp().app_config->set_str("ip_address", obj->dev_id, obj->dev_ip);
|
||||||
@@ -373,6 +377,10 @@ namespace Slic3r
|
|||||||
obj->bind_sec_link = sec_link;
|
obj->bind_sec_link = sec_link;
|
||||||
obj->dev_connection_name = connection_name;
|
obj->dev_connection_name = connection_name;
|
||||||
obj->bind_ssdp_version = ssdp_version;
|
obj->bind_ssdp_version = ssdp_version;
|
||||||
|
// Discovery establishes the initial reachability state. Do not report it as an
|
||||||
|
// online transition; DeviceDiscovered below is the lifecycle event for a new
|
||||||
|
// device. Subsequent updates route through set_online_state(), so a known device
|
||||||
|
// still emits DeviceOnlineChanged when its reachability actually changes.
|
||||||
obj->m_is_online = true;
|
obj->m_is_online = true;
|
||||||
|
|
||||||
//load access code
|
//load access code
|
||||||
@@ -389,6 +397,15 @@ namespace Slic3r
|
|||||||
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << " New Machine, dev_id= " << dev_id
|
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << " New Machine, dev_id= " << dev_id
|
||||||
<< ", ip = " << dev_ip <<", printer_name = " << dev_name
|
<< ", ip = " << dev_ip <<", printer_name = " << dev_name
|
||||||
<< ", con_type= " << connect_type <<", signal= " << printer_signal << ", bind_state= " << bind_state;
|
<< ", con_type= " << connect_type <<", signal= " << printer_signal << ", bind_state= " << bind_state;
|
||||||
|
|
||||||
|
// First discovery of a genuinely new device (not a periodic SSDP/heartbeat update to
|
||||||
|
// an already-known one, which is handled in the branch above).
|
||||||
|
{
|
||||||
|
LifecycleEventContext ctx;
|
||||||
|
ctx.name = dev_id;
|
||||||
|
ctx.code = LifecycleEvtCode::Ok;
|
||||||
|
fire_lifecycle_event(LifecycleEvent::DeviceDiscovered, ctx);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
update_local_machine(*obj);
|
update_local_machine(*obj);
|
||||||
}
|
}
|
||||||
@@ -984,8 +1001,14 @@ namespace Slic3r
|
|||||||
}
|
}
|
||||||
|
|
||||||
void DeviceManager::OnSelectedMachineChanged(const std::string& /*pre_dev_id*/,
|
void DeviceManager::OnSelectedMachineChanged(const std::string& /*pre_dev_id*/,
|
||||||
const std::string& /*new_dev_id*/)
|
const std::string& new_dev_id)
|
||||||
{
|
{
|
||||||
|
{
|
||||||
|
LifecycleEventContext ctx;
|
||||||
|
ctx.name = new_dev_id; // empty string is a valid deselection
|
||||||
|
ctx.code = LifecycleEvtCode::Ok;
|
||||||
|
fire_lifecycle_event(LifecycleEvent::DeviceSelected, ctx);
|
||||||
|
}
|
||||||
if (MachineObject* obj_ = get_selected_machine()) {
|
if (MachineObject* obj_ = get_selected_machine()) {
|
||||||
GUI::wxGetApp().sidebar().update_sync_status(obj_);
|
GUI::wxGetApp().sidebar().update_sync_status(obj_);
|
||||||
if(m_agent->get_filament_sync_mode() == FilamentSyncMode::subscription)
|
if(m_agent->get_filament_sync_mode() == FilamentSyncMode::subscription)
|
||||||
|
|||||||
@@ -5,6 +5,7 @@
|
|||||||
#include "libslic3r/Time.hpp"
|
#include "libslic3r/Time.hpp"
|
||||||
#include "libslic3r/Thread.hpp"
|
#include "libslic3r/Thread.hpp"
|
||||||
#include "slic3r/Utils/NetworkAgent.hpp"
|
#include "slic3r/Utils/NetworkAgent.hpp"
|
||||||
|
#include "slic3r/plugin/PluginManager.hpp"
|
||||||
#include "slic3r/Utils/NetworkAgentFactory.hpp"
|
#include "slic3r/Utils/NetworkAgentFactory.hpp"
|
||||||
#include "GuiColor.hpp"
|
#include "GuiColor.hpp"
|
||||||
|
|
||||||
@@ -2626,7 +2627,15 @@ void MachineObject::reset()
|
|||||||
|
|
||||||
void MachineObject::set_print_state(std::string status)
|
void MachineObject::set_print_state(std::string status)
|
||||||
{
|
{
|
||||||
|
const bool changed = (print_status != status);
|
||||||
print_status = status;
|
print_status = status;
|
||||||
|
if (changed) {
|
||||||
|
LifecycleEventContext ctx;
|
||||||
|
ctx.name = dev_id;
|
||||||
|
ctx.code = LifecycleEvtCode::Ok;
|
||||||
|
ctx.msg = print_status;
|
||||||
|
fire_lifecycle_event(LifecycleEvent::PrintStateChanged, ctx);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int MachineObject::connect(bool use_openssl)
|
int MachineObject::connect(bool use_openssl)
|
||||||
@@ -2648,6 +2657,10 @@ int MachineObject::connect(bool use_openssl)
|
|||||||
int MachineObject::disconnect()
|
int MachineObject::disconnect()
|
||||||
{
|
{
|
||||||
if (m_agent) {
|
if (m_agent) {
|
||||||
|
LifecycleEventContext ctx;
|
||||||
|
ctx.name = dev_id;
|
||||||
|
ctx.code = LifecycleEvtCode::Ok;
|
||||||
|
fire_lifecycle_event(LifecycleEvent::DeviceDisconnected, ctx);
|
||||||
return m_agent->disconnect_printer();
|
return m_agent->disconnect_printer();
|
||||||
}
|
}
|
||||||
return -1;
|
return -1;
|
||||||
@@ -2678,8 +2691,16 @@ bool MachineObject::is_connecting()
|
|||||||
|
|
||||||
void MachineObject::set_online_state(bool on_off)
|
void MachineObject::set_online_state(bool on_off)
|
||||||
{
|
{
|
||||||
|
const bool changed = (m_is_online != on_off);
|
||||||
m_is_online = on_off;
|
m_is_online = on_off;
|
||||||
if (!on_off) m_active_state = NotActive;
|
if (!on_off) m_active_state = NotActive;
|
||||||
|
if (changed) {
|
||||||
|
LifecycleEventContext ctx;
|
||||||
|
ctx.name = dev_id;
|
||||||
|
ctx.code = LifecycleEvtCode::Ok;
|
||||||
|
ctx.msg = on_off ? "online" : "offline";
|
||||||
|
fire_lifecycle_event(LifecycleEvent::DeviceOnlineChanged, ctx);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool MachineObject::is_info_ready(bool check_version) const
|
bool MachineObject::is_info_ready(bool check_version) const
|
||||||
@@ -4601,8 +4622,13 @@ int MachineObject::parse_json(std::string tunnel, std::string payload, bool key_
|
|||||||
try {
|
try {
|
||||||
if (j.contains("event")) {
|
if (j.contains("event")) {
|
||||||
if (j["event"].contains("event")) {
|
if (j["event"].contains("event")) {
|
||||||
if (j["event"]["event"].get<std::string>() == "client.disconnected")
|
if (j["event"]["event"].get<std::string>() == "client.disconnected") {
|
||||||
set_online_state(false);
|
set_online_state(false);
|
||||||
|
LifecycleEventContext ctx;
|
||||||
|
ctx.name = dev_id;
|
||||||
|
ctx.code = LifecycleEvtCode::Ok;
|
||||||
|
fire_lifecycle_event(LifecycleEvent::DeviceDisconnected, ctx);
|
||||||
|
}
|
||||||
else if (j["event"]["event"].get<std::string>() == "client.connected")
|
else if (j["event"]["event"].get<std::string>() == "client.connected")
|
||||||
set_online_state(true);
|
set_online_state(true);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -42,6 +42,7 @@
|
|||||||
#include "slic3r/GUI/Gizmos/GLGizmoPainterBase.hpp"
|
#include "slic3r/GUI/Gizmos/GLGizmoPainterBase.hpp"
|
||||||
#include "slic3r/Utils/UndoRedo.hpp"
|
#include "slic3r/Utils/UndoRedo.hpp"
|
||||||
#include "slic3r/Utils/MacDarkMode.hpp"
|
#include "slic3r/Utils/MacDarkMode.hpp"
|
||||||
|
#include "slic3r/plugin/PluginManager.hpp"
|
||||||
|
|
||||||
#include <slic3r/GUI/GUI_Utils.hpp>
|
#include <slic3r/GUI/GUI_Utils.hpp>
|
||||||
|
|
||||||
@@ -5208,8 +5209,16 @@ void GLCanvas3D::do_move(const std::string& snapshot_type)
|
|||||||
//BBS: nofity object list to update
|
//BBS: nofity object list to update
|
||||||
wxGetApp().plater()->sidebar().obj_list()->update_plate_values_for_items();
|
wxGetApp().plater()->sidebar().obj_list()->update_plate_values_for_items();
|
||||||
|
|
||||||
if (object_moved)
|
if (object_moved) {
|
||||||
|
Slic3r::LifecycleEventContext ctx;
|
||||||
|
ctx.code = Slic3r::LifecycleEvtCode::Ok;
|
||||||
|
ctx.msg = "moved";
|
||||||
|
if (done.size() == 1)
|
||||||
|
ctx.name = m_model->objects[done.begin()->first]->name;
|
||||||
|
Slic3r::fire_lifecycle_event(Slic3r::LifecycleEvent::ObjectTransformed, ctx);
|
||||||
|
|
||||||
post_event(SimpleEvent(EVT_GLCANVAS_INSTANCE_MOVED));
|
post_event(SimpleEvent(EVT_GLCANVAS_INSTANCE_MOVED));
|
||||||
|
}
|
||||||
|
|
||||||
// BBS: support wipe-tower for multi-plates
|
// BBS: support wipe-tower for multi-plates
|
||||||
for (int plate_id = 0; plate_id < wipe_tower_origins.size(); plate_id++) {
|
for (int plate_id = 0; plate_id < wipe_tower_origins.size(); plate_id++) {
|
||||||
@@ -5330,8 +5339,16 @@ void GLCanvas3D::do_rotate(const std::string& snapshot_type)
|
|||||||
//BBS: nofity object list to update
|
//BBS: nofity object list to update
|
||||||
wxGetApp().plater()->sidebar().obj_list()->update_plate_values_for_items();
|
wxGetApp().plater()->sidebar().obj_list()->update_plate_values_for_items();
|
||||||
|
|
||||||
if (!done.empty())
|
if (!done.empty()) {
|
||||||
|
Slic3r::LifecycleEventContext ctx;
|
||||||
|
ctx.code = Slic3r::LifecycleEvtCode::Ok;
|
||||||
|
ctx.msg = "rotated";
|
||||||
|
if (done.size() == 1)
|
||||||
|
ctx.name = m_model->objects[done.begin()->first]->name;
|
||||||
|
Slic3r::fire_lifecycle_event(Slic3r::LifecycleEvent::ObjectTransformed, ctx);
|
||||||
|
|
||||||
post_event(SimpleEvent(EVT_GLCANVAS_INSTANCE_ROTATED));
|
post_event(SimpleEvent(EVT_GLCANVAS_INSTANCE_ROTATED));
|
||||||
|
}
|
||||||
|
|
||||||
m_dirty = true;
|
m_dirty = true;
|
||||||
}
|
}
|
||||||
@@ -5422,8 +5439,16 @@ void GLCanvas3D::do_scale(const std::string& snapshot_type)
|
|||||||
//BBS: notify object info update
|
//BBS: notify object info update
|
||||||
wxGetApp().plater()->show_object_info();
|
wxGetApp().plater()->show_object_info();
|
||||||
|
|
||||||
if (!done.empty())
|
if (!done.empty()) {
|
||||||
|
Slic3r::LifecycleEventContext ctx;
|
||||||
|
ctx.code = Slic3r::LifecycleEvtCode::Ok;
|
||||||
|
ctx.msg = "scaled";
|
||||||
|
if (done.size() == 1)
|
||||||
|
ctx.name = m_model->objects[done.begin()->first]->name;
|
||||||
|
Slic3r::fire_lifecycle_event(Slic3r::LifecycleEvent::ObjectTransformed, ctx);
|
||||||
|
|
||||||
post_event(SimpleEvent(EVT_GLCANVAS_INSTANCE_SCALED));
|
post_event(SimpleEvent(EVT_GLCANVAS_INSTANCE_SCALED));
|
||||||
|
}
|
||||||
|
|
||||||
m_dirty = true;
|
m_dirty = true;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2179,6 +2179,11 @@ void GUI_App::init_networking_callbacks()
|
|||||||
obj->command_get_access_code();
|
obj->command_get_access_code();
|
||||||
if (m_agent)
|
if (m_agent)
|
||||||
m_agent->install_device_cert(obj->get_dev_id(), obj->is_lan_mode_printer());
|
m_agent->install_device_cert(obj->get_dev_id(), obj->is_lan_mode_printer());
|
||||||
|
|
||||||
|
LifecycleEventContext ctx;
|
||||||
|
ctx.name = obj->get_dev_id();
|
||||||
|
ctx.code = LifecycleEvtCode::Ok;
|
||||||
|
fire_lifecycle_event(LifecycleEvent::DeviceConnected, ctx);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -2217,6 +2222,11 @@ void GUI_App::init_networking_callbacks()
|
|||||||
obj->command_get_version();
|
obj->command_get_version();
|
||||||
event.SetInt(0);
|
event.SetInt(0);
|
||||||
event.SetString(obj->get_dev_id());
|
event.SetString(obj->get_dev_id());
|
||||||
|
|
||||||
|
LifecycleEventContext ctx;
|
||||||
|
ctx.name = obj->get_dev_id();
|
||||||
|
ctx.code = LifecycleEvtCode::Ok;
|
||||||
|
fire_lifecycle_event(LifecycleEvent::DeviceConnected, ctx);
|
||||||
} else if (state == ConnectStatus::ConnectStatusFailed) {
|
} else if (state == ConnectStatus::ConnectStatusFailed) {
|
||||||
// Orca: only update status if same device id
|
// Orca: only update status if same device id
|
||||||
if (m_device_manager->selected_machine != dev_id) return;
|
if (m_device_manager->selected_machine != dev_id) return;
|
||||||
@@ -2232,10 +2242,22 @@ void GUI_App::init_networking_callbacks()
|
|||||||
wxGetApp().show_dialog(text);
|
wxGetApp().show_dialog(text);
|
||||||
}
|
}
|
||||||
event.SetInt(-1);
|
event.SetInt(-1);
|
||||||
|
|
||||||
|
{
|
||||||
|
LifecycleEventContext ctx;
|
||||||
|
ctx.name = dev_id;
|
||||||
|
ctx.code = LifecycleEvtCode::Ok;
|
||||||
|
fire_lifecycle_event(LifecycleEvent::DeviceDisconnected, ctx);
|
||||||
|
}
|
||||||
} else if (state == ConnectStatus::ConnectStatusLost) {
|
} else if (state == ConnectStatus::ConnectStatusLost) {
|
||||||
m_device_manager->set_selected_machine("");
|
m_device_manager->set_selected_machine("");
|
||||||
event.SetInt(-1);
|
event.SetInt(-1);
|
||||||
BOOST_LOG_TRIVIAL(info) << "set_on_local_connect_fn: state = lost";
|
BOOST_LOG_TRIVIAL(info) << "set_on_local_connect_fn: state = lost";
|
||||||
|
|
||||||
|
LifecycleEventContext ctx;
|
||||||
|
ctx.name = dev_id;
|
||||||
|
ctx.code = LifecycleEvtCode::Ok;
|
||||||
|
fire_lifecycle_event(LifecycleEvent::DeviceDisconnected, ctx);
|
||||||
} else {
|
} else {
|
||||||
event.SetInt(-1);
|
event.SetInt(-1);
|
||||||
BOOST_LOG_TRIVIAL(info) << "set_on_local_connect_fn: state = " << state;
|
BOOST_LOG_TRIVIAL(info) << "set_on_local_connect_fn: state = " << state;
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
#include "libslic3r/libslic3r.h"
|
#include "libslic3r/libslic3r.h"
|
||||||
#include "libslic3r/PresetBundle.hpp"
|
#include "libslic3r/PresetBundle.hpp"
|
||||||
|
#include "libslic3r/LifecycleEvents.hpp"
|
||||||
#include "GUI_ObjectList.hpp"
|
#include "GUI_ObjectList.hpp"
|
||||||
#include "GUI_Factories.hpp"
|
#include "GUI_Factories.hpp"
|
||||||
//#include "GUI_ObjectLayers.hpp"
|
//#include "GUI_ObjectLayers.hpp"
|
||||||
@@ -9,6 +10,7 @@
|
|||||||
#include "BitmapComboBox.hpp"
|
#include "BitmapComboBox.hpp"
|
||||||
#include "MainFrame.hpp"
|
#include "MainFrame.hpp"
|
||||||
#include "slic3r/Utils/UndoRedo.hpp"
|
#include "slic3r/Utils/UndoRedo.hpp"
|
||||||
|
#include "slic3r/plugin/PluginManager.hpp"
|
||||||
|
|
||||||
#include "OptionsGroup.hpp"
|
#include "OptionsGroup.hpp"
|
||||||
#include "Tab.hpp"
|
#include "Tab.hpp"
|
||||||
@@ -1199,17 +1201,39 @@ void ObjectList::update_name_in_model(const wxDataViewItem& item) const
|
|||||||
if (m_objects_model->GetItemType(item) & itObject) {
|
if (m_objects_model->GetItemType(item) & itObject) {
|
||||||
std::string name = m_objects_model->GetName(item).ToUTF8().data();
|
std::string name = m_objects_model->GetName(item).ToUTF8().data();
|
||||||
if (obj->name != name) {
|
if (obj->name != name) {
|
||||||
|
const std::string previous_name = obj->name;
|
||||||
obj->name = name;
|
obj->name = name;
|
||||||
// if object has just one volume, rename this volume too
|
// if object has just one volume, rename this volume too
|
||||||
if (obj->volumes.size() == 1)
|
if (obj->volumes.size() == 1)
|
||||||
obj->volumes[0]->name = obj->name;
|
obj->volumes[0]->name = obj->name;
|
||||||
Slic3r::save_object_mesh(*obj);
|
Slic3r::save_object_mesh(*obj);
|
||||||
|
|
||||||
|
LifecycleEventContext ctx;
|
||||||
|
ctx.name = name;
|
||||||
|
ctx.previous_name = previous_name;
|
||||||
|
ctx.id = std::to_string(obj->id().id);
|
||||||
|
ctx.index = obj_idx;
|
||||||
|
ctx.source = "object";
|
||||||
|
fire_lifecycle_event(LifecycleEvent::ObjectRenamed, ctx);
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (volume_id < 0) return;
|
if (volume_id < 0) return;
|
||||||
obj->volumes[volume_id]->name = m_objects_model->GetName(item).ToUTF8().data();
|
std::string name = m_objects_model->GetName(item).ToUTF8().data();
|
||||||
|
if (obj->volumes[volume_id]->name == name)
|
||||||
|
return;
|
||||||
|
|
||||||
|
const std::string previous_name = obj->volumes[volume_id]->name;
|
||||||
|
obj->volumes[volume_id]->name = name;
|
||||||
|
|
||||||
|
LifecycleEventContext ctx;
|
||||||
|
ctx.name = name;
|
||||||
|
ctx.previous_name = previous_name;
|
||||||
|
ctx.id = std::to_string(obj->volumes[volume_id]->id().id);
|
||||||
|
ctx.index = volume_id;
|
||||||
|
ctx.source = "volume";
|
||||||
|
fire_lifecycle_event(LifecycleEvent::ObjectRenamed, ctx);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ObjectList::update_name_in_list(int obj_idx, int vol_idx) const
|
void ObjectList::update_name_in_list(int obj_idx, int vol_idx) const
|
||||||
@@ -3548,7 +3572,14 @@ void ObjectList::delete_all_connectors_for_object(int obj_idx)
|
|||||||
obj->delete_connectors();
|
obj->delete_connectors();
|
||||||
|
|
||||||
if (obj->volumes.empty() || !obj->has_solid_mesh()) {
|
if (obj->volumes.empty() || !obj->has_solid_mesh()) {
|
||||||
|
const std::string deleted_obj_name = obj->name;
|
||||||
model.delete_object(idx);
|
model.delete_object(idx);
|
||||||
|
{
|
||||||
|
Slic3r::LifecycleEventContext ctx;
|
||||||
|
ctx.name = deleted_obj_name;
|
||||||
|
ctx.code = Slic3r::LifecycleEvtCode::Ok;
|
||||||
|
Slic3r::fire_lifecycle_event(Slic3r::LifecycleEvent::ObjectDeleted, ctx);
|
||||||
|
}
|
||||||
m_objects_model->Delete(m_objects_model->GetItemById(idx));
|
m_objects_model->Delete(m_objects_model->GetItemById(idx));
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@@ -4117,6 +4148,13 @@ void ObjectList::add_object_to_list(size_t obj_idx, bool call_selection_changed,
|
|||||||
const auto item = m_objects_model->AddObject(model_object, warning_bitmap, model_object->is_cut());
|
const auto item = m_objects_model->AddObject(model_object, warning_bitmap, model_object->is_cut());
|
||||||
Expand(m_objects_model->GetParent(item));
|
Expand(m_objects_model->GetParent(item));
|
||||||
|
|
||||||
|
{
|
||||||
|
Slic3r::LifecycleEventContext ctx;
|
||||||
|
ctx.name = model_object->name;
|
||||||
|
ctx.code = Slic3r::LifecycleEvtCode::Ok;
|
||||||
|
Slic3r::fire_lifecycle_event(Slic3r::LifecycleEvent::ObjectAdded, ctx);
|
||||||
|
}
|
||||||
|
|
||||||
if (!do_info_update)
|
if (!do_info_update)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
|||||||
@@ -15,7 +15,6 @@
|
|||||||
#endif
|
#endif
|
||||||
#include <imgui/imgui_internal.h>
|
#include <imgui/imgui_internal.h>
|
||||||
|
|
||||||
#define L(s) Slic3r::GUI::I18N::translate((s)).c_str()
|
|
||||||
#define UL(s) Slic3r::GUI::I18N::translate_utf8((s)).c_str()
|
#define UL(s) Slic3r::GUI::I18N::translate_utf8((s)).c_str()
|
||||||
|
|
||||||
namespace Slic3r {
|
namespace Slic3r {
|
||||||
@@ -374,15 +373,15 @@ void GLGizmoSketch::on_render_input_window(float x, float y, float bottom_limit)
|
|||||||
};
|
};
|
||||||
|
|
||||||
if (m_sp.is_pocket && has_sel) {
|
if (m_sp.is_pocket && has_sel) {
|
||||||
if (btn(L("Pocket (Cut)"), ok)) apply_pocket();
|
if (btn(_u8L("Pocket (Cut)").c_str(), ok)) apply_pocket();
|
||||||
} else if (is_revolve) {
|
} else if (is_revolve) {
|
||||||
if (btn(L("Revolve"), ok)) apply_revolve();
|
if (btn(_u8L("Revolve").c_str(), ok)) apply_revolve();
|
||||||
} else {
|
} else {
|
||||||
if (btn(L("Extrude"), ok)) apply_extrude();
|
if (btn(_u8L("Extrude").c_str(), ok)) apply_extrude();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ImGui::Button(L("Clear All"), {-1,0})) clear_all();
|
if (ImGui::Button(_u8L("Clear All").c_str(), {-1,0})) clear_all();
|
||||||
if (ImGui::Button(L("Close"), {-1,0})) m_parent.reset_all_gizmos();
|
if (ImGui::Button(_u8L("Close").c_str(), {-1,0})) m_parent.reset_all_gizmos();
|
||||||
|
|
||||||
GizmoImguiEnd();
|
GizmoImguiEnd();
|
||||||
ImGuiWrapper::pop_toolbar_style();
|
ImGuiWrapper::pop_toolbar_style();
|
||||||
|
|||||||
@@ -13,6 +13,7 @@
|
|||||||
#include "slic3r/GUI/NotificationManager.hpp"
|
#include "slic3r/GUI/NotificationManager.hpp"
|
||||||
#include "slic3r/GUI/format.hpp"
|
#include "slic3r/GUI/format.hpp"
|
||||||
#include "slic3r/GUI/GUI_ObjectList.hpp"
|
#include "slic3r/GUI/GUI_ObjectList.hpp"
|
||||||
|
#include "slic3r/plugin/PluginManager.hpp"
|
||||||
|
|
||||||
#include "libnest2d/common.hpp"
|
#include "libnest2d/common.hpp"
|
||||||
|
|
||||||
@@ -698,6 +699,13 @@ void ArrangeJob::finalize(bool canceled, std::exception_ptr &eptr) {
|
|||||||
BOOST_LOG_TRIVIAL(debug) << __FUNCTION__ << boost::format(":arrange m_unprintable: name: %4%, bed_id %1%, trans {%2%,%3%}") % ap.bed_idx % unscale<double>(ap.translation(X)) % unscale<double>(ap.translation(Y)) % ap.name;
|
BOOST_LOG_TRIVIAL(debug) << __FUNCTION__ << boost::format(":arrange m_unprintable: name: %4%, bed_id %1%, trans {%2%,%3%}") % ap.bed_idx % unscale<double>(ap.translation(X)) % unscale<double>(ap.translation(Y)) % ap.name;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
Slic3r::LifecycleEventContext ctx;
|
||||||
|
ctx.code = Slic3r::LifecycleEvtCode::Ok;
|
||||||
|
ctx.msg = "arranged";
|
||||||
|
Slic3r::fire_lifecycle_event(Slic3r::LifecycleEvent::ObjectTransformed, ctx);
|
||||||
|
}
|
||||||
|
|
||||||
m_plater->update();
|
m_plater->update();
|
||||||
// BBS
|
// BBS
|
||||||
//wxGetApp().obj_manipul()->set_dirty();
|
//wxGetApp().obj_manipul()->set_dirty();
|
||||||
|
|||||||
@@ -6,6 +6,7 @@
|
|||||||
#include "slic3r/GUI/Plater.hpp"
|
#include "slic3r/GUI/Plater.hpp"
|
||||||
#include "slic3r/GUI/GLCanvas3D.hpp"
|
#include "slic3r/GUI/GLCanvas3D.hpp"
|
||||||
#include "slic3r/GUI/GUI_ObjectList.hpp"
|
#include "slic3r/GUI/GUI_ObjectList.hpp"
|
||||||
|
#include "slic3r/plugin/PluginManager.hpp"
|
||||||
#include "libnest2d/common.hpp"
|
#include "libnest2d/common.hpp"
|
||||||
|
|
||||||
#include <numeric>
|
#include <numeric>
|
||||||
@@ -347,6 +348,13 @@ void FillBedJob::finalize(bool canceled, std::exception_ptr &eptr)
|
|||||||
m_plater->arrange();
|
m_plater->arrange();
|
||||||
}
|
}
|
||||||
m_plater->update();
|
m_plater->update();
|
||||||
|
|
||||||
|
{
|
||||||
|
Slic3r::LifecycleEventContext ctx;
|
||||||
|
ctx.code = Slic3r::LifecycleEvtCode::Ok;
|
||||||
|
ctx.msg = "arranged";
|
||||||
|
Slic3r::fire_lifecycle_event(Slic3r::LifecycleEvent::ObjectTransformed, ctx);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
m_plater->mark_plate_toolbar_image_dirty();
|
m_plater->mark_plate_toolbar_image_dirty();
|
||||||
|
|||||||
@@ -5,6 +5,7 @@
|
|||||||
#include "slic3r/GUI/GUI.hpp"
|
#include "slic3r/GUI/GUI.hpp"
|
||||||
#include "slic3r/GUI/GUI_App.hpp"
|
#include "slic3r/GUI/GUI_App.hpp"
|
||||||
#include "slic3r/GUI/NotificationManager.hpp"
|
#include "slic3r/GUI/NotificationManager.hpp"
|
||||||
|
#include "slic3r/plugin/PluginManager.hpp"
|
||||||
#include "libslic3r/PresetBundle.hpp"
|
#include "libslic3r/PresetBundle.hpp"
|
||||||
|
|
||||||
|
|
||||||
@@ -254,6 +255,12 @@ void OrientJob::finalize(bool canceled, std::exception_ptr &eptr)
|
|||||||
mesh.apply();
|
mesh.apply();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!m_selected.empty()) {
|
||||||
|
Slic3r::LifecycleEventContext ctx;
|
||||||
|
ctx.code = Slic3r::LifecycleEvtCode::Ok;
|
||||||
|
ctx.msg = "auto_oriented";
|
||||||
|
Slic3r::fire_lifecycle_event(Slic3r::LifecycleEvent::ObjectTransformed, ctx);
|
||||||
|
}
|
||||||
|
|
||||||
m_plater->update();
|
m_plater->update();
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
#include "PrintJob.hpp"
|
#include "PrintJob.hpp"
|
||||||
|
#include "libslic3r/LifecycleEvents.hpp"
|
||||||
#include "libslic3r/MTUtils.hpp"
|
#include "libslic3r/MTUtils.hpp"
|
||||||
#include "libslic3r/Model.hpp"
|
#include "libslic3r/Model.hpp"
|
||||||
#include "libslic3r/PresetBundle.hpp"
|
#include "libslic3r/PresetBundle.hpp"
|
||||||
@@ -133,6 +134,13 @@ wxString PrintJob::get_http_error_msg(unsigned int status, std::string body)
|
|||||||
|
|
||||||
void PrintJob::process(Ctl &ctl)
|
void PrintJob::process(Ctl &ctl)
|
||||||
{
|
{
|
||||||
|
LifecycleEventContext start_ctx;
|
||||||
|
start_ctx.name = m_project_name;
|
||||||
|
start_ctx.device_id = m_dev_id;
|
||||||
|
start_ctx.source = "print_job";
|
||||||
|
fire_lifecycle_event(LifecycleEvent::PrintJobStarted, start_ctx);
|
||||||
|
m_lifecycle_started = true;
|
||||||
|
|
||||||
/* display info */
|
/* display info */
|
||||||
std::string msg;
|
std::string msg;
|
||||||
int curr_percent = 10;
|
int curr_percent = 10;
|
||||||
@@ -686,6 +694,7 @@ void PrintJob::process(Ctl &ctl)
|
|||||||
}
|
}
|
||||||
wxQueueEvent(m_plater, evt);
|
wxQueueEvent(m_plater, evt);
|
||||||
m_job_finished = true;
|
m_job_finished = true;
|
||||||
|
m_lifecycle_success = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -698,6 +707,19 @@ void PrintJob::finalize(bool canceled, std::exception_ptr &eptr) {
|
|||||||
eptr = std::current_exception();
|
eptr = std::current_exception();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (m_lifecycle_started && !m_lifecycle_finished) {
|
||||||
|
LifecycleEventContext finish_ctx;
|
||||||
|
finish_ctx.name = m_project_name;
|
||||||
|
finish_ctx.device_id = m_dev_id;
|
||||||
|
finish_ctx.source = "print_job";
|
||||||
|
finish_ctx.code = canceled ? LifecycleEvtCode::Warn :
|
||||||
|
(eptr || !m_lifecycle_success ? LifecycleEvtCode::Error : LifecycleEvtCode::Ok);
|
||||||
|
finish_ctx.msg = canceled ? "cancelled" : (eptr ? "exception" :
|
||||||
|
(m_lifecycle_success ? "" : "failed"));
|
||||||
|
fire_lifecycle_event(LifecycleEvent::PrintJobFinished, finish_ctx);
|
||||||
|
m_lifecycle_finished = true;
|
||||||
|
}
|
||||||
|
|
||||||
if (canceled || eptr)
|
if (canceled || eptr)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -43,6 +43,9 @@ class PrintJob : public Job
|
|||||||
std::function<void()> m_success_fun{nullptr};
|
std::function<void()> m_success_fun{nullptr};
|
||||||
std::string m_dev_id;
|
std::string m_dev_id;
|
||||||
bool m_job_finished{ false };
|
bool m_job_finished{ false };
|
||||||
|
bool m_lifecycle_started{ false };
|
||||||
|
bool m_lifecycle_finished{ false };
|
||||||
|
bool m_lifecycle_success{ false };
|
||||||
int m_print_job_completed_id = 0;
|
int m_print_job_completed_id = 0;
|
||||||
wxString m_completed_evt_data;
|
wxString m_completed_evt_data;
|
||||||
std::function<void()> m_enter_ip_address_fun_fail{ nullptr };
|
std::function<void()> m_enter_ip_address_fun_fail{ nullptr };
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
#include "SendJob.hpp"
|
#include "SendJob.hpp"
|
||||||
|
#include "libslic3r/LifecycleEvents.hpp"
|
||||||
#include "slic3r/GUI/I18N.hpp"
|
#include "slic3r/GUI/I18N.hpp"
|
||||||
#include "slic3r/Utils/NetworkAgent.hpp"
|
#include "slic3r/Utils/NetworkAgent.hpp"
|
||||||
#include "libslic3r/MTUtils.hpp"
|
#include "libslic3r/MTUtils.hpp"
|
||||||
@@ -148,6 +149,13 @@ void SendJob::process(Ctl &ctl)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
LifecycleEventContext start_ctx;
|
||||||
|
start_ctx.name = m_project_name;
|
||||||
|
start_ctx.device_id = m_dev_id;
|
||||||
|
start_ctx.source = "send_job";
|
||||||
|
fire_lifecycle_event(LifecycleEvent::SendJobStarted, start_ctx);
|
||||||
|
m_lifecycle_started = true;
|
||||||
|
|
||||||
int total_plate_num = m_plater->get_partplate_list().get_plate_count();
|
int total_plate_num = m_plater->get_partplate_list().get_plate_count();
|
||||||
|
|
||||||
PartPlate* plate = m_plater->get_partplate_list().get_plate(job_data.plate_idx);
|
PartPlate* plate = m_plater->get_partplate_list().get_plate(job_data.plate_idx);
|
||||||
@@ -424,6 +432,19 @@ void SendJob::finalize(bool canceled, std::exception_ptr &eptr)
|
|||||||
eptr = std::current_exception();
|
eptr = std::current_exception();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (m_lifecycle_started && !m_lifecycle_finished) {
|
||||||
|
LifecycleEventContext finish_ctx;
|
||||||
|
finish_ctx.name = m_project_name;
|
||||||
|
finish_ctx.device_id = m_dev_id;
|
||||||
|
finish_ctx.source = "send_job";
|
||||||
|
finish_ctx.code = canceled ? LifecycleEvtCode::Warn :
|
||||||
|
(eptr ? LifecycleEvtCode::Error : (m_job_finished ? LifecycleEvtCode::Ok : LifecycleEvtCode::Error));
|
||||||
|
finish_ctx.msg = canceled ? "cancelled" : (eptr ? "exception" :
|
||||||
|
(m_job_finished ? "" : "failed"));
|
||||||
|
fire_lifecycle_event(LifecycleEvent::SendJobFinished, finish_ctx);
|
||||||
|
m_lifecycle_finished = true;
|
||||||
|
}
|
||||||
|
|
||||||
if (canceled || eptr)
|
if (canceled || eptr)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,6 +22,8 @@ class SendJob : public Job
|
|||||||
PrintPrepareData job_data;
|
PrintPrepareData job_data;
|
||||||
std::string m_dev_id;
|
std::string m_dev_id;
|
||||||
bool m_job_finished{ false };
|
bool m_job_finished{ false };
|
||||||
|
bool m_lifecycle_started{ false };
|
||||||
|
bool m_lifecycle_finished{ false };
|
||||||
int m_print_job_completed_id = 0;
|
int m_print_job_completed_id = 0;
|
||||||
bool m_is_check_mode{false};
|
bool m_is_check_mode{false};
|
||||||
bool m_check_and_continue{false};
|
bool m_check_and_continue{false};
|
||||||
|
|||||||
@@ -29,6 +29,7 @@
|
|||||||
#include "libslic3r/Tesselate.hpp"
|
#include "libslic3r/Tesselate.hpp"
|
||||||
#include "libslic3r/GCode/ThumbnailData.hpp"
|
#include "libslic3r/GCode/ThumbnailData.hpp"
|
||||||
#include "libslic3r/Utils.hpp"
|
#include "libslic3r/Utils.hpp"
|
||||||
|
#include "libslic3r/LifecycleEvents.hpp"
|
||||||
|
|
||||||
#include "I18N.hpp"
|
#include "I18N.hpp"
|
||||||
#include "GUI_App.hpp"
|
#include "GUI_App.hpp"
|
||||||
@@ -2636,11 +2637,20 @@ void PartPlate::set_plate_name(const std::string& name)
|
|||||||
if (boost::equals(m_name, name))
|
if (boost::equals(m_name, name))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
const std::string previous_name = m_name;
|
||||||
m_name = name;
|
m_name = name;
|
||||||
if (m_print != nullptr)
|
if (m_print != nullptr)
|
||||||
m_print->set_plate_name(name);
|
m_print->set_plate_name(name);
|
||||||
|
|
||||||
invalidate_plate_name_texture();
|
invalidate_plate_name_texture();
|
||||||
|
|
||||||
|
if (m_plater != nullptr && !m_plater->is_loading_project()) {
|
||||||
|
LifecycleEventContext ctx;
|
||||||
|
ctx.name = name;
|
||||||
|
ctx.previous_name = previous_name;
|
||||||
|
ctx.index = m_plate_index;
|
||||||
|
fire_lifecycle_event(LifecycleEvent::PlateRenamed, ctx);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//get the print's object, result and index
|
//get the print's object, result and index
|
||||||
@@ -4751,9 +4761,16 @@ int PartPlateList::create_plate(bool adjust_position)
|
|||||||
|
|
||||||
if (m_plater) {
|
if (m_plater) {
|
||||||
// In GUI mode
|
// In GUI mode
|
||||||
wxGetApp().obj_list()->on_plate_added(plate);
|
wxGetApp().obj_list()->on_plate_added(plate);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (m_plater != nullptr && m_intialized && !m_plater->is_loading_project()) {
|
||||||
|
LifecycleEventContext ctx;
|
||||||
|
ctx.name = plate->get_plate_name();
|
||||||
|
ctx.index = new_index;
|
||||||
|
fire_lifecycle_event(LifecycleEvent::PlateCreated, ctx);
|
||||||
|
}
|
||||||
|
|
||||||
BOOST_LOG_TRIVIAL(debug) << __FUNCTION__ << boost::format(":created a new plate %1%") % new_index;
|
BOOST_LOG_TRIVIAL(debug) << __FUNCTION__ << boost::format(":created a new plate %1%") % new_index;
|
||||||
return new_index;
|
return new_index;
|
||||||
}
|
}
|
||||||
@@ -4852,6 +4869,7 @@ int PartPlateList::delete_plate(int index)
|
|||||||
BOOST_LOG_TRIVIAL(warning) << __FUNCTION__ << boost::format(":plate %1%, has an invalid index %2%") % index % plate->get_index();
|
BOOST_LOG_TRIVIAL(warning) << __FUNCTION__ << boost::format(":plate %1%, has an invalid index %2%") % index % plate->get_index();
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
const std::string plate_name = plate->get_plate_name();
|
||||||
|
|
||||||
if (m_plater) {
|
if (m_plater) {
|
||||||
// In GUI mode
|
// In GUI mode
|
||||||
@@ -4934,6 +4952,13 @@ int PartPlateList::delete_plate(int index)
|
|||||||
|
|
||||||
delete plate;
|
delete plate;
|
||||||
|
|
||||||
|
if (m_plater != nullptr && m_intialized && !m_plater->is_loading_project()) {
|
||||||
|
LifecycleEventContext ctx;
|
||||||
|
ctx.name = plate_name;
|
||||||
|
ctx.index = index;
|
||||||
|
fire_lifecycle_event(LifecycleEvent::PlateDeleted, ctx);
|
||||||
|
}
|
||||||
|
|
||||||
// FIX: context of BackgroundSliceProcess and gcode preview need to be updated before ObjectList::reload_all_plates().
|
// FIX: context of BackgroundSliceProcess and gcode preview need to be updated before ObjectList::reload_all_plates().
|
||||||
#if 0
|
#if 0
|
||||||
if (m_plater != nullptr) {
|
if (m_plater != nullptr) {
|
||||||
@@ -5042,6 +5067,7 @@ int PartPlateList::select_plate(int index)
|
|||||||
if (m_plate_list.empty() || index >= m_plate_list.size()) {
|
if (m_plate_list.empty() || index >= m_plate_list.size()) {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
const int previous_index = m_current_plate;
|
||||||
|
|
||||||
// BBS: erase unnecessary snapshot
|
// BBS: erase unnecessary snapshot
|
||||||
if (get_curr_plate_index() != index && m_intialized) {
|
if (get_curr_plate_index() != index && m_intialized) {
|
||||||
@@ -5068,6 +5094,13 @@ int PartPlateList::select_plate(int index)
|
|||||||
//wxQueueEvent(m_plater, new SimpleEvent(EVT_GLCANVAS_PLATE_SELECT));
|
//wxQueueEvent(m_plater, new SimpleEvent(EVT_GLCANVAS_PLATE_SELECT));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (previous_index != index && m_intialized && m_plater != nullptr && !m_plater->is_loading_project()) {
|
||||||
|
LifecycleEventContext ctx;
|
||||||
|
ctx.name = m_plate_list[index]->get_plate_name();
|
||||||
|
ctx.index = index;
|
||||||
|
fire_lifecycle_event(LifecycleEvent::PlateSelected, ctx);
|
||||||
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+105
-13
@@ -57,6 +57,7 @@
|
|||||||
#include <wx/aui/aui.h>
|
#include <wx/aui/aui.h>
|
||||||
|
|
||||||
#include "libslic3r/libslic3r.h"
|
#include "libslic3r/libslic3r.h"
|
||||||
|
#include "libslic3r/LifecycleEvents.hpp"
|
||||||
#include "libslic3r/Format/STL.hpp"
|
#include "libslic3r/Format/STL.hpp"
|
||||||
#include "libslic3r/Format/DRC.hpp"
|
#include "libslic3r/Format/DRC.hpp"
|
||||||
#include "libslic3r/Format/STEP.hpp"
|
#include "libslic3r/Format/STEP.hpp"
|
||||||
@@ -10123,7 +10124,14 @@ void Plater::priv::remove(size_t obj_idx)
|
|||||||
view3D->enable_layers_editing(false);
|
view3D->enable_layers_editing(false);
|
||||||
|
|
||||||
m_worker.cancel_all();
|
m_worker.cancel_all();
|
||||||
|
std::string obj_name = (obj_idx < model.objects.size()) ? model.objects[obj_idx]->name : std::to_string(obj_idx);
|
||||||
model.delete_object(obj_idx);
|
model.delete_object(obj_idx);
|
||||||
|
{
|
||||||
|
Slic3r::LifecycleEventContext ctx;
|
||||||
|
ctx.name = obj_name;
|
||||||
|
ctx.code = Slic3r::LifecycleEvtCode::Ok;
|
||||||
|
Slic3r::fire_lifecycle_event(Slic3r::LifecycleEvent::ObjectDeleted, ctx);
|
||||||
|
}
|
||||||
//BBS: notify partplate the instance removed
|
//BBS: notify partplate the instance removed
|
||||||
partplate_list.notify_instance_removed(obj_idx, -1);
|
partplate_list.notify_instance_removed(obj_idx, -1);
|
||||||
update();
|
update();
|
||||||
@@ -10156,7 +10164,14 @@ bool Plater::priv::delete_object_from_model(size_t obj_idx, bool refresh_immedia
|
|||||||
if (obj->is_cut())
|
if (obj->is_cut())
|
||||||
sidebar->obj_list()->invalidate_cut_info_for_object(obj_idx);
|
sidebar->obj_list()->invalidate_cut_info_for_object(obj_idx);
|
||||||
|
|
||||||
|
std::string obj_name = obj->name;
|
||||||
model.delete_object(obj_idx);
|
model.delete_object(obj_idx);
|
||||||
|
{
|
||||||
|
Slic3r::LifecycleEventContext ctx;
|
||||||
|
ctx.name = obj_name;
|
||||||
|
ctx.code = Slic3r::LifecycleEvtCode::Ok;
|
||||||
|
Slic3r::fire_lifecycle_event(Slic3r::LifecycleEvent::ObjectDeleted, ctx);
|
||||||
|
}
|
||||||
//BBS: notify partplate the instance removed
|
//BBS: notify partplate the instance removed
|
||||||
partplate_list.notify_instance_removed(obj_idx, -1);
|
partplate_list.notify_instance_removed(obj_idx, -1);
|
||||||
|
|
||||||
@@ -10202,10 +10217,22 @@ void Plater::priv::delete_all_objects_from_model()
|
|||||||
|
|
||||||
void Plater::priv::reset(bool apply_presets_change)
|
void Plater::priv::reset(bool apply_presets_change)
|
||||||
{
|
{
|
||||||
|
// TakeSnapshot below and load_current_presets() further down each re-evaluate the
|
||||||
|
// aggregate dirty flag against a baseline that hasn't been reset yet, so they can toggle
|
||||||
|
// is_dirty() back and forth several times before it settles; coalesce those into one event.
|
||||||
|
ProjectDirtyStateManager::NotificationSuppressor dirty_notify_suppressor(dirty_state);
|
||||||
|
|
||||||
Plater::TakeSnapshot snapshot(q, _u8L("Reset Project"), UndoRedo::SnapshotType::ProjectSeparator);
|
Plater::TakeSnapshot snapshot(q, _u8L("Reset Project"), UndoRedo::SnapshotType::ProjectSeparator);
|
||||||
|
|
||||||
clear_warnings();
|
clear_warnings();
|
||||||
|
|
||||||
|
const std::string closed_project_name = into_u8(get_project_filename());
|
||||||
|
if (!closed_project_name.empty() || !model.objects.empty()) {
|
||||||
|
Slic3r::LifecycleEventContext ctx;
|
||||||
|
ctx.name = closed_project_name;
|
||||||
|
ctx.code = Slic3r::LifecycleEvtCode::Ok;
|
||||||
|
Slic3r::fire_lifecycle_event(Slic3r::LifecycleEvent::ProjectClosed, ctx);
|
||||||
|
}
|
||||||
// A new project must not inherit the previous project's published selection (Feature A/B).
|
// A new project must not inherit the previous project's published selection (Feature A/B).
|
||||||
m_has_pending_published = false;
|
m_has_pending_published = false;
|
||||||
m_pending_published_keys.clear();
|
m_pending_published_keys.clear();
|
||||||
@@ -12633,11 +12660,15 @@ void Plater::priv::on_process_completed(SlicingProcessCompletedEvent &evt)
|
|||||||
notification_manager->set_slicing_progress_export_possible();
|
notification_manager->set_slicing_progress_export_possible();
|
||||||
|
|
||||||
// Reset the "export G-code path" name, so that the automatic background processing will be enabled again.
|
// Reset the "export G-code path" name, so that the automatic background processing will be enabled again.
|
||||||
|
const std::string lifecycle_job_name = this->background_process.fff_print() ?
|
||||||
|
this->background_process.fff_print()->output_filename() : std::string();
|
||||||
this->background_process.reset_export();
|
this->background_process.reset_export();
|
||||||
// This bool stops showing export finished notification even when process_completed_with_error is false
|
// This bool stops showing export finished notification even when process_completed_with_error is false
|
||||||
bool has_error = false;
|
bool has_error = false;
|
||||||
|
std::string lifecycle_error_msg;
|
||||||
if (evt.error()) {
|
if (evt.error()) {
|
||||||
auto message = evt.format_error_message();
|
auto message = evt.format_error_message();
|
||||||
|
lifecycle_error_msg = message.first;
|
||||||
if (evt.critical_error()) {
|
if (evt.critical_error()) {
|
||||||
if (q->m_tracking_popup_menu) {
|
if (q->m_tracking_popup_menu) {
|
||||||
// We don't want to pop-up a message box when tracking a pop-up menu.
|
// We don't want to pop-up a message box when tracking a pop-up menu.
|
||||||
@@ -12675,6 +12706,14 @@ void Plater::priv::on_process_completed(SlicingProcessCompletedEvent &evt)
|
|||||||
is_finished = true;
|
is_finished = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
Slic3r::LifecycleEventContext ctx;
|
||||||
|
ctx.name = lifecycle_job_name;
|
||||||
|
ctx.code = evt.cancelled() ? Slic3r::LifecycleEvtCode::Warn : (has_error ? Slic3r::LifecycleEvtCode::Error : Slic3r::LifecycleEvtCode::Ok);
|
||||||
|
ctx.msg = evt.cancelled() ? "cancelled" : (has_error ? lifecycle_error_msg : std::string());
|
||||||
|
Slic3r::fire_lifecycle_event(Slic3r::LifecycleEvent::SlicingJobComplete, ctx);
|
||||||
|
}
|
||||||
|
|
||||||
//BBS: set the current plater's slice result to valid
|
//BBS: set the current plater's slice result to valid
|
||||||
if (!this->background_process.empty())
|
if (!this->background_process.empty())
|
||||||
this->background_process.get_current_plate()->update_slice_result_valid_state(evt.success());
|
this->background_process.get_current_plate()->update_slice_result_valid_state(evt.success());
|
||||||
@@ -15217,21 +15256,33 @@ int Plater::new_project(bool skip_confirm, bool silent, const wxString& project_
|
|||||||
//get_partplate_list().reinit();
|
//get_partplate_list().reinit();
|
||||||
//get_partplate_list().update_slice_context_to_current_plate(p->background_process);
|
//get_partplate_list().update_slice_context_to_current_plate(p->background_process);
|
||||||
//p->preview->update_gcode_result(p->partplate_list.get_current_slice_result());
|
//p->preview->update_gcode_result(p->partplate_list.get_current_slice_result());
|
||||||
reset(transfer_preset_changes);
|
if (!silent) {
|
||||||
reset_project_dirty_after_save();
|
Slic3r::LifecycleEventContext ctx;
|
||||||
reset_project_dirty_initial_presets();
|
ctx.code = Slic3r::LifecycleEvtCode::Ok;
|
||||||
wxGetApp().update_saved_preset_from_current_preset();
|
Slic3r::fire_lifecycle_event(Slic3r::LifecycleEvent::NewProject, ctx);
|
||||||
update_project_dirty_from_presets();
|
}
|
||||||
|
{
|
||||||
|
// Same rationale as in Plater::priv::reset(): the whole reset + preset-reload +
|
||||||
|
// baseline-reset sequence below settles into its final dirty state only once it
|
||||||
|
// completes, so hold notifications until then to avoid firing on transient flips.
|
||||||
|
ProjectDirtyStateManager::NotificationSuppressor dirty_notify_suppressor(p->dirty_state);
|
||||||
|
|
||||||
//reset project
|
reset(transfer_preset_changes);
|
||||||
p->project.reset();
|
reset_project_dirty_after_save();
|
||||||
//set project name
|
reset_project_dirty_initial_presets();
|
||||||
if (project_name.empty())
|
wxGetApp().update_saved_preset_from_current_preset();
|
||||||
p->set_project_name(_L("Untitled"));
|
update_project_dirty_from_presets();
|
||||||
else
|
|
||||||
p->set_project_name(project_name);
|
|
||||||
|
|
||||||
Plater::TakeSnapshot snapshot(this, "New Project", UndoRedo::SnapshotType::ProjectSeparator);
|
//reset project
|
||||||
|
p->project.reset();
|
||||||
|
//set project name
|
||||||
|
if (project_name.empty())
|
||||||
|
p->set_project_name(_L("Untitled"));
|
||||||
|
else
|
||||||
|
p->set_project_name(project_name);
|
||||||
|
|
||||||
|
Plater::TakeSnapshot snapshot(this, "New Project", UndoRedo::SnapshotType::ProjectSeparator);
|
||||||
|
}
|
||||||
|
|
||||||
Model m;
|
Model m;
|
||||||
model().load_from(m); // new id avoid same path name
|
model().load_from(m); // new id avoid same path name
|
||||||
@@ -15349,6 +15400,13 @@ void Plater::load_project(wxString const& filename2,
|
|||||||
p->set_project_name(_L("Untitled"));
|
p->set_project_name(_L("Untitled"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
Slic3r::LifecycleEventContext ctx;
|
||||||
|
ctx.name = into_u8(load_restore ? originfile : filename);
|
||||||
|
ctx.code = Slic3r::LifecycleEvtCode::Ok;
|
||||||
|
Slic3r::fire_lifecycle_event(Slic3r::LifecycleEvent::ProjectOpened, ctx);
|
||||||
|
}
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
if (using_exported_file()) {
|
if (using_exported_file()) {
|
||||||
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << __LINE__ << " using ecported set project filename: " << filename;
|
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << __LINE__ << " using ecported set project filename: " << filename;
|
||||||
@@ -15420,11 +15478,23 @@ int Plater::save_project(bool saveAs)
|
|||||||
if (full_pathnames) {
|
if (full_pathnames) {
|
||||||
save_strategy = save_strategy | SaveStrategy::FullPathSources;
|
save_strategy = save_strategy | SaveStrategy::FullPathSources;
|
||||||
}
|
}
|
||||||
|
{
|
||||||
|
Slic3r::LifecycleEventContext ctx;
|
||||||
|
ctx.name = into_u8(filename);
|
||||||
|
ctx.code = Slic3r::LifecycleEvtCode::Ok;
|
||||||
|
Slic3r::fire_lifecycle_event(Slic3r::LifecycleEvent::ProjectBeforeSave, ctx);
|
||||||
|
}
|
||||||
if (export_3mf(into_path(filename), save_strategy) < 0) {
|
if (export_3mf(into_path(filename), save_strategy) < 0) {
|
||||||
MessageDialog(this, _L("Failed to save the project.\nPlease check whether the folder exists online or if other programs have the project file open."),
|
MessageDialog(this, _L("Failed to save the project.\nPlease check whether the folder exists online or if other programs have the project file open."),
|
||||||
_L("Save project"), wxOK | wxICON_WARNING).ShowModal();
|
_L("Save project"), wxOK | wxICON_WARNING).ShowModal();
|
||||||
return wxID_CANCEL;
|
return wxID_CANCEL;
|
||||||
}
|
}
|
||||||
|
{
|
||||||
|
Slic3r::LifecycleEventContext ctx;
|
||||||
|
ctx.name = into_u8(filename);
|
||||||
|
ctx.code = Slic3r::LifecycleEvtCode::Ok;
|
||||||
|
Slic3r::fire_lifecycle_event(Slic3r::LifecycleEvent::ProjectAfterSave, ctx);
|
||||||
|
}
|
||||||
|
|
||||||
Slic3r::remove_backup(model(), false);
|
Slic3r::remove_backup(model(), false);
|
||||||
|
|
||||||
@@ -20744,6 +20814,14 @@ void Plater::changed_object(ModelObject &object){
|
|||||||
|
|
||||||
// Check outside bed
|
// Check outside bed
|
||||||
get_current_canvas3D()->requires_check_outside_state();
|
get_current_canvas3D()->requires_check_outside_state();
|
||||||
|
|
||||||
|
if (!is_loading_project()) {
|
||||||
|
LifecycleEventContext ctx;
|
||||||
|
ctx.name = object.name;
|
||||||
|
ctx.id = std::to_string(object.id().id);
|
||||||
|
ctx.source = "geometry";
|
||||||
|
fire_lifecycle_event(LifecycleEvent::ObjectChanged, ctx);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Plater::changed_object(int obj_idx)
|
void Plater::changed_object(int obj_idx)
|
||||||
@@ -20780,6 +20858,20 @@ void Plater::changed_objects(const std::vector<size_t>& object_idxs)
|
|||||||
|
|
||||||
// update print
|
// update print
|
||||||
this->p->schedule_background_process();
|
this->p->schedule_background_process();
|
||||||
|
|
||||||
|
if (!is_loading_project()) {
|
||||||
|
for (size_t obj_idx : object_idxs) {
|
||||||
|
if (obj_idx >= p->model.objects.size() || p->model.objects[obj_idx] == nullptr)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
LifecycleEventContext ctx;
|
||||||
|
ctx.name = p->model.objects[obj_idx]->name;
|
||||||
|
ctx.id = std::to_string(p->model.objects[obj_idx]->id().id);
|
||||||
|
ctx.index = static_cast<int>(obj_idx);
|
||||||
|
ctx.source = "geometry";
|
||||||
|
fire_lifecycle_event(LifecycleEvent::ObjectChanged, ctx);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Plater::schedule_background_process(bool schedule/* = true*/)
|
void Plater::schedule_background_process(bool schedule/* = true*/)
|
||||||
|
|||||||
@@ -6,6 +6,7 @@
|
|||||||
#include "MainFrame.hpp"
|
#include "MainFrame.hpp"
|
||||||
#include "I18N.hpp"
|
#include "I18N.hpp"
|
||||||
#include "Plater.hpp"
|
#include "Plater.hpp"
|
||||||
|
#include "libslic3r/LifecycleEvents.hpp"
|
||||||
|
|
||||||
#include <boost/algorithm/string/predicate.hpp>
|
#include <boost/algorithm/string/predicate.hpp>
|
||||||
|
|
||||||
@@ -17,13 +18,23 @@ namespace GUI {
|
|||||||
|
|
||||||
void ProjectDirtyStateManager::update_from_undo_redo_stack(bool dirty)
|
void ProjectDirtyStateManager::update_from_undo_redo_stack(bool dirty)
|
||||||
{
|
{
|
||||||
|
const bool was_dirty = is_dirty();
|
||||||
m_plater_dirty = dirty;
|
m_plater_dirty = dirty;
|
||||||
|
notify_dirty_change(was_dirty, "undo_redo");
|
||||||
if (const Plater *plater = wxGetApp().plater(); plater && wxGetApp().initialized())
|
if (const Plater *plater = wxGetApp().plater(); plater && wxGetApp().initialized())
|
||||||
wxGetApp().mainframe->update_title();
|
wxGetApp().mainframe->update_title();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ProjectDirtyStateManager::set_plater_dirty(bool is_dirty)
|
||||||
|
{
|
||||||
|
const bool was_dirty = this->is_dirty();
|
||||||
|
m_plater_dirty = is_dirty;
|
||||||
|
notify_dirty_change(was_dirty, "plater");
|
||||||
|
}
|
||||||
|
|
||||||
void ProjectDirtyStateManager::update_from_presets()
|
void ProjectDirtyStateManager::update_from_presets()
|
||||||
{
|
{
|
||||||
|
const bool was_dirty = is_dirty();
|
||||||
m_presets_dirty = false;
|
m_presets_dirty = false;
|
||||||
// check switching of the presets only for exist/loaded project, but not for new
|
// check switching of the presets only for exist/loaded project, but not for new
|
||||||
GUI_App &app = wxGetApp();
|
GUI_App &app = wxGetApp();
|
||||||
@@ -45,18 +56,53 @@ void ProjectDirtyStateManager::update_from_presets()
|
|||||||
}
|
}
|
||||||
m_presets_dirty |= app.has_unsaved_preset_changes();
|
m_presets_dirty |= app.has_unsaved_preset_changes();
|
||||||
m_project_config_dirty = m_initial_project_config != app.preset_bundle->project_config;
|
m_project_config_dirty = m_initial_project_config != app.preset_bundle->project_config;
|
||||||
|
notify_dirty_change(was_dirty, "presets");
|
||||||
app.mainframe->update_title();
|
app.mainframe->update_title();
|
||||||
}
|
}
|
||||||
|
|
||||||
void ProjectDirtyStateManager::reset_after_save()
|
void ProjectDirtyStateManager::reset_after_save()
|
||||||
{
|
{
|
||||||
|
const bool was_dirty = is_dirty();
|
||||||
this->reset_initial_presets();
|
this->reset_initial_presets();
|
||||||
m_plater_dirty = false;
|
m_plater_dirty = false;
|
||||||
m_presets_dirty = false;
|
m_presets_dirty = false;
|
||||||
m_project_config_dirty = false;
|
m_project_config_dirty = false;
|
||||||
|
notify_dirty_change(was_dirty, "save");
|
||||||
wxGetApp().mainframe->update_title();
|
wxGetApp().mainframe->update_title();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ProjectDirtyStateManager::notify_dirty_change(bool was_dirty, const char *source)
|
||||||
|
{
|
||||||
|
if (m_suppress_depth > 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
const bool dirty = is_dirty();
|
||||||
|
if (was_dirty == dirty)
|
||||||
|
return;
|
||||||
|
|
||||||
|
LifecycleEventContext ctx;
|
||||||
|
ctx.code = LifecycleEvtCode::Ok;
|
||||||
|
ctx.dirty = dirty;
|
||||||
|
ctx.source = source;
|
||||||
|
fire_lifecycle_event(LifecycleEvent::ProjectDirtyChanged, ctx);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ProjectDirtyStateManager::begin_suppress_notifications()
|
||||||
|
{
|
||||||
|
if (m_suppress_depth == 0)
|
||||||
|
m_suppress_entry_dirty = is_dirty();
|
||||||
|
++m_suppress_depth;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ProjectDirtyStateManager::end_suppress_notifications()
|
||||||
|
{
|
||||||
|
assert(m_suppress_depth > 0);
|
||||||
|
if (m_suppress_depth > 0)
|
||||||
|
--m_suppress_depth;
|
||||||
|
if (m_suppress_depth == 0)
|
||||||
|
notify_dirty_change(m_suppress_entry_dirty, "batch");
|
||||||
|
}
|
||||||
|
|
||||||
void ProjectDirtyStateManager::reset_initial_presets()
|
void ProjectDirtyStateManager::reset_initial_presets()
|
||||||
{
|
{
|
||||||
m_initial_presets.fill(std::string{});
|
m_initial_presets.fill(std::string{});
|
||||||
@@ -168,4 +214,3 @@ void ProjectDirtyStateManager::render_debug_window() const
|
|||||||
|
|
||||||
} // namespace GUI
|
} // namespace GUI
|
||||||
} // namespace Slic3r
|
} // namespace Slic3r
|
||||||
|
|
||||||
|
|||||||
@@ -14,21 +14,42 @@ public:
|
|||||||
void reset_after_save();
|
void reset_after_save();
|
||||||
void reset_initial_presets();
|
void reset_initial_presets();
|
||||||
|
|
||||||
void set_plater_dirty(bool is_dirty) { m_plater_dirty = is_dirty; }
|
void set_plater_dirty(bool is_dirty);
|
||||||
bool is_dirty() const { return m_plater_dirty || m_project_config_dirty || m_presets_dirty; }
|
bool is_dirty() const { return m_plater_dirty || m_project_config_dirty || m_presets_dirty; }
|
||||||
bool is_presets_dirty() const { return m_presets_dirty; }
|
bool is_presets_dirty() const { return m_presets_dirty; }
|
||||||
|
|
||||||
|
// RAII guard coalescing dirty-state updates: while any guard is alive, ProjectDirtyChanged
|
||||||
|
// notifications are held back; when the outermost guard is destroyed, at most one
|
||||||
|
// notification fires, reflecting only the net change across the whole guarded scope.
|
||||||
|
class NotificationSuppressor
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
explicit NotificationSuppressor(ProjectDirtyStateManager &owner) : m_owner(owner) { m_owner.begin_suppress_notifications(); }
|
||||||
|
~NotificationSuppressor() { m_owner.end_suppress_notifications(); }
|
||||||
|
NotificationSuppressor(const NotificationSuppressor &) = delete;
|
||||||
|
NotificationSuppressor &operator=(const NotificationSuppressor &) = delete;
|
||||||
|
private:
|
||||||
|
ProjectDirtyStateManager &m_owner;
|
||||||
|
};
|
||||||
|
|
||||||
#if ENABLE_PROJECT_DIRTY_STATE_DEBUG_WINDOW
|
#if ENABLE_PROJECT_DIRTY_STATE_DEBUG_WINDOW
|
||||||
void render_debug_window() const;
|
void render_debug_window() const;
|
||||||
#endif // ENABLE_PROJECT_DIRTY_STATE_DEBUG_WINDOW
|
#endif // ENABLE_PROJECT_DIRTY_STATE_DEBUG_WINDOW
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
void notify_dirty_change(bool was_dirty, const char *source);
|
||||||
|
void begin_suppress_notifications();
|
||||||
|
void end_suppress_notifications();
|
||||||
|
|
||||||
// Does the Undo / Redo stack indicate the project is dirty?
|
// Does the Undo / Redo stack indicate the project is dirty?
|
||||||
bool m_plater_dirty { false };
|
bool m_plater_dirty { false };
|
||||||
// Do the presets indicate the project is dirty?
|
// Do the presets indicate the project is dirty?
|
||||||
bool m_presets_dirty { false };
|
bool m_presets_dirty { false };
|
||||||
// Is the project config dirty?
|
// Is the project config dirty?
|
||||||
bool m_project_config_dirty { false };
|
bool m_project_config_dirty { false };
|
||||||
|
// NotificationSuppressor nesting depth and the dirty state observed when the outermost guard began.
|
||||||
|
int m_suppress_depth { 0 };
|
||||||
|
bool m_suppress_entry_dirty { false };
|
||||||
// Keeps track of preset names selected at the time of last project save.
|
// Keeps track of preset names selected at the time of last project save.
|
||||||
std::array<std::string, Preset::TYPE_COUNT> m_initial_presets;
|
std::array<std::string, Preset::TYPE_COUNT> m_initial_presets;
|
||||||
DynamicPrintConfig m_initial_project_config;
|
DynamicPrintConfig m_initial_project_config;
|
||||||
|
|||||||
@@ -38,6 +38,7 @@
|
|||||||
#include "slic3r/Utils/NetworkAgentFactory.hpp"
|
#include "slic3r/Utils/NetworkAgentFactory.hpp"
|
||||||
#include "slic3r/Utils/PresetUpdater.hpp"
|
#include "slic3r/Utils/PresetUpdater.hpp"
|
||||||
#include "slic3r/plugin/PluginConfig.hpp"
|
#include "slic3r/plugin/PluginConfig.hpp"
|
||||||
|
#include "slic3r/plugin/PluginManager.hpp"
|
||||||
#include "Plater.hpp"
|
#include "Plater.hpp"
|
||||||
#include "MainFrame.hpp"
|
#include "MainFrame.hpp"
|
||||||
#include "format.hpp"
|
#include "format.hpp"
|
||||||
@@ -6918,6 +6919,20 @@ bool Tab::select_preset(
|
|||||||
}
|
}
|
||||||
load_current_preset();
|
load_current_preset();
|
||||||
|
|
||||||
|
{
|
||||||
|
Slic3r::LifecycleEventContext ctx;
|
||||||
|
ctx.name = preset_name;
|
||||||
|
ctx.code = Slic3r::LifecycleEvtCode::Ok;
|
||||||
|
switch (m_type) {
|
||||||
|
case Preset::TYPE_PRINT: ctx.msg = "print"; break;
|
||||||
|
case Preset::TYPE_SLA_PRINT: ctx.msg = "sla_print"; break;
|
||||||
|
case Preset::TYPE_FILAMENT: ctx.msg = "filament"; break;
|
||||||
|
case Preset::TYPE_SLA_MATERIAL: ctx.msg = "sla_material"; break;
|
||||||
|
case Preset::TYPE_PRINTER: ctx.msg = "printer"; break;
|
||||||
|
default: break;
|
||||||
|
}
|
||||||
|
Slic3r::fire_lifecycle_event(Slic3r::LifecycleEvent::PresetSelected, ctx);
|
||||||
|
}
|
||||||
|
|
||||||
if (delete_third_printer) {
|
if (delete_third_printer) {
|
||||||
wxGetApp().CallAfter([filament_presets, process_presets]() {
|
wxGetApp().CallAfter([filament_presets, process_presets]() {
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
#include "TaskManager.hpp"
|
#include "TaskManager.hpp"
|
||||||
|
|
||||||
#include "libslic3r/Thread.hpp"
|
#include "libslic3r/Thread.hpp"
|
||||||
|
#include "libslic3r/LifecycleEvents.hpp"
|
||||||
#include "nlohmann/json.hpp"
|
#include "nlohmann/json.hpp"
|
||||||
#include "MainFrame.hpp"
|
#include "MainFrame.hpp"
|
||||||
#include "GUI_App.hpp"
|
#include "GUI_App.hpp"
|
||||||
@@ -210,6 +211,13 @@ int TaskManager::schedule(TaskStateInfo* task)
|
|||||||
assert(task->state() == TaskState::TS_PENDING);
|
assert(task->state() == TaskState::TS_PENDING);
|
||||||
task->set_state(TaskState::TS_SENDING);
|
task->set_state(TaskState::TS_SENDING);
|
||||||
|
|
||||||
|
LifecycleEventContext start_ctx;
|
||||||
|
start_ctx.name = task->params().project_name;
|
||||||
|
start_ctx.device_id = task->params().dev_id;
|
||||||
|
start_ctx.job_id = std::to_string(task->task_info_id);
|
||||||
|
start_ctx.source = "task_manager";
|
||||||
|
fire_lifecycle_event(LifecycleEvent::PrintJobStarted, start_ctx);
|
||||||
|
|
||||||
BOOST_LOG_TRIVIAL(trace) << "task_manager: schedule a task to dev_id = " << task->params().dev_id;
|
BOOST_LOG_TRIVIAL(trace) << "task_manager: schedule a task to dev_id = " << task->params().dev_id;
|
||||||
boost::thread* new_sending_thread = new boost::thread();
|
boost::thread* new_sending_thread = new boost::thread();
|
||||||
*new_sending_thread = Slic3r::create_thread(
|
*new_sending_thread = Slic3r::create_thread(
|
||||||
@@ -237,6 +245,16 @@ int TaskManager::schedule(TaskStateInfo* task)
|
|||||||
task->set_state(TaskState::TS_SEND_CANCELED);
|
task->set_state(TaskState::TS_SEND_CANCELED);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
LifecycleEventContext finish_ctx;
|
||||||
|
finish_ctx.name = task->params().project_name;
|
||||||
|
finish_ctx.device_id = task->params().dev_id;
|
||||||
|
finish_ctx.job_id = std::to_string(task->task_info_id);
|
||||||
|
finish_ctx.source = "task_manager";
|
||||||
|
finish_ctx.code = result == 0 ? LifecycleEvtCode::Ok :
|
||||||
|
(task->is_canceled() ? LifecycleEvtCode::Warn : LifecycleEvtCode::Error);
|
||||||
|
finish_ctx.msg = result == 0 ? "" : (task->is_canceled() ? "cancelled" : "failed");
|
||||||
|
fire_lifecycle_event(LifecycleEvent::PrintJobFinished, finish_ctx);
|
||||||
|
|
||||||
/* remove from sending task list */
|
/* remove from sending task list */
|
||||||
m_scedule_mutex.lock();
|
m_scedule_mutex.lock();
|
||||||
|
|||||||
@@ -23,6 +23,7 @@
|
|||||||
#include "CrealityPrint.hpp"
|
#include "CrealityPrint.hpp"
|
||||||
#include "../GUI/PrintHostDialogs.hpp"
|
#include "../GUI/PrintHostDialogs.hpp"
|
||||||
#include "../GUI/MainFrame.hpp"
|
#include "../GUI/MainFrame.hpp"
|
||||||
|
#include "slic3r/plugin/PluginManager.hpp"
|
||||||
#include "Obico.hpp"
|
#include "Obico.hpp"
|
||||||
#include "Flashforge.hpp"
|
#include "Flashforge.hpp"
|
||||||
#include "SimplyPrint.hpp"
|
#include "SimplyPrint.hpp"
|
||||||
@@ -358,12 +359,29 @@ void PrintHostJobQueue::priv::perform_job(PrintHostJob the_job)
|
|||||||
{
|
{
|
||||||
emit_progress(0); // Indicate the upload is starting
|
emit_progress(0); // Indicate the upload is starting
|
||||||
|
|
||||||
|
// Captured before upload_data is moved into upload() below.
|
||||||
|
const std::string upload_filename = the_job.upload_data.source_path.filename().string();
|
||||||
|
|
||||||
|
{
|
||||||
|
LifecycleEventContext ctx;
|
||||||
|
ctx.name = upload_filename;
|
||||||
|
ctx.code = LifecycleEvtCode::Ok;
|
||||||
|
fire_lifecycle_event(LifecycleEvent::UploadStarted, ctx);
|
||||||
|
}
|
||||||
|
|
||||||
bool success = the_job.printhost->upload(std::move(the_job.upload_data),
|
bool success = the_job.printhost->upload(std::move(the_job.upload_data),
|
||||||
[this](Http::Progress progress, bool &cancel) { this->progress_fn(std::move(progress), cancel); },
|
[this](Http::Progress progress, bool &cancel) { this->progress_fn(std::move(progress), cancel); },
|
||||||
[this](wxString error) { this->error_fn(std::move(error)); },
|
[this](wxString error) { this->error_fn(std::move(error)); },
|
||||||
[this](wxString tag, wxString host) { this->info_fn(std::move(tag), std::move(host)); }
|
[this](wxString tag, wxString host) { this->info_fn(std::move(tag), std::move(host)); }
|
||||||
);
|
);
|
||||||
|
|
||||||
|
{
|
||||||
|
LifecycleEventContext ctx;
|
||||||
|
ctx.name = upload_filename;
|
||||||
|
ctx.code = success ? LifecycleEvtCode::Ok : LifecycleEvtCode::Error;
|
||||||
|
fire_lifecycle_event(LifecycleEvent::UploadFinished, ctx);
|
||||||
|
}
|
||||||
|
|
||||||
if (success) {
|
if (success) {
|
||||||
emit_progress(100);
|
emit_progress(100);
|
||||||
if (the_job.switch_to_device_tab) {
|
if (the_job.switch_to_device_tab) {
|
||||||
|
|||||||
@@ -7,6 +7,7 @@
|
|||||||
|
|
||||||
#include "libslic3r/Config.hpp"
|
#include "libslic3r/Config.hpp"
|
||||||
#include "libslic3r/Exception.hpp"
|
#include "libslic3r/Exception.hpp"
|
||||||
|
#include "libslic3r/LifecycleEvents.hpp"
|
||||||
#include "libslic3r/Print.hpp"
|
#include "libslic3r/Print.hpp"
|
||||||
#include "libslic3r_version.h"
|
#include "libslic3r_version.h"
|
||||||
|
|
||||||
@@ -46,6 +47,16 @@ void install_capability_resolver()
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Global libslic3r-side seam (Slic3r::fire_lifecycle_event, in libslic3r/LifecycleEvents.hpp):
|
||||||
|
// broadcasts to every loaded, enabled capability regardless of type, unlike the SlicingPipeline
|
||||||
|
// hook below which only targets picker-selected SlicingPipeline capabilities.
|
||||||
|
void install_lifecycle_event_hook()
|
||||||
|
{
|
||||||
|
set_lifecycle_hook_fn([](LifecycleEvent event, const LifecycleEventContext& ctx) {
|
||||||
|
PluginManager::instance().dispatch_lifecycle_event(event, ctx);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
// Print::process() fires this hook at each pipeline seam on the slicing worker
|
// Print::process() fires this hook at each pipeline seam on the slicing worker
|
||||||
// thread; here we run the picker-selected SlicingPipeline capabilities. Per
|
// thread; here we run the picker-selected SlicingPipeline capabilities. Per
|
||||||
// capability we acquire the GIL, honor cancellation, and convert a plugin
|
// capability we acquire the GIL, honor cancellation, and convert a plugin
|
||||||
@@ -122,12 +133,14 @@ void install()
|
|||||||
{
|
{
|
||||||
install_capability_resolver();
|
install_capability_resolver();
|
||||||
install_slicing_pipeline_hook();
|
install_slicing_pipeline_hook();
|
||||||
|
install_lifecycle_event_hook();
|
||||||
}
|
}
|
||||||
|
|
||||||
void uninstall()
|
void uninstall()
|
||||||
{
|
{
|
||||||
ConfigBase::set_resolve_capability_fn(nullptr);
|
ConfigBase::set_resolve_capability_fn(nullptr);
|
||||||
Print::set_slicing_pipeline_hook_fn(nullptr);
|
Print::set_slicing_pipeline_hook_fn(nullptr);
|
||||||
|
set_lifecycle_hook_fn(nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace Slic3r::plugin_hooks
|
} // namespace Slic3r::plugin_hooks
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
#include "PluginManager.hpp"
|
#include "PluginManager.hpp"
|
||||||
|
|
||||||
|
#include <exception>
|
||||||
#include <libslic3r/Utils.hpp>
|
#include <libslic3r/Utils.hpp>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <pybind11/embed.h>
|
#include <pybind11/embed.h>
|
||||||
@@ -2042,6 +2043,8 @@ bool PluginManager::delete_and_unsubscribe_cloud_plugin(const std::string& plugi
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
unload_plugin(plugin_key);
|
||||||
|
|
||||||
if (!m_cloud_service.request_cloud_unsubscribe(descriptor, error)) {
|
if (!m_cloud_service.request_cloud_unsubscribe(descriptor, error)) {
|
||||||
set_plugin_error(plugin_key, error);
|
set_plugin_error(plugin_key, error);
|
||||||
return false;
|
return false;
|
||||||
@@ -2087,4 +2090,24 @@ ExecutionResult PluginManager::run_script_capability(const std::string& plugin_k
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void PluginManager::dispatch_lifecycle_event(LifecycleEvent evt, const LifecycleEventContext& ctx) {
|
||||||
|
for (const auto& cap : get_plugin_capabilities()) {
|
||||||
|
if (!cap || !cap->is_enabled()) continue;
|
||||||
|
try {
|
||||||
|
cap->on_lifecycle_event(evt, ctx);
|
||||||
|
} catch (const std::exception& ex) {
|
||||||
|
BOOST_LOG_TRIVIAL(warning) << __FUNCTION__ << ": plugin '" << cap->audit_plugin_key() << "/" << cap->name()
|
||||||
|
<< "' on_lifecycle_event(" << lifecycle_event_to_string(evt) << ") threw: " << ex.what()
|
||||||
|
<< " [ctx name='" << ctx.name << "', code=" << lifecycle_evt_code_to_string(ctx.code)
|
||||||
|
<< ", msg='" << ctx.msg << "']";
|
||||||
|
} catch (...) {
|
||||||
|
BOOST_LOG_TRIVIAL(warning) << __FUNCTION__ << ": plugin '" << cap->audit_plugin_key() << "/" << cap->name()
|
||||||
|
<< "' on_lifecycle_event(" << lifecycle_event_to_string(evt)
|
||||||
|
<< ") threw a non-standard exception"
|
||||||
|
<< " [ctx name='" << ctx.name << "', code=" << lifecycle_evt_code_to_string(ctx.code)
|
||||||
|
<< ", msg='" << ctx.msg << "']";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace Slic3r
|
} // namespace Slic3r
|
||||||
|
|||||||
@@ -8,6 +8,7 @@
|
|||||||
#include <condition_variable>
|
#include <condition_variable>
|
||||||
#include <functional>
|
#include <functional>
|
||||||
#include <libslic3r/Config.hpp>
|
#include <libslic3r/Config.hpp>
|
||||||
|
#include <libslic3r/LifecycleEvents.hpp>
|
||||||
#include <map>
|
#include <map>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
@@ -20,7 +21,6 @@
|
|||||||
#include <pybind11/embed.h>
|
#include <pybind11/embed.h>
|
||||||
|
|
||||||
#include "CloudPluginService.hpp"
|
#include "CloudPluginService.hpp"
|
||||||
#include "PluginFsUtils.hpp"
|
|
||||||
#include "PluginDescriptor.hpp"
|
#include "PluginDescriptor.hpp"
|
||||||
#include "PluginLoader.hpp"
|
#include "PluginLoader.hpp"
|
||||||
#include "PluginConfig.hpp"
|
#include "PluginConfig.hpp"
|
||||||
@@ -210,6 +210,8 @@ public:
|
|||||||
|
|
||||||
ExecutionResult run_script_capability(const std::string& plugin_key, const std::string& capability_name, std::string& error);
|
ExecutionResult run_script_capability(const std::string& plugin_key, const std::string& capability_name, std::string& error);
|
||||||
|
|
||||||
|
void dispatch_lifecycle_event(LifecycleEvent evt, const LifecycleEventContext& ctx);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
PluginManager() = default;
|
PluginManager() = default;
|
||||||
PluginManager(const PluginManager&) = delete;
|
PluginManager(const PluginManager&) = delete;
|
||||||
|
|||||||
@@ -132,6 +132,11 @@ public:
|
|||||||
{
|
{
|
||||||
ORCA_PY_OVERRIDE_AUDITED([] {}, PYBIND11_OVERRIDE, void, Base, on_cancelled);
|
ORCA_PY_OVERRIDE_AUDITED([] {}, PYBIND11_OVERRIDE, void, Base, on_cancelled);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void on_lifecycle_event(LifecycleEvent event, const LifecycleEventContext& ctx) override
|
||||||
|
{
|
||||||
|
ORCA_PY_OVERRIDE_AUDITED([] {}, PYBIND11_OVERRIDE, void, Base, on_lifecycle_event, event, ctx);
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
class PyPluginInterfaceTrampoline : public PyPluginCommonTrampoline<PluginCapabilityInterface>
|
class PyPluginInterfaceTrampoline : public PyPluginCommonTrampoline<PluginCapabilityInterface>
|
||||||
|
|||||||
@@ -377,6 +377,62 @@ void bind_python_api(pybind11::module_& m)
|
|||||||
.value("FatalError", PluginResult::FatalError)
|
.value("FatalError", PluginResult::FatalError)
|
||||||
.export_values();
|
.export_values();
|
||||||
|
|
||||||
|
py::enum_<LifecycleEvent>(m, "LifecycleEvent", "Application lifecycle moment passed to on_lifecycle_event")
|
||||||
|
.value("NewProject", LifecycleEvent::NewProject)
|
||||||
|
.value("ProjectOpened", LifecycleEvent::ProjectOpened)
|
||||||
|
.value("ProjectBeforeSave", LifecycleEvent::ProjectBeforeSave)
|
||||||
|
.value("ProjectAfterSave", LifecycleEvent::ProjectAfterSave)
|
||||||
|
.value("ProjectClosed", LifecycleEvent::ProjectClosed)
|
||||||
|
.value("ProjectDirtyChanged", LifecycleEvent::ProjectDirtyChanged)
|
||||||
|
.value("SliceStarted", LifecycleEvent::SliceStarted)
|
||||||
|
.value("SliceGeometryFinished", LifecycleEvent::SliceGeometryFinished)
|
||||||
|
.value("GCodeExportStarted", LifecycleEvent::GCodeExportStarted)
|
||||||
|
.value("GCodeExportFinished", LifecycleEvent::GCodeExportFinished)
|
||||||
|
.value("SlicingJobComplete", LifecycleEvent::SlicingJobComplete)
|
||||||
|
.value("ObjectAdded", LifecycleEvent::ObjectAdded)
|
||||||
|
.value("ObjectDeleted", LifecycleEvent::ObjectDeleted)
|
||||||
|
.value("ObjectTransformed", LifecycleEvent::ObjectTransformed)
|
||||||
|
.value("ObjectChanged", LifecycleEvent::ObjectChanged)
|
||||||
|
.value("ObjectRenamed", LifecycleEvent::ObjectRenamed)
|
||||||
|
.value("PlateCreated", LifecycleEvent::PlateCreated)
|
||||||
|
.value("PlateDeleted", LifecycleEvent::PlateDeleted)
|
||||||
|
.value("PlateSelected", LifecycleEvent::PlateSelected)
|
||||||
|
.value("PlateRenamed", LifecycleEvent::PlateRenamed)
|
||||||
|
.value("PresetSelected", LifecycleEvent::PresetSelected)
|
||||||
|
.value("PresetSaved", LifecycleEvent::PresetSaved)
|
||||||
|
.value("PrintStateChanged", LifecycleEvent::PrintStateChanged)
|
||||||
|
.value("DeviceOnlineChanged", LifecycleEvent::DeviceOnlineChanged)
|
||||||
|
.value("DeviceDiscovered", LifecycleEvent::DeviceDiscovered)
|
||||||
|
.value("DeviceSelected", LifecycleEvent::DeviceSelected)
|
||||||
|
.value("DeviceConnected", LifecycleEvent::DeviceConnected)
|
||||||
|
.value("DeviceDisconnected", LifecycleEvent::DeviceDisconnected)
|
||||||
|
.value("UploadStarted", LifecycleEvent::UploadStarted)
|
||||||
|
.value("UploadFinished", LifecycleEvent::UploadFinished)
|
||||||
|
.value("PrintJobStarted", LifecycleEvent::PrintJobStarted)
|
||||||
|
.value("PrintJobFinished", LifecycleEvent::PrintJobFinished)
|
||||||
|
.value("SendJobStarted", LifecycleEvent::SendJobStarted)
|
||||||
|
.value("SendJobFinished", LifecycleEvent::SendJobFinished)
|
||||||
|
.export_values();
|
||||||
|
|
||||||
|
py::enum_<LifecycleEvtCode>(m, "LifecycleEvtCode", "Outcome code accompanying a LifecycleEventContext")
|
||||||
|
.value("Ok", LifecycleEvtCode::Ok)
|
||||||
|
.value("Error", LifecycleEvtCode::Error)
|
||||||
|
.value("Warn", LifecycleEvtCode::Warn)
|
||||||
|
.export_values();
|
||||||
|
|
||||||
|
py::class_<LifecycleEventContext>(m, "LifecycleEventContext", "Payload accompanying a LifecycleEvent")
|
||||||
|
.def(py::init<>())
|
||||||
|
.def_readonly("name", &LifecycleEventContext::name)
|
||||||
|
.def_readonly("code", &LifecycleEventContext::code)
|
||||||
|
.def_readonly("msg", &LifecycleEventContext::msg)
|
||||||
|
.def_readonly("id", &LifecycleEventContext::id)
|
||||||
|
.def_readonly("previous_name", &LifecycleEventContext::previous_name)
|
||||||
|
.def_readonly("device_id", &LifecycleEventContext::device_id)
|
||||||
|
.def_readonly("job_id", &LifecycleEventContext::job_id)
|
||||||
|
.def_readonly("source", &LifecycleEventContext::source)
|
||||||
|
.def_readonly("index", &LifecycleEventContext::index)
|
||||||
|
.def_readonly("dirty", &LifecycleEventContext::dirty);
|
||||||
|
|
||||||
py::class_<PluginContext>(m, "PluginContext", "Context shared with plugin entry points")
|
py::class_<PluginContext>(m, "PluginContext", "Context shared with plugin entry points")
|
||||||
.def(py::init<>())
|
.def(py::init<>())
|
||||||
.def_readwrite("orca_version", &PluginContext::orca_version);
|
.def_readwrite("orca_version", &PluginContext::orca_version);
|
||||||
@@ -401,6 +457,9 @@ void bind_python_api(pybind11::module_& m)
|
|||||||
.def("get_type", &PluginCapabilityInterface::get_type)
|
.def("get_type", &PluginCapabilityInterface::get_type)
|
||||||
.def("on_load", &PluginCapabilityInterface::on_load)
|
.def("on_load", &PluginCapabilityInterface::on_load)
|
||||||
.def("on_unload", &PluginCapabilityInterface::on_unload)
|
.def("on_unload", &PluginCapabilityInterface::on_unload)
|
||||||
|
.def("on_lifecycle_event", &PluginCapabilityInterface::on_lifecycle_event,
|
||||||
|
"Override to react to an application lifecycle moment (LifecycleEvent) and its\n"
|
||||||
|
"LifecycleEventContext payload. Available on every capability type.")
|
||||||
.def("has_config_ui", &PluginCapabilityInterface::has_config_ui,
|
.def("has_config_ui", &PluginCapabilityInterface::has_config_ui,
|
||||||
"Override to return True to replace the host's default JSON editor with your own HTML\n"
|
"Override to return True to replace the host's default JSON editor with your own HTML\n"
|
||||||
"UI, returned by get_config_ui(). Every capability is configurable and appears in the\n"
|
"UI, returned by get_config_ui(). Every capability is configurable and appears in the\n"
|
||||||
|
|||||||
@@ -10,6 +10,8 @@
|
|||||||
#include <nlohmann/json.hpp>
|
#include <nlohmann/json.hpp>
|
||||||
#include <pybind11/embed.h>
|
#include <pybind11/embed.h>
|
||||||
|
|
||||||
|
#include <libslic3r/LifecycleEvents.hpp>
|
||||||
|
|
||||||
namespace Slic3r {
|
namespace Slic3r {
|
||||||
|
|
||||||
enum class PluginCapabilityType { PrinterConnection = 0, Pages, Analysis, Importer, Exporter, Visualization, Script, SlicingPipeline, Unknown };
|
enum class PluginCapabilityType { PrinterConnection = 0, Pages, Analysis, Importer, Exporter, Visualization, Script, SlicingPipeline, Unknown };
|
||||||
@@ -167,6 +169,8 @@ public:
|
|||||||
virtual void on_unload() {}
|
virtual void on_unload() {}
|
||||||
virtual void on_cancelled() {}
|
virtual void on_cancelled() {}
|
||||||
|
|
||||||
|
virtual void on_lifecycle_event(LifecycleEvent event, const LifecycleEventContext& ctx) { (void) event; (void) ctx; }
|
||||||
|
|
||||||
// ── C++-only host state, never exposed to Python. Set by the loader at materialization. ──
|
// ── C++-only host state, never exposed to Python. Set by the loader at materialization. ──
|
||||||
//
|
//
|
||||||
// The capability owns its own identity and enable flag: they are read once under the GIL, live
|
// The capability owns its own identity and enable flag: they are read once under the GIL, live
|
||||||
|
|||||||
Reference in New Issue
Block a user