Compare commits

...

9 Commits

Author SHA1 Message Date
Argo
7e63fce706 Fix 3MF import crash for silent-mode machine limits with legacy vector sizes (#12289)
Normalize printer_options_with_variant_2 (stride=2) machine limit vectors during preset merge to handle legacy 3MF/projects that store only a single (normal,silent) pair despite multiple printer variants, preventing set_with_restore() size-mismatch crashes.

Crash error message during 3MF import as project:

<img width="833" height="274" alt="image" src="https://github.com/user-attachments/assets/f92148a9-98c6-47b7-a0ab-d5ac8b1f2be4" />

3MF attached that causes the bug. 
[cube.3mf.zip](https://github.com/user-attachments/files/25318309/cube.3mf.zip)
2026-02-15 20:50:17 +08:00
Matthias Blaicher
380f4b4a18 Fix EGL/GLX mismatch causing blank 3D preview on Linux (#12308)
- Add configurable GLEW_USE_EGL option (default OFF) to match wxWidgets
- Explicitly set wxUSE_GLCANVAS_EGL=OFF for vendored wxWidgets build
- Add compile-time check to detect EGL/GLX backend mismatch between
  GLEW and wxWidgets, preventing silent rendering failures

The bug occurred when GLEW was compiled with EGL support (using
eglGetProcAddress) but wxWidgets created GLX contexts. This mismatch
caused OpenGL function pointers to fail loading, resulting in blank
3D model preview.

Co-authored-by: SoftFever <softfeverever@gmail.com>
2026-02-15 16:27:07 +08:00
SoftFever
240cf9ab5d bump version to 2.3.2-beta2 2026-02-15 14:31:57 +08:00
Branden Cash
05cb8b4d89 feat(MoonrakerPrinterAgent): support Happy Hare as alternative to AFC for filament sync (#12307)
# Description


# Screenshots/Recordings/Graphs


https://github.com/user-attachments/assets/5558b4be-24eb-4f2d-83fd-8482560a0014

<img width="445" height="285" alt="Screenshot 2026-02-14 at 7 31 57 PM" src="https://github.com/user-attachments/assets/e71fee66-05da-4f9c-8123-0f52e93f0ebb" />


## Tests

Removed configured filaments and pressed the sync button. Observed the filaments configured in my system were populated.
2026-02-15 14:31:29 +08:00
SoftFever
897a3e915f bump profile version to "02.03.02.40" (#12309) 2026-02-15 14:26:33 +08:00
Ian Bassi
586e96479a Linux: Repaired VFA tower (#12290)
* Update CalibUtils.cpp

* VFA.drc case to vfa.drc
2026-02-15 14:26:33 +08:00
Ian Bassi
0879b2079b Spanish minor update (#12268) 2026-02-15 14:26:33 +08:00
Ian Bassi
c4801250ea Spanish Update (#12254)
* Spanish Update
2026-02-15 14:26:33 +08:00
SoftFever
f0386d981f Revert "Switch to self hosted mac runner (#12024)"
This reverts commit f1212be6bb.
2026-02-14 18:11:06 +08:00
77 changed files with 3863 additions and 2548 deletions

View File

@@ -66,7 +66,7 @@ jobs:
matrix: matrix:
include: include:
- os: windows-latest - os: windows-latest
- os: orca-macos-arm64 - os: macos-14
arch: arm64 arch: arm64
# Don't run scheduled builds on forks: # Don't run scheduled builds on forks:
if: ${{ !cancelled() && (github.event_name != 'schedule' || github.repository == 'OrcaSlicer/OrcaSlicer') }} if: ${{ !cancelled() && (github.event_name != 'schedule' || github.repository == 'OrcaSlicer/OrcaSlicer') }}

View File

@@ -33,7 +33,7 @@ jobs:
- name: set outputs - name: set outputs
id: set_outputs id: set_outputs
env: env:
dep-folder-name: ${{ inputs.os != 'orca-macos-arm64' && '/OrcaSlicer_dep' || '' }} dep-folder-name: ${{ inputs.os != 'macos-14' && '/OrcaSlicer_dep' || '' }}
output-cmd: ${{ inputs.os == 'windows-latest' && '$env:GITHUB_OUTPUT' || '"$GITHUB_OUTPUT"'}} output-cmd: ${{ inputs.os == 'windows-latest' && '$env:GITHUB_OUTPUT' || '"$GITHUB_OUTPUT"'}}
run: | run: |
echo cache-key=${{ inputs.os }}-cache-orcaslicer_deps-build-${{ hashFiles('deps/**') }} >> ${{ env.output-cmd }} echo cache-key=${{ inputs.os }}-cache-orcaslicer_deps-build-${{ hashFiles('deps/**') }} >> ${{ env.output-cmd }}

View File

@@ -74,18 +74,18 @@ jobs:
cd ${{ github.workspace }}/deps/build cd ${{ github.workspace }}/deps/build
- name: Build on Mac ${{ inputs.arch }} - name: Build on Mac ${{ inputs.arch }}
if: inputs.os == 'orca-macos-arm64' if: inputs.os == 'macos-14'
working-directory: ${{ github.workspace }} working-directory: ${{ github.workspace }}
run: | run: |
# brew install automake texinfo libtool brew install automake texinfo libtool
# brew list brew list
# brew uninstall --ignore-dependencies zstd brew uninstall --ignore-dependencies zstd
./build_release_macos.sh -dx -a universal -t 10.15 ./build_release_macos.sh -dx -a universal -t 10.15 -1
for arch in arm64 x86_64; do for arch in arm64 x86_64; do
(cd "${{ github.workspace }}/deps/build/${arch}" && \ (cd "${{ github.workspace }}/deps/build/${arch}" && \
find . -mindepth 1 -maxdepth 1 ! -name 'OrcaSlicer_dep' -exec rm -rf {} +) find . -mindepth 1 -maxdepth 1 ! -name 'OrcaSlicer_dep' -exec rm -rf {} +)
done done
# brew install zstd brew install zstd
- name: Apt-Install Dependencies - name: Apt-Install Dependencies
@@ -104,7 +104,7 @@ jobs:
# Upload Artifacts # Upload Artifacts
# - name: Upload Mac ${{ inputs.arch }} artifacts # - name: Upload Mac ${{ inputs.arch }} artifacts
# if: inputs.os == 'orca-macos-arm64' # if: inputs.os == 'macos-14'
# uses: actions/upload-artifact@v6 # uses: actions/upload-artifact@v6
# with: # with:
# name: OrcaSlicer_dep_mac_${{ env.date }} # name: OrcaSlicer_dep_mac_${{ env.date }}

View File

@@ -86,29 +86,29 @@ jobs:
# Mac # Mac
- name: Install tools mac - name: Install tools mac
if: inputs.os == 'orca-macos-arm64' if: inputs.os == 'macos-14'
run: | run: |
# brew install libtool brew install libtool
# brew list brew list
mkdir -p ${{ github.workspace }}/deps/build mkdir -p ${{ github.workspace }}/deps/build
# - name: Free disk space - name: Free disk space
# if: inputs.os == 'orca-macos-arm64' if: inputs.os == 'macos-14'
# run: | run: |
# df -hI /dev/disk3s1s1 df -hI /dev/disk3s1s1
# sudo find /Applications -maxdepth 1 -type d -name "Xcode_*.app" ! -name "Xcode_15.4.app" -exec rm -rf {} + sudo find /Applications -maxdepth 1 -type d -name "Xcode_*.app" ! -name "Xcode_15.4.app" -exec rm -rf {} +
# sudo rm -rf ~/Library/Developer/CoreSimulator/Caches/* sudo rm -rf ~/Library/Developer/CoreSimulator/Caches/*
# df -hI /dev/disk3s1s1 df -hI /dev/disk3s1s1
- name: Build slicer mac - name: Build slicer mac
if: inputs.os == 'orca-macos-arm64' if: inputs.os == 'macos-14'
working-directory: ${{ github.workspace }} working-directory: ${{ github.workspace }}
run: | run: |
./build_release_macos.sh -s -n -x -a universal -t 10.15 ./build_release_macos.sh -s -n -x -a universal -t 10.15 -1
# Thanks to RaySajuuk, it's working now # Thanks to RaySajuuk, it's working now
- name: Sign app and notary - name: Sign app and notary
if: github.repository == 'OrcaSlicer/OrcaSlicer' && (github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/heads/release/')) && inputs.os == 'orca-macos-arm64' if: github.repository == 'OrcaSlicer/OrcaSlicer' && (github.ref == 'refs/heads/main' || startsWith(github.ref, 'refs/heads/release/')) && inputs.os == 'macos-14'
working-directory: ${{ github.workspace }} working-directory: ${{ github.workspace }}
env: env:
BUILD_CERTIFICATE_BASE64: ${{ secrets.BUILD_CERTIFICATE_BASE64 }} BUILD_CERTIFICATE_BASE64: ${{ secrets.BUILD_CERTIFICATE_BASE64 }}
@@ -162,7 +162,7 @@ jobs:
fi fi
- name: Create DMG without notary - name: Create DMG without notary
if: github.ref != 'refs/heads/main' && inputs.os == 'orca-macos-arm64' if: github.ref != 'refs/heads/main' && inputs.os == 'macos-14'
working-directory: ${{ github.workspace }} working-directory: ${{ github.workspace }}
run: | run: |
mkdir -p ${{ github.workspace }}/build/universal/OrcaSlicer_dmg mkdir -p ${{ github.workspace }}/build/universal/OrcaSlicer_dmg
@@ -181,14 +181,14 @@ jobs:
fi fi
- name: Upload artifacts mac - name: Upload artifacts mac
if: inputs.os == 'orca-macos-arm64' if: inputs.os == 'macos-14'
uses: actions/upload-artifact@v6 uses: actions/upload-artifact@v6
with: with:
name: OrcaSlicer_Mac_universal_${{ env.ver }} name: OrcaSlicer_Mac_universal_${{ env.ver }}
path: ${{ github.workspace }}/OrcaSlicer_Mac_universal_${{ env.ver }}.dmg path: ${{ github.workspace }}/OrcaSlicer_Mac_universal_${{ env.ver }}.dmg
- name: Upload OrcaSlicer_profile_validator DMG mac - name: Upload OrcaSlicer_profile_validator DMG mac
if: inputs.os == 'orca-macos-arm64' if: inputs.os == 'macos-14'
uses: actions/upload-artifact@v6 uses: actions/upload-artifact@v6
with: with:
name: OrcaSlicer_profile_validator_Mac_universal_DMG_${{ env.ver }} name: OrcaSlicer_profile_validator_Mac_universal_DMG_${{ env.ver }}
@@ -196,7 +196,7 @@ jobs:
if-no-files-found: ignore if-no-files-found: ignore
- name: Deploy Mac release - name: Deploy Mac release
if: github.repository == 'OrcaSlicer/OrcaSlicer' && github.ref == 'refs/heads/main' && inputs.os == 'orca-macos-arm64' if: github.repository == 'OrcaSlicer/OrcaSlicer' && github.ref == 'refs/heads/main' && inputs.os == 'macos-14'
uses: WebFreak001/deploy-nightly@v3.2.0 uses: WebFreak001/deploy-nightly@v3.2.0
with: with:
upload_url: https://uploads.github.com/repos/OrcaSlicer/OrcaSlicer/releases/137995723/assets{?name,label} upload_url: https://uploads.github.com/repos/OrcaSlicer/OrcaSlicer/releases/137995723/assets{?name,label}
@@ -207,7 +207,7 @@ jobs:
max_releases: 1 # optional, if there are more releases than this matching the asset_name, the oldest ones are going to be deleted max_releases: 1 # optional, if there are more releases than this matching the asset_name, the oldest ones are going to be deleted
- name: Deploy Mac OrcaSlicer_profile_validator DMG release - name: Deploy Mac OrcaSlicer_profile_validator DMG release
if: github.repository == 'OrcaSlicer/OrcaSlicer' && github.ref == 'refs/heads/main' && inputs.os == 'orca-macos-arm64' if: github.repository == 'OrcaSlicer/OrcaSlicer' && github.ref == 'refs/heads/main' && inputs.os == 'macos-14'
uses: WebFreak001/deploy-nightly@v3.2.0 uses: WebFreak001/deploy-nightly@v3.2.0
with: with:
upload_url: https://uploads.github.com/repos/OrcaSlicer/OrcaSlicer/releases/137995723/assets{?name,label} upload_url: https://uploads.github.com/repos/OrcaSlicer/OrcaSlicer/releases/137995723/assets{?name,label}

View File

@@ -5,6 +5,8 @@ find_package(OpenGL QUIET REQUIRED)
orcaslicer_add_cmake_project( orcaslicer_add_cmake_project(
GLEW GLEW
SOURCE_DIR ${CMAKE_CURRENT_LIST_DIR}/glew SOURCE_DIR ${CMAKE_CURRENT_LIST_DIR}/glew
CMAKE_ARGS
-DGLEW_USE_EGL=OFF
) )
if (MSVC) if (MSVC)

View File

@@ -3,9 +3,17 @@ project(GLEW)
find_package(OpenGL REQUIRED) find_package(OpenGL REQUIRED)
if(OpenGL_EGL_FOUND) # Allow parent project to control EGL usage.
message(STATUS "building GLEW for EGL (hope that wxWidgets agrees, otherwise you won't have any output!)") # Default to OFF since OrcaSlicer forces GDK_BACKEND=x11 (using GLX contexts).
# GLEW must use glXGetProcAddressARB (GLX) to match wxWidgets GL canvas.
# Using EGL function loading with GLX contexts causes rendering failures.
option(GLEW_USE_EGL "Use EGL instead of GLX for OpenGL function loading" OFF)
if(GLEW_USE_EGL)
message(STATUS "Building GLEW with EGL support")
set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -DGLEW_EGL") set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -DGLEW_EGL")
else()
message(STATUS "Building GLEW with GLX support")
endif() endif()
add_library(GLEW src/glew.c) add_library(GLEW src/glew.c)

View File

@@ -51,6 +51,7 @@ orcaslicer_add_cmake_project(
-DwxUSE_UNICODE=ON -DwxUSE_UNICODE=ON
-DwxUSE_PRIVATE_FONTS=ON -DwxUSE_PRIVATE_FONTS=ON
-DwxUSE_OPENGL=ON -DwxUSE_OPENGL=ON
-DwxUSE_GLCANVAS_EGL=OFF
-DwxUSE_WEBREQUEST=ON -DwxUSE_WEBREQUEST=ON
-DwxUSE_WEBVIEW=ON -DwxUSE_WEBVIEW=ON
${_wx_edge} ${_wx_edge}

File diff suppressed because it is too large Load Diff

View File

@@ -1,6 +1,6 @@
{ {
"name": "Afinia", "name": "Afinia",
"version": "02.03.01.10", "version": "02.03.02.40",
"force_update": "0", "force_update": "0",
"description": "Afinia configurations", "description": "Afinia configurations",
"machine_model_list": [ "machine_model_list": [

View File

@@ -1,6 +1,6 @@
{ {
"name": "Anker", "name": "Anker",
"version": "02.03.01.20", "version": "02.03.02.40",
"force_update": "0", "force_update": "0",
"description": "Anker configurations", "description": "Anker configurations",
"machine_model_list": [ "machine_model_list": [

View File

@@ -1,6 +1,6 @@
{ {
"name": "Anycubic", "name": "Anycubic",
"version": "02.03.01.10", "version": "02.03.02.40",
"force_update": "0", "force_update": "0",
"description": "Anycubic configurations", "description": "Anycubic configurations",
"machine_model_list": [ "machine_model_list": [

View File

@@ -1,6 +1,6 @@
{ {
"name": "Artillery", "name": "Artillery",
"version": "02.03.02.10", "version": "02.03.02.40",
"force_update": "0", "force_update": "0",
"description": "Artillery configurations", "description": "Artillery configurations",
"machine_model_list": [ "machine_model_list": [

View File

@@ -1,7 +1,7 @@
{ {
"name": "Bambulab", "name": "Bambulab",
"url": "http://www.bambulab.com/Parameters/vendor/BBL.json", "url": "http://www.bambulab.com/Parameters/vendor/BBL.json",
"version": "02.00.00.56", "version": "02.01.00.00",
"force_update": "0", "force_update": "0",
"description": "the initial version of BBL configurations", "description": "the initial version of BBL configurations",
"machine_model_list": [ "machine_model_list": [

View File

@@ -1,6 +1,6 @@
{ {
"name": "BIQU", "name": "BIQU",
"version": "02.03.01.10", "version": "02.03.02.40",
"force_update": "0", "force_update": "0",
"description": "BIQU configurations", "description": "BIQU configurations",
"machine_model_list": [ "machine_model_list": [

View File

@@ -1,6 +1,6 @@
{ {
"name": "Blocks", "name": "Blocks",
"version": "02.03.01.10", "version": "02.03.02.40",
"force_update": "0", "force_update": "0",
"description": "Blocks configurations", "description": "Blocks configurations",
"machine_model_list": [ "machine_model_list": [

View File

@@ -1,6 +1,6 @@
{ {
"name": "CONSTRUCT3D", "name": "CONSTRUCT3D",
"version": "02.03.01.10", "version": "02.03.02.40",
"force_update": "0", "force_update": "0",
"description": "Construct3D configurations", "description": "Construct3D configurations",
"machine_model_list": [ "machine_model_list": [

View File

@@ -1,7 +1,7 @@
{ {
"name": "Chuanying", "name": "Chuanying",
"url": "", "url": "",
"version": "02.03.01.10", "version": "02.03.02.40",
"force_update": "0", "force_update": "0",
"description": "Chuanying configurations", "description": "Chuanying configurations",
"machine_model_list": [ "machine_model_list": [

View File

@@ -1,6 +1,6 @@
{ {
"name": "Co Print", "name": "Co Print",
"version": "02.03.01.10", "version": "02.03.02.40",
"force_update": "0", "force_update": "0",
"description": "CoPrint configurations", "description": "CoPrint configurations",
"machine_model_list": [ "machine_model_list": [

View File

@@ -1,6 +1,6 @@
{ {
"name": "CoLiDo", "name": "CoLiDo",
"version": "02.03.01.10", "version": "02.03.02.40",
"force_update": "0", "force_update": "0",
"description": "CoLiDo configurations", "description": "CoLiDo configurations",
"machine_model_list": [ "machine_model_list": [

View File

@@ -1,6 +1,6 @@
{ {
"name": "Comgrow", "name": "Comgrow",
"version": "02.03.01.10", "version": "02.03.02.40",
"force_update": "0", "force_update": "0",
"description": "Comgrow configurations", "description": "Comgrow configurations",
"machine_model_list": [ "machine_model_list": [

View File

@@ -1,6 +1,6 @@
{ {
"name": "Creality", "name": "Creality",
"version": "02.03.01.20", "version": "02.03.02.40",
"force_update": "0", "force_update": "0",
"description": "Creality configurations", "description": "Creality configurations",
"machine_model_list": [ "machine_model_list": [

View File

@@ -1,6 +1,6 @@
{ {
"name": "Cubicon", "name": "Cubicon",
"version": "02.03.01.10", "version": "02.03.02.40",
"force_update": "0", "force_update": "0",
"description": "Cubicon configurations", "description": "Cubicon configurations",
"machine_model_list": [ "machine_model_list": [

View File

@@ -1,6 +1,6 @@
{ {
"name": "Custom Printer", "name": "Custom Printer",
"version": "02.03.01.10", "version": "02.03.02.40",
"force_update": "0", "force_update": "0",
"description": "My configurations", "description": "My configurations",
"machine_model_list": [ "machine_model_list": [

View File

@@ -1,7 +1,7 @@
{ {
"name": "DeltaMaker", "name": "DeltaMaker",
"url": "", "url": "",
"version": "02.03.01.11", "version": "02.03.02.40",
"force_update": "0", "force_update": "0",
"description": "DeltaMaker configurations", "description": "DeltaMaker configurations",
"machine_model_list": [ "machine_model_list": [

View File

@@ -1,6 +1,6 @@
{ {
"name": "Dremel", "name": "Dremel",
"version": "02.03.01.10", "version": "02.03.02.40",
"force_update": "0", "force_update": "0",
"description": "Dremel configurations", "description": "Dremel configurations",
"machine_model_list": [ "machine_model_list": [

View File

@@ -1,6 +1,6 @@
{ {
"name": "Elegoo", "name": "Elegoo",
"version": "02.03.01.20", "version": "02.03.02.40",
"force_update": "0", "force_update": "0",
"description": "Elegoo configurations", "description": "Elegoo configurations",
"machine_model_list": [ "machine_model_list": [

View File

@@ -1,6 +1,6 @@
{ {
"name": "Eryone", "name": "Eryone",
"version": "02.03.01.10", "version": "02.03.02.40",
"force_update": "0", "force_update": "0",
"description": "Eryone configurations", "description": "Eryone configurations",
"machine_model_list": [ "machine_model_list": [

View File

@@ -1,6 +1,6 @@
{ {
"name": "FLSun", "name": "FLSun",
"version": "02.03.01.10", "version": "02.03.02.40",
"force_update": "0", "force_update": "0",
"description": "FLSun configurations", "description": "FLSun configurations",
"machine_model_list": [ "machine_model_list": [

View File

@@ -1,7 +1,7 @@
{ {
"name": "Flashforge", "name": "Flashforge",
"url": "", "url": "",
"version": "02.03.01.10", "version": "02.03.02.40",
"force_update": "0", "force_update": "0",
"description": "Flashforge configurations", "description": "Flashforge configurations",
"machine_model_list": [ "machine_model_list": [

View File

@@ -1,6 +1,6 @@
{ {
"name": "FlyingBear", "name": "FlyingBear",
"version": "02.03.01.00", "version": "02.03.02.40",
"force_update": "1", "force_update": "1",
"description": "FlyingBear configurations", "description": "FlyingBear configurations",
"machine_model_list": [ "machine_model_list": [

View File

@@ -1,6 +1,6 @@
{ {
"name": "Folgertech", "name": "Folgertech",
"version": "02.03.01.10", "version": "02.03.02.40",
"force_update": "0", "force_update": "0",
"description": "Folgertech configurations", "description": "Folgertech configurations",
"machine_model_list": [ "machine_model_list": [

View File

@@ -1,6 +1,6 @@
{ {
"name": "Geeetech", "name": "Geeetech",
"version": "02.03.01.11", "version": "02.03.02.40",
"force_update": "0", "force_update": "0",
"description": "Geeetech configurations", "description": "Geeetech configurations",
"machine_model_list": [ "machine_model_list": [

View File

@@ -1,6 +1,6 @@
{ {
"name": "Ginger Additive", "name": "Ginger Additive",
"version": "02.03.01.11", "version": "02.03.02.40",
"force_update": "1", "force_update": "1",
"description": "Ginger configuration", "description": "Ginger configuration",
"machine_model_list": [ "machine_model_list": [

View File

@@ -1,6 +1,6 @@
{ {
"name": "InfiMech", "name": "InfiMech",
"version": "02.03.01.10", "version": "02.03.02.40",
"force_update": "1", "force_update": "1",
"description": "InfiMech configurations", "description": "InfiMech configurations",
"machine_model_list": [ "machine_model_list": [

View File

@@ -1,7 +1,7 @@
{ {
"name": "Kingroon", "name": "Kingroon",
"url": "https://kingroon.com/", "url": "https://kingroon.com/",
"version": "02.03.01.10", "version": "02.03.02.40",
"force_update": "1", "force_update": "1",
"description": "Kingroon configuration files", "description": "Kingroon configuration files",
"machine_model_list": [ "machine_model_list": [

View File

@@ -1,6 +1,6 @@
{ {
"name": "LONGER", "name": "LONGER",
"version": "02.03.01.10", "version": "02.03.02.40",
"force_update": "0", "force_update": "0",
"description": "LONGER configurations", "description": "LONGER configurations",
"machine_model_list": [ "machine_model_list": [

View File

@@ -1,7 +1,7 @@
{ {
"name": "Lulzbot", "name": "Lulzbot",
"url": "https://ohai.lulzbot.com/group/taz-6/", "url": "https://ohai.lulzbot.com/group/taz-6/",
"version": "02.03.01.10", "version": "02.03.02.40",
"force_update": "0", "force_update": "0",
"description": "Lulzbot configurations", "description": "Lulzbot configurations",
"machine_model_list": [ "machine_model_list": [

View File

@@ -1,6 +1,6 @@
{ {
"name": "M3D", "name": "M3D",
"version": "1.0.0", "version": "02.03.02.40",
"force_update": "0", "force_update": "0",
"description": "Configuration for M3D printers", "description": "Configuration for M3D printers",
"machine_model_list": [ "machine_model_list": [

View File

@@ -1,6 +1,6 @@
{ {
"name": "MagicMaker", "name": "MagicMaker",
"version": "02.03.01.10", "version": "02.03.02.40",
"force_update": "0", "force_update": "0",
"description": "MagicMaker configurations", "description": "MagicMaker configurations",
"machine_model_list": [ "machine_model_list": [

View File

@@ -1,6 +1,6 @@
{ {
"name": "Mellow", "name": "Mellow",
"version": "02.03.01.10", "version": "02.03.02.40",
"force_update": "0", "force_update": "0",
"description": "Mellow Printer Profiles", "description": "Mellow Printer Profiles",
"machine_model_list": [ "machine_model_list": [

View File

@@ -1,7 +1,7 @@
{ {
"name": "OpenEYE", "name": "OpenEYE",
"url": "http://www.openeye.tech", "url": "http://www.openeye.tech",
"version": "01.00.00.03", "version": "02.03.02.40",
"force_update": "0", "force_update": "0",
"description": "OpenEYE Printers Configurations", "description": "OpenEYE Printers Configurations",
"machine_model_list": [ "machine_model_list": [

View File

@@ -1,7 +1,7 @@
{ {
"name": "Orca Arena Printer", "name": "Orca Arena Printer",
"url": "", "url": "",
"version": "02.03.01.10", "version": "02.03.02.40",
"force_update": "0", "force_update": "0",
"description": "Orca Arena configuration files", "description": "Orca Arena configuration files",
"machine_model_list": [ "machine_model_list": [

View File

@@ -1,6 +1,6 @@
{ {
"name": "OrcaFilamentLibrary", "name": "OrcaFilamentLibrary",
"version": "02.03.01.10", "version": "02.03.02.40",
"force_update": "0", "force_update": "0",
"description": "Orca Filament Library", "description": "Orca Filament Library",
"filament_list": [ "filament_list": [

View File

@@ -1,6 +1,6 @@
{ {
"name": "Peopoly", "name": "Peopoly",
"version": "02.03.01.10", "version": "02.03.02.40",
"force_update": "0", "force_update": "0",
"description": "Peopoly configurations", "description": "Peopoly configurations",
"machine_model_list": [ "machine_model_list": [

View File

@@ -1,6 +1,6 @@
{ {
"name": "Phrozen", "name": "Phrozen",
"version": "02.03.01.11", "version": "02.03.02.40",
"force_update": "0", "force_update": "0",
"description": "Phrozen configurations", "description": "Phrozen configurations",
"machine_model_list": [ "machine_model_list": [

View File

@@ -1,6 +1,6 @@
{ {
"name": "Positron 3D", "name": "Positron 3D",
"version": "02.03.01.10", "version": "02.03.02.40",
"force_update": "0", "force_update": "0",
"description": "Positron 3D Printer Profile", "description": "Positron 3D Printer Profile",
"machine_model_list": [ "machine_model_list": [

View File

@@ -1,6 +1,6 @@
{ {
"name": "Prusa", "name": "Prusa",
"version": "02.03.01.11", "version": "02.03.02.40",
"force_update": "0", "force_update": "0",
"description": "Prusa configurations", "description": "Prusa configurations",
"machine_model_list": [ "machine_model_list": [

View File

@@ -1,6 +1,6 @@
{ {
"name": "Qidi", "name": "Qidi",
"version": "02.03.01.20", "version": "02.03.02.40",
"force_update": "0", "force_update": "0",
"description": "Qidi configurations", "description": "Qidi configurations",
"machine_model_list": [ "machine_model_list": [

View File

@@ -1,6 +1,6 @@
{ {
"name": "RH3D", "name": "RH3D",
"version": "00.06.10.25", "version": "02.03.02.40",
"force_update": "0", "force_update": "0",
"description": "RH3D - printer profiles", "description": "RH3D - printer profiles",
"machine_model_list": [ "machine_model_list": [

View File

@@ -1,7 +1,7 @@
{ {
"name": "Raise3D", "name": "Raise3D",
"url": "", "url": "",
"version": "02.03.01.10", "version": "02.03.02.40",
"force_update": "0", "force_update": "0",
"description": "Raise3D configurations", "description": "Raise3D configurations",
"machine_model_list": [ "machine_model_list": [

View File

@@ -1,6 +1,6 @@
{ {
"name": "RatRig", "name": "RatRig",
"version": "02.03.01.11", "version": "02.03.02.40",
"force_update": "0", "force_update": "0",
"description": "RatRig configurations", "description": "RatRig configurations",
"machine_model_list": [ "machine_model_list": [

View File

@@ -1,6 +1,6 @@
{ {
"name": "RolohaunDesign", "name": "RolohaunDesign",
"version": "02.03.02.10", "version": "02.03.02.40",
"force_update": "0", "force_update": "0",
"description": "RolohaunDesign Printer Profiles", "description": "RolohaunDesign Printer Profiles",
"machine_model_list": [ "machine_model_list": [

View File

@@ -1,6 +1,6 @@
{ {
"name": "SecKit", "name": "SecKit",
"version": "02.03.01.10", "version": "02.03.02.40",
"force_update": "0", "force_update": "0",
"description": "SecKit configurations", "description": "SecKit configurations",
"machine_model_list": [ "machine_model_list": [

View File

@@ -1,6 +1,6 @@
{ {
"name": "Snapmaker", "name": "Snapmaker",
"version": "02.03.01.20", "version": "02.03.02.40",
"force_update": "0", "force_update": "0",
"description": "Snapmaker configurations", "description": "Snapmaker configurations",
"machine_model_list": [ "machine_model_list": [

View File

@@ -1,7 +1,7 @@
{ {
"name": "Sovol", "name": "Sovol",
"url": "", "url": "",
"version": "02.03.01.10", "version": "02.03.02.40",
"force_update": "0", "force_update": "0",
"description": "Sovol configurations", "description": "Sovol configurations",
"machine_model_list": [ "machine_model_list": [

View File

@@ -1,6 +1,6 @@
{ {
"name": "Tiertime", "name": "Tiertime",
"version": "02.03.01.10", "version": "02.03.02.40",
"force_update": "0", "force_update": "0",
"description": "Tiertime configurations", "description": "Tiertime configurations",
"machine_model_list": [ "machine_model_list": [

View File

@@ -1,6 +1,6 @@
{ {
"name": "Tronxy", "name": "Tronxy",
"version": "02.03.01.10", "version": "02.03.02.40",
"force_update": "0", "force_update": "0",
"description": "Tronxy configurations", "description": "Tronxy configurations",
"machine_model_list": [ "machine_model_list": [

View File

@@ -1,6 +1,6 @@
{ {
"name": "TwoTrees", "name": "TwoTrees",
"version": "02.03.01.10", "version": "02.03.02.40",
"force_update": "1", "force_update": "1",
"description": "TwoTrees configurations", "description": "TwoTrees configurations",
"machine_model_list": [ "machine_model_list": [

View File

@@ -1,7 +1,7 @@
{ {
"name": "UltiMaker", "name": "UltiMaker",
"url": "", "url": "",
"version": "02.03.01.10", "version": "02.03.02.40",
"force_update": "0", "force_update": "0",
"description": "UltiMaker configurations", "description": "UltiMaker configurations",
"machine_model_list": [ "machine_model_list": [

View File

@@ -1,6 +1,6 @@
{ {
"name": "Vivedino", "name": "Vivedino",
"version": "02.03.01.10", "version": "02.03.02.40",
"force_update": "0", "force_update": "0",
"description": "Vivedino configurations", "description": "Vivedino configurations",
"machine_model_list": [ "machine_model_list": [

View File

@@ -1,6 +1,6 @@
{ {
"name": "Volumic", "name": "Volumic",
"version": "02.03.01.10", "version": "02.03.02.40",
"force_update": "1", "force_update": "1",
"description": "VOLUMIC configurations", "description": "VOLUMIC configurations",
"machine_model_list": [ "machine_model_list": [

View File

@@ -1,6 +1,6 @@
{ {
"name": "Voron", "name": "Voron",
"version": "02.03.01.11", "version": "02.03.02.40",
"force_update": "0", "force_update": "0",
"description": "Voron configurations", "description": "Voron configurations",
"machine_model_list": [ "machine_model_list": [

View File

@@ -1,7 +1,7 @@
{ {
"name": "Voxelab", "name": "Voxelab",
"url": "", "url": "",
"version": "02.03.01.10", "version": "02.03.02.40",
"force_update": "0", "force_update": "0",
"description": "Voxelab configurations", "description": "Voxelab configurations",
"machine_model_list": [ "machine_model_list": [

View File

@@ -1,6 +1,6 @@
{ {
"name": "Vzbot", "name": "Vzbot",
"version": "02.03.01.10", "version": "02.03.02.40",
"force_update": "0", "force_update": "0",
"description": "Vzbot configurations", "description": "Vzbot configurations",
"machine_model_list": [ "machine_model_list": [

View File

@@ -1,6 +1,6 @@
{ {
"name": "WEMAKE3D", "name": "WEMAKE3D",
"version": "02.03.01.20", "version": "02.03.02.40",
"force_update": "0", "force_update": "0",
"description": "WEMAKE3D configurations", "description": "WEMAKE3D configurations",
"machine_model_list": [ "machine_model_list": [

View File

@@ -1,6 +1,6 @@
{ {
"name": "Wanhao France", "name": "Wanhao France",
"version": "02.03.01.11", "version": "02.03.02.40",
"force_update": "0", "force_update": "0",
"description": "Wanhao France D12 configurations", "description": "Wanhao France D12 configurations",
"machine_model_list": [ "machine_model_list": [

View File

@@ -1,6 +1,6 @@
{ {
"name": "Wanhao", "name": "Wanhao",
"version": "02.03.01.10", "version": "02.03.02.40",
"force_update": "0", "force_update": "0",
"description": "Wanhao configurations", "description": "Wanhao configurations",
"machine_model_list": [ "machine_model_list": [

View File

@@ -1,7 +1,7 @@
{ {
"name": "WonderMaker", "name": "WonderMaker",
"url": "", "url": "",
"version": "02.03.01.10", "version": "02.03.02.40",
"force_update": "0", "force_update": "0",
"description": "WonderMaker configurations", "description": "WonderMaker configurations",
"machine_model_list": [ "machine_model_list": [

View File

@@ -1,7 +1,7 @@
{ {
"name": "Z-Bolt", "name": "Z-Bolt",
"url": "", "url": "",
"version": "02.03.01.10", "version": "02.03.02.40",
"force_update": "0", "force_update": "0",
"description": "Z-Bolt configurations", "description": "Z-Bolt configurations",
"machine_model_list": [ "machine_model_list": [

View File

@@ -1,6 +1,6 @@
{ {
"name": "innovatiQ", "name": "innovatiQ",
"version": "02.03.01.10", "version": "02.03.02.40",
"force_update": "1", "force_update": "1",
"description": "innovatiQ configuration", "description": "innovatiQ configuration",
"machine_model_list": [ "machine_model_list": [

View File

@@ -9288,6 +9288,61 @@ void DynamicPrintConfig::update_values_to_printer_extruders_for_multiple_filamen
} }
} }
namespace {
// Options in printer_options_with_variant_2 are stored as (normal,silent) pairs per printer variant.
// Some legacy presets/projects carry a variant list but still store only one pair; normalize to avoid crashes.
static void normalize_stride2_floats(ConfigOptionFloats &opt, size_t expected_size)
{
auto &v = opt.values;
if (expected_size == 0) {
v.clear();
return;
}
if (v.empty()) {
// Fallback: keep behavior predictable instead of crashing. This should be rare.
v.resize(expected_size, 0.0);
return;
}
const double first = v[0];
const double second = (v.size() >= 2) ? v[1] : first;
// Ensure we have at least one (normal,silent) pair to replicate.
if (v.size() < 2) {
v.resize(2, first);
v[1] = second;
}
// Keep pair alignment if some legacy preset produced odd length.
if (v.size() % 2 != 0)
v.push_back(second);
if (v.size() > expected_size) {
v.resize(expected_size);
return;
}
const size_t have_variants = v.size() / 2;
const size_t want_variants = expected_size / 2;
v.resize(expected_size);
for (size_t vi = have_variants; vi < want_variants; ++vi) {
v[vi * 2] = first;
if (vi * 2 + 1 < v.size())
v[vi * 2 + 1] = second;
}
}
static void log_normalize_legacy_vector_size(const char *fn, const std::string &key, int stride, size_t src_size, size_t dest_size, size_t expected_size,
size_t restore_n, int cur_variant_count, int target_variant_count, size_t cur_ids, size_t target_ids,
const ConfigOption *opt_src, const ConfigOption *opt_target)
{
BOOST_LOG_TRIVIAL(debug) << fn << ": normalizing legacy vector size for key '" << key << "'"
<< " stride=" << stride << " src_size=" << src_size << " dest_size=" << dest_size << " expected=" << expected_size
<< " restore_index.size=" << restore_n << " cur_variants=" << cur_variant_count << " target_variants=" << target_variant_count
<< " cur_ids=" << cur_ids << " target_ids=" << target_ids << " cur_value=" << opt_src->serialize()
<< " target_value=" << opt_target->serialize();
}
} // namespace
void DynamicPrintConfig::update_non_diff_values_to_base_config(DynamicPrintConfig& new_config, const t_config_option_keys& keys, const std::set<std::string>& different_keys, void DynamicPrintConfig::update_non_diff_values_to_base_config(DynamicPrintConfig& new_config, const t_config_option_keys& keys, const std::set<std::string>& different_keys,
std::string extruder_id_name, std::string extruder_variant_name, std::set<std::string>& key_set1, std::set<std::string>& key_set2) std::string extruder_id_name, std::string extruder_variant_name, std::set<std::string>& key_set1, std::set<std::string>& key_set2)
{ {
@@ -9310,7 +9365,10 @@ void DynamicPrintConfig::update_non_diff_values_to_base_config(DynamicPrintConfi
variant_index.resize(target_variant_count, -1); variant_index.resize(target_variant_count, -1);
if (cur_variant_count == 0) { if (cur_variant_count == 0) {
variant_index[0] = 0; // Defensive: target_variant_count may be 0 if the preset doesn't carry extruder_variant_name.
// In that case keep variant_index empty and let the downstream size checks produce a useful error.
if (!variant_index.empty())
variant_index[0] = 0;
} }
else if ((cur_extruder_ids.size() > 0) && cur_variant_count != cur_extruder_ids.size()){ else if ((cur_extruder_ids.size() > 0) && cur_variant_count != cur_extruder_ids.size()){
//should not happen //should not happen
@@ -9352,12 +9410,53 @@ void DynamicPrintConfig::update_non_diff_values_to_base_config(DynamicPrintConfi
//nothing to do, keep the original one //nothing to do, keep the original one
} }
else { else {
ConfigOptionVectorBase* opt_vec_src = static_cast<ConfigOptionVectorBase*>(opt_src);
const ConfigOptionVectorBase* opt_vec_dest = static_cast<const ConfigOptionVectorBase*>(opt_target);
int stride = 1; int stride = 1;
if (key_set2.find(opt) != key_set2.end()) if (key_set2.find(opt) != key_set2.end())
stride = 2; stride = 2;
opt_vec_src->set_with_restore(opt_vec_dest, variant_index, stride);
const size_t restore_n = variant_index.size();
const size_t expected_size = restore_n * size_t(stride);
if (stride == 2) {
// Options in key_set2 are machine limits stored as (normal,silent) pairs per printer variant.
if (opt_src->type() != coFloats || opt_target->type() != coFloats)
throw ConfigurationError((boost::format("%1%: key '%2%' is expected to be ConfigOptionFloats for stride=2.") % __FUNCTION__ % opt).str());
auto *src_f = static_cast<ConfigOptionFloats*>(opt_src);
ConfigOptionFloats rhs_tmp(*static_cast<const ConfigOptionFloats*>(opt_target));
const size_t src_size = src_f->values.size();
const size_t dest_size = rhs_tmp.values.size();
if (src_size != expected_size || dest_size != expected_size)
log_normalize_legacy_vector_size(__FUNCTION__, opt, stride, src_size, dest_size, expected_size, restore_n, cur_variant_count,
target_variant_count, cur_extruder_ids.size(), target_extruder_ids.size(), opt_src, opt_target);
// Normalize src in-place so backup_values indexing is safe, normalize rhs via a temporary copy.
normalize_stride2_floats(*src_f, expected_size);
normalize_stride2_floats(rhs_tmp, expected_size);
src_f->set_with_restore(&rhs_tmp, variant_index, stride);
} else {
ConfigOptionVectorBase* opt_vec_src = static_cast<ConfigOptionVectorBase*>(opt_src);
const size_t src_size = opt_vec_src->size();
const size_t dest_size = static_cast<const ConfigOptionVectorBase*>(opt_target)->size();
if (src_size != expected_size || dest_size != expected_size)
log_normalize_legacy_vector_size(__FUNCTION__, opt, stride, src_size, dest_size, expected_size, restore_n, cur_variant_count,
target_variant_count, cur_extruder_ids.size(), target_extruder_ids.size(), opt_src, opt_target);
if (opt_vec_src->size() != expected_size)
opt_vec_src->resize(expected_size, opt_target);
// Normalize rhs via a cloned temporary (rhs itself is const).
ConfigOptionUniquePtr rhs_owner(opt_target->clone());
ConfigOptionVectorBase *rhs_vec = dynamic_cast<ConfigOptionVectorBase*>(rhs_owner.get());
if (rhs_vec == nullptr)
throw ConfigurationError((boost::format("%1%: key '%2%' is expected to be a vector option.") % __FUNCTION__ % opt).str());
if (rhs_vec->size() != expected_size)
rhs_vec->resize(expected_size, opt_target);
opt_vec_src->set_with_restore(rhs_vec, variant_index, stride);
}
} }
} }
} }

View File

@@ -25,6 +25,18 @@
#include "../Utils/MacDarkMode.hpp" #include "../Utils/MacDarkMode.hpp"
#endif // __APPLE__ #endif // __APPLE__
// Verify GLEW and wxWidgets use the same OpenGL backend (EGL vs GLX).
// A mismatch causes rendering failures: GLEW's function loading must match
// the context type created by wxWidgets.
#if defined(__linux__)
#if defined(GLEW_EGL) && (!defined(wxUSE_GLCANVAS_EGL) || !wxUSE_GLCANVAS_EGL)
#error "OpenGL backend mismatch: GLEW has EGL support enabled but wxWidgets does not. Ensure GLEW_USE_EGL and wxUSE_GLCANVAS_EGL are both ON or both OFF."
#endif
#if !defined(GLEW_EGL) && defined(wxUSE_GLCANVAS_EGL) && wxUSE_GLCANVAS_EGL
#error "OpenGL backend mismatch: wxWidgets has EGL support enabled but GLEW does not. Ensure GLEW_USE_EGL and wxUSE_GLCANVAS_EGL are both ON or both OFF."
#endif
#endif
namespace Slic3r { namespace Slic3r {
namespace GUI { namespace GUI {

View File

@@ -461,42 +461,6 @@ void MoonrakerPrinterAgent::build_ams_payload(int ams_count, int max_lane_index,
return; return;
} }
// Color normalization helper (handles #RRGGBB, 0xRRGGBB -> RRGGBBAA)
auto normalize_color = [](const std::string& color) -> std::string {
std::string value = color;
boost::trim(value);
// Remove 0x or 0X prefix if present
if (value.size() >= 2 && (value.rfind("0x", 0) == 0 || value.rfind("0X", 0) == 0)) {
value = value.substr(2);
}
// Remove # prefix if present
if (!value.empty() && value[0] == '#') {
value = value.substr(1);
}
// Extract only hex digits
std::string normalized;
for (char c : value) {
if (std::isxdigit(static_cast<unsigned char>(c))) {
normalized.push_back(static_cast<char>(std::toupper(static_cast<unsigned char>(c))));
}
}
// If 6 hex digits, add FF alpha
if (normalized.size() == 6) {
normalized += "FF";
}
// Validate length - return default if invalid
if (normalized.size() != 8) {
return "00000000";
}
return normalized;
};
// Build BBL-format JSON for DevFilaSystemParser::ParseV1_0 // Build BBL-format JSON for DevFilaSystemParser::ParseV1_0
nlohmann::json ams_json = nlohmann::json::object(); nlohmann::json ams_json = nlohmann::json::object();
nlohmann::json ams_array = nlohmann::json::array(); nlohmann::json ams_array = nlohmann::json::array();
@@ -535,7 +499,7 @@ void MoonrakerPrinterAgent::build_ams_payload(int ams_count, int max_lane_index,
tray_json["tray_info_idx"] = tray->tray_info_idx; tray_json["tray_info_idx"] = tray->tray_info_idx;
tray_json["tray_type"] = tray->tray_type; tray_json["tray_type"] = tray->tray_type;
tray_json["tray_color"] = normalize_color(tray->tray_color); tray_json["tray_color"] = normalize_color_value(tray->tray_color);
// Add temperature data if provided // Add temperature data if provided
if (tray->bed_temp > 0) { if (tray->bed_temp > 0) {
@@ -604,119 +568,30 @@ void MoonrakerPrinterAgent::build_ams_payload(int ams_count, int max_lane_index,
bool MoonrakerPrinterAgent::fetch_filament_info(std::string dev_id) bool MoonrakerPrinterAgent::fetch_filament_info(std::string dev_id)
{ {
// Fetch AFC lane data from Moonraker database (inline)
std::string url = join_url(device_info.base_url, "/server/database/item?namespace=lane_data");
std::string response_body;
bool success = false;
std::string http_error;
auto http = Http::get(url);
if (!device_info.api_key.empty()) {
http.header("X-Api-Key", device_info.api_key);
}
http.timeout_connect(5)
.timeout_max(10)
.on_complete([&](std::string body, unsigned status) {
if (status == 200) {
response_body = body;
success = true;
} else {
http_error = "HTTP error: " + std::to_string(status);
}
})
.on_error([&](std::string body, std::string err, unsigned status) {
http_error = err;
if (status > 0) {
http_error += " (HTTP " + std::to_string(status) + ")";
}
})
.perform_sync();
if (!success) {
BOOST_LOG_TRIVIAL(warning) << "MoonrakerPrinterAgent::fetch_filament_info: Failed to fetch lane data: " << http_error;
return false;
}
auto json = nlohmann::json::parse(response_body, nullptr, false, true);
if (json.is_discarded()) {
BOOST_LOG_TRIVIAL(warning) << "MoonrakerPrinterAgent::fetch_filament_info: Invalid JSON response";
return false;
}
// Expected structure: { "result": { "namespace": "lane_data", "value": { "lane1": {...}, ... } } }
if (!json.contains("result") || !json["result"].contains("value") || !json["result"]["value"].is_object()) {
BOOST_LOG_TRIVIAL(warning) << "MoonrakerPrinterAgent::fetch_filament_info: Unexpected JSON structure or no lane_data found";
return false;
}
// Parse response into AmsTrayData
const auto& value = json["result"]["value"];
std::vector<AmsTrayData> trays; std::vector<AmsTrayData> trays;
int max_lane_index = 0; int max_lane_index = 0;
// Null-safe JSON accessors: nlohmann::json::value() throws type_error // Try Happy Hare first (more widely adopted, supports more filament changers)
// when the key exists but the value is null (type mismatch). if (fetch_hh_filament_info(trays, max_lane_index)) {
auto safe_string = [](const nlohmann::json& obj, const char* key) -> std::string { BOOST_LOG_TRIVIAL(info) << "MoonrakerPrinterAgent::fetch_filament_info: Detected Happy Hare MMU with "
auto it = obj.find(key); << (max_lane_index + 1) << " gates";
if (it != obj.end() && it->is_string()) int ams_count = (max_lane_index + 4) / 4;
return it->get<std::string>(); build_ams_payload(ams_count, max_lane_index, trays);
return ""; return true;
};
auto safe_int = [](const nlohmann::json& obj, const char* key) -> int {
auto it = obj.find(key);
if (it != obj.end() && it->is_number())
return it->get<int>();
return 0;
};
for (const auto& [lane_key, lane_obj] : value.items()) {
if (!lane_obj.is_object()) {
continue;
}
// Extract lane index from the "lane" field (tool number, 0-based)
std::string lane_str = safe_string(lane_obj, "lane");
int lane_index = -1;
if (!lane_str.empty()) {
try {
lane_index = std::stoi(lane_str);
} catch (...) {
lane_index = -1;
}
}
if (lane_index < 0) {
continue;
}
AmsTrayData tray;
tray.slot_index = lane_index;
tray.tray_color = safe_string(lane_obj, "color");
tray.tray_type = safe_string(lane_obj, "material");
tray.bed_temp = safe_int(lane_obj, "bed_temp");
tray.nozzle_temp = safe_int(lane_obj, "nozzle_temp");
tray.has_filament = !tray.tray_type.empty();
auto* bundle = GUI::wxGetApp().preset_bundle;
tray.tray_info_idx = bundle
? bundle->filaments.filament_id_by_type(tray.tray_type)
: map_filament_type_to_generic_id(tray.tray_type);
max_lane_index = std::max(max_lane_index, lane_index);
trays.push_back(tray);
} }
if (trays.empty()) { // Fallback to AFC
BOOST_LOG_TRIVIAL(info) << "MoonrakerPrinterAgent::fetch_filament_info: No AFC lanes found"; if (fetch_afc_filament_info(trays, max_lane_index)) {
return false; BOOST_LOG_TRIVIAL(info) << "MoonrakerPrinterAgent::fetch_filament_info: Detected AFC with "
<< (max_lane_index + 1) << " lanes";
int ams_count = (max_lane_index + 4) / 4;
build_ams_payload(ams_count, max_lane_index, trays);
return true;
} }
// Calculate AMS count from max lane index (4 trays per AMS unit) // No MMU detected - this is normal for printers without MMU, not an error
int ams_count = (max_lane_index + 4) / 4; BOOST_LOG_TRIVIAL(info) << "MoonrakerPrinterAgent::fetch_filament_info: No MMU system detected (neither HH nor AFC)";
return false;
// Build and parse the AMS payload
build_ams_payload(ams_count, max_lane_index, trays);
return true;
} }
std::string MoonrakerPrinterAgent::trim_and_upper(const std::string& input) std::string MoonrakerPrinterAgent::trim_and_upper(const std::string& input)
@@ -780,6 +655,298 @@ std::string MoonrakerPrinterAgent::map_filament_type_to_generic_id(const std::st
return UNKNOWN_FILAMENT_ID; return UNKNOWN_FILAMENT_ID;
} }
// JSON helper methods - null-safe accessors
std::string MoonrakerPrinterAgent::safe_json_string(const nlohmann::json& obj, const char* key)
{
auto it = obj.find(key);
if (it != obj.end() && it->is_string())
return it->get<std::string>();
return "";
}
int MoonrakerPrinterAgent::safe_json_int(const nlohmann::json& obj, const char* key)
{
auto it = obj.find(key);
if (it != obj.end() && it->is_number())
return it->get<int>();
return 0;
}
std::string MoonrakerPrinterAgent::safe_array_string(const nlohmann::json& arr, int idx)
{
if (arr.is_array() && idx >= 0 && idx < static_cast<int>(arr.size()) && arr[idx].is_string())
return arr[idx].get<std::string>();
return "";
}
int MoonrakerPrinterAgent::safe_array_int(const nlohmann::json& arr, int idx)
{
if (arr.is_array() && idx >= 0 && idx < static_cast<int>(arr.size()) && arr[idx].is_number())
return arr[idx].get<int>();
return 0;
}
std::string MoonrakerPrinterAgent::normalize_color_value(const std::string& color)
{
std::string value = color;
boost::trim(value);
// Remove 0x or 0X prefix if present
if (value.size() >= 2 && (value.rfind("0x", 0) == 0 || value.rfind("0X", 0) == 0)) {
value = value.substr(2);
}
// Remove # prefix if present
if (!value.empty() && value[0] == '#') {
value = value.substr(1);
}
// Extract only hex digits
std::string normalized;
for (char c : value) {
if (std::isxdigit(static_cast<unsigned char>(c))) {
normalized.push_back(static_cast<char>(std::toupper(static_cast<unsigned char>(c))));
}
}
// If 6 hex digits, add FF alpha
if (normalized.size() == 6) {
normalized += "FF";
}
// Validate length - return default if invalid
if (normalized.size() != 8) {
return "00000000";
}
return normalized;
}
// Fetch filament info from Armored Turtle AFC
bool MoonrakerPrinterAgent::fetch_afc_filament_info(std::vector<AmsTrayData>& trays, int& max_lane_index)
{
// Fetch AFC lane data from Moonraker database
std::string url = join_url(device_info.base_url, "/server/database/item?namespace=lane_data");
std::string response_body;
bool success = false;
std::string http_error;
auto http = Http::get(url);
if (!device_info.api_key.empty()) {
http.header("X-Api-Key", device_info.api_key);
}
http.timeout_connect(5)
.timeout_max(10)
.on_complete([&](std::string body, unsigned status) {
if (status == 200) {
response_body = body;
success = true;
} else {
http_error = "HTTP error: " + std::to_string(status);
}
})
.on_error([&](std::string body, std::string err, unsigned status) {
http_error = err;
if (status > 0) {
http_error += " (HTTP " + std::to_string(status) + ")";
}
})
.perform_sync();
if (!success) {
BOOST_LOG_TRIVIAL(warning) << "MoonrakerPrinterAgent::fetch_afc_filament_info: Failed to fetch lane data: " << http_error;
return false;
}
auto json = nlohmann::json::parse(response_body, nullptr, false, true);
if (json.is_discarded()) {
BOOST_LOG_TRIVIAL(warning) << "MoonrakerPrinterAgent::fetch_afc_filament_info: Invalid JSON response";
return false;
}
// Expected structure: { "result": { "namespace": "lane_data", "value": { "lane1": {...}, ... } } }
if (!json.contains("result") || !json["result"].contains("value") || !json["result"]["value"].is_object()) {
BOOST_LOG_TRIVIAL(warning) << "MoonrakerPrinterAgent::fetch_afc_filament_info: Unexpected JSON structure or no lane_data found";
return false;
}
// Parse response into AmsTrayData
const auto& value = json["result"]["value"];
trays.clear();
max_lane_index = 0;
for (const auto& [lane_key, lane_obj] : value.items()) {
if (!lane_obj.is_object()) {
continue;
}
// Extract lane index from the "lane" field (tool number, 0-based)
std::string lane_str = safe_json_string(lane_obj, "lane");
int lane_index = -1;
if (!lane_str.empty()) {
try {
lane_index = std::stoi(lane_str);
} catch (...) {
lane_index = -1;
}
}
if (lane_index < 0) {
continue;
}
AmsTrayData tray;
tray.slot_index = lane_index;
tray.tray_color = safe_json_string(lane_obj, "color");
tray.tray_type = safe_json_string(lane_obj, "material");
tray.bed_temp = safe_json_int(lane_obj, "bed_temp");
tray.nozzle_temp = safe_json_int(lane_obj, "nozzle_temp");
tray.has_filament = !tray.tray_type.empty();
auto* bundle = GUI::wxGetApp().preset_bundle;
tray.tray_info_idx = bundle
? bundle->filaments.filament_id_by_type(tray.tray_type)
: map_filament_type_to_generic_id(tray.tray_type);
max_lane_index = std::max(max_lane_index, lane_index);
trays.push_back(tray);
}
if (trays.empty()) {
BOOST_LOG_TRIVIAL(info) << "MoonrakerPrinterAgent::fetch_afc_filament_info: No AFC lanes found";
return false;
}
return true;
}
// Fetch filament info from Happy Hare MMU
bool MoonrakerPrinterAgent::fetch_hh_filament_info(std::vector<AmsTrayData>& trays, int& max_lane_index)
{
// Query Happy Hare MMU status
std::string url = join_url(device_info.base_url, "/printer/objects/query?mmu");
std::string response_body;
bool success = false;
std::string http_error;
auto http = Http::get(url);
if (!device_info.api_key.empty()) {
http.header("X-Api-Key", device_info.api_key);
}
http.timeout_connect(5)
.timeout_max(10)
.on_complete([&](std::string body, unsigned status) {
if (status == 200) {
response_body = body;
success = true;
} else {
http_error = "HTTP error: " + std::to_string(status);
}
})
.on_error([&](std::string body, std::string err, unsigned status) {
http_error = err;
if (status > 0) {
http_error += " (HTTP " + std::to_string(status) + ")";
}
})
.perform_sync();
if (!success) {
BOOST_LOG_TRIVIAL(debug) << "MoonrakerPrinterAgent::fetch_hh_filament_info: Failed to fetch HH data: " << http_error;
return false;
}
auto json = nlohmann::json::parse(response_body, nullptr, false, true);
if (json.is_discarded()) {
BOOST_LOG_TRIVIAL(debug) << "MoonrakerPrinterAgent::fetch_hh_filament_info: Invalid JSON response";
return false;
}
// Expected structure: { "result": { "status": { "mmu": { ... } } } }
if (!json.contains("result") || !json["result"].contains("status") ||
!json["result"]["status"].contains("mmu") || !json["result"]["status"]["mmu"].is_object()) {
BOOST_LOG_TRIVIAL(debug) << "MoonrakerPrinterAgent::fetch_hh_filament_info: No mmu object in response";
return false;
}
const auto& mmu = json["result"]["status"]["mmu"];
// Check if HH is installed (empty mmu object means HH not installed)
if (mmu.empty()) {
BOOST_LOG_TRIVIAL(debug) << "MoonrakerPrinterAgent::fetch_hh_filament_info: Empty mmu object (HH not installed)";
return false;
}
// Get num_gates
if (!mmu.contains("num_gates") || !mmu["num_gates"].is_number()) {
BOOST_LOG_TRIVIAL(debug) << "MoonrakerPrinterAgent::fetch_hh_filament_info: No num_gates field";
return false;
}
int num_gates = mmu["num_gates"].get<int>();
if (num_gates <= 0) {
BOOST_LOG_TRIVIAL(warning) << "MoonrakerPrinterAgent::fetch_hh_filament_info: Invalid num_gates: " << num_gates;
return false;
}
// Get arrays
const auto& gate_status = mmu.contains("gate_status") ? mmu["gate_status"] : nlohmann::json::array();
const auto& gate_material = mmu.contains("gate_material") ? mmu["gate_material"] : nlohmann::json::array();
const auto& gate_color = mmu.contains("gate_color") ? mmu["gate_color"] : nlohmann::json::array();
const auto& gate_temperature = mmu.contains("gate_temperature") ? mmu["gate_temperature"] : nlohmann::json::array();
if (!gate_status.is_array() || !gate_material.is_array() ||
!gate_color.is_array() || !gate_temperature.is_array()) {
BOOST_LOG_TRIVIAL(warning) << "MoonrakerPrinterAgent::fetch_hh_filament_info: HH arrays not found or invalid type";
return false;
}
// Parse gate data
trays.clear();
max_lane_index = 0;
for (int gate_idx = 0; gate_idx < num_gates; ++gate_idx) {
// Check gate_status: -1 = unknown, 0 = empty, 1 or 2 = available
int status = safe_array_int(gate_status, gate_idx);
if (status <= 0) {
continue; // Skip unknown or empty gates
}
// Extract gate data
std::string material = safe_array_string(gate_material, gate_idx);
std::string color = safe_array_string(gate_color, gate_idx);
int nozzle_temp = safe_array_int(gate_temperature, gate_idx);
// Skip if no material type (empty gate)
if (material.empty()) {
continue;
}
AmsTrayData tray;
tray.slot_index = gate_idx;
tray.tray_type = material;
tray.tray_color = color;
tray.nozzle_temp = nozzle_temp;
tray.bed_temp = 0; // HH doesn't provide bed temp in gate arrays
tray.has_filament = true;
auto* bundle = GUI::wxGetApp().preset_bundle;
tray.tray_info_idx = bundle
? bundle->filaments.filament_id_by_type(tray.tray_type)
: map_filament_type_to_generic_id(tray.tray_type);
max_lane_index = std::max(max_lane_index, gate_idx);
trays.push_back(tray);
}
if (trays.empty()) {
BOOST_LOG_TRIVIAL(info) << "MoonrakerPrinterAgent::fetch_hh_filament_info: No valid HH gates found";
return false;
}
return true;
}
int MoonrakerPrinterAgent::handle_request(const std::string& dev_id, const std::string& json_str) int MoonrakerPrinterAgent::handle_request(const std::string& dev_id, const std::string& json_str)
{ {
auto json = nlohmann::json::parse(json_str, nullptr, false); auto json = nlohmann::json::parse(json_str, nullptr, false);

View File

@@ -160,6 +160,17 @@ private:
const std::string& api_key, const std::string& api_key,
uint64_t generation); uint64_t generation);
// System-specific filament fetch methods
bool fetch_hh_filament_info(std::vector<AmsTrayData>& trays, int& max_lane_index);
bool fetch_afc_filament_info(std::vector<AmsTrayData>& trays, int& max_lane_index);
// JSON helper methods
static std::string safe_json_string(const nlohmann::json& obj, const char* key);
static int safe_json_int(const nlohmann::json& obj, const char* key);
static std::string safe_array_string(const nlohmann::json& arr, int idx);
static int safe_array_int(const nlohmann::json& arr, int idx);
static std::string normalize_color_value(const std::string& color);
std::string ssdp_announced_host; std::string ssdp_announced_host;
std::string ssdp_announced_id; std::string ssdp_announced_id;
std::shared_ptr<ICloudServiceAgent> m_cloud_agent; std::shared_ptr<ICloudServiceAgent> m_cloud_agent;

View File

@@ -7,7 +7,7 @@ set(SLIC3R_APP_KEY "OrcaSlicer")
if(NOT DEFINED BBL_INTERNAL_TESTING) if(NOT DEFINED BBL_INTERNAL_TESTING)
set(BBL_INTERNAL_TESTING "0") set(BBL_INTERNAL_TESTING "0")
endif() endif()
set(SoftFever_VERSION "2.3.2-beta") set(SoftFever_VERSION "2.3.2-beta2")
string(REGEX MATCH "^([0-9]+)\\.([0-9]+)\\.([0-9]+)" string(REGEX MATCH "^([0-9]+)\\.([0-9]+)\\.([0-9]+)"
SoftFever_VERSION_MATCH ${SoftFever_VERSION}) SoftFever_VERSION_MATCH ${SoftFever_VERSION})
set(ORCA_VERSION_MAJOR ${CMAKE_MATCH_1}) set(ORCA_VERSION_MAJOR ${CMAKE_MATCH_1})