Compare commits

...
Author SHA1 Message Date
Ian Chua 6ca0fc5ef1 fix: clang-tidy 2026-10-07 19:33:58 +08:00
Ian Chua bcf7d078cd Merge branch 'main' into feat/generic-n-extruders 2026-10-07 19:25:52 +08:00
Ian Chua 15fc96fca7 Merge branch 'main' into feat/generic-n-extruders 2026-10-07 19:25:35 +08:00
SoftFever 6d02a945e0 Speed up the Linux incremental rebuild (optimizes packaging step) (#16244)
## Speeds up OrcaSlicer incremental rebuild on Linux

Profiled `build_linux_image.sh`: 96 s, of which 51 s in the dependency
audit.

**`appimage_is_elf_file()`** ran `file` and `grep` per candidate. An
AppDir holds ~9.6k of them, 4.8k being the bundled Python runtime and
none of them ELF: ~19k processes, 14 s. Reads the four-byte magic
instead. Checked against the old result on 4000 files, no disagreement.

**The dependency walk** popped its queue with `"${queue[@]:1}"`, which
rebuilds the whole array each time. At ~4.8k entries that was 22 s of
copying an array around. Uses a read index.

Audit still passes. `shellcheck` v0.11.0, the version CI uses, is clean.

## Notes
The 96 s -> 12.7 s. Measured on a 32-core / 48 GB machine, but the audit
is a serial bash loop, so cores and RAM is not the bottleneck. On slower
hardware the saving should be larger

## Images
<img width="1987" height="782" alt="Screenshot_20261007_092516"
src="https://github.com/user-attachments/assets/96fcb917-38e5-49e7-8cbe-b37be1a2f23a"
/>
<img width="1807" height="742" alt="Screenshot_20261007_092621"
src="https://github.com/user-attachments/assets/4a65ffae-4d59-41f0-a1c7-ee5b49f3c4be"
/>
2026-10-07 19:07:22 +08:00
SoftFever 01afd3b5b9 Use multiswitch for extruders instead new tabs on printer settings & publish dialog (#16162)
# CHANGES / TESTS
• Uses "Extruders" as tab name if it has multiple or it uses "Extruder" for single ones
• Keeps selected extruder while switching between "Extruders" and "Motion ability" tab
• Revert functions are working
• New extruders generated with values so they will shown in "Unchanged values" dialog if you try to change preset while its edited. revert functions not works properly without this
• BBL printers visible as Left / Right while normal printers visible as T1 / T2. i think there should be a separate option for how many toolheads and how many extruders it has. we might see 4 nozzles on same toolhead if one brand is brave enough :)
• Sidebar and other sections updates itself properly


<img width="751" height="173" alt="Screenshot-20261005173918" src="https://github.com/user-attachments/assets/3be0a6bd-84bd-4d15-822c-ed34acd98a9f" />
<img width="768" height="184" alt="Screenshot-20261005173905" src="https://github.com/user-attachments/assets/46164926-e385-4482-9fd1-41325eb9f61d" />
<img width="755" height="289" alt="Screenshot-20261005175318" src="https://github.com/user-attachments/assets/4550fa51-4c71-4a0a-b15e-9ca82dd0f1ad" />

# FIXES
• Extruders count on parameters section not updated when extruder count changed on printer settings. fixed on this PR
<img width="800" height="478" alt="Screenshot-20261005174702" src="https://github.com/user-attachments/assets/734e53df-f23f-4f83-8f87-2ecfeb6c162c" />

• New extruders gets randomly modifed parameters. fixed on this PR
<img width="871" height="87" alt="Screenshot-20261005174840" src="https://github.com/user-attachments/assets/254dae2a-fcfa-44ed-b3b0-038faf019abb" />

• Changed parameters not triggers revert / modified on extruder tabs. fixed on this PR

• Multi switch on motion ability tab not updated on extruder count change. fixed on this PR
<img width="813" height="184" alt="Screenshot-20261005174929" src="https://github.com/user-attachments/assets/f4f33f8f-b1bd-4a30-b91b-6ff632d08c2a" />
2026-10-07 19:05:37 +08:00
Ian Chua 1fffda0533 chore: add tests and HLSD 2026-10-07 19:05:06 +08:00
SoftFever b805f2ffd6 Fill a new extruder's machine limits the same way as its other settings 2026-10-07 18:42:09 +08:00
Ian Chua 33e26a127e fix: warn when toolhead count differs from the selected printer preset's nozzle_diameter count 2026-10-07 18:40:41 +08:00
SoftFever 6b2b200f50 Merge branch 'main' into pr/yw4z/16162 2026-10-07 18:40:37 +08:00
Ian Chua 6dd0cb9eba fix: harden generic N-extruder device paths 2026-10-07 18:30:49 +08:00
yw4z a38e6c61f2 Show dummy image for printers without cover on printer selectiondialog (#16235)
init
2026-10-07 12:57:28 +03:00
SoftFever cc3769453f Merge branch 'main' into pr/yw4z/16162 2026-10-07 17:25:29 +08:00
HanifKoh f9ddb23804 Keep Each Filament's Device Drying Values When the CLI Merges Filaments (#16157)
The device drying options hold several values per filament, as many as
the filament preset gives, and a project stores them as the filaments'
values one after another. The CLI filament merge wrote them like an
option with one value per filament, putting each preset's first value at
the filament's own index, so a project with three filaments whose preset
gives "1", "0" was exported with 1;1;1;0;1;0 where the GUI writes
1;0;1;0;1;0.

The merge now leaves these options out of the per-filament pass and
rebuilds them afterwards from every filament's values in slot order.
Without a fixed number of values per filament one slot cannot be
replaced in place, so the stored values are kept when any slot has no
config to rebuild from.
2026-10-07 15:54:53 +08:00
HanifKoh ff8aebe76f Allow Unsigned Executable Memory in the macOS Entitlements (#16203)
The Bambu network plug-in's code protector rewrites one page of its own signed __TEXT after loading. The hardened runtime tolerates that until the page is evicted; the next read of it then kills OrcaSlicer with CODESIGNING Invalid Page. Bambu Studio signs with allow-unsigned-executable-memory for this reason; with it added, the same build survives critical memory pressure that killed it in 30 s without.
2026-10-07 15:52:18 +08:00
HanifKoh a56d1bf33e Keep a Project's Changed Values on Extruder Variants It Doesn't List (#16107)
A project's listed settings are carried onto its base preset by update_non_diff_values_to_base_config, which matched variants by exact name and id. A variant the base gained after the project was saved got the base's value, while the same value in a user preset now falls back to the preset's first variant of that extruder. So an old project opened with its printer preset already modified, and saving it wrote the base's values into the 3MF.

The function now maps variants with map_variant_indices, as update_diff_values_to_child_config does: a base variant the project does not list takes the project's first variant of the same extruder. The variant lists themselves stay the base's, so a fallback never writes one variant's name over another's.
2026-10-07 15:51:49 +08:00
HanifKoh 662a8e340d Support the 02.08.04 Bambu Network Plug-in Series (#16202)
The 02.08.02 series appended queue_plate_id to PrintParams and nothing
after it changed the ABI OrcaSlicer calls, so adding the field brings
the current layout up to 02.08.04. Make 02.08.04 the latest series and
drop 02.08.01 from the whitelist: its PrintParams no longer matches, and
its malformed bind table is refused by dyld on macOS 27, so it cannot
load there. A stored 02.08.01 falls back to the latest series through
the existing unsupported-version path.
2026-10-07 15:51:21 +08:00
ExPikaPaka bd0ef35b90 Speed up the Linux image build
Profiling build_linux_image.sh: 96 s, of which 51 s in the dependency audit.

appimage_is_elf_file() ran file(1) and grep per candidate. An AppDir holds ~9.6k
of them, 4.8k being the bundled Python runtime, none of them ELF: ~19k processes
for 14 s. Read the four-byte magic instead; checked against the old result on
4000 files, no disagreement.

The dependency walk popped its queue with "${queue[@]:1}", which rebuilds the
whole array each time. At ~4.8k entries that was 22 s of copying. Use a read
index.

96 s -> 12.7 s. The audit still passes.
2026-10-07 09:25:19 +02:00
HanifKoh 6639a32b0c Remove using namespace std from json_diff.hpp (#16222)
The directive sat at global scope in a header that DeviceManager.hpp
includes, so most of the GUI compiled with all of std in the global
namespace. 42 files had come to rely on it, mostly for string, vector
and unordered_map, four of them for the ""sv and ""ms literals.

Those sites are qualified. GCodeViewer.cpp spelled the type as
std::vector<::string>, which only resolved through the directive. The
files that use the ""sv and ""ms literals get a file-scope
"using namespace std::string_view_literals;" or
"using namespace std::chrono_literals;", as other sources already do.
2026-10-07 14:58:49 +08:00
SoftFever b0da039eae Merge branch 'main' into pr/yw4z/16162 2026-10-07 14:44:59 +08:00
SoftFever 1ec195a221 Update build commands in AGENTS.md to use Release configuration 2026-10-07 14:44:04 +08:00
SoftFever ba5ecc5470 Edit the selected extruder's machine limits on the Motion ability page 2026-10-07 13:41:42 +08:00
SoftFever 82349e1ba4 Give each extruder its own machine limits 2026-10-07 13:41:42 +08:00
SoftFever c740ddf0a6 Publish each extruder's own retraction and Z-hop values 2026-10-07 01:45:24 +08:00
SoftFever d068468bbb Simplify the unified Extruder page and its Publish dialog switch 2026-10-07 01:40:10 +08:00
SoftFever 67e29f69e7 Merge branch 'main' into exturuder-tab-multi-switch 2026-10-07 01:38:10 +08:00
Ian Chua 9bf0fc2a8e test: reset virtual trays in the generic AMS tray index map case 2026-10-06 20:53:46 +08:00
SoftFever 919f507cca Merge branch 'main' into exturuder-tab-multi-switch 2026-10-06 20:04:09 +08:00
SoftFever 26329e7147 Show Left/Right Extruder or Extruder N on the extruder switch 2026-10-06 20:03:07 +08:00
Ian Chua 5bf6db95b3 fix: clang-tidy 2026-10-06 19:56:53 +08:00
Ian Chua 0feb87b417 feat: generic N extruders, multiple AMS and AMS slots 2026-10-06 19:22:10 +08:00
SoftFever e47fdcaef7 Merge branch 'main' into exturuder-tab-multi-switch 2026-10-06 17:56:06 +08:00
yw4z 26be825234 init 2026-10-05 17:37:52 +03:00
66 changed files with 3124 additions and 687 deletions
@@ -105,11 +105,12 @@ Rules:
`single_extruder_multi_material` off, and in the app when the printer tab loads a printer with a
different number of extruders, the pair is rebuilt from `extruder_variant_list` (one
`Direct Drive Standard` per extruder when the list is absent) and the variant arrays are resized to
the rebuilt pair, padded with their first value or cut. The resize skips the `machine_max_*` limits
and `hotend_heating_rate` / `hotend_cooling_rate`: they keep their width, and an extruder beyond it
reads their first value, so extruder 2 and up of a list-less printer take extruder 1's normal limit
as their silent one too. With the three in agreement that changes nothing; a pair written without
the list is replaced. A listed variant the pair lacks is a menu choice that reads variant index 0.
the rebuilt pair, padded with their first value or cut. The `machine_max_*` limits are padded the
same way, so extruder 2 and up of a list-less printer take extruder 1's normal limit as their
silent one too. The resize skips `hotend_heating_rate` / `hotend_cooling_rate`: they keep their
width, and an extruder beyond it reads their first value. With the three in agreement that changes
nothing; a pair written without the list is replaced. A listed variant the pair lacks is a menu
choice that reads variant index 0.
- The pair without `extruder_variant_list` slices, but the sidebar offers no variant switch and
the app cannot add variants to a list-less process: nothing is lost while every extruder
has exactly one variant, and every further variant is unreachable.
+6 -4
View File
@@ -4,15 +4,17 @@ OrcaSlicer — open-source C++17 3D slicer. wxWidgets GUI, CMake build system.
## Build Commands
Build the Release configuration unless asked otherwise.
```bash
# macOS
cmake --build build/arm64 --config RelWithDebInfo --target all --
cmake --build build/arm64 --config Release --target all --
# Linux
cmake --build build --config RelWithDebInfo --target all --
cmake --build build --config Release --target all --
# Windows (replace %build_type% with Debug/Release/RelWithDebInfo)
cmake --build . --config %build_type% --target ALL_BUILD -- -m
# Windows
cmake --build . --config Release --target ALL_BUILD -- -m
```
## Testing
+195
View File
@@ -0,0 +1,195 @@
# Multi-extruder and multi-AMS device control
`DeviceManager`/`MachineObject` model the toolheads, AMS units, and filament slots of a connected
printer and drive the device-control UI from that model. The model represents the counts as
device-reported runtime values rather than compile-time caps, so a printer with an arbitrary number
of toolheads, several AMS units, and a non-uniform number of slots per unit is handled by the same
code paths. The printer agent is responsible for translating its printer's state into the shared
JSON dialect this model consumes; the agent boundary is described in
[printer-agent.md](printer-agent.md), and this document describes the dialect and the model.
## Model layout
`MachineObject` owns the device subsystems that this document covers:
- `DevExtderSystem` — the toolheads (extruders) and their per-toolhead filament/temperature state.
- `DevFilaSystem` — the AMS units (`DevAms`) and their trays (`DevAmsTray`).
- `DevNozzleSystem` — the nozzles and, on rack printers, `DevNozzleRack`.
- `DevFilaSwitch` — a filament switch that can feed several AMS units to more than one extruder.
Each subsystem exposes its own count — `DevExtderSystem::GetTotalExtderCount()`,
`DevFilaSystem::GetAmsCount()`, `DevAms::GetSlotCount()` — and the rest of the application derives
its layout from those values rather than from fixed assumptions.
## Wire dialect
The dialect is the Bambu-shaped JSON that `DeviceManager` and `MachineObject` already consume. An
agent whose printer speaks something else is expected to translate into it; the agent is not free
to invent a new shape. Two representations coexist for extruders, and one for AMS/slots.
### Extruders
The generic representation is `print.device.extruder`, parsed by
`ExtderSystemParser::ParseV2_0` (`DevExtruderSystem.cpp`). It has a packed `state` integer and an
`info` array with one object per toolhead:
```text
device.extruder.state packed integer
bits 0..3 total extruder count
bits 4..7 current extruder id
bits 8..11 target extruder id
bits 12..14 switch state
bits 15..18 currently loading extruder id
bit 19 busy for loading
device.extruder.info[] one entry per toolhead
id extruder id
filam_bak array of backup-group bits
info bit 1 has filament, bit 2 buffer has filament, bit 3 nozzle present
temp bits 0..15 current temperature, bits 16..31 target temperature
spre slot_id bits 0..7, ams_id bits 8..15 (previous)
snow same packing (current)
star same packing (target)
stat bit 0..15 AMS status, bits 16..31 RFID status
hnow current nozzle id
```
The legacy single/dual representation is the pair of scalar fields `nozzle_temper` /
`nozzle_target_temper`, plus the `print.ams.tray_tar` / `tray_now` tray pointer, parsed by
`ExtderSystemParser::ParseV1_0`. That parser is a fallback and only fills the main extruder when the
reported toolhead count is one.
Physical extruder ids are fixed (`MAIN_EXTRUDER_ID = 0`, `DEPUTY_EXTRUDER_ID = 1`). Bambu printers
present the two toolheads as a left/right pair and invert the physical-to-logical mapping; generic
printers use the toolhead id itself as the logical id. `DevNozzle::GetLogicExtruderId()` /
`GetExtruderId()` centralise that distinction.
### AMS units and slots
AMS units arrive as `print.ams.ams[]` and are parsed by `DevFilaSystemParser::ParseV1_0`. Each unit
carries an `id` and an `info` bitfield:
```text
info bits 0..3 type: 0 dummy, 1 AMS, 2 AMS-Lite, 3 N3F, 4 N3S, 5 AMS-Lite-mixed (N9)
info bits 8..11 bound extruder id (0xE marks a switch-bound unit)
info bits 24..27 switch inlet when the unit is switch-bound
```
`DevAmsType` (`DevDefs.h`) enumerates the resulting unit kinds: `EXT_SPOOL`, `AMS`, `AMS_LITE`,
`N3F`, `N3S`, and the `AMS_LITE_MIXED` N9 variant. `AMS_LITE_MIXED` reports itself as `AMS_LITE` to
generic callers; code that needs the distinction uses `IsAmsLiteMixed()`.
A unit whose bound-extruder nibble is `0xE` is switch-bound: it is kept only when a `DevFilaSwitch`
is installed, in which case its extruder id is pinned to the main extruder and its binding set
becomes `{0, 1}`; otherwise the unit is dropped from the AMS list.
Each unit owns a map of `DevAmsTray` keyed by the tray id from the payload. The slot count is a
property of the unit kind and the reported trays:
- `AMS`, `AMS_LITE`, and `N3F` report four slots on Bambu printers, matching the firmware contract;
- `N3S` reports one slot;
- `EXT_SPOOL` and any non-Bambu unit report the number of trays actually present.
The tray ids are the payload's slot indices; they are expected to be unique, 0-based, and contiguous
within a unit, because the generic tray-index mapping below is cumulative.
Bulk/extended spools are carried by `print.vir_slot`, which `DeviceManager` parses into
`MachineObject::vt_slot`. Two ids are fixed: `VIRTUAL_TRAY_MAIN_ID = 255` (extruder 0) and
`VIRTUAL_TRAY_DEPUTY_ID = 254` (extruder 1); the id convention is `vt_id = 255 - extruder_id`, so
further extruders use 253, 252, … . The `vir_slot` parser recognises the 255/254 pair; the agent
filament descriptor path `DevFilaSystemParser::ParseAgentFilament` is the entry that stores a fuller
descending set in `vt_slot`.
## Mapping
Several subsystems relate AMS units, trays, and extruders, and more than one place derives a tray
index. The Bambu mapping path (`DevMappingUtil`) computes `ams_id * 4 + slot_id` and knows only the
Bambu unit kinds; `DevFilaSystem` provides the device model's own mapping below. The two agree for
Bambu units and diverge for a non-Bambu unit whose slot count is not four.
### AMS id to extruder id
`DevFilaSystem::GetExtruderIdByAmsId()` resolves an AMS id to the extruder it feeds:
- an AMS unit returns its bound extruder (`DevAms::GetExtruderId()`);
- a virtual tray returns `VIRTUAL_TRAY_MAIN_ID - vt_id`;
- Bambu's virtual AMS ids map to the main/deputy extruder.
A unit may be bound to more than one extruder when a `DevFilaSwitch` feeds it; the binding set
(`DevAms::m_binded_extruder_set`) and switch inlet are read by switch-aware code while the
single-extruder id stays pinned for legacy callers.
### Tray index
`DevFilaSystem::GetTrayIndexMap()` maps a global tray index to an `(ams_id, slot_id)` pair and is
used for backup-slot translation, calibration addressing, and the mapping popups.
- Bambu AMS: `ams_id * 4 + slot_id`; N3S: the AMS id; AMS-Lite-mixed (N9): `24 + slot_id`.
- Generic (non-Bambu): a running lane index — the count of trays in preceding units plus the slot
id. This depends on each unit's tray ids being 0-based and contiguous.
### Virtual-slot recognition
`devPrinterUtil::IsVirtualSlot()` decides whether an id denotes a virtual tray. On Bambu printers it
is the fixed pair `{255, 254}`. On generic printers it is the descending range
`[255 - count + 1, 255]`, where `count` is the selected printer preset's extruder count. The preset
is expected to match the connected device; when the two disagree the virtual range is wrong, which
is why a mismatch is reported rather than silently accepted.
## Device-control UI
The device-control widgets follow the same device-reported counts.
- `AMSModel` (`AMSItem.hpp`) mirrors `DevAmsType` numerically: `EXT_AMS`, `GENERIC_AMS`, `AMS_LITE`,
`N3F_AMS`, `N3S_AMS`. A plain `AMS` maps to `GENERIC_AMS`.
- On non-Bambu printers `use_generic_ams_layout()` routes `GENERIC_AMS` units through a variable-lane
rendering that draws one lane per reported tray; Bambu keeps its fixed four-slot rendering.
- `AMSControl` picks its layout by vendor: Bambu keeps the left/right presentation
(`CreateAmsSingleNozzle` for one toolhead, `CreateAmsDoubleNozzle` for two), while non-Bambu
printers use `CreateAmsMultiNozzle`, which builds one preview and one AMS simplebook per toolhead
for any count.
- The status panel's `ExtruderImage` renders one per-toolhead state per nozzle and is resized to
`GetTotalExtderCount()`. Non-Bambu printers choose the active toolhead through
`m_generic_nozzle_selector` (shown when more than one toolhead exists); Bambu keeps its left/right
`m_nozzle_btn_panel`. Nozzle temperature controls are created on demand per toolhead.
## Constraints
- **Single dialect.** The agent translates its protocol into this JSON; the model does not learn
vendor protocols. Extending the model with a new unit kind or field means extending the dialect.
- **Agent-produced counts.** Toolhead, AMS, and slot counts come from the device payload. Nothing
caps them at two toolheads or four slots except the Bambu contract described above.
- **Extruder dialect invariants.** `device.extruder.state`'s count field must equal the length of
`info[]`, and `info[]` must be ordered by extruder id with `id` equal to the array index: the
parser places toolheads by array position while `GetExtderById()` indexes by id.
- **Preset/device agreement.** The generic layout and virtual-slot range derive their count from the
selected printer preset. A printer preset must therefore declare the same number of
`nozzle_diameter` entries as the device has toolheads; a mismatch is surfaced because the sidebar
and extruder logic would otherwise disagree.
- **Bambu compatibility.** Bambu-specific presentation (left/right pair, fixed 4-slot units, fixed
virtual ids, physical/logical inversion) is preserved behind the vendor check. Generic paths do
not alter it.
- **Tray-index invariant.** The generic tray-index map is cumulative, so payload tray ids must be
unique, 0-based, and contiguous per unit. A malformed id is a producer error.
- **Protocol markers.** The dialect has no neutral equivalent for some Bambu state, so agents that
lack it send empty `print.cfg`, `print.fun`, `print.aux`, and `print.stat`. Their presence selects
the new-protocol parse path; they are compatibility markers, not feature switches.
## Main implementation locations
- [`DevExtruderSystem`](../../src/slic3r/GUI/DeviceCore/DevExtruderSystem.h) — toolhead model and the
`device.extruder` parser
- [`DevFilaSystem`](../../src/slic3r/GUI/DeviceCore/DevFilaSystem.h) — AMS/tray model, the AMS parser,
and the mapping helpers
- [`DevDefs.h`](../../src/slic3r/GUI/DeviceCore/DevDefs.h) — `DevAmsType`, extruder ids, and virtual
tray ids
- [`DeviceManager.cpp`](../../src/slic3r/GUI/DeviceManager.cpp) — message dispatch, `vir_slot`
parsing, and device state assembly
- [`AMSControl.cpp`](../../src/slic3r/GUI/Widgets/AMSControl.cpp) — per-toolhead AMS panes
(`CreateAmsMultiNozzle`) and the generic layout
- [`AMSItem.hpp`](../../src/slic3r/GUI/Widgets/AMSItem.hpp) — `AMSModel` and the variable-lane AMS
rendering
- [`StatusPanel.cpp`](../../src/slic3r/GUI/StatusPanel.cpp) — `ExtruderImage`, the toolhead selector,
and per-toolhead temperature controls
- [`MoonrakerPrinterAgent.cpp`](../../src/slic3r/Utils/MoonrakerPrinterAgent.cpp) — producer example
(AMS payload and the `device.extruder` encoding)
+1 -1
View File
@@ -311,7 +311,7 @@ function CreatePrinterBlock(OneModel)
return '<div class="PrinterBlock" onClick="ChooseModel(\''+vendor+'\',\''+OneModel['model']+'\')">'+
' <div class="PImg">'+
' <img class="ModelThumbnail" src="' + OneModel['cover'] + '" />'+
' <img class="ModelThumbnail" src="' + OneModel['cover'] + '" onerror="this.onerror=null;this.src=\'../img/printer-dummy.png\';"/>'+
' </div>'+
' <div class="PrinterInfoMark">?</div>'+
' <div class="PrinterInfo">'+
Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

+7 -1
View File
@@ -23,7 +23,13 @@ appimage_is_host_library() {
}
appimage_is_elf_file() {
file -b "$1" 2>/dev/null | grep -q '^ELF '
# Read the four-byte ELF magic rather than asking file(1): this runs once per candidate, and an
# AppDir holds thousands of them (the bundled Python runtime alone is ~5k files, none of them
# ELF). Two processes per call made this ~14 s of a ~95 s image build on its own.
local magic
[[ -f "$1" && -r "$1" ]] || return 1
IFS= read -r -n 4 -d '' magic < "$1" 2>/dev/null
[[ $magic == $'\177ELF' ]]
}
appimage_list_direct_dependencies() {
+5
View File
@@ -4,5 +4,10 @@
<dict>
<key>com.apple.security.cs.disable-library-validation</key>
<true/>
<!-- The Bambu network plug-in's code protector rewrites one of its own signed code pages
after loading. Under the hardened runtime macOS kills the process when that page is
paged back in; this lets it run, as Bambu Studio's signature does. -->
<key>com.apple.security.cs.allow-unsigned-executable-memory</key>
<true/>
</dict>
</plist>
+11
View File
@@ -3618,6 +3618,9 @@ int CLI::run(int argc, char **argv)
{
if (opt_key == "compatible_prints" || opt_key == "compatible_printers" || opt_key == "model_id" || opt_key == "dev_model_name" || opt_key == "filament_settings_id")
continue;
// rebuilt from every filament after this loop
if (filament_dev_options.find(opt_key) != filament_dev_options.end())
continue;
ConfigOption *opt = m_print_config.option(opt_key, true);
if (opt == nullptr) {
// opt_key does not exist in this ConfigBase and it cannot be created, because it is not defined by this->def().
@@ -3681,6 +3684,14 @@ int CLI::run(int argc, char **argv)
}
}
// The stored values cannot be told apart per filament, so they are kept as they are unless every slot has a config.
std::vector<const DynamicPrintConfig *> filament_configs(filament_count, nullptr);
for (size_t index = 0; index < load_filaments_config.size(); index++)
if (load_filaments_index[index] >= 1 && load_filaments_index[index] <= filament_count)
filament_configs[load_filaments_index[index] - 1] = &load_filaments_config[index];
if (std::find(filament_configs.begin(), filament_configs.end(), nullptr) == filament_configs.end())
set_filament_dev_options(m_print_config, filament_configs);
if (m_print_config.option<ConfigOptionStrings>("filament_extruder_variant")) {
std::vector<int>& filament_self_indice = m_print_config.option<ConfigOptionInts>("filament_self_index", true)->values;
int index_size = m_print_config.option<ConfigOptionStrings>("filament_extruder_variant")->size();
@@ -5,5 +5,10 @@
<!-- for dynamic loading of libraries without signature validation. Used for 3dconnection drivers.-->
<key>com.apple.security.cs.disable-library-validation</key>
<true/>
<!-- The Bambu network plug-in's code protector rewrites one of its own signed code pages
after loading. Under the hardened runtime macOS kills the process when that page is
paged back in; this lets it run, as Bambu Studio's signature does. -->
<key>com.apple.security.cs.allow-unsigned-executable-memory</key>
<true/>
</dict>
</plist>
@@ -118,9 +118,13 @@ bundle_dependency_closure() {
# (scripts/check_appimage_libs.sh).
local -a search_dirs=("$dst_dir")
while [ ${#queue[@]} -gt 0 ]; do
target="${queue[0]}"
queue=("${queue[@]:1}")
# Walked with a read index rather than by reslicing the array: "${queue[@]:1}" rebuilds the whole
# queue on every pop, so draining it costs O(n^2). At the ~5k entries this bundle reaches that was
# ~22 s of the image build spent copying an array around.
local head=0
while [ "$head" -lt ${#queue[@]} ]; do
target="${queue[$head]}"
head=$((head + 1))
if [ ! -e "$target" ] || ! appimage_is_elf_file "$target"; then
continue
+38 -13
View File
@@ -10030,6 +10030,13 @@ static void extend_extruder_variant(DynamicPrintConfig& config, const unsigned i
printer_extruder_variant_opt->values.insert(printer_extruder_variant_opt->values.end(), variants_list.begin(), variants_list.end());
}
}
// 3. Size the machine limits to the rebuilt variants, padded with their first value like the other variant keys.
// They are not extruder option keys, so the resize loop in set_num_extruders skips them.
const auto &defaults = FullPrintConfig::defaults();
for (const std::string &key : printer_options_with_variant_2)
if (auto *opt = config.option<ConfigOptionFloats>(key))
opt->resize(config.get_parameter_size(key, num_extruders), defaults.option(key));
}
void DynamicPrintConfig::set_num_extruders(unsigned int num_extruders)
@@ -10822,6 +10829,22 @@ void normalize_filament_values_to_variants(DynamicPrintConfig &config)
}
}
void set_filament_dev_options(DynamicPrintConfig &config, const std::vector<const DynamicPrintConfig *> &filament_configs)
{
for (const std::string &key : filament_dev_options) {
if (std::none_of(filament_configs.begin(), filament_configs.end(), [&key](const DynamicPrintConfig *filament) { return filament->has(key); }))
continue;
const ConfigOption *default_value = print_config_def.get(key)->default_value.get();
auto *dst = static_cast<ConfigOptionVectorBase *>(config.option(key, true));
dst->clear();
for (const DynamicPrintConfig *filament : filament_configs) {
const auto *src = static_cast<const ConfigOptionVectorBase *>(filament->has(key) ? filament->option(key) : default_value);
if (!src->empty())
dst->append(src);
}
}
}
//used for object/region config
//use the smallest of multiple to single
@@ -11520,12 +11543,18 @@ void DynamicPrintConfig::update_non_diff_values_to_base_config(DynamicPrintConfi
int cur_variant_count = cur_extruder_variants.size();
int target_variant_count = target_extruder_variants.size();
// A base variant this config does not list (the base gained it after the config was saved, or the
// config lists none) takes this config's first variant of the same extruder, as a user preset's
// values do in update_diff_values_to_child_config. Left unmatched, the base's value would silently
// replace the user's.
variant_index.resize(target_variant_count, -1);
if (cur_variant_count == 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;
// This config's one value belongs to the extruder of the base's first variant.
variant_index = map_variant_indices(target_extruder_variants, target_extruder_ids, {},
target_extruder_ids.empty() ? std::vector<int>() : std::vector<int>{target_extruder_ids[0]});
}
else if ((cur_extruder_ids.size() > 0) && cur_variant_count != cur_extruder_ids.size()){
//should not happen
@@ -11538,18 +11567,7 @@ void DynamicPrintConfig::update_non_diff_values_to_base_config(DynamicPrintConfi
%extruder_variant_name %target_variant_count %extruder_id_name %target_extruder_ids.size();
}
else {
for (int i = 0; i < target_variant_count; i++)
{
for (int j = 0; j < cur_variant_count; j++)
{
if ((target_extruder_variants[i] == cur_extruder_variants[j])
&&(target_extruder_ids.empty() || (target_extruder_ids[i] == cur_extruder_ids[j])))
{
variant_index[i] = j;
break;
}
}
}
variant_index = map_variant_indices(target_extruder_variants, target_extruder_ids, cur_extruder_variants, cur_extruder_ids);
}
for (auto& opt : keys) {
@@ -11574,6 +11592,13 @@ void DynamicPrintConfig::update_non_diff_values_to_base_config(DynamicPrintConfi
if (cur_variant_count > target_variant_count)
continue;
// The variant lists are the base's layout itself, which every other value is
// carried onto: a variant this config lacks keeps its own name and id.
if (opt == extruder_id_name || opt == extruder_variant_name) {
opt_src->set(opt_target);
continue;
}
int stride = 1;
if (key_set2.find(opt) != key_set2.end())
stride = 2;
+5
View File
@@ -928,6 +928,11 @@ void normalize_filament_values_to_variants(DynamicPrintConfig &config);
extern std::set<std::string> filament_dev_options;
// Orca: a filament_dev_options option holds several values per filament, and how many is up to the
// filament preset, so one filament's values cannot be replaced in place. This rebuilds each option from
// filament_configs, one config per filament in slot order, as the filaments' values one after another.
void set_filament_dev_options(DynamicPrintConfig &config, const std::vector<const DynamicPrintConfig *> &filament_configs);
extern void update_static_print_config_from_dynamic(ConfigBase& config, const DynamicPrintConfig& dest_config, std::vector<int> variant_index, std::set<std::string>& key_set1, int stride = 1);
extern void compute_filament_override_value(const std::string& opt_key, const ConfigOption *opt_old_machine, const ConfigOption *opt_new_machine, const ConfigOption *opt_new_filament, const DynamicPrintConfig& new_full_config,
t_config_option_keys& diff_keys, DynamicPrintConfig& filament_overrides, std::vector<int>& f_map_indices);
+2 -2
View File
@@ -974,8 +974,8 @@ bool AMSMaterialsSetting::Show(bool show)
static void _collect_filament_info(const wxString& shown_name,
const Preset& filament,
unordered_map<wxString, wxString>& query_filament_vendors,
unordered_map<wxString, wxString>& query_filament_types)
std::unordered_map<wxString, wxString>& query_filament_vendors,
std::unordered_map<wxString, wxString>& query_filament_types)
{
query_filament_vendors[shown_name] = filament.config.get_filament_vendor();
query_filament_types[shown_name] = filament.config.get_filament_type();
+3 -3
View File
@@ -856,14 +856,14 @@ void AuxiliaryPanel::Split(const std::string &src, const std::string &separator,
dest.clear();
index = str.find_first_of(separator, start);
do {
if (index != string::npos) {
if (index != std::string::npos) {
substring = str.substr(start, index - start);
dest.push_back(substring);
start = index + separator.size();
index = str.find(separator, start);
if (start == string::npos) break;
if (start == std::string::npos) break;
}
} while (index != string::npos);
} while (index != std::string::npos);
// the last part
substring = str.substr(start);
@@ -1307,7 +1307,7 @@ void CalibrationPresetPage::stripWhiteSpace(std::string& str)
{
if (str == "") { return; }
string::iterator cur_it;
std::string::iterator cur_it;
cur_it = str.begin();
while (cur_it != str.end()) {
@@ -2597,7 +2597,7 @@ void CalibrationPresetPage::update_multi_extruder_filament_combobox(const std::s
int ams_id_int = 0;
try {
if (!ams_id.empty())
ams_id_int = stoi(ams_id.c_str());
ams_id_int = std::stoi(ams_id.c_str());
} catch (...) {}
@@ -2685,7 +2685,7 @@ void CalibrationPresetPage::update_filament_combobox(std::string ams_id)
int ams_id_int = 0;
try {
if (!ams_id.empty())
ams_id_int = stoi(ams_id.c_str());
ams_id_int = std::stoi(ams_id.c_str());
} catch (...) {}
+4 -4
View File
@@ -347,7 +347,7 @@ static std::string get_curr_timestmp()
// return timestampString;
}
static void get_filament_compatible_printer(Preset* preset, vector<std::string>& printers)
static void get_filament_compatible_printer(Preset* preset, std::vector<std::string>& printers)
{
auto compatible_printers = dynamic_cast<ConfigOptionStrings *>(preset->config.option("compatible_printers"));
if (compatible_printers == nullptr) return;
@@ -1581,7 +1581,7 @@ void CreateFilamentPresetDialog::sort_printer_by_nozzle(std::vector<std::pair<st
{
std::unordered_map<std::string, float> nozzle_diameter = nozzle_diameter_map;
std::sort(printer_name_to_filament_preset.begin(), printer_name_to_filament_preset.end(),
[&nozzle_diameter](const std::pair<string, T> &a, const std::pair<string, T> &b) {
[&nozzle_diameter](const std::pair<std::string, T> &a, const std::pair<std::string, T> &b) {
size_t nozzle_index_a = a.first.find(" nozzle");
size_t nozzle_index_b = b.first.find(" nozzle");
if (nozzle_index_a == std::string::npos || nozzle_index_b == std::string::npos) return a.first < b.first;
@@ -3143,7 +3143,7 @@ void CreatePrinterPresetDialog::set_current_visible_printer()
wxArrayString CreatePrinterPresetDialog::printer_preset_sort_with_nozzle_diameter(const VendorProfile &vendor_profile, float nozzle_diameter)
{
std::vector<pair<float, std::string>> preset_sort;
std::vector<std::pair<float, std::string>> preset_sort;
auto get_nozzle_size_for_printer_model = [this](const std::string & model_name) -> size_t {
auto iter = m_printer_name_to_preset.find(model_name);
@@ -4964,7 +4964,7 @@ wxBoxSizer *CreatePresetForPrinterDialog::create_selected_filament_preset_sizer(
m_selected_printer->Bind(wxEVT_COMBOBOX, [this](wxCommandEvent &e) {
wxString printer_name = m_selected_printer->GetStringSelection();
std::unordered_map<string, std::vector<std::shared_ptr<Preset>>>::iterator filament_iter = m_printer_compatible_filament_presets.find(into_u8(printer_name));
std::unordered_map<std::string, std::vector<std::shared_ptr<Preset>>>::iterator filament_iter = m_printer_compatible_filament_presets.find(into_u8(printer_name));
if (m_printer_compatible_filament_presets.end() != filament_iter) {
filament_choice_to_filament_preset.clear();
wxArrayString filament_choices;
+3 -3
View File
@@ -163,8 +163,8 @@ public:
~devPrinterUtil() = delete;
public:
static bool IsVirtualSlot(int ams_id) { return (ams_id == VIRTUAL_TRAY_MAIN_ID || ams_id == VIRTUAL_TRAY_DEPUTY_ID);}
static bool IsVirtualSlot(const std::string& ams_id) { return (ams_id == VIRTUAL_AMS_MAIN_ID_STR || ams_id == VIRTUAL_AMS_DEPUTY_ID_STR); }
static bool IsVirtualSlot(int ams_id);
static bool IsVirtualSlot(const std::string& ams_id);
};
namespace GUI
@@ -200,4 +200,4 @@ template<> struct std::hash<NozzleDef>
};
// key(extruder_id) -> { key1(nozzle type info), val1( number of the nozzle type)}
using ExtruderNozzleInfos = std::unordered_map<int, std::unordered_map<NozzleDef, int>>;
using ExtruderNozzleInfos = std::unordered_map<int, std::unordered_map<NozzleDef, int>>;
+82 -23
View File
@@ -8,6 +8,7 @@
#include <cstdlib>
#include <chrono>
#include <nlohmann/json.hpp>
#include <wx/app.h>
#include <wx/colour.h>
#include <string>
#include <wx/string.h>
@@ -33,6 +34,25 @@
using namespace nlohmann;
namespace Slic3r {
bool devPrinterUtil::IsVirtualSlot(int ams_id)
{
if (wxTheApp == nullptr || GUI::wxGetApp().preset_bundle == nullptr || GUI::wxGetApp().preset_bundle->is_bbl_vendor())
return ams_id == VIRTUAL_TRAY_MAIN_ID || ams_id == VIRTUAL_TRAY_DEPUTY_ID;
const int extruder_count = GUI::wxGetApp().preset_bundle->get_printer_extruder_count();
return ams_id >= VIRTUAL_TRAY_MAIN_ID - extruder_count + 1 && ams_id <= VIRTUAL_TRAY_MAIN_ID;
}
bool devPrinterUtil::IsVirtualSlot(const std::string& ams_id)
{
try {
return IsVirtualSlot(std::stoi(ams_id));
} catch (...) {
return false;
}
}
static int _hex_digit_to_int(const char c) { return (c >= '0' && c <= '9') ? c - '0' : (c >= 'A' && c <= 'F') ? c - 'A' + 10 : (c >= 'a' && c <= 'f') ? c - 'a' + 10 : -1; }
wxColour DevAmsTray::decode_color(const std::string &color)
@@ -160,7 +180,7 @@ DevAms::~DevAms()
m_trays.clear();
}
static unordered_map<int, wxString> s_ams_display_formats = {
static std::unordered_map<int, wxString> s_ams_display_formats = {
{DevAms::AMS, "AMS-%d"},
{DevAms::AMS_LITE, "AMS Lite-%d"},
{DevAms::N3F, "AMS 2 PRO-%d"},
@@ -200,18 +220,21 @@ wxString DevAms::GetDisplayName() const
int DevAms::GetSlotCount() const
{
// GetAmsType() maps AMS_LITE_MIXED -> AMS_LITE, so N9 reports 4 slots like AMS-Lite.
auto ams_type = GetAmsType();
if (ams_type == AMS || ams_type == AMS_LITE || ams_type == N3F)
{
return 4;
}
else if (ams_type == N3S)
{
return 1;
if (wxTheApp != nullptr && GUI::wxGetApp().preset_bundle != nullptr &&
GUI::wxGetApp().preset_bundle->is_bbl_vendor()) {
// GetAmsType() maps AMS_LITE_MIXED -> AMS_LITE, so N9 reports 4 slots like AMS-Lite.
auto ams_type = GetAmsType();
if (ams_type == AMS || ams_type == AMS_LITE || ams_type == N3F)
{
return 4;
}
else if (ams_type == N3S)
{
return 1;
}
}
return 1;
return static_cast<int>(m_trays.size());
}
DevAmsTray* DevAms::GetTray(const std::string& tray_id) const
@@ -302,13 +325,21 @@ int DevFilaSystem::GetExtruderIdByAmsId(const std::string& ams_id) const
{
return it->second->GetExtruderId();
}
else if (stoi(ams_id) == VIRTUAL_TRAY_MAIN_ID)
{
return MAIN_EXTRUDER_ID;
const bool is_bbl_vendor = wxTheApp != nullptr && GUI::wxGetApp().preset_bundle != nullptr && GUI::wxGetApp().preset_bundle->is_bbl_vendor();
if (!is_bbl_vendor && GetOwner()) {
for (const auto& tray : GetOwner()->vt_slot) {
if (tray.id == ams_id)
return VIRTUAL_TRAY_MAIN_ID - std::stoi(ams_id);
}
}
else if (stoi(ams_id) == VIRTUAL_TRAY_DEPUTY_ID)
{
if (is_bbl_vendor && ams_id == VIRTUAL_AMS_MAIN_ID_STR)
return MAIN_EXTRUDER_ID;
if (is_bbl_vendor && ams_id == VIRTUAL_AMS_DEPUTY_ID_STR)
return DEPUTY_EXTRUDER_ID;
if (!is_bbl_vendor && devPrinterUtil::IsVirtualSlot(ams_id)) {
return VIRTUAL_TRAY_MAIN_ID - std::stoi(ams_id);
}
assert(false && __FUNCTION__);
@@ -325,9 +356,22 @@ std::string DevFilaSystem::GetNozzleFlowStringByAmsId(const std::string& ams_id)
std::map<int, DevAmsSlotId> DevFilaSystem::GetTrayIndexMap()
{
std::map<int, DevAmsSlotId> tray_id_map;
tray_id_map[VIRTUAL_TRAY_MAIN_ID] = DevAmsSlotId{VIRTUAL_TRAY_MAIN_ID, 0};
tray_id_map[VIRTUAL_TRAY_DEPUTY_ID] = DevAmsSlotId{VIRTUAL_TRAY_DEPUTY_ID, 0};
const bool is_bbl_vendor = wxTheApp != nullptr && GUI::wxGetApp().preset_bundle != nullptr && GUI::wxGetApp().preset_bundle->is_bbl_vendor();
if (is_bbl_vendor) {
tray_id_map[VIRTUAL_TRAY_MAIN_ID] = DevAmsSlotId{VIRTUAL_TRAY_MAIN_ID, 0};
tray_id_map[VIRTUAL_TRAY_DEPUTY_ID] = DevAmsSlotId{VIRTUAL_TRAY_DEPUTY_ID, 0};
} else if (GetOwner()) {
for (const auto& tray : GetOwner()->vt_slot) {
try {
const int tray_id = std::stoi(tray.id);
tray_id_map[tray_id] = DevAmsSlotId{tray_id, 0};
} catch (...) {
BOOST_LOG_TRIVIAL(warning) << __FUNCTION__ << " invalid virtual tray id: " << tray.id;
}
}
}
int generic_tray_index = 0;
for (auto& [ams_id, ams_item] : GetAmsList()) {
for (auto &[slot_id, slot_item] : ams_item->GetTrays()) {
if (ams_item && slot_item) {
@@ -335,9 +379,11 @@ std::map<int, DevAmsSlotId> DevFilaSystem::GetTrayIndexMap()
int ams_id_int = stoi(ams_id);
int slot_id_int = stoi(slot_id);
int tray_index = -1;
if (ams_item->GetAmsType() == DevAms::N3S) {
if (!is_bbl_vendor) {
tray_index = generic_tray_index + slot_id_int;
} else if (ams_item->GetAmsType() == DevAms::N3S) {
tray_index = ams_id_int;
} else if(ams_item->GetAmsType() == DevAms::AMS_LITE && ams_item->IsAmsLiteMixed()) {
} else if (ams_item->GetAmsType() == DevAms::AMS_LITE && ams_item->IsAmsLiteMixed()) {
tray_index = 24 + slot_id_int;
} else {
tray_index = (ams_id_int * 4 + slot_id_int);
@@ -348,6 +394,8 @@ std::map<int, DevAmsSlotId> DevFilaSystem::GetTrayIndexMap()
}
}
}
if (!is_bbl_vendor)
generic_tray_index += static_cast<int>(ams_item->GetTrays().size());
}
return tray_id_map;
@@ -913,6 +961,8 @@ void DevFilaSystemParser::ParseAgentFilament(const json& data, MachineObject* ob
}
ams->m_exist = true;
ams->m_binded_extruder_set = {ext_id};
ams->m_binded_switcher_pos.reset();
ams->m_current_temperature = u.value("temperature", (float) INVALID_AMS_TEMPERATURE);
ams->m_humidity_percent = u.value("humidity_percent", -1);
ams->m_left_dry_time = u.value("dry_time_min", 0);
@@ -995,16 +1045,22 @@ void DevFilaSystemParser::ParseAgentFilament(const json& data, MachineObject* ob
}
// --- external / direct spools -> obj->vt_slot ---
// extruder 0 -> main virtual slot, extruder >0 -> deputy.
// Keep one virtual slot per extruder. The legacy two-extruder mapping uses
// 255 for extruder 0 and 254 for extruder 1; continue that sequence for
// additional extruders (253, 252, ...).
if (obj && data.contains("external") && data["external"].is_array())
{
std::map<int, DevAmsTray> external_slots;
obj->vt_slot.clear();
for (const auto& e : data["external"])
{
if (!e.is_object())
continue;
const int ext = e.value("extruder", MAIN_EXTRUDER_ID);
const int vt_id = (ext == MAIN_EXTRUDER_ID) ? VIRTUAL_TRAY_MAIN_ID : VIRTUAL_TRAY_DEPUTY_ID;
if (ext < MAIN_EXTRUDER_ID || ext > VIRTUAL_TRAY_MAIN_ID)
continue;
const int vt_id = VIRTUAL_TRAY_MAIN_ID - ext;
DevAmsTray tray(std::to_string(vt_id));
tray.is_exists = e.value("loaded", false);
tray.m_fila_type = e.value("material", std::string());
@@ -1013,8 +1069,11 @@ void DevFilaSystemParser::ParseAgentFilament(const json& data, MachineObject* ob
tray.nozzle_temp_min = std::to_string(e.value("nozzle_temp_min", 0));
tray.nozzle_temp_max = std::to_string(e.value("nozzle_temp_max", 0));
tray.remain = e.value("remain_percent", -1);
obj->vt_slot.push_back(tray);
external_slots.insert_or_assign(ext, std::move(tray));
}
for (auto& entry : external_slots)
obj->vt_slot.push_back(std::move(entry.second));
}
}
+2 -2
View File
@@ -349,7 +349,7 @@ public:
DevAmsTray* GetAmsTray(const std::string& ams_id, const std::string& tray_id) const;
void CollectAmsColors(std::vector<wxColour>& ams_colors) const;
// Map a linear tray index -> {ams_id, slot_id}. Includes the two virtual (external-spool) trays,
// Map a linear tray index -> {ams_id, slot_id}. Includes virtual external-spool trays,
// N3S single-slot units, and the A2L/N9 AMS-Lite-mixed layout (trays 24-27).
std::map<int, DevAmsSlotId> GetTrayIndexMap();
@@ -434,4 +434,4 @@ struct DevFilamentDryingPreset
float filament_dev_ams_drying_heat_distortion_temperature = 0.0f;
};
}// namespace Slic3r
}// namespace Slic3r
@@ -250,7 +250,7 @@ int DevNozzleMappingCtrl::CtrlGetAutoNozzleMappingV1(Slic3r::GUI::Plater* plater
void DevNozzleMappingCtrl::ParseAutoNozzleMapping(const json& print_jj)
{
if (print_jj.contains("command") && print_jj["command"].get<string>() == "get_auto_nozzle_mapping") {
if (print_jj.contains("command") && print_jj["command"].get<std::string>() == "get_auto_nozzle_mapping") {
if (print_jj.contains("sequence_id") && print_jj["sequence_id"] == m_sequence_id) {
Clear();
DevJsonValParser::ParseVal(print_jj, "result", m_result);
+39 -19
View File
@@ -13,6 +13,7 @@
#include "slic3r/GUI/DeviceCore/DevDefs.h"
#include "libslic3r/PrintConfig.hpp"
#include <boost/log/trivial.hpp>
#include <wx/app.h>
#include <wx/string.h>
#include <string>
#include "libslic3r/CommonDefs.hpp"
@@ -27,6 +28,7 @@
#include <unordered_map>
#include <optional>
#include <cmath>
#include "slic3r/GUI/GUI_App.hpp"
using json = nlohmann::json;
@@ -176,34 +178,52 @@ DevFirmwareVersionInfo DevNozzle::GetFirmwareInfo() const
int DevNozzle::GetLogicExtruderId() const
{
int total_ext_count = GetTotalExtruderCount();
if (total_ext_count == 1) {
return LOGIC_UNIQUE_EXTRUDER_ID;
} else if (total_ext_count == 2) {
if (AtLeftExtruder()) {
return LOGIC_L_EXTRUDER_ID;
} else if (AtRightExtruder()) {
return LOGIC_R_EXTRUDER_ID;
const bool is_bbl_vendor = wxTheApp != nullptr && GUI::wxGetApp().preset_bundle != nullptr &&
GUI::wxGetApp().preset_bundle->is_bbl_vendor();
if (is_bbl_vendor) {
if (total_ext_count == 1) {
return LOGIC_UNIQUE_EXTRUDER_ID;
} else if (total_ext_count == 2) {
if (AtLeftExtruder()) {
return LOGIC_L_EXTRUDER_ID;
} else if (AtRightExtruder()) {
return LOGIC_R_EXTRUDER_ID;
}
}
assert(0);
return LOGIC_UNIQUE_EXTRUDER_ID;
}
assert(0);
return LOGIC_UNIQUE_EXTRUDER_ID;
// For some reason, BBL's logical extruder ID is inverted:
// physical extruder id = 0 (MAIN_EXTRUDER_ID) vs logical extruder id = 1 (LOGIC_R_EXTRUDER_ID)
// Likely because logical id reads from left to right (left = 0, right = 1)
// For generic N extruders, this inversion does not apply.
if (IsOnRack()) return INVALID_EXTRUDER_ID;
return m_nozzle_id;
}
int DevNozzle::GetExtruderId() const
{
int total_ext_count = GetTotalExtruderCount();
if (total_ext_count == 1) {
return MAIN_EXTRUDER_ID;
} else if (total_ext_count == 2) {
if (AtRightExtruder()) {
const bool is_bbl_vendor = wxTheApp != nullptr && GUI::wxGetApp().preset_bundle != nullptr &&
GUI::wxGetApp().preset_bundle->is_bbl_vendor();
if (is_bbl_vendor) {
int total_ext_count = GetTotalExtruderCount();
if (total_ext_count == 1) {
return MAIN_EXTRUDER_ID;
} else if (AtLeftExtruder()) {
return DEPUTY_EXTRUDER_ID;
} else if (total_ext_count == 2) {
if (AtRightExtruder()) {
return MAIN_EXTRUDER_ID;
} else if (AtLeftExtruder()) {
return DEPUTY_EXTRUDER_ID;
}
}
return MAIN_EXTRUDER_ID;
}
return MAIN_EXTRUDER_ID;
return m_nozzle_id;
}
bool DevNozzle::AtLeftExtruder() const
@@ -426,7 +446,7 @@ void DevNozzleSystem::ClearNozzles()
// ---- parsing ----------------------------------------------------------------------------------------
static unordered_map<string, NozzleFlowType> _str2_nozzle_flow_type = {
static std::unordered_map<std::string, NozzleFlowType> _str2_nozzle_flow_type = {
{"S", NozzleFlowType::S_FLOW},
{"H", NozzleFlowType::H_FLOW},
{"A", NozzleFlowType::S_FLOW},
@@ -436,7 +456,7 @@ static unordered_map<string, NozzleFlowType> _str2_nozzle_flow_type = {
{"B", NozzleFlowType::E_FLOW}, // E3D High Flow -> nvtE3DHighFlow
};
static unordered_map<string, NozzleType> _str2_nozzle_type = {
static std::unordered_map<std::string, NozzleType> _str2_nozzle_type = {
{"00", NozzleType::ntStainlessSteel},
{"01", NozzleType::ntHardenedSteel},
{"05", NozzleType::ntTungstenCarbide}
+1 -1
View File
@@ -368,7 +368,7 @@ wxString DeviceErrorDialog::parse_error_level(int error_code)
}
}
static const std::unordered_set<string> s_jump_liveview_error_codes = { "0300-8003", "0300-8002", "0300-800A"};
static const std::unordered_set<std::string> s_jump_liveview_error_codes = { "0300-8003", "0300-8002", "0300-800A"};
wxString DeviceErrorDialog::show_error_code(int error_code)
{
if (m_error_code == error_code) { return wxEmptyString;}
+36 -22
View File
@@ -65,6 +65,7 @@
#include <unordered_set>
#include <utility>
#include <vector>
#include <wx/app.h>
#include <wx/colour.h>
#include <tuple>
#include <wx/dir.h>
@@ -110,6 +111,7 @@ using json = nlohmann::json;
class wxWindow;
namespace fs = boost::filesystem;
using namespace std::chrono_literals;
#define CALI_DEBUG
#define MINUTE_30 1800000 //ms
@@ -1273,19 +1275,24 @@ int MachineObject::get_bed_temperature_limit()
bool MachineObject::is_filament_installed()
{
if (m_extder_system->GetTotalExtderCount() > 0) {
// right//or single
auto ext = m_extder_system->m_extders[MAIN_EXTRUDER_ID];
if (ext.m_ext_has_filament) {
// if (m_extder_system->GetTotalExtderCount() > 0) {
// // right//or single
// auto ext = m_extder_system->m_extders[MAIN_EXTRUDER_ID];
// if (ext.m_ext_has_filament) {
// return true;
// }
// }
// /*left*/
// if (m_extder_system->GetTotalExtderCount() > 1) {
// auto ext = m_extder_system->m_extders[DEPUTY_EXTRUDER_ID];
// if (ext.m_ext_has_filament) {
// return true;
// }
// }
for (auto& ext : m_extder_system->m_extders) {
if (ext.m_ext_has_filament)
return true;
}
}
/*left*/
if (m_extder_system->GetTotalExtderCount() > 1) {
auto ext = m_extder_system->m_extders[DEPUTY_EXTRUDER_ID];
if (ext.m_ext_has_filament) {
return true;
}
}
return false;
}
@@ -3103,7 +3110,7 @@ int MachineObject::parse_json(std::string tunnel, std::string payload, bool key_
DevFirmwareVersionInfo ver_info;
ver_info.name = (*it)["name"].get<std::string>();
if ((*it).contains("product_name"))
ver_info.product_name = wxString::FromUTF8((*it)["product_name"].get<string>());
ver_info.product_name = wxString::FromUTF8((*it)["product_name"].get<std::string>());
if ((*it).contains("sw_ver"))
ver_info.sw_ver = (*it)["sw_ver"].get<std::string>();
if ((*it).contains("sw_new_ver"))
@@ -5262,16 +5269,17 @@ DevAmsTray MachineObject::parse_vt_tray(json vtray)
bool MachineObject::contains_tray(const std::string &ams_id, const std::string &tray_id) const
{
if (ams_id != VIRTUAL_AMS_MAIN_ID_STR && ams_id != VIRTUAL_AMS_DEPUTY_ID_STR) {
const bool is_bbl_vendor = wxTheApp != nullptr && GUI::wxGetApp().preset_bundle != nullptr && GUI::wxGetApp().preset_bundle->is_bbl_vendor();
if (is_bbl_vendor && ams_id != VIRTUAL_AMS_MAIN_ID_STR && ams_id != VIRTUAL_AMS_DEPUTY_ID_STR)
return m_fila_system->GetAmsTray(ams_id, tray_id) != nullptr;
} else {
if (!is_bbl_vendor || ams_id == VIRTUAL_AMS_MAIN_ID_STR || ams_id == VIRTUAL_AMS_DEPUTY_ID_STR) {
for (const auto& tray : vt_slot) {
if (tray.id == ams_id) { return true; }
if (tray.id == ams_id) return true;
}
}
return false;
return !is_bbl_vendor && m_fila_system->GetAmsTray(ams_id, tray_id) != nullptr;
}
DevAmsTray MachineObject::get_tray(const std::string &ams_id, const std::string &tray_id) const
@@ -5281,13 +5289,19 @@ DevAmsTray MachineObject::get_tray(const std::string &ams_id, const std::string
return DevAmsTray(tray_id);
}
if (ams_id != VIRTUAL_AMS_MAIN_ID_STR && ams_id != VIRTUAL_AMS_DEPUTY_ID_STR) {
const bool is_bbl_vendor = wxTheApp != nullptr && GUI::wxGetApp().preset_bundle != nullptr && GUI::wxGetApp().preset_bundle->is_bbl_vendor();
if (is_bbl_vendor && ams_id != VIRTUAL_AMS_MAIN_ID_STR && ams_id != VIRTUAL_AMS_DEPUTY_ID_STR) {
auto tray = m_fila_system->GetAmsTray(ams_id, tray_id);
if (tray) { return *tray;};
if (tray) return *tray;
}
else {
else if (!is_bbl_vendor || ams_id == VIRTUAL_AMS_MAIN_ID_STR || ams_id == VIRTUAL_AMS_DEPUTY_ID_STR) {
for (const auto &tray : vt_slot) {
if (tray.id == ams_id) { return tray; }
if (tray.id == ams_id) return tray;
}
if (!is_bbl_vendor) {
auto tray = m_fila_system->GetAmsTray(ams_id, tray_id);
if (tray) return *tray;
}
}
+3 -3
View File
@@ -294,8 +294,8 @@ wxDataViewItem EditGCodeDialog::add_presets_placeholders()
// Orca: create subgroups from the pages of the tabs
auto init_from_tab = [this, full_config](wxDataViewItem parent, Tab* tab, const set<string>& preset_keys){
set extra_keys(preset_keys);
auto init_from_tab = [this, full_config](wxDataViewItem parent, Tab* tab, const std::set<std::string>& preset_keys){
std::set extra_keys(preset_keys);
for (const auto& page : tab->m_pages) {
// ORCA: Pull icons from tabs for subgroups, icons are hidden on tabs
std::string icon_name = "empty"; // use empty icon if not defined
@@ -549,7 +549,7 @@ void ParamsNode::RefreshSearch(const wxString& search_text)
if (GetEnabledChildren().empty())
if (auto pos = text.find(search_text); IsParamNode() && pos != wxString::npos) {
m_highlight_index = make_unique<pair<int, int>>(pos, search_text.Len());
m_highlight_index = make_unique<std::pair<int, int>>(pos, search_text.Len());
Enable();
} else {
Disable();
+4 -4
View File
@@ -702,11 +702,11 @@ void Field::get_value_by_opt_type(wxString& str, const bool check_value/* = true
set_value(str, true);
}
} else if (m_opt.opt_key == "sparse_infill_rotate_template" || m_opt.opt_key == "solid_infill_rotate_template") {
string ustr(str.utf8_string());
std::string ustr(str.utf8_string());
if (!ConfigOptionFloats::validate_string(ustr)) {
string v;
std::string v;
std::smatch match;
string ps = (m_opt.opt_key == "sparse_infill_rotate_template") ?
std::string ps = (m_opt.opt_key == "sparse_infill_rotate_template") ?
u8"[BT][!]?|[#][\\d]+[!]?|[+\\-]?[\\d.]+[%]?[*]?[\\d]*[/NnZz$LlUuQq~^|#]?[+\\-]?[\\d.]*[%#\'\"cm]?[m]?[BT]?[!*]?" :
u8"[#][\\d]+[!]?|[+\\-]?[\\d.]+[%]?[*]?[\\d]*[/NnZz$LlUuQq~^|#]?[+\\-]?[\\d.]*[%#\'\"cm]?[m]?[!*]?";
@@ -729,7 +729,7 @@ void Field::get_value_by_opt_type(wxString& str, const bool check_value/* = true
}
break;
} else if (m_opt.opt_key == "extra_solid_infills") {
string ustr(str.utf8_string());
std::string ustr(str.utf8_string());
// New rule: accept either interval form (N or N#K) or explicit list (e.g. 1,7,9), with optional quotes.
const std::regex rx_interval(u8R"(^\s*['"]?\s*\d+\s*(?:#\s*\d*)?\s*['"]?\s*$)");
// List entries may be plain numbers or number with optional #K count, e.g., 5, 9#2, 18
+3 -1
View File
@@ -94,6 +94,8 @@
#include "slic3r/GUI/Gizmos/GizmoObjectManipulation.hpp"
#include "slic3r/GUI/PartPlate.hpp"
using namespace std::string_view_literals;
namespace Slic3r { class PrintBase; }
@@ -3184,7 +3186,7 @@ void GCodeViewer::render_legend_color_arr_recommen(float window_padding)
float ams_item_height = 0;
float filament_group_item_align_width = 0;
{
float three_words_width = imgui.calc_text_size(std::string_view("ABC")).x;
float three_words_width = imgui.calc_text_size("ABC"sv).x;
const int line_capacity = 4;
for (const auto& extruder_filaments : {m_left_extruder_filament,m_right_extruder_filament })
+2
View File
@@ -177,6 +177,8 @@ extern wxPopupWindow* wxCurrentPopupWindow;
#endif
#endif
using namespace std::string_view_literals;
static constexpr const float TRACKBALLSIZE = 0.8f;
static Slic3r::ColorRGBA DEFAULT_BG_LIGHT_COLOR = { 0.906f, 0.906f, 0.906f, 1.0f };
+3 -3
View File
@@ -9040,7 +9040,7 @@ std::map<std::string, std::string> GUI_App::get_delete_cache_presets_lock()
void GUI_App::process_delete_presets()
{
std::map<string, string> delete_cache_presets = get_delete_cache_presets_lock();
std::map<std::string, std::string> delete_cache_presets = get_delete_cache_presets_lock();
for (auto it = delete_cache_presets.begin(); it != delete_cache_presets.end();) {
if (it->first.empty()) continue;
std::string del_setting_id = it->first;
@@ -10068,7 +10068,7 @@ bool is_soluble_filament(int extruder_id)
return support_option->get_at(0);
};
bool has_filaments(const std::vector<string>& model_filaments) {
bool has_filaments(const std::vector<std::string>& model_filaments) {
auto &filament_presets = Slic3r::GUI::wxGetApp().preset_bundle->filament_presets;
if (!Slic3r::GUI::wxGetApp().plater()) return false;
auto model_objects = Slic3r::GUI::wxGetApp().plater()->model().objects;
@@ -10103,7 +10103,7 @@ bool is_support_filament(int extruder_id, bool strict_check)
Slic3r::ConfigOptionBools *support_option = dynamic_cast<Slic3r::ConfigOptionBools *>(filament->config.option("filament_is_support"));
if(!strict_check &&(filament_type == "PETG" || filament_type == "PLA")) {
std::vector<string> model_filaments;
std::vector<std::string> model_filaments;
if (filament_type == "PETG")
model_filaments.emplace_back("PLA");
else {
+2 -2
View File
@@ -1108,7 +1108,7 @@ void ObjectList::update_name_column_width() const
}
}
GetColumn(colName)->SetWidth(max(0, client_size.x - (others_width)*em));
GetColumn(colName)->SetWidth(std::max(0, client_size.x - (others_width)*em));
}
void ObjectList::set_filament_column_hidden(const bool hide) const
@@ -3970,7 +3970,7 @@ wxDataViewItem ObjectList::add_settings_item(wxDataViewItem parent_item, const D
if (config->opt_float("layer_height") == object_cfg->opt_float("layer_height")) {
SettingsFactory::Bundle new_cat_options;
for (auto cat_opt : cat_options) {
std::vector<string> temp;
std::vector<std::string> temp;
for (auto value : cat_opt.second) {
if (value != "layer_height")
temp.push_back(value);
@@ -44,6 +44,8 @@
#define MAX_NUM 9999.99
#define MAX_SIZE std::string_view{"9999.99"}
using namespace std::string_view_literals;
namespace Slic3r
{
namespace GUI
+7 -7
View File
@@ -33,7 +33,7 @@ static const char* HMS_LOCAL_IMG_PATH = "hms/local_image";
// the local HMS info
// Orca: dev-id-type set trimmed to the devices Orca ships local HMS images for
static unordered_set<string> package_dev_id_types {"094", "239", "093", "22E"};
static std::unordered_set<std::string> package_dev_id_types {"094", "239", "093", "22E"};
// Orca: HMS should be disabled when stealth mode is on or networking is not installed
static bool should_disable_hms()
@@ -345,17 +345,17 @@ wxString HMSQuery::query_hms_msg(const std::string& dev_id, const std::string& l
return _query_hms_msg(dev_id.substr(0, 3), long_error_code, lang_code);
}
string HMSQuery::get_dev_id_type(const MachineObject* obj) const
std::string HMSQuery::get_dev_id_type(const MachineObject* obj) const
{
if (obj)
{
return obj->get_dev_id().substr(0, 3);
}
return string();
return std::string();
}
wxString HMSQuery::_query_hms_msg(const string& dev_id_type, const string& long_error_code, const string& lang_code)
wxString HMSQuery::_query_hms_msg(const std::string& dev_id_type, const std::string& long_error_code, const std::string& lang_code)
{
if (long_error_code.empty())
{
@@ -426,9 +426,9 @@ wxString HMSQuery::_query_hms_msg(const string& dev_id_type, const string& long_
return wxEmptyString;
}
bool HMSQuery::_is_internal_error(const string &dev_id_type,
const string &error_code,
const string &lang_code)
bool HMSQuery::_is_internal_error(const std::string &dev_id_type,
const std::string &error_code,
const std::string &lang_code)
{
init_hms_info(dev_id_type);
auto iter = m_hms_info_jsons.find(dev_id_type);
+1 -1
View File
@@ -335,7 +335,7 @@ void PrintJob::process(Ctl &ctl)
auto origin_profile_id = model_info->metadata_items.find(BBL_DESIGNER_PROFILE_ID_TAG);
if (origin_profile_id != model_info->metadata_items.end()) {
try {
params.origin_profile_id = stoi(origin_profile_id->second.c_str());
params.origin_profile_id = std::stoi(origin_profile_id->second.c_str());
}
catch(...) {}
}
+5 -5
View File
@@ -536,12 +536,12 @@ void NotificationManager::PopNotification::count_lines()
return;
// handle with marks
if (pos_start == string::npos && pos_end == string::npos) {
if (pos_start == std::string::npos && pos_end == std::string::npos) {
pos_start = text.find(error_start);
if (pos_start != string::npos) {
if (pos_start != std::string::npos) {
text.erase(pos_start, error_start.length());
pos_end = text.find(error_end);
if (pos_end != string::npos) {
if (pos_end != std::string::npos) {
text.erase(pos_end, error_end.length());
}
}
@@ -675,7 +675,7 @@ void NotificationManager::PopNotification::bbl_render_block_notif_text(ImGuiWrap
if (m_text1.size() > m_endlines[i])
last_end += (m_text1[m_endlines[i]] == '\n' || m_text1[m_endlines[i]] == ' ' ? 1 : 0);
if (pos_start != string::npos && pos_end != string::npos && m_endlines[i] - line.length() >= pos_start && m_endlines[i] <= pos_end) {
if (pos_start != std::string::npos && pos_end != std::string::npos && m_endlines[i] - line.length() >= pos_start && m_endlines[i] <= pos_end) {
push_style_color(ImGuiCol_Text, m_ErrorColor, m_state == EState::FadingOut, m_current_fade_opacity);
imgui.text(line.c_str());
ImGui::PopStyleColor();
@@ -737,7 +737,7 @@ void NotificationManager::PopNotification::render_text(ImGuiWrapper& imgui, cons
if (m_text1.size() > m_endlines[i])
last_end += (m_text1[m_endlines[i]] == '\n' || m_text1[m_endlines[i]] == ' ' ? 1 : 0);
if (pos_start != string::npos && pos_end != string::npos&& m_endlines[i] - line.length() >= pos_start && m_endlines[i] <= pos_end) {
if (pos_start != std::string::npos && pos_end != std::string::npos&& m_endlines[i] - line.length() >= pos_start && m_endlines[i] <= pos_end) {
push_style_color(ImGuiCol_Text, m_ErrorColor, m_state == EState::FadingOut, m_current_fade_opacity);
imgui.text(line.c_str());
ImGui::PopStyleColor();
+4 -4
View File
@@ -86,7 +86,7 @@ PartSkipDialog::PartSkipDialog(wxWindow *parent) : DPIDialog(parent, wxID_ANY, _
{
std::time_t t = std::time(0);
std::stringstream buf;
buf << put_time(std::localtime(&t), "%a_%b_%d_%H_%M_%S/");
buf << std::put_time(std::localtime(&t), "%a_%b_%d_%H_%M_%S/");
m_timestamp = buf.str();
SetBackgroundColour(*wxWHITE);
@@ -402,7 +402,7 @@ std::string PartSkipDialog::create_tmp_path()
return tmp_path;
}
bool PartSkipDialog::is_local_file_existed(const std::vector<string> &local_paths)
bool PartSkipDialog::is_local_file_existed(const std::vector<std::string> &local_paths)
{
for (auto path : local_paths) {
if (!std::filesystem::exists(path)) { return false; }
@@ -703,8 +703,8 @@ void PartSkipDialog::InitDialogUI()
m_parts_state.clear();
m_parts_name.clear();
string pick_img = m_local_paths[0];
string slice_info = m_local_paths[2];
std::string pick_img = m_local_paths[0];
std::string slice_info = m_local_paths[2];
m_switch_drag_btn->SetIcon("canvas_drag");
m_switch_drag_btn->SetBackgroundColor(*wxWHITE);
+3 -3
View File
@@ -139,11 +139,11 @@ private:
boost::shared_ptr<PrinterFileSystem> m_file_sys;
std::string m_timestamp;
std::string m_tmp_path;
std::vector<string> m_local_paths;
std::vector<string> m_target_paths;
std::vector<std::string> m_local_paths;
std::vector<std::string> m_target_paths;
std::string create_tmp_path();
bool is_local_file_existed(const std::vector<string> &local_paths);
bool is_local_file_existed(const std::vector<std::string> &local_paths);
void DownloadPartsFile();
void OnFileSystemEvent(wxCommandEvent &event);
+4 -4
View File
@@ -357,7 +357,7 @@ wxDEFINE_EVENT(EVT_NOTICE_FULL_SCREEN_CHANGED, IntEvent);
#define PRINTER_PANEL_RADIUS (6) // ORCA
#define BTN_SYNC_SIZE (wxSize(FromDIP(96), FromDIP(98)))
static string get_diameter_string(float diameter)
static std::string get_diameter_string(float diameter)
{
std::ostringstream stream; // ORCA ensure 0.25 returned as 0.25. previous code returned as 0.2 because of std::setprecision(1)
stream << std::fixed << std::setprecision(2) << diameter; // Use 2 decimals to capture 0.25 / 0.15 reliably
@@ -6323,7 +6323,7 @@ void Sidebar::sync_ams_list(bool is_from_big_sync_btn)
}
// BBS:Record consumables information before synchronization
std::vector<string> color_before_sync;
std::vector<std::string> color_before_sync;
std::vector<bool> is_support_before;
DynamicPrintConfig& project_config = wxGetApp().preset_bundle->project_config;
ConfigOptionStrings* color_opt = project_config.option<ConfigOptionStrings>("filament_colour");
@@ -12762,7 +12762,7 @@ void Plater::priv::on_select_preset(wxCommandEvent &evt)
Vec3d old_plate_pos = old_plate->get_center_origin();
// BBS: Save the model in the current platelist
std::vector<vector<int> > plate_object;
std::vector<std::vector<int> > plate_object;
for (size_t i = 0; i < old_plate_list.get_plate_count(); ++i) {
PartPlate* plate = old_plate_list.get_plate(i);
std::vector<int> obj_idxs;
@@ -17975,7 +17975,7 @@ bool Plater::load_files(const wxArrayString& filenames)
// Orca: Iters through given paths and imports files from zip then remove zip from paths
// returns true if zip files were found
auto handle_zips = [this](vector<fs::path>& paths) { // NOLINT(*-no-recursion) - Recursion is intended and should be managed properly
auto handle_zips = [this](std::vector<fs::path>& paths) { // NOLINT(*-no-recursion) - Recursion is intended and should be managed properly
bool res = false;
for (auto it = paths.begin(); it != paths.end();) {
if (boost::algorithm::iends_with(it->string(), ".zip")) {
+2 -2
View File
@@ -378,7 +378,7 @@ wxBoxSizer *PreferencesDialog::create_item_combobox(wxString title, wxString too
auto current_setting = app_config->get(param);
if (!current_setting.empty()) {
auto compare = [current_setting](string possible_setting) { return current_setting == possible_setting; };
auto compare = [current_setting](std::string possible_setting) { return current_setting == possible_setting; };
auto iterator = find_if(config_name_index.begin(), config_name_index.end(), compare);
if (iterator != config_name_index.end())
current_index = static_cast<unsigned int>(iterator - config_name_index.begin());
@@ -1714,7 +1714,7 @@ void PreferencesDialog::create_items()
g_sizer->Add(create_item_title(_L("Project")), 1, wxEXPAND);
std::vector<wxString> projectLoadSettingsBehaviourOptions = {_L("Load All"), _L("Ask When Relevant"), _L("Always Ask"), _L("Load Geometry Only")};
std::vector<string> projectLoadSettingsConfigOptions = { OPTION_PROJECT_LOAD_BEHAVIOUR_LOAD_ALL, OPTION_PROJECT_LOAD_BEHAVIOUR_ASK_WHEN_RELEVANT, OPTION_PROJECT_LOAD_BEHAVIOUR_ALWAYS_ASK, OPTION_PROJECT_LOAD_BEHAVIOUR_LOAD_GEOMETRY };
std::vector<std::string> projectLoadSettingsConfigOptions = { OPTION_PROJECT_LOAD_BEHAVIOUR_LOAD_ALL, OPTION_PROJECT_LOAD_BEHAVIOUR_ASK_WHEN_RELEVANT, OPTION_PROJECT_LOAD_BEHAVIOUR_ALWAYS_ASK, OPTION_PROJECT_LOAD_BEHAVIOUR_LOAD_GEOMETRY };
auto item_project_load = create_item_combobox(_L("Load behavior"), _L("Should printer/filament/process settings be loaded when opening a 3MF file?"), SETTING_PROJECT_LOAD_BEHAVIOUR, projectLoadSettingsBehaviourOptions, projectLoadSettingsConfigOptions);
g_sizer->Add(item_project_load);
+1 -1
View File
@@ -239,7 +239,7 @@ void ProjectPanel::on_reload(wxCommandEvent& evt)
}
bool has_content = false;
for (const string& v : {
for (const std::string& v : {
update_type,
license,
model_name,
+58 -25
View File
@@ -14,6 +14,7 @@
#include "Widgets/DialogButtons.hpp"
#include "Widgets/StaticLine.hpp"
#include "Widgets/StateColor.hpp"
#include "Widgets/SwitchButton.hpp"
#include "libslic3r/PrintConfig.hpp"
#include "libslic3r/Preset.hpp"
@@ -864,34 +865,25 @@ void PublishSettingsDialog::build_option_model()
};
// --- Phase 1: printer per-extruder retraction settings (first, mirroring the sidebar's
// Printer group), from the printer tab's "Extruder"/"Extruder N" pages. One inner tab per
// extruder (e.g. "Left Extruder"/"Right Extruder" via Tab::translate_category), each holding
// that extruder's Retraction and Z-Hop rows with per-extruder "#N" values.
// Printer group), from the printer tab's "Extruder" page. One inner tab per extruder (named
// as on the printer tab's switch, e.g. "Left Extruder"/"Right Extruder" via
// Tab::translate_category), each holding that extruder's Retraction and Z-Hop rows with
// per-extruder "#N" values.
{
size_t g = section_group_for(Section::Printer);
std::set<std::string> printer_added;
for (Tab* tab : wxGetApp().tabs_list) {
if (tab->m_type != Preset::TYPE_PRINTER)
// The page's controls edit the extruder chosen on the printer tab's switch, so its
// option list is read once per extruder.
auto* printer_tab = dynamic_cast<TabPrinter*>(tab);
const Page* page = printer_tab ? printer_tab->extruder_page() : nullptr;
if (page == nullptr)
continue;
for (const PageShp& page : tab->m_pages) {
if (!page->title().StartsWith("Extruder"))
continue;
// The extruder index of this page: its options are appended with the same
// "#N" opt_index (opt.second.second), so derive the tab's index from the first
// allowlisted option; skip the page when none is found (defensive).
int extruder_idx = -1;
for (const ConfigOptionsGroupShp& optgroup : page->m_optgroups) {
if (optgroup->title != "Retraction" && optgroup->title != "Z-Hop")
continue;
for (const auto& opt : optgroup->opt_map())
if (extruder_idx < 0)
extruder_idx = opt.second.second;
if (extruder_idx >= 0)
break;
}
if (extruder_idx < 0)
continue;
const wxString page_title = Tab::translate_category(page->title(), tab->m_type);
const size_t extruders_count = printer_tab->m_extruders_count;
for (size_t extruder_idx = 0; extruder_idx < extruders_count; ++extruder_idx) {
const wxString page_title = Tab::translate_category(extruders_count > 1 ? wxString::Format("Extruder %d", int(extruder_idx + 1)) : wxString("Extruder"), tab->m_type);
// Retraction and Z-Hop values are stored per variant column, not per extruder.
const int variant_index = printer_tab->extruder_variant_index(int(extruder_idx));
for (const ConfigOptionsGroupShp& optgroup : page->m_optgroups) {
// Allowlist on the untranslated optgroup title; the "Retraction when
// switching material" group is intentionally skipped.
@@ -899,17 +891,17 @@ void PublishSettingsDialog::build_option_model()
continue;
const wxString subcategory = _(optgroup->title);
for (const auto& opt : optgroup->opt_map()) {
const std::string& opt_id = opt.first;
const std::string& pure_key = opt.second.first;
// Rows are keyed by the full per-extruder "#N" opt_id so each extruder
// tab publishes its own value; GetPublishedKeys() emits the checked rows
// as-is.
const std::string opt_id = pure_key + "#" + std::to_string(variant_index);
if (!printer_added.insert(opt_id).second)
continue;
wxString label, value, unit;
if (!option_text(opt_id, pure_key, label, value, unit))
continue;
size_t cat_index = category_index_for(page_title, Section::Printer, g, size_t(extruder_idx));
size_t cat_index = category_index_for(page_title, Section::Printer, g, extruder_idx);
size_t sub_index = subcategory_index_for(cat_index, subcategory, optgroup->icon);
add_row_ui(opt_id, label, value, unit, cat_index, sub_index);
}
@@ -1098,6 +1090,10 @@ void PublishSettingsDialog::build_option_model()
for (SectionGroup& section : m_sections)
if (!section.categories.empty())
section.tabs->SelectItem(0);
// Orca: the Printer section shows its extruders on the same switch as the printer tab's Extruder page.
for (size_t s = 0; s < m_sections.size(); ++s)
if (m_sections[s].kind == Section::Printer && m_sections[s].categories.size() > 1)
setup_variant_switch(s);
if (!m_sections.empty()) {
m_outer_tabs->SelectItem(0);
show_outer_page(0);
@@ -1630,6 +1626,8 @@ void PublishSettingsDialog::show_inner_page(size_t section_index, int inner_inde
section.selected_mixed = -1;
}
section.selected_inner = inner_index;
if (section.variant_switch != nullptr)
section.variant_switch->SetSelection(inner_index); // fires its event, which ignores the shown page
Category& category = m_categories[section.categories[inner_index]];
category.page->Show();
category.scroll->FitInside();
@@ -1639,6 +1637,32 @@ void PublishSettingsDialog::show_inner_page(size_t section_index, int inner_inde
section.page_host_sizer->Layout();
}
void PublishSettingsDialog::setup_variant_switch(size_t section_index)
{
SectionGroup& section = m_sections[section_index];
std::vector<wxString> titles;
for (size_t category : section.categories)
titles.push_back(m_categories[category].title);
section.variant_switch = new MultiSwitchButton(section.page);
section.variant_switch->SetFitToOptions();
section.variant_switch->SetOptions(titles);
section.variant_switch->SetSelection(section.selected_inner);
section.variant_switch->Bind(wxCUSTOMEVT_MULTISWITCH_SELECTION, [this, section_index](wxCommandEvent& evt) {
evt.Skip();
// The hidden tab strip stays the selection model; its event shows the page.
SectionGroup& sec = m_sections[section_index];
if (evt.GetInt() != sec.selected_inner)
sec.tabs->SelectItem(evt.GetInt());
});
// The switch takes the place of the tab strip, centered like on the printer tab.
wxSizer* page_sizer = section.page->GetSizer();
page_sizer->Insert(1, section.variant_switch, 0, wxALIGN_CENTER_HORIZONTAL | wxTOP, FromDIP(4));
section.tabs->Hide();
section.page->Layout();
}
void PublishSettingsDialog::show_mixed_page(size_t section_index, int mixed_index)
{
if (section_index >= m_sections.size())
@@ -2184,6 +2208,13 @@ void PublishSettingsDialog::refresh_tab_indicators()
for (size_t i = 0; i < section.categories.size(); ++i) {
const bool on = category_has_selection(m_categories[section.categories[i]]);
section.tabs->SetItemIndicator(static_cast<unsigned int>(i), on);
// The switch has no indicator dot; mark its option text instead.
if (section.variant_switch != nullptr) {
const wxString& title = m_categories[section.categories[i]].title;
const wxString text = on ? title + wxString(" ") + wxString(wxUniChar(0x2022)) : title;
if (section.variant_switch->GetOptionText(static_cast<unsigned int>(i)) != text)
section.variant_switch->SetOptionText(static_cast<unsigned int>(i), text);
}
any = any || on;
}
if (section.mixed_tabs != nullptr)
@@ -2248,6 +2279,8 @@ void PublishSettingsDialog::on_dpi_changed(const wxRect& suggested_rect)
section.tabs->Rescale();
if (section.mixed_tabs != nullptr)
section.mixed_tabs->Rescale();
if (section.variant_switch != nullptr)
section.variant_switch->Rescale();
}
// Refresh the per-row Color chips at the new DPI (they carry the slot number too).
+7
View File
@@ -37,6 +37,7 @@ namespace Slic3r { class DynamicPrintConfig; }
class TextInput;
class StaticLine;
class CheckBox;
class MultiSwitchButton;
namespace Slic3r { namespace GUI {
@@ -195,6 +196,10 @@ private:
ScalableBitmap icon_bmp; // tab icon next to the title; rescaled on DPI change
wxPanel* page{nullptr};
TabCtrl* tabs{nullptr};
// Printer section with several extruders: the extruder switch shown instead of `tabs`, the
// same MultiSwitchButton (and option names) as the printer tab's Extruder page. One option
// per entry of `categories`, named by its title; `tabs` stays as the hidden selection model.
MultiSwitchButton* variant_switch{nullptr};
// Second tab strip, below the main one, listing only the mixed-color filament slots.
// Present on the Material section only (null elsewhere).
TabCtrl* mixed_tabs{nullptr};
@@ -277,6 +282,8 @@ private:
bool row_is_visible(const Row& row) const;
void apply_visibility();
void bind_tab_events();
// Replaces a section's inner tab strip with a variant switch (see SectionGroup::variant_switch).
void setup_variant_switch(size_t section_index);
TabCtrl* m_outer_tabs{nullptr};
wxPanel* m_outer_host{nullptr};
+4 -4
View File
@@ -182,7 +182,7 @@ std::string get_nozzle_volume_type_cloud_string(NozzleVolumeType nozzle_volume_t
static int s_nozzle_mapping_last_request_time = 0;
std::vector<wxString> SelectMachineDialog::MACHINE_BED_TYPE_STRING;
std::vector<string> SelectMachineDialog::MachineBedTypeString;
std::vector<std::string> SelectMachineDialog::MachineBedTypeString;
void SelectMachineDialog::init_machine_bed_types()
{
if (MACHINE_BED_TYPE_STRING.size() == 0) {
@@ -2748,8 +2748,8 @@ void SelectMachineDialog::on_ok_btn(wxCommandEvent &event)
std::vector<ConfirmBeforeSendInfo> confirm_text;
// check more than one using in same external spool
std::unordered_set<string> main_external_spool_filas;
std::unordered_set<string> deputy_external_spool_filas;
std::unordered_set<std::string> main_external_spool_filas;
std::unordered_set<std::string> deputy_external_spool_filas;
for (const auto& mapping_info : m_ams_mapping_result) {
if (mapping_info.ams_id == VIRTUAL_AMS_MAIN_ID_STR){
main_external_spool_filas.insert(mapping_info.filament_id);
@@ -5239,7 +5239,7 @@ void SelectMachineDialog::update_show_status(MachineObject* obj_)
/*Check high temperture slicing*/
if (m_print_type == PrintFromType::FROM_NORMAL) {
std::set<string> high_temp_filaments;
std::set<std::string> high_temp_filaments;
std::unordered_set<int> known_fila_soften_extruders;
std::unordered_set<int> unknown_fila_soften_extruders;
auto preset_full_config = wxGetApp().preset_bundle->full_config();
+1 -1
View File
@@ -337,7 +337,7 @@ private:
wxColour m_colour_bold_color{wxColour(38, 46, 48)};
StateColor m_btn_bg_enable;
std::unordered_map<string, PrintOption*> m_checkbox_list;
std::unordered_map<std::string, PrintOption*> m_checkbox_list;
std::list<PrintOption*> m_checkbox_list_order;
std::shared_ptr<int> m_token = std::make_shared<int>(0);
+1 -1
View File
@@ -126,7 +126,7 @@ void SendToPrinterDialog::stripWhiteSpace(std::string& str)
{
if (str == "") { return; }
string::iterator cur_it;
std::string::iterator cur_it;
cur_it = str.begin();
while (cur_it != str.end()) {
File diff suppressed because it is too large Load Diff
+37 -6
View File
@@ -1,6 +1,8 @@
#ifndef slic3r_StatusPanel_hpp_
#define slic3r_StatusPanel_hpp_
#include <vector>
#include "libslic3r/ProjectTask.hpp"
#include "DeviceManager.hpp"
#include "MonitorPage.hpp"
@@ -66,6 +68,7 @@
#include "StagedBuild.hpp"
class StepIndicator;
class wxChoice;
#define COMMAND_TIMEOUT 5
@@ -140,28 +143,35 @@ class ExtruderImage : public wxWindow
ScalableBitmap *m_left_extruder_active_empty;
ScalableBitmap *m_left_extruder_unactive_filled;
ScalableBitmap *m_left_extruder_unactive_empty;
ScalableBitmap *m_right_extruder_active_filled;
ScalableBitmap *m_right_extruder_active_empty;
ScalableBitmap *m_right_extruder_unactive_filled;
ScalableBitmap *m_right_extruder_unactive_empty;
ScalableBitmap *m_extruder_single_nozzle_empty_load;
ScalableBitmap *m_extruder_single_nozzle_empty_unload;
ScalableBitmap *m_extruder_single_nozzle_filled_load;
ScalableBitmap *m_extruder_single_nozzle_filled_unload;
ExtruderState m_left_ext_state = {ExtruderState::EMPTY_LOAD};
ExtruderState m_right_ext_state = {ExtruderState::EMPTY_LOAD};
ExtruderState m_single_ext_state = {ExtruderState::EMPTY_LOAD};
std::vector<ExtruderState> m_multi_extruder_states;
bool m_generic_nozzle_display{false};
public:
void update(int nozzle_num, int nozzle_id);
void update(ExtruderState single_state);
void update(ExtruderState right_state, ExtruderState left_state);
void update(ExtruderState state, int idx);
void msw_rescale();
void setExtruderCount(int nozzle_num);
void setGenericNozzleDisplay(bool enabled);
void setExtruderUsed(std::string loc);
void setExtruderUsed(int nozzle_idx);
void paintEvent(wxPaintEvent &evt);
void render(wxDC &dc);
@@ -171,7 +181,7 @@ public:
std::string current_nozzle_loc = "";
wxColour m_colour;
string m_file_name;
std::string m_file_name;
bool m_ams_loading{false};
void doRender(wxDC &dc);
ExtruderImage(wxWindow *parent, wxWindowID id, int nozzle_num, const wxPoint &pos = wxDefaultPosition, const wxSize &size = wxDefaultSize);
@@ -244,7 +254,7 @@ protected:
{
wxString local_image_url; //local image path
std::string img_url_paths; // oss url path
vector<wxPanel *> image_broad;
std::vector<wxPanel *> image_broad;
bool is_selected;
bool is_uploaded; // load
wxBoxSizer * image_tb_broad = nullptr;
@@ -495,6 +505,8 @@ protected:
std::vector<ExtruderImage *> m_extruderImage;
SwitchBoard * m_nozzle_btn_panel;
wxChoice* m_generic_nozzle_selector{nullptr};
int m_generic_nozzle_selector_count{0};
wxStaticText * m_text_tasklist_caption;
@@ -507,10 +519,23 @@ protected:
wxBoxSizer * m_misc_ctrl_sizer;
StaticBox* m_fan_panel;
StaticLine * m_line_nozzle;
TempInput* m_tempCtrl_nozzle;
wxWindowID m_nozzle_temp_control_id{wxID_ANY};
wxWindow* m_temp_nozzle_parent{nullptr};
wxBoxSizer* m_temp_nozzle_sizer{nullptr};
size_t m_temp_nozzle_active_count{0};
TempInput* m_tempCtrl_nozzle{nullptr};
int m_temp_nozzle_timeout{ 0 };
TempInput* m_tempCtrl_nozzle_deputy;
TempInput* m_tempCtrl_nozzle_deputy{nullptr};
int m_temp_nozzle_deputy_timeout{ 0 };
std::vector<TempInput*> m_tempCtrl_nozzles;
std::vector<int> m_temp_nozzle_timeouts;
// Last (dev_id, device toolhead count, preset nozzle count) we warned about, to avoid log spam.
std::string m_last_mismatch_dev_id;
int m_last_mismatch_device_count{-1};
int m_last_mismatch_preset_count{-1};
TempInput * m_tempCtrl_bed;
int m_temp_bed_timeout {0};
TempInput * m_tempCtrl_chamber;
@@ -550,6 +575,7 @@ protected:
/* AMS control box <-> live nozzle-rack panel toggle (rack printers only) */
SwitchBoard* m_ams_rack_switch{ nullptr };
MultiSwitchButton* m_ams_nozzle_switch{ nullptr };
AMSControl* m_ams_control;
StaticBox* m_ams_control_box;
@@ -624,6 +650,9 @@ public:
wxBoxSizer *create_temp_axis_group(wxWindow *parent);
wxBoxSizer *create_temp_control(wxWindow *parent);
TempInput* create_nozzle_temp_control(wxWindow *parent, wxWindowID id);
void set_temp_input_colors(TempInput* temp_ctrl);
void ensure_nozzle_temp_controls(size_t count);
wxBoxSizer *create_misc_control(wxWindow *parent);
wxBoxSizer *create_axis_control(wxWindow *parent);
wxPanel *create_bed_control(wxWindow *parent);
@@ -654,6 +683,7 @@ private:
friend class MonitorPanel;
void wire_controls();
bool load_thumbnail_from_url(const wxString &url, MachineObject *obj);
void sync_nozzle_temp_controls(size_t count);
protected:
std::shared_ptr<SliceInfoPopup> m_slice_info_popup;
@@ -801,6 +831,7 @@ protected:
void update_cloud_subtask(MachineObject *obj);
void update_sdcard_subtask(MachineObject *obj);
void update_temp_ctrl(MachineObject *obj);
void check_extruder_count_mismatch(MachineObject* obj);
void update_misc_ctrl(MachineObject *obj);
void update_ams(MachineObject* obj);
void update_rack(MachineObject* obj);
+2 -2
View File
@@ -1576,7 +1576,7 @@ bool SyncAmsInfoDialog::is_nozzle_type_match(DevExtderSystem data, wxString &err
}
}
vector<int> map_extruders = {1, 0};
std::vector<int> map_extruders = {1, 0};
// The default two extruders are left, right, but the order of the extruders on the machine is right, left.
std::vector<std::string> flow_type_of_machine;
@@ -1676,7 +1676,7 @@ void SyncAmsInfoDialog::stripWhiteSpace(std::string &str)
{
if (str == "") { return; }
string::iterator cur_it;
std::string::iterator cur_it;
cur_it = str.begin();
while (cur_it != str.end()) {
+242 -105
View File
@@ -811,6 +811,12 @@ wxString Tab::translate_category(const wxString& title, Preset::Type preset_type
}
return _("Extruder") + title.SubString(8, title.Last());
}
// Orca: one "Extruder" page serves all extruders; name it "Extruders" when there are several.
if (preset_type == Preset::TYPE_PRINTER && title == "Extruder") {
auto preset = wxGetApp().preset_bundle;
if (preset && preset->get_printer_extruder_count() > 1)
return _("Extruders");
}
return _(title);
}
@@ -1153,6 +1159,38 @@ std::string Tab::options_list_storage_key(const std::string& opt_key) const
return (serialized || is_plugin_field) ? opt_key : opt_key + "#0";
}
// Orca: deep_diff() flags every vector entry at or past the reference vector's length as changed,
// whatever its value (e.g. the values of an extruder added by raising the extruder count). A vector
// grows by copying its first entry (ConfigOptionVector::resize), so such an entry only counts as
// changed when it differs from the reference's first entry, as before Orca's deep_diff() change.
// The change of the count itself shows on "extruders_count".
static void drop_unchanged_added_entries(std::vector<std::string> &options, const DynamicPrintConfig &current, const Preset *reference)
{
if (reference == nullptr)
return;
// deep_diff() lists a key's entries one after another, so serialize each key's vectors once.
std::string serialized_key;
std::vector<std::string> cur_values;
std::string ref_first;
options.erase(std::remove_if(options.begin(), options.end(), [&](const std::string &opt) {
const auto pos = opt.find('#');
if (pos == std::string::npos)
return false;
const std::string key = opt.substr(0, pos);
const size_t idx = size_t(std::atoi(opt.c_str() + pos + 1));
auto ref = dynamic_cast<const ConfigOptionVectorBase *>(reference->config.option(key));
auto cur = dynamic_cast<const ConfigOptionVectorBase *>(current.option(key));
if (ref == nullptr || cur == nullptr || idx < ref->size() || ref->size() == 0 || idx >= cur->size())
return false;
if (key != serialized_key) {
serialized_key = key;
cur_values = cur->vserialize();
ref_first = ref->vserialize().front();
}
return cur_values[idx] == ref_first;
}), options.end());
}
void Tab::update_all_extruder_options_status()
{
if (!m_extruder_switch && !m_variant_combo) {
@@ -1183,6 +1221,8 @@ void Tab::update_all_extruder_options_status()
auto dirty_options = m_presets->current_dirty_options(true);
auto nonsys_options = m_presets->current_different_from_parent_options(true);
if (m_type == Preset::TYPE_PRINTER)
update_custom_dirty(dirty_options, nonsys_options);
auto filter_extruder_options = [](const std::vector<std::string>& options) {
std::vector<std::string> filtered_options;
for (const auto& opt : options) {
@@ -1231,7 +1271,7 @@ void Tab::update_extruder_switch_colors()
if (m_active_page) {
if (m_active_page->title() == "Speed" || m_active_page->title() == "Motion ability" || m_active_page->title() == "Filament" ||
m_active_page->title() == "Setting Overrides" || m_active_page->title() == "Multimaterial") {
m_active_page->title() == "Setting Overrides" || m_active_page->title() == "Multimaterial" || is_printer_extruder_page(m_active_page)) {
for (auto page_ptr : m_pages) {
if (page_ptr.get() == m_active_page) {
pages_to_check.push_back(page_ptr);
@@ -1262,8 +1302,8 @@ void Tab::update_extruder_switch_colors()
void Tab::check_extruder_options_status(int index, bool &sys_extruder, bool &modified_extruder, const std::vector<PageShp>& pages_to_check)
{
int config_index = index;
int extruder_id = index;
if (m_type == Preset::TYPE_PRINT || m_type == Preset::TYPE_PRINTER || m_type == Preset::TYPE_MODEL) {
int extruder_id;
NozzleVolumeType nozzle_type;
parse_extruder_selection(index, extruder_id, nozzle_type);
@@ -1296,6 +1336,10 @@ void Tab::check_extruder_options_status(int index, bool &sys_extruder, bool &mod
}
std::string target_opt_key = base_opt_key + "#" + std::to_string(config_index * stride);
// Orca: on the printer's Extruder page only per-variant options use the variant column,
// the others (nozzle_diameter, extruder_offset, ...) are indexed by the extruder.
if (is_printer_extruder_page(page.get()) && printer_options_with_variant_1.count(base_opt_key) == 0)
target_opt_key = base_opt_key + "#" + std::to_string(extruder_id);
auto status_iter = m_all_extruder_options_status.find(target_opt_key);
if (status_iter != m_all_extruder_options_status.end()) {
@@ -1336,18 +1380,6 @@ void TabPrinter::init_options_list()
Tab::init_options_list();
if (m_printer_technology == ptFFF)
m_options_list.emplace("extruders_count", m_opt_status_value);
for (size_t i = 1; i < m_extruders_count; ++i) {
wxString target_title = wxString::Format("Extruder %d", int(i + 1));
for (auto &page : m_pages) {
if (page->title() == target_title) {
for (auto group : page->m_optgroups) {
for (auto &opt : group->opt_map())
m_options_list.emplace(opt.first, m_opt_status_value);
}
break;
}
}
}
}
void TabPrinter::msw_rescale()
@@ -1437,7 +1469,7 @@ void Tab::update_changed_tree_ui()
get_sys_and_mod_flags("compatible_printers", sys_page, modified_page);
}
}
if (page->title() == "Speed" || page->title() == "Motion ability" || page->title() == "Filament" || page->title() == "Setting Overrides" || page->title() == "Multimaterial") {
if (page->title() == "Speed" || page->title() == "Motion ability" || page->title() == "Filament" || page->title() == "Setting Overrides" || page->title() == "Multimaterial" || is_printer_extruder_page(page.get())) {
auto options = generate_extruder_options();
for (size_t switch_index = 0; switch_index < options.size(); ++switch_index) {
std::vector<PageShp> pages_to_check = { page };
@@ -1540,6 +1572,10 @@ void Tab::on_roll_back_value(const bool to_sys /*= true*/)
m_postpone_update_ui = false;
// Orca: the restored config may have another extruder count than the tab shows.
if (auto printer_tab = dynamic_cast<TabPrinter *>(this))
printer_tab->sync_extruders_count();
// When all values are rolled, then we have to update whole tab in respect to the reverted values
update();
if (m_active_page)
@@ -1877,6 +1913,17 @@ static wxString pad_combo_value_for_config(const DynamicPrintConfig &config)
return config.opt_bool("pad_enable") ? (config.opt_bool("pad_around_object") ? _("Around object") : _("Below object")) : _("None");
}
// Rebuilds the variant switch of every tab that has one, e.g. after the extruder count or a nozzle volume type changed.
static void update_all_extruder_variants(int extruder_idx = -1)
{
for (auto tab : wxGetApp().tabs_list)
tab->update_extruder_variants(extruder_idx);
if (auto tab = wxGetApp().plate_tab)
tab->update_extruder_variants(extruder_idx);
for (auto tab : wxGetApp().model_tabs_list)
tab->update_extruder_variants(extruder_idx);
}
void Tab::on_value_change(const std::string& opt_key, const boost::any& value)
{
// Orca:
@@ -2257,7 +2304,7 @@ void Tab::on_value_change(const std::string& opt_key, const boost::any& value)
wxGetApp().plater()->update();
}
string opt_key_without_idx = opt_key.substr(0, opt_key.find('#'));
std::string opt_key_without_idx = opt_key.substr(0, opt_key.find('#'));
if (opt_key_without_idx == "long_retractions_when_cut") {
unsigned char activate = boost::any_cast<unsigned char>(value);
@@ -2319,15 +2366,7 @@ void Tab::on_value_change(const std::string& opt_key, const boost::any& value)
if (opt_key.find("nozzle_volume_type") != std::string::npos) {
int extruder_idx = std::atoi(opt_key.substr(opt_key.find_last_of('#') + 1).c_str());
for (auto tab : wxGetApp().tabs_list) {
tab->update_extruder_variants(extruder_idx);
}
if (auto tab = wxGetApp().plate_tab) {
tab->update_extruder_variants(extruder_idx);
}
for (auto tab : wxGetApp().model_tabs_list) {
tab->update_extruder_variants(extruder_idx);
}
update_all_extruder_variants(extruder_idx);
if (wxGetApp().app_config->get("auto_calculate_flush") == "all") {
wxGetApp().plater()->sidebar().auto_calc_flushing_volumes(-1,extruder_idx);
}
@@ -5685,9 +5724,13 @@ if (is_marlin_flavor)
size_t extruders_count = size_t(boost::any_cast<int>(v));
wxTheApp->CallAfter([this, opt_key, value, extruders_count]() {
if (opt_key == "extruders_count" || opt_key == "single_extruder_multi_material") {
const size_t old_extruders_count = m_extruders_count;
extruders_count_changed(extruders_count);
init_options_list(); // m_options_list should be updated before UI updating
update_dirty();
// Orca: the variant switches (here, Process in the sidebar, ...) list one option per extruder.
if (m_extruders_count != old_extruders_count)
update_all_extruder_variants();
if (opt_key == "single_extruder_multi_material") { // the single_extruder_multimaterial was added to force pages
on_value_change(opt_key, value); // rebuild - let's make sure the on_value_change is not skipped
@@ -5697,6 +5740,7 @@ if (is_marlin_flavor)
// Orca: we use a different logic here. If SEMM is enabled, we set extruder count to 1.
#if 1
extruders_count_changed(1);
update_all_extruder_variants();
#else
std::vector<double> nozzle_diameters =
@@ -5768,14 +5812,13 @@ if (is_marlin_flavor)
m_pages.insert(m_pages.end() - n_after_single_extruder_MM, page);
}
// Orca: build missed extruder pages
for (auto extruder_idx = m_extruders_count_old; extruder_idx < m_extruders_count; ++extruder_idx) {
const wxString& page_name = (m_extruders_count > 1) ? wxString::Format("Extruder %d", int(extruder_idx + 1)) : wxString::Format("Extruder");
//# build page
//const wxString& page_name = wxString::Format("Extruder %d", int(extruder_idx + 1));
auto page = add_options_page(page_name, "custom-gcode_extruder", true); // ORCA: icon only visible on placeholders
m_pages.insert(m_pages.begin() + n_before_extruders + extruder_idx, page);
// Orca: a single "Extruder" page serves all extruders. Its controls are created once for extruder 0;
// switch_excluder() re-targets them to the extruder selected on the variant switch (m_extruder_switch),
// which the printer tab shows on this page too.
if (extruder_page() == nullptr && m_extruders_count > 0) {
const size_t extruder_idx = 0;
auto page = add_options_page(L("Extruder"), "custom-gcode_extruder", true); // ORCA: icon only visible on placeholders
m_pages.insert(m_pages.begin() + n_before_extruders, page);
auto optgroup = page->new_optgroup(L("Basic information"), L"param_information", -1, true);
optgroup->append_single_option_line("nozzle_diameter", "printer_extruder_basic_information#nozzle-diameter", extruder_idx);
@@ -5788,8 +5831,10 @@ if (is_marlin_flavor)
option.opt.full_width = true;
optgroup->append_single_option_line(option, "printer_extruder_basic_information#extruder-offset-position");
optgroup->m_on_change = [this, extruder_idx](const t_config_option_key& opt_key, boost::any value)
optgroup->m_on_change = [this](const t_config_option_key& opt_key, boost::any value)
{
// The page edits the extruder selected on the variant switch.
const size_t extruder_idx = size_t(get_current_active_extruder());
bool is_SEMM = m_config->opt_bool("single_extruder_multi_material");
if (is_SEMM && m_extruders_count > 1 && boost::starts_with(opt_key, "nozzle_diameter"))
{
@@ -5884,22 +5929,22 @@ if (is_marlin_flavor)
//optgroup->append_line(line);
#endif
}
// BBS. No extra extruder page for single physical extruder machine
// # remove extra pages
auto &first_extruder_title = const_cast<wxString &>(m_pages[n_before_extruders]->title());
if (m_extruders_count < m_extruders_count_old) {
m_pages.erase( m_pages.begin() + n_before_extruders + m_extruders_count,
m_pages.begin() + n_before_extruders + m_extruders_count_old);
if (m_extruders_count == 1)
first_extruder_title = wxString::Format("Extruder");
} else if (m_extruders_count_old == 1) {
first_extruder_title = wxString::Format("Extruder %d", 1);
}
auto & index = wxGetApp().sidebar().settings_index();
for (auto &group : m_pages[n_before_extruders]->m_optgroups) {
group->set_config_category_and_type(first_extruder_title, m_type);
for (auto &opt : group->opt_map())
index.add_key(opt.first + "#0", m_type, group->title, first_extruder_title, group->icon);
// The page holds only the "#0" controls, so register every extruder's options with the search
// index under "Extruder N"; a search hit selects that extruder (TabPrinter::activate_option()).
if (Page *page = extruder_page()) {
auto &index = wxGetApp().sidebar().settings_index();
for (auto &group : page->m_optgroups) {
group->set_config_category_and_type(page->title(), m_type);
for (auto &opt : group->opt_map()) {
if (opt.second.second < 0)
continue;
for (size_t i = 0; i < m_extruders_count; ++i) {
const wxString category = m_extruders_count > 1 ? wxString::Format("Extruder %d", int(i + 1)) : wxString("Extruder");
index.add_key(opt.second.first + "#" + std::to_string(i), m_type, group->title, category, group->icon);
}
}
}
}
Thaw();
@@ -6180,12 +6225,6 @@ void TabPrinter::toggle_options()
return;
auto nozzle_volumes = m_preset_bundle->project_config.option<ConfigOptionEnumsGeneric>("nozzle_volume_type");
auto extruders = m_config->option<ConfigOptionEnumsGeneric>("extruder_type");
auto get_index_for_extruder =
[this, &extruders](int extruder_id, int stride = 1) {
return m_config->get_index_for_extruder(extruder_id + 1, "printer_extruder_id",
ExtruderType(extruders->values[extruder_id]), get_actual_nozzle_volume_type(extruder_id), "printer_extruder_variant", stride);
};
//BBS: whether the preset is Bambu Lab printer
bool is_BBL_printer = false;
@@ -6260,14 +6299,13 @@ void TabPrinter::toggle_options()
toggle_option("tool_change_on_wipe_tower", !bSEMM && supports_wipe_tower_2 && extruders_count > 1);
toggle_option("wait_for_temp_on_wipe_tower", !bSEMM && supports_wipe_tower_2 && extruders_count > 1);
}
wxString extruder_number;
long val = 1;
if ( m_active_page->title().IsSameAs(L("Extruder")) ||
(m_active_page->title().StartsWith("Extruder ", &extruder_number) && extruder_number.ToLong(&val) &&
val > 0 && (size_t)val <= m_extruders_count))
if (m_active_page->title() == L("Extruder") && m_extruders_count > 0)
{
size_t i = size_t(val - 1);
int variant_index = get_index_for_extruder(i);
// Orca: the single Extruder page edits the extruder selected on the variant switch; its
// controls carry field index 0 (i), the values are read for `extruder`.
const size_t i = 0;
const size_t extruder = std::min<size_t>(size_t(get_current_active_extruder()), m_extruders_count - 1);
const int variant_index = extruder_variant_index(int(extruder));
bool have_retract_length = m_config->opt_float("retraction_length", variant_index) > 0;
toggle_option("extruder_printable_area", false, i); // disable
@@ -6294,7 +6332,7 @@ void TabPrinter::toggle_options()
vec.resize(0);
vec = {"retract_lift_above", "retract_lift_below", "retract_lift_enforce"};
for (auto el : vec)
toggle_option(el, retraction && (m_config->opt_float("z_hop", i) > 0), i);
toggle_option(el, retraction && (m_config->opt_float("z_hop", variant_index) > 0), i);
// some options only apply when not using firmware retraction
vec.resize(0);
@@ -6347,7 +6385,7 @@ void TabPrinter::toggle_options()
toggle_option("long_retractions_when_cut", !use_firmware_retraction && m_config->opt_int("enable_long_retraction_when_cut"), i);
toggle_line("retraction_distances_when_cut", m_config->opt_bool("long_retractions_when_cut", variant_index), i);
toggle_option("travel_slope", m_config->opt_enum("z_hop_types", i) != ZHopType::zhtNormal, i);
toggle_option("travel_slope", m_config->opt_enum("z_hop_types", variant_index) != ZHopType::zhtNormal, i);
}
if (m_active_page->title() == L("Motion ability")) {
@@ -6421,12 +6459,16 @@ void TabPrinter::on_value_change(const std::string& opt_key, const boost::any& v
return;
const int pos = opt_key.find("#");
// Orca: fields of the single Extruder page keep index 0 but edit the selected extruder.
const int data_idx = pos > 0 ? extruder_page_data_index(opt_key) : -1;
if (pos > 0) {
std::string temp_str = opt_key;
boost::erase_head(temp_str, pos + 1);
int orig_opt_idx = static_cast<size_t>(atoi(temp_str.c_str()));
int opt_idx = orig_opt_idx >= 0 ? orig_opt_idx : 0;
if (data_idx >= 0)
opt_idx = data_idx;
std::string opt_key_pure = opt_key;
boost::erase_tail(opt_key_pure, opt_key_pure.size() - pos);
@@ -6461,7 +6503,12 @@ void TabPrinter::on_value_change(const std::string& opt_key, const boost::any& v
}
}
Tab::on_value_change(opt_key, value);
// Orca: report Extruder page changes as "key#<extruder>", as the former "Extruder N" pages did,
// so per-extruder handling in Tab::on_value_change() sees the extruder that was edited.
if (data_idx >= 0)
Tab::on_value_change(opt_key.substr(0, pos) + "#" + std::to_string(get_current_active_extruder()), value);
else
Tab::on_value_change(opt_key, value);
}
void TabPrinter::update()
@@ -7393,12 +7440,7 @@ bool Tab::tree_sel_change_delayed(wxCommandEvent& event)
// update_undo_buttons();
this->OnActivate();
m_parent->set_active_tab(this);
if (m_variant_sizer) {
wxWindow *variant_ctrl = m_extruder_switch ? (wxWindow *) m_extruder_switch : m_variant_combo;
m_main_sizer->Show(m_variant_sizer, variant_ctrl->IsThisEnabled() && !m_active_page->m_opt_id_map.empty() && !m_active_page->title().StartsWith("Extruder "));
if (m_extruder_sync) m_extruder_sync->Show(variant_ctrl->IsShown());
GetParent()->Layout();
}
update_variant_sizer_visibility();
m_page_view->Thaw();
return false;
@@ -7409,12 +7451,7 @@ bool Tab::tree_sel_change_delayed(wxCommandEvent& event)
return false;
m_active_page = page;
if (m_variant_sizer) {
wxWindow *variant_ctrl = m_extruder_switch ? (wxWindow *) m_extruder_switch : m_variant_combo;
m_main_sizer->Show(m_variant_sizer, variant_ctrl->IsThisEnabled() && !m_active_page->m_opt_id_map.empty() && !m_active_page->title().StartsWith("Extruder"));
if (m_extruder_sync) m_extruder_sync->Show(variant_ctrl->IsShown());
GetParent()->Layout();
}
update_variant_sizer_visibility();
auto throw_if_canceled = std::function<void()>([this](){
#ifdef WIN32
@@ -8240,9 +8277,11 @@ void Tab::update_extruder_variants(int extruder_id, bool reload)
m_actual_nozzle_volumes.resize(extruder_nums, NozzleVolumeType::nvtStandard);
for (int i = 0; i < extruder_nums; i++) m_actual_nozzle_volumes[i] = (NozzleVolumeType)nozzle_volumes->values[i];
// Orca: a non-Bambu dual-nozzle printer has two extruders but a single variant column, so
// the nozzle switch and sync button have nothing to act on. Only enable with real variants.
if (extruder_nums >= 2 && m_preset_bundle->support_different_extruders()) {
// Orca: when every extruder uses the same variant (e.g. a non-Bambu dual-nozzle printer), the
// switch has no nozzle variants to select and nothing to sync. The printer tab still enables it
// to choose the extruder its Extruder and Motion ability pages edit.
m_extruder_switch_variants = extruder_nums >= 2 && m_preset_bundle->support_different_extruders();
if (m_extruder_switch_variants || (m_type == Preset::TYPE_PRINTER && extruder_nums >= 2)) {
auto options = generate_extruder_options();
m_extruder_switch->SetOptions(options);
@@ -8281,12 +8320,8 @@ void Tab::update_extruder_variants(int extruder_id, bool reload)
if (m_type == Preset::TYPE_PRINT) {
update_pages_with_multi_variant();
}
if (m_variant_sizer) {
wxWindow *variant_ctrl = m_extruder_switch ? (wxWindow *) m_extruder_switch : m_variant_combo;
m_main_sizer->Show(m_variant_sizer, variant_ctrl->IsThisEnabled() && m_active_page && !m_active_page->m_opt_id_map.empty() && !m_active_page->title().StartsWith("Extruder "));
if (m_extruder_sync) m_extruder_sync->Show(variant_ctrl->IsShown());
GetParent()->Layout();
}
update_extruder_switch_colors();
update_variant_sizer_visibility();
}
// The variant switch tags are the narrowest place a volume type is named, so they abbreviate it;
@@ -8361,12 +8396,21 @@ std::vector<wxString> Tab::generate_extruder_options()
return options;
}
// Orca: the printer tab has one tag per extruder, named as its former "Extruder N" pages were.
// parse_extruder_selection() counts a hybrid extruder as two tags; that still lines up because
// only the last extruder can be hybrid.
if (m_type == Preset::TYPE_PRINTER) {
for (int i = 0; i < extruder_nums; ++i)
options.push_back(translate_category(wxString::Format("Extruder %d", i + 1), m_type));
return options;
}
std::string pt = m_preset_bundle->printers.get_edited_preset().get_printer_type(m_preset_bundle);
// Orca: the main/deputy toolhead names describe a dual-nozzle printer, where extruder 0 is the
// left (deputy) and extruder 1 the right (main) nozzle. From three extruders on the tools are
// interchangeable, so name them by index instead of repeating one side.
// Orca: the main/deputy toolhead names describe a Bambu dual-nozzle printer, where extruder 0 is
// the left (deputy) and extruder 1 the right (main) nozzle. Other printers number their tools.
const bool toolhead_names = extruder_nums == 2 && m_preset_bundle->is_bbl_vendor();
for (int i = 0; i < extruder_nums; ++i) {
wxString extruder_name = extruder_nums > 2 ? wxString::Format("T%d", i + 1) :
wxString extruder_name = !toolhead_names ? wxString::Format("T%d", i + 1) :
_L(DevPrinterConfigUtil::get_toolhead_display_name(
pt, (i == 0) ? DEPUTY_EXTRUDER_ID : MAIN_EXTRUDER_ID,
ToolHeadComponent::Nozzle, ToolHeadNameCase::TitleCase, true));
@@ -8383,6 +8427,71 @@ std::vector<wxString> Tab::generate_extruder_options()
return options;
}
Page *TabPrinter::extruder_page() const
{
for (const PageShp &page : m_pages)
if (page->title() == "Extruder")
return page.get();
return nullptr;
}
int TabPrinter::extruder_page_data_index(const std::string &field_id) const
{
if (Page *page = extruder_page())
for (const auto &group : page->m_optgroups)
if (auto it = group->opt_map().find(field_id); it != group->opt_map().end())
return it->second.second;
return -1;
}
int TabPrinter::extruder_variant_index(int extruder)
{
const auto *extruders = m_config->option<ConfigOptionEnumsGeneric>("extruder_type");
const int index = extruder < int(extruders->size()) ?
m_config->get_index_for_extruder(extruder + 1, "printer_extruder_id", ExtruderType(extruders->values[extruder]),
get_actual_nozzle_volume_type(extruder), "printer_extruder_variant") :
-1;
return index < 0 ? extruder : index;
}
void TabPrinter::update_custom_dirty(std::vector<std::string> &dirty_options, std::vector<std::string> &nonsys_options)
{
drop_unchanged_added_entries(dirty_options, *m_config, &m_presets->get_selected_preset());
drop_unchanged_added_entries(nonsys_options, *m_config, m_presets->get_selected_preset_parent());
}
void TabPrinter::sync_extruders_count()
{
if (m_printer_technology != ptFFF)
return;
const auto *nozzle_diameter = m_config->option<ConfigOptionFloats>("nozzle_diameter");
if (nozzle_diameter == nullptr || nozzle_diameter->size() == m_extruders_count)
return;
extruders_count_changed(nozzle_diameter->size());
init_options_list();
update_all_extruder_variants();
}
void TabPrinter::activate_option(const std::string &opt_key, const wxString &category)
{
wxString number;
long n = 0;
const bool numbered = category.StartsWith("Extruder ", &number) && number.ToLong(&n);
if (extruder_page() == nullptr || (!numbered && category != "Extruder")) {
Tab::activate_option(opt_key, category);
return;
}
// Selecting fires the switch's event, which re-targets the page (switch_excluder()).
const int extruder_idx = (n >= 1 && n <= long(m_extruders_count)) ? int(n - 1) : 0;
if (m_extruder_switch && m_extruder_switch->IsThisEnabled() && extruder_idx != get_current_active_extruder())
m_extruder_switch->SetSelection(calculate_selection_index_for_extruder(extruder_idx, get_actual_nozzle_volume_type(extruder_idx)));
// The page's controls are created for index 0.
const auto pos = opt_key.find('#');
Tab::activate_option(pos == std::string::npos ? opt_key : opt_key.substr(0, pos) + "#0", "Extruder");
}
NozzleVolumeType Tab::get_actual_nozzle_volume_type(int extruder_id)
{
int extruder_count = m_preset_bundle->get_printer_extruder_count();
@@ -8459,18 +8568,44 @@ bool Tab::get_extruder_sync_enable_state(int extruder_id)
return false;
}
bool Tab::variant_switch_active() const
{
if (m_extruder_switch)
return m_extruder_switch->IsThisEnabled();
return m_variant_combo && m_variant_combo->IsThisEnabled();
}
void Tab::update_variant_sizer_visibility()
{
if (!m_variant_sizer)
return;
const bool show = variant_switch_active() && m_active_page && !m_active_page->m_opt_id_map.empty();
m_main_sizer->Show(m_variant_sizer, show);
if (m_extruder_sync) {
m_extruder_sync->Show(show);
// Orca: copying between extruders is offered only between nozzle variants, and not on the Extruder page.
m_extruder_sync->Enable(m_extruder_switch_variants && !is_printer_extruder_page(m_active_page) &&
get_extruder_sync_enable_state(get_current_active_extruder()));
}
GetParent()->Layout();
}
void Tab::switch_excluder(int extruder_id, bool reload)
{
Preset & printer_preset = m_preset_bundle->printers.get_edited_preset();
auto nozzle_volumes = m_preset_bundle->project_config.option<ConfigOptionEnumsGeneric>("nozzle_volume_type");
auto extruders = printer_preset.config.option<ConfigOptionEnumsGeneric>("extruder_type");
// Orca: the printer tab re-targets its pages to the extruder selected on the switch. Resolved before
// the range check below, which keeps the extruder_type read in get_index_for_extruder in range.
if (m_type == Preset::TYPE_PRINTER && extruder_id == -1)
extruder_id = get_current_active_extruder();
if (!m_variant_combo && (extruder_id >= (int)nozzle_volumes->size() || extruder_id >= (int)extruders->size()))
extruder_id = 0;
if (m_extruder_switch) {
int current_extruder = get_current_active_extruder();
bool sync_enable = get_extruder_sync_enable_state(current_extruder);
m_extruder_sync->Enable(m_extruder_switch->IsThisEnabled() && sync_enable);
m_extruder_sync->Enable(m_extruder_switch_variants && sync_enable && !is_printer_extruder_page(m_active_page));
m_extruder_sync->Show();
if (m_type != Preset::TYPE_PRINTER) {
if (extruder_id == -1)
@@ -8497,24 +8632,26 @@ void Tab::switch_excluder(int extruder_id, bool reload)
return;
if (m_extruder_switch) m_extruder_switch->SetClientData(reinterpret_cast<void*>(static_cast<std::uintptr_t>(index)));
if (m_variant_combo) m_variant_combo->SetClientData(reinterpret_cast<void *>(static_cast<std::uintptr_t>(index)));
wxWindow *variant_ctrl = m_extruder_switch ? (wxWindow *) m_extruder_switch : m_variant_combo;
for (auto page : m_pages) {
bool is_extruder = false;
int page_index = index;
if (m_type == Preset::TYPE_PRINTER) {
if (page->title().StartsWith("Extruder")) {
int extruder_id2 = std::atoi(page->title().Mid(9).ToUTF8()) - 1;
if (extruder_id >= 0 && extruder_id2 != extruder_id)
continue;
if (extruder_id2 > 0)
index = get_index_for_extruder(extruder_id2);
if (page->title() == "Extruder")
is_extruder = true;
} else if (page->title().StartsWith("Motion ability")) {
index = get_index_for_extruder(extruder_id == -1 ? 0 : extruder_id, 2);
}
else if (page->title().StartsWith("Motion ability"))
page_index = get_index_for_extruder(extruder_id == -1 ? 0 : extruder_id, 2);
}
page->m_opt_id_map.clear();
for (auto group : page->m_optgroups) {
for (auto &opt : group->opt_map()) {
if (is_extruder && opt.second.second >= 0) {
// Per-variant options use the variant column, the others (nozzle_diameter,
// extruder_offset, ...) are sized by the extruder count and use the extruder.
const int idx = printer_options_with_variant_1.count(opt.second.first) > 0 ? page_index : extruder_id;
const_cast<int &>(opt.second.second) = idx;
page->m_opt_id_map.insert({opt.second.first + "#" + std::to_string(idx), opt.first});
continue;
}
auto iter = std::find(printer_extruder_options.begin(), printer_extruder_options.end(), opt.second.first);
if (iter != printer_extruder_options.end()) {
page->m_opt_id_map.insert({opt.first, opt.first});
@@ -8522,9 +8659,9 @@ void Tab::switch_excluder(int extruder_id, bool reload)
}
if (opt.second.second >= 0) {
const_cast<int &>(opt.second.second) = index;
page->m_opt_id_map.insert({opt.second.first + "#" + std::to_string(index), opt.first});
group->draw_multi_extruder = !is_extruder && variant_ctrl->IsThisEnabled();
const_cast<int &>(opt.second.second) = page_index;
page->m_opt_id_map.insert({opt.second.first + "#" + std::to_string(page_index), opt.first});
group->draw_multi_extruder = variant_switch_active();
}
}
}
+31 -1
View File
@@ -331,6 +331,10 @@ public:
MultiSwitchButton * m_variant_combo = nullptr;
ScalableButton *m_extruder_sync = nullptr;
wxPanel * m_extruder_sync_box = nullptr;
// Orca: whether m_extruder_switch switches nozzle variants (it then offers sync between them).
// The printer tab also enables the switch for printers without variants, to choose the extruder
// its Extruder and Motion ability pages edit.
bool m_extruder_switch_variants = false;
std::vector<NozzleVolumeType> m_actual_nozzle_volumes;
public:
@@ -387,6 +391,13 @@ public:
void update_changed_tree_ui();
void update_undo_buttons();
void update_extruder_switch_colors();
// Whether the variant switch (m_extruder_switch / m_variant_combo) is enabled: on the printer tab for
// any multi-extruder printer, on the other tabs when it switches nozzle variants.
bool variant_switch_active() const;
// Orca: whether `page` is the printer tab's single "Extruder" page, which edits the extruder selected on the switch.
bool is_printer_extruder_page(const Page* page) const { return m_type == Preset::TYPE_PRINTER && page && page->title() == "Extruder"; }
// Shows the variant switch row on pages with options that follow it.
void update_variant_sizer_visibility();
void update_all_extruder_options_status();
void check_extruder_options_status(int index, bool &sys_extruder, bool &modified_extruder, const std::vector<PageShp>& pages_to_check);
@@ -444,7 +455,7 @@ public:
virtual void on_value_change(const std::string& opt_key, const boost::any& value);
void update_wiping_button_visibility();
void activate_option(const std::string& opt_key, const wxString& category);
virtual void activate_option(const std::string& opt_key, const wxString& category);
void apply_searcher();
void cache_config_diff(const std::vector<std::string>& selected_options, const DynamicPrintConfig* config = nullptr);
void apply_config_from_cache();
@@ -710,6 +721,25 @@ public:
void cache_extruder_cnt(const DynamicPrintConfig* config = nullptr);
bool apply_extruder_cnt_from_cache();
void refresh_printer_agent_dropdown() const;
// Orca: a single "Extruder" page for all extruders. Its controls are created once (index 0) and
// switch_excluder() re-targets them to the extruder selected on m_extruder_switch.
Page* extruder_page() const;
// Config index an "Extruder" page field (e.g. "retraction_length#0") currently edits, -1 if not on that page.
int extruder_page_data_index(const std::string& field_id) const;
// Config index of an extruder's per-variant options (Retraction, Z-Hop, ...): its variant column
// for the nozzle selected on the switch, or the extruder index on a printer without variants.
int extruder_variant_index(int extruder);
// After the config was restored (roll back): follow its extruder count, if it differs.
void sync_extruders_count();
// Search jump to "Extruder N" / "key#N": selects extruder N on the switch, then activates the
// page's own field on the "Extruder" page.
void activate_option(const std::string& opt_key, const wxString& category) override;
protected:
// Orca: values of extruders added by raising the extruder count have no saved / system value to
// revert to; the change is shown on "extruders_count", not on each of their parameters.
void update_custom_dirty(std::vector<std::string> &dirty_options, std::vector<std::string> &nonsys_options) override;
};
class TabSLAMaterial : public Tab
+5 -5
View File
@@ -1682,13 +1682,13 @@ int GuideFrame::SaveProfileData()
return 0;
}
void StringReplace(string &strBase, string strSrc, string strDes)
void StringReplace(std::string &strBase, std::string strSrc, std::string strDes)
{
string::size_type pos = 0;
string::size_type srcLen = strSrc.size();
string::size_type desLen = strDes.size();
std::string::size_type pos = 0;
std::string::size_type srcLen = strSrc.size();
std::string::size_type desLen = strDes.size();
pos = strBase.find(strSrc, pos);
while ((pos != string::npos)) {
while ((pos != std::string::npos)) {
strBase.replace(pos, srcLen, strDes);
pos = strBase.find(strSrc, (pos + desLen));
}
+463 -20
View File
@@ -244,7 +244,7 @@ AMSControl::AMSControl(wxWindow *parent, wxWindowID id, const wxPoint &pos, cons
m_sizer_ams_body->Add(m_sizer_ams_area_right, wxALIGN_CENTER, 0);
m_sizer_body->Add(m_sizer_ams_items, 0, wxALIGN_CENTER, 0);
m_sizer_body->Add(0, 0, 1, wxEXPAND | wxTOP, FromDIP(10));
m_sizer_ams_gap = m_sizer_body->Add(0, 0, 1, wxEXPAND | wxTOP, FromDIP(10));
m_sizer_body->Add(m_sizer_ams_body, 0, wxALIGN_CENTER, 0);
m_sizer_body->Add(m_sizer_down_road, 0, wxALIGN_CENTER, 0);
m_sizer_body->Add(m_sizer_ams_option, 0, wxEXPAND, 0);
@@ -371,7 +371,7 @@ bool AMSControl::IsAmsInRightPanel(std::string ams_id) {
return false;
}
}
else{
else if (m_total_ext_count == 1){
for (auto id : m_item_ids[MAIN_EXTRUDER_ID]){
if (id == ams_id){
return true;
@@ -379,6 +379,10 @@ bool AMSControl::IsAmsInRightPanel(std::string ams_id) {
}
return false;
}
else {
// Generic N Nozzles
}
return false;
}
void AMSControl::AmsSelectedSwitch(wxCommandEvent& event) {
@@ -519,7 +523,7 @@ void AMSControl::msw_rescale()
m_button_ams_setting_press.msw_rescale();
m_button_ams_setting->SetBitmap(m_button_ams_setting_normal.bmp());
m_extruder->msw_rescale();
if (m_extruder) m_extruder->msw_rescale();
if (m_button_extruder_feed) m_button_extruder_feed->Rescale(); // ORCA
if (m_button_extruder_back) m_button_extruder_back->Rescale(); // ORCA
@@ -603,6 +607,8 @@ void AMSControl::CreateAms()
void AMSControl::ClearAms() {
m_sizer_ams_items->Clear(false);
m_simplebook_ams_right->DeleteAllPages();
m_simplebook_ams_left->DeleteAllPages();
m_simplebook_ams_right->DestroyChildren();
@@ -613,7 +619,7 @@ void AMSControl::ClearAms() {
m_simplebook_ams_left->Refresh();
for (auto it : m_ams_preview_list) {
delete it.second;
if (it.second) it.second->Destroy();
}
m_ams_preview_list.clear();
m_ext_image_list.clear();
@@ -624,12 +630,167 @@ void AMSControl::ClearAms() {
m_ams_item_list.clear();
m_sizer_prv_right->Clear();
m_sizer_prv_left->Clear();
m_item_ids = { {}, {} };
if (m_nozzle_book) {
m_nozzle_book->Destroy();
m_nozzle_book = nullptr;
} else {
for (auto &pane : m_nozzle_panes) {
if (pane.preview_panel) pane.preview_panel->Destroy();
if (pane.ams_book) pane.ams_book->Destroy();
}
}
m_item_ids.assign(std::max(m_total_ext_count, 2), {});
m_nozzle_panes.clear();
m_active_nozzle_id = 0;
m_current_ams.clear();
m_current_show_ams_left.clear();
m_current_show_ams_right.clear();
pair_id.clear();
}
void AMSControl::restore_legacy_ams_layout()
{
m_sizer_ams_items->Clear(false);
m_sizer_ams_items->SetOrientation(wxHORIZONTAL);
m_sizer_body->GetItem(m_sizer_ams_items)->SetFlag(wxALIGN_CENTER);
m_sizer_ams_items->Add(m_panel_prv_left, 0, wxLEFT | wxRIGHT, FromDIP(5));
m_sizer_ams_items->Add(m_panel_prv_right, 0, wxLEFT | wxRIGHT, FromDIP(5));
m_panel_prv_left->Show();
m_panel_prv_right->Show();
m_simplebook_ams_left->Show();
m_simplebook_ams_right->Show();
m_panel_down_road->Show();
m_sizer_body->Show(m_sizer_ams_body, true);
m_sizer_option_mid->Show(m_extruder, true);
m_down_road->SetSingleSideLayout(false);
m_sizer_ams_gap->SetProportion(1);
m_sizer_ams_gap->SetBorder(FromDIP(10));
m_down_road->SetNozzleCount(m_total_ext_count);
}
void AMSControl::CreateAmsMultiNozzle(const std::string &series_name, const std::string &printer_type) {
const size_t pane_count = static_cast<size_t>(std::max(m_total_ext_count, 0));
m_nozzle_panes.resize(pane_count);
m_active_nozzle_id = 0;
m_sizer_ams_items->Clear(false);
m_sizer_ams_items->SetOrientation(wxVERTICAL);
m_sizer_body->GetItem(m_sizer_ams_items)->SetFlag(wxALIGN_CENTER);
m_sizer_body->Show(m_sizer_ams_body, false);
m_sizer_ams_gap->SetProportion(0);
m_sizer_ams_gap->SetBorder(0);
m_panel_prv_left->Hide();
m_panel_prv_right->Hide();
m_simplebook_ams_left->Hide();
m_simplebook_ams_right->Hide();
m_panel_down_road->Show();
m_sizer_option_mid->Show(m_extruder, true);
m_switcher->Hide();
m_down_road->SetNozzleCount(1);
m_down_road->SetSingleSideLayout(true, AMSPanelPos::LEFT_PANEL);
m_down_road->UpdateLeft(1, AMSRoadShowMode::AMS_ROAD_MODE_SINGLE);
m_down_road->UpdateRight(1, AMSRoadShowMode::AMS_ROAD_MODE_NONE);
m_extruder->updateNozzleNum(1, series_name);
m_nozzle_book = new wxSimplebook(m_amswin, wxID_ANY, wxDefaultPosition, wxDefaultSize, 0);
m_nozzle_book->SetMinSize(wxSize(FromDIP(264), -1));
m_nozzle_book->SetBackgroundColour(StateColor::darkModeColorFor(AMS_CONTROL_DEF_BLOCK_BK_COLOUR));
m_sizer_ams_items->Add(m_nozzle_book, 0, wxALIGN_CENTER);
for (size_t nozzle_id = 0; nozzle_id < pane_count; ++nozzle_id) {
auto &pane = m_nozzle_panes[nozzle_id];
// A generic page is one H2C side. Keep the left-side geometry as the
// canonical single-nozzle presentation; the road and AMS item code
// still use the same side-specific paths as the legacy layout.
pane.panel_pos = AMSPanelPos::LEFT_PANEL;
pane.page = new wxPanel(m_nozzle_book, wxID_ANY);
pane.page->SetMinSize(wxSize(FromDIP(264), -1));
pane.page->SetBackgroundColour(StateColor::darkModeColorFor(AMS_CONTROL_DEF_BLOCK_BK_COLOUR));
pane.page_sizer = new wxBoxSizer(wxVERTICAL);
pane.page->SetSizer(pane.page_sizer);
pane.preview_panel = new wxScrolledWindow(pane.page, wxID_ANY);
pane.preview_panel->SetScrollRate(10, 0);
pane.preview_panel->SetSize(AMS_ITEMS_PANEL_SIZE);
pane.preview_panel->SetMinSize(AMS_ITEMS_PANEL_SIZE);
pane.preview_panel->SetBackgroundColour(StateColor::darkModeColorFor(AMS_CONTROL_DEF_BLOCK_BK_COLOUR));
pane.preview_sizer = new wxBoxSizer(wxHORIZONTAL);
pane.preview_panel->SetSizer(pane.preview_sizer);
// Generic lane cards grow to their calculated width for 1-8 lanes. Keep the
// book at the legacy minimum, but allow the page to expand to that width.
pane.ams_book = new wxSimplebook(pane.page, wxID_ANY, wxDefaultPosition, wxDefaultSize, 0);
pane.ams_book->SetMinSize(wxSize(FromDIP(264), -1));
pane.ams_book->SetBackgroundColour(StateColor::darkModeColorFor(AMS_CONTROL_DEF_BLOCK_BK_COLOUR));
pane.ams_area = new wxBoxSizer(wxHORIZONTAL);
pane.ams_area->Add(pane.ams_book, 0, wxALIGN_CENTER, 0);
pane.page_sizer->Add(pane.preview_panel, 0, wxALIGN_CENTER | wxTOP, FromDIP(5));
pane.page_sizer->Add(pane.ams_area, 0, wxALIGN_CENTER | wxTOP, FromDIP(10));
m_nozzle_book->AddPage(pane.page, wxEmptyString, nozzle_id == 0);
}
auto add_info = [this, series_name, printer_type, pane_count](AMSinfo info) {
if (pane_count == 0) return;
info.nozzle_id = std::clamp(info.nozzle_id, 0, static_cast<int>(pane_count) - 1);
AddAmsPreview(info, info.ams_type);
if (info.ams_type == AMSModel::EXT_AMS && info.cans.size() == 1)
AddAms({info}, series_name, printer_type, m_nozzle_panes[info.nozzle_id].panel_pos);
else
AddAms(info);
};
for (const auto &info : m_ams_info)
add_info(info);
for (auto &info : m_ext_info) {
const int virtual_tray_id = std::atoi(info.ams_id.c_str());
if (virtual_tray_id <= VIRTUAL_TRAY_MAIN_ID &&
virtual_tray_id >= VIRTUAL_TRAY_MAIN_ID - static_cast<int>(pane_count) + 1)
info.nozzle_id = VIRTUAL_TRAY_MAIN_ID - virtual_tray_id;
add_info(info);
}
for (auto &pane : m_nozzle_panes) {
if (pane.ams_book && pane.ams_book->GetPageCount() > 0)
pane.ams_book->SetSelection(0);
if (pane.preview_sizer) pane.preview_sizer->Layout();
if (pane.preview_panel) pane.preview_panel->FitInside();
if (pane.ams_book) pane.ams_book->Layout();
if (pane.page) pane.page->Layout();
if (!pane.current_ams.empty()) {
auto preview = m_ams_preview_list.find(pane.current_ams);
if (preview != m_ams_preview_list.end() && preview->second)
preview->second->OnSelected();
}
}
if (m_nozzle_book && pane_count > 0) {
m_nozzle_book->SetSelection(0);
m_current_ams = m_nozzle_panes[0].current_ams;
if (!m_current_ams.empty())
SwitchAms(m_current_ams);
else {
m_down_road->UpdateLeft(1, AMSRoadShowMode::AMS_ROAD_MODE_NONE);
m_down_road->UpdateRight(1, AMSRoadShowMode::AMS_ROAD_MODE_NONE);
}
}
m_amswin->Layout();
m_amswin->Fit();
// Generic N Nozzles
}
void AMSControl::CreateAmsDoubleNozzle(const std::string &series_name, const std::string &printer_type)
{
restore_legacy_ams_layout();
std::vector<AMSinfo> single_info_left;
std::vector<AMSinfo> single_info_right;
@@ -751,6 +912,8 @@ void AMSControl::CreateAmsDoubleNozzle(const std::string &series_name, const std
void AMSControl::CreateAmsSingleNozzle(const std::string &series_name, const std::string &printer_type)
{
restore_legacy_ams_layout();
std::vector<int>m_item_nums{0,0};
std::vector<AMSinfo> single_info;
@@ -979,6 +1142,17 @@ void AMSControl::UpdateAms(const std::string &series_name,
if (m_ams_info[i].routes_to_main_extruder() != ams_info[i].routes_to_main_extruder()) {
fresh = true;
}
if (m_ams_info[i].nozzle_id != ams_info[i].nozzle_id) {
fresh = true;
}
}
for (int i = 0; i < static_cast<int>(m_ext_info.size()); ++i) {
if (m_ext_info[i].ams_id != ext_info[i].ams_id ||
m_ext_info[i].nozzle_id != ext_info[i].nozzle_id ||
m_ext_info[i].ext_type != ext_info[i].ext_type) {
fresh = true;
}
}
}
else{
@@ -993,11 +1167,19 @@ void AMSControl::UpdateAms(const std::string &series_name,
m_dev_id = dev_id;
if (fresh){
ClearAms();
if (m_total_ext_count >= 2){
CreateAmsDoubleNozzle(series_name, printer_type);
}else{
CreateAmsSingleNozzle(series_name, printer_type);
// Keep the established Bambu left/right AMS presentation. Generic N-nozzle
// tabs are for non-Bambu printers; the current checkout exposes this behavior
// through PresetBundle::is_bbl_vendor().
const bool is_bbl_behavior = wxGetApp().preset_bundle && wxGetApp().preset_bundle->is_bbl_vendor();
if (is_bbl_behavior) {
if (m_total_ext_count == 1)
CreateAmsSingleNozzle(series_name, printer_type);
else if (m_total_ext_count == 2)
CreateAmsDoubleNozzle(series_name, printer_type);
}
else
CreateAmsMultiNozzle(series_name, printer_type);
SetSize(wxSize(FromDIP(578), -1));
SetMinSize(wxSize(FromDIP(578), -1));
Layout();
@@ -1052,7 +1234,7 @@ void AMSControl::UpdateAms(const std::string &series_name,
/*update humidity popup*/
if (m_percent_humidity_dry_popup->IsShown())
{
string target_id = m_percent_humidity_dry_popup->get_owner_ams_id();
std::string target_id = m_percent_humidity_dry_popup->get_owner_ams_id();
for (const auto& the_info : ams_info)
{
if (target_id == the_info.ams_id)
@@ -1070,7 +1252,7 @@ void AMSControl::UpdateAms(const std::string &series_name,
}
/*update ams extruder*/
if (m_extruder->updateNozzleNum(m_total_ext_count, series_name))
if (m_total_ext_count <= 2 && m_extruder->updateNozzleNum(m_total_ext_count, series_name))
{
m_amswin->Layout();
}
@@ -1106,7 +1288,13 @@ void AMSControl::AddAmsPreview(AMSinfo info, AMSModel type)
{
AMSPreview *ams_prv = nullptr;
if (info.routes_to_main_extruder())
if (m_total_ext_count > 2) {
const int nozzle_id = std::clamp(info.nozzle_id, 0, m_total_ext_count - 1);
auto &pane = m_nozzle_panes[nozzle_id];
ams_prv = new AMSPreview(pane.preview_panel, wxID_ANY, info, type);
pane.preview_sizer->Add(ams_prv, 0, wxALIGN_CENTER | wxLEFT, FromDIP(6));
}
else if (info.routes_to_main_extruder())
{
ams_prv = new AMSPreview(m_panel_prv_right, wxID_ANY, info, type);
m_sizer_prv_right->Add(ams_prv, 0, wxALIGN_CENTER | wxLEFT, FromDIP(6));
@@ -1129,6 +1317,40 @@ void AMSControl::AddAmsPreview(AMSinfo info, AMSModel type)
void AMSControl::createAms(wxSimplebook* parent, int& idx, AMSinfo info, AMSPanelPos pos) {
auto ams_item = new AmsItem(parent, info, info.ams_type, pos);
parent->InsertPage(idx, ams_item, wxEmptyString, true);
// Generic AMS cards may be wider than the legacy 264 DIP page. Propagate
// that calculated width through the page and simplebook so 5-8 lanes are
// laid out fully instead of being clipped by the old minimum width.
if (info.ams_type == AMSModel::GENERIC_AMS) {
const int required_width = ams_item->GetMinSize().x;
if (required_width > 0) {
const int page_width = std::max(parent->GetMinSize().x, required_width);
parent->SetMinSize(wxSize(page_width, -1));
if (auto *page = parent->GetParent()) {
page->SetMinSize(wxSize(std::max(page->GetMinSize().x, page_width), -1));
if (auto *nozzle_book = page->GetParent())
nozzle_book->SetMinSize(wxSize(std::max(nozzle_book->GetMinSize().x, page_width), -1));
}
}
// Above eight lanes, AmsItem is a fixed-minimum viewport over a wider
// lane strip. Expand the viewport through its page's sizers rather
// than propagating the strip's virtual width into the card/book.
const bool generic_lane_layout = !wxGetApp().preset_bundle || !wxGetApp().preset_bundle->is_bbl_vendor();
if (generic_lane_layout && info.cans.size() > 8) {
if (auto *page = parent->GetParent()) {
auto pane = std::find_if(m_nozzle_panes.begin(), m_nozzle_panes.end(),
[page](const NozzleAmsPane &candidate) { return candidate.page == page; });
if (pane != m_nozzle_panes.end()) {
pane->ams_area->GetItem(pane->ams_book)->SetProportion(1);
pane->ams_area->GetItem(pane->ams_book)->SetFlag(wxEXPAND);
pane->page_sizer->GetItem(pane->ams_area)->SetFlag(wxEXPAND | wxTOP);
m_sizer_ams_items->GetItem(m_nozzle_book)->SetFlag(wxEXPAND);
m_sizer_body->GetItem(m_sizer_ams_items)->SetFlag(wxEXPAND);
m_amswin->Layout();
}
}
}
}
ams_item->set_selection(idx);
idx++;
@@ -1153,7 +1375,10 @@ AMSRoadShowMode AMSControl::findFirstMode(AMSPanelPos pos) {
if (item->second->get_ams_model() == AMSModel::EXT_AMS && item->second->get_ext_type() == AMSModelOriginType::LITE_EXT) return AMSRoadShowMode::AMS_ROAD_MODE_AMS_LITE;
return AMSRoadShowMode::AMS_ROAD_MODE_FOUR;
}
else{
else if (item->second->get_can_count() > GENERIC_AMS_SLOT_NUM) {
return AMSRoadShowMode::AMS_ROAD_MODE_GENERIC;
}
else {
for (auto ids : pair_id){
if (ids.first == ams_id || ids.second == ams_id){
return AMSRoadShowMode::AMS_ROAD_MODE_DOUBLE;
@@ -1200,6 +1425,11 @@ void AMSControl::createAmsPanel(wxSimplebook *parent, int &idx, std::vector<AMSi
//book_sizer->Add(ams1, 0, wxALIGN_CENTER_HORIZONTAL, 0);
book_sizer->Add(ams1, 0, wxLEFT, (book_panel->GetSize().x - ams1->GetSize().x) / 2);
}
else if (pos == AMSPanelPos::SINGLE_PANEL) {
book_sizer->AddStretchSpacer(1);
book_sizer->Add(ams1, 0, wxALIGN_CENTER_VERTICAL, 0);
book_sizer->AddStretchSpacer(1);
}
else{
auto ext_image = new AMSExtImage(book_panel, pos, m_total_ext_count, false);
book_sizer->Add(ams1, 0, wxLEFT, FromDIP(30));
@@ -1228,7 +1458,17 @@ void AMSControl::createAmsPanel(wxSimplebook *parent, int &idx, std::vector<AMSi
void AMSControl::AddAms(AMSinfo info, AMSPanelPos pos)
{
if (m_total_ext_count > 1){
if (m_total_ext_count > 2) {
const int nozzle_id = std::clamp(info.nozzle_id, 0, m_total_ext_count - 1);
auto &pane = m_nozzle_panes[nozzle_id];
pane.item_ids.push_back(info.ams_id);
m_item_ids[nozzle_id].push_back(info.ams_id);
if (pane.current_ams.empty()) pane.current_ams = info.ams_id;
createAms(pane.ams_book, pane.page_index, info, AMSPanelPos::SINGLE_PANEL);
pane.ams_book->Fit();
pane.ams_book->Layout();
}
else if (m_total_ext_count == 2){
if (info.routes_to_main_extruder()){
createAms(m_simplebook_ams_right, m_right_page_index, info, AMSPanelPos::RIGHT_PANEL);
}
@@ -1239,6 +1479,9 @@ void AMSControl::AddAms(AMSinfo info, AMSPanelPos pos)
else if (m_total_ext_count == 1){
createAms(m_simplebook_ams_left, m_left_page_index, info, AMSPanelPos::LEFT_PANEL);
}
else {
// Generic N Nozzles
}
m_simplebook_ams_left->Layout();
m_simplebook_ams_right->Layout();
m_simplebook_ams_left->Refresh();
@@ -1269,7 +1512,19 @@ void AMSControl::AddAms(std::vector<AMSinfo> single_info, const std::string &ser
if (single_info.size() <= 0){
return;
}
if (m_total_ext_count == 2) {
if (m_total_ext_count > 2) {
const int nozzle_id = std::clamp(single_info.front().nozzle_id, 0, m_total_ext_count - 1);
auto &pane = m_nozzle_panes[nozzle_id];
for (const auto &info : single_info) {
pane.item_ids.push_back(info.ams_id);
m_item_ids[nozzle_id].push_back(info.ams_id);
}
if (pane.current_ams.empty()) pane.current_ams = single_info.front().ams_id;
createAmsPanel(pane.ams_book, pane.page_index, single_info, series_name, printer_type, AMSPanelPos::SINGLE_PANEL, m_total_ext_count);
pane.ams_book->Fit();
pane.ams_book->Layout();
}
else if (m_total_ext_count == 2) {
if (single_info[0].routes_to_main_extruder()) {
createAmsPanel(m_simplebook_ams_right, m_right_page_index, single_info, series_name, printer_type, AMSPanelPos::RIGHT_PANEL, m_total_ext_count);
}
@@ -1285,6 +1540,9 @@ void AMSControl::AddAms(std::vector<AMSinfo> single_info, const std::string &ser
createAmsPanel(m_simplebook_ams_left, m_left_page_index, single_info, series_name, printer_type, AMSPanelPos::LEFT_PANEL, m_total_ext_count);
}
}
else {
// Generic N Nozzles
}
m_simplebook_ams_left->Layout();
m_simplebook_ams_right->Layout();
@@ -1322,6 +1580,21 @@ void AMSControl::AddAms(std::vector<AMSinfo> single_info, const std::string &ser
void AMSControl::AddAmsPreview(std::vector<AMSinfo>single_info, AMSPanelPos pos) {
if (single_info.size() <= 0) return;
if (m_total_ext_count > 2) {
const int nozzle_id = std::clamp(single_info.front().nozzle_id, 0, m_total_ext_count - 1);
auto &pane = m_nozzle_panes[nozzle_id];
for (const auto &info : single_info) {
auto *ams_prv = new AMSPreview(pane.preview_panel, wxID_ANY, info, info.ams_type);
pane.preview_sizer->Add(ams_prv, 0, wxALIGN_CENTER | wxLEFT, FromDIP(6));
ams_prv->Bind(wxEVT_LEFT_DOWN, [this, ams_prv](wxMouseEvent &e) {
SwitchAms(ams_prv->get_ams_id());
e.Skip();
});
m_ams_preview_list[info.ams_id] = ams_prv;
}
return;
}
AMSPreview* ams_prv = nullptr;
AMSPreview* ams_prv2 = nullptr;
if (pos == AMSPanelPos::RIGHT_PANEL){
@@ -1360,8 +1633,97 @@ void AMSControl::AddAmsPreview(std::vector<AMSinfo>single_info, AMSPanelPos pos)
}
}
void AMSControl::SelectNozzle(int nozzle_id)
{
if (nozzle_id < 0 || nozzle_id >= static_cast<int>(m_nozzle_panes.size()))
return;
if (m_active_nozzle_id == nozzle_id &&
(!m_nozzle_book || m_nozzle_book->GetSelection() == nozzle_id))
return;
m_active_nozzle_id = nozzle_id;
if (m_nozzle_book && m_nozzle_book->GetSelection() != nozzle_id)
m_nozzle_book->SetSelection(nozzle_id);
m_current_ams = m_nozzle_panes[nozzle_id].current_ams;
if (!m_current_ams.empty()) {
SwitchAms(m_current_ams);
return;
}
if (m_down_road) {
m_down_road->UpdateLeft(1, AMSRoadShowMode::AMS_ROAD_MODE_NONE);
m_down_road->UpdateRight(1, AMSRoadShowMode::AMS_ROAD_MODE_NONE);
}
if (m_extruder)
m_extruder->OnAmsLoading(false, 0);
post_event(SimpleEvent(EVT_AMS_SWITCH));
}
void AMSControl::SwitchAms(std::string ams_id)
{
if (m_total_ext_count > 2) {
NozzleAmsPane *pane = nullptr;
int nozzle_id = -1;
for (int index = 0; index < static_cast<int>(m_nozzle_panes.size()); ++index) {
auto &candidate = m_nozzle_panes[index];
if (std::find(candidate.item_ids.begin(), candidate.item_ids.end(), ams_id) != candidate.item_ids.end()) {
pane = &candidate;
nozzle_id = index;
break;
}
}
if (!pane) return;
m_active_nozzle_id = nozzle_id;
if (m_nozzle_book && m_nozzle_book->GetSelection() != nozzle_id)
m_nozzle_book->SetSelection(nozzle_id);
pane->current_ams = ams_id;
m_current_ams = ams_id;
m_current_select = ams_id;
for (const auto &id : pane->item_ids) {
auto preview = m_ams_preview_list.find(id);
if (preview == m_ams_preview_list.end() || !preview->second) continue;
if (id == ams_id) preview->second->OnSelected();
else preview->second->UnSelected();
}
auto item = m_ams_item_list.find(ams_id);
if (item != m_ams_item_list.end() && item->second) {
if (pane->ams_book)
pane->ams_book->SetSelection(item->second->get_selection());
AMSRoadShowMode road_mode = AMSRoadShowMode::AMS_ROAD_MODE_SINGLE;
if (item->second->get_can_count() == GENERIC_AMS_SLOT_NUM)
road_mode = AMSRoadShowMode::AMS_ROAD_MODE_FOUR;
else if (item->second->get_can_count() > GENERIC_AMS_SLOT_NUM)
road_mode = AMSRoadShowMode::AMS_ROAD_MODE_GENERIC;
else if (item->second->get_ams_model() == AMSModel::N3S_AMS)
road_mode = AMSRoadShowMode::AMS_ROAD_MODE_SINGLE_N3S;
else if (item->second->get_ams_model() == AMSModel::AMS_LITE ||
(item->second->get_ams_model() == AMSModel::EXT_AMS &&
item->second->get_ext_type() == AMSModelOriginType::LITE_EXT))
road_mode = AMSRoadShowMode::AMS_ROAD_MODE_AMS_LITE;
if (m_down_road) {
if (pane->panel_pos == AMSPanelPos::LEFT_PANEL) {
m_down_road->UpdateLeft(1, road_mode);
m_down_road->UpdateRight(1, AMSRoadShowMode::AMS_ROAD_MODE_NONE);
} else {
m_down_road->UpdateLeft(1, AMSRoadShowMode::AMS_ROAD_MODE_NONE);
m_down_road->UpdateRight(1, road_mode);
}
m_down_road->UpdatePassRoad(pane->panel_pos, -1, AMSPassRoadSTEP::AMS_ROAD_STEP_NONE);
}
if (m_extruder)
m_extruder->OnAmsLoading(false, 0);
}
post_event(SimpleEvent(EVT_AMS_SWITCH));
return;
}
if(ams_id == m_current_show_ams_left || ams_id == m_current_show_ams_right){return;}
bool is_in_right = IsAmsInRightPanel(ams_id);
@@ -1432,13 +1794,17 @@ void AMSControl::SwitchAms(std::string ams_id)
else {
AMSRoadShowMode mode = AMSRoadShowMode::AMS_ROAD_MODE_SINGLE;
if (item->get_ams_model() == AMSModel::N3S_AMS)
if (item->get_can_count() > GENERIC_AMS_SLOT_NUM)
mode = AMSRoadShowMode::AMS_ROAD_MODE_GENERIC;
else if (item->get_ams_model() == AMSModel::N3S_AMS)
mode = AMSRoadShowMode::AMS_ROAD_MODE_SINGLE_N3S;
for (auto it : pair_id) {
if (it.first == ams_id || it.second == ams_id) {
mode = AMSRoadShowMode::AMS_ROAD_MODE_DOUBLE;
break;
if (item->get_can_count() <= GENERIC_AMS_SLOT_NUM) {
for (auto it : pair_id) {
if (it.first == ams_id || it.second == ams_id) {
mode = AMSRoadShowMode::AMS_ROAD_MODE_DOUBLE;
break;
}
}
}
pos == AMSPanelPos::LEFT_PANEL ? m_down_road->UpdateLeft(m_total_ext_count, mode)
@@ -1500,6 +1866,20 @@ void AMSControl::SetExtruder(bool on_off, int nozzle_id, std::string ams_id, std
AmsItem *item = nullptr;
if (m_ams_item_list.find(ams_id) != m_ams_item_list.end()) { item = m_ams_item_list[ams_id]; }
if (m_total_ext_count > 2) {
if (nozzle_id < 0 || nozzle_id >= static_cast<int>(m_nozzle_panes.size()))
return;
if (!m_extruder)
return;
if (on_off && item)
m_extruder->OnAmsLoading(true, 0, item->GetTagColr(slot_id));
else
m_extruder->OnAmsLoading(false, 0);
return;
}
if (on_off && item) {
auto col = item->GetTagColr(slot_id);
m_extruder->OnAmsLoading(true, nozzle_id, col);
@@ -1624,6 +2004,69 @@ void AMSControl::SetAmsStep(std::string ams_id, std::string canid, AMSPassRoadTy
else{
return;
}
if (m_total_ext_count > 2) {
const int nozzle_id = std::clamp(ams->get_nozzle_id(), 0, m_total_ext_count - 1);
if (nozzle_id >= static_cast<int>(m_nozzle_panes.size()))
return;
auto &pane = m_nozzle_panes[nozzle_id];
AMSRoadShowMode road_mode = AMSRoadShowMode::AMS_ROAD_MODE_SINGLE;
if (ams->get_can_count() == GENERIC_AMS_SLOT_NUM)
road_mode = AMSRoadShowMode::AMS_ROAD_MODE_FOUR;
else if (ams->get_can_count() > GENERIC_AMS_SLOT_NUM)
road_mode = AMSRoadShowMode::AMS_ROAD_MODE_GENERIC;
else if (ams->get_ams_model() == AMSModel::N3S_AMS)
road_mode = AMSRoadShowMode::AMS_ROAD_MODE_SINGLE_N3S;
else if (ams->get_ams_model() == AMSModel::AMS_LITE ||
(ams->get_ams_model() == AMSModel::EXT_AMS && ams->get_ext_type() == AMSModelOriginType::LITE_EXT))
road_mode = AMSRoadShowMode::AMS_ROAD_MODE_AMS_LITE;
if (can_index >= 0 && can_index < static_cast<int>(info.cans.size()) && m_down_road)
m_down_road->SetPassRoadColour(pane.panel_pos == AMSPanelPos::LEFT_PANEL,
info.cans[can_index].material_colour);
if (m_down_road) {
if (pane.panel_pos == AMSPanelPos::LEFT_PANEL) {
m_down_road->UpdateLeft(1, road_mode);
m_down_road->UpdateRight(1, AMSRoadShowMode::AMS_ROAD_MODE_NONE);
} else {
m_down_road->UpdateLeft(1, AMSRoadShowMode::AMS_ROAD_MODE_NONE);
m_down_road->UpdateRight(1, road_mode);
}
}
int road_length = 0;
if (step == AMSPassRoadSTEP::AMS_ROAD_STEP_NONE) {
ams->SetAmsStep(ams_id, canid, type, AMSPassRoadSTEP::AMS_ROAD_STEP_NONE);
if (m_down_road)
m_down_road->UpdatePassRoad(pane.panel_pos, -1, AMSPassRoadSTEP::AMS_ROAD_STEP_NONE);
if (m_extruder)
m_extruder->OnAmsLoading(false, 0);
}
else if (step == AMSPassRoadSTEP::AMS_ROAD_STEP_COMBO_LOAD_STEP1) {
ams->SetAmsStep(ams_id, canid, type, AMSPassRoadSTEP::AMS_ROAD_STEP_1);
if (m_down_road)
m_down_road->UpdatePassRoad(pane.panel_pos, road_length, AMSPassRoadSTEP::AMS_ROAD_STEP_1);
if (m_extruder)
m_extruder->OnAmsLoading(false, 0);
}
else if (step == AMSPassRoadSTEP::AMS_ROAD_STEP_COMBO_LOAD_STEP2) {
ams->SetAmsStep(ams_id, canid, type, AMSPassRoadSTEP::AMS_ROAD_STEP_2);
if (m_down_road)
m_down_road->UpdatePassRoad(pane.panel_pos, road_length, AMSPassRoadSTEP::AMS_ROAD_STEP_2);
if (m_extruder)
m_extruder->OnAmsLoading(true, 0, ams->GetTagColr(canid));
}
else if (step == AMSPassRoadSTEP::AMS_ROAD_STEP_COMBO_LOAD_STEP3) {
ams->SetAmsStep(ams_id, canid, type, AMSPassRoadSTEP::AMS_ROAD_STEP_3);
if (m_down_road)
m_down_road->UpdatePassRoad(pane.panel_pos, road_length, AMSPassRoadSTEP::AMS_ROAD_STEP_3);
if (m_extruder)
m_extruder->OnAmsLoading(true, 0, ams->GetTagColr(canid));
}
return;
}
if (can_index >= 0 && can_index < info.cans.size())
{
m_down_road->SetPassRoadColour(left, info.cans[can_index].material_colour);
+24 -2
View File
@@ -47,6 +47,20 @@ public:
void on_retry();
protected:
struct NozzleAmsPane
{
wxPanel* page{nullptr};
wxBoxSizer* page_sizer{nullptr};
wxScrolledWindow* preview_panel{nullptr};
wxBoxSizer* preview_sizer{nullptr};
wxSimplebook* ams_book{nullptr};
wxBoxSizer* ams_area{nullptr};
AMSPanelPos panel_pos{AMSPanelPos::LEFT_PANEL};
std::vector<std::string> item_ids;
std::string current_ams;
int page_index{0};
};
std::string m_current_ams;
std::string m_current_slot_left;
std::string m_current_slot_right;
@@ -63,7 +77,10 @@ protected:
std::string m_dev_id;
std::vector<std::vector<std::string>> m_item_ids{ {}, {} };
std::vector<std::pair<string, string>> pair_id;
std::vector<NozzleAmsPane> m_nozzle_panes;
wxSimplebook* m_nozzle_book{nullptr};
int m_active_nozzle_id{0};
std::vector<std::pair<std::string, std::string>> pair_id;
int m_total_ext_count = 1;
AMSextruder *m_extruder{nullptr};
@@ -103,6 +120,7 @@ protected:
wxSimplebook *m_simplebook_ams_left{nullptr};
wxSimplebook *m_simplebook_ams_right{ nullptr };
wxSimplebook *m_simplebook_bottom{nullptr};
wxSizerItem *m_sizer_ams_gap{nullptr};
wxPanel *m_panel_down_road{ nullptr };
int m_left_page_index = 0;
int m_right_page_index = 0;
@@ -156,6 +174,7 @@ public:
void createAms(wxSimplebook* parent, int& idx, AMSinfo info, AMSPanelPos pos);
void createAmsPanel(wxSimplebook *parent, int &idx, std::vector<AMSinfo> infos, const std::string &series_name, const std::string &printer_type, AMSPanelPos pos, int total_ext_num);
AMSRoadShowMode findFirstMode(AMSPanelPos pos);
void restore_legacy_ams_layout();
AMSModel m_ams_model{AMSModel::EXT_AMS};
AMSModel m_ext_model{AMSModel::EXT_AMS};
@@ -181,8 +200,9 @@ public:
std::tuple<bool, bool> isFilaSwitchReady();
void show_switcher_status(bool show);
void UpdatePassRoad(string ams_id, AMSPassRoadType type, AMSPassRoadSTEP step);
void UpdatePassRoad(std::string ams_id, AMSPassRoadType type, AMSPassRoadSTEP step);
void CreateAms();
void CreateAmsMultiNozzle(const std::string& series_name, const std::string& printer_type);
void CreateAmsDoubleNozzle(const std::string &series_name, const std::string& printer_type);
void CreateAmsSingleNozzle(const std::string &series_name, const std::string &printer_type);
void ClearAms();
@@ -210,6 +230,8 @@ public:
void SetExtruder(bool on_off, int nozzle_id, std::string ams_id, std::string slot_id);
void SetAmsStep(std::string ams_id, std::string canid, AMSPassRoadType type, AMSPassRoadSTEP step);
void SwitchAms(std::string ams_id);
void SelectNozzle(int nozzle_id);
int GetActiveNozzleId() const { return m_active_nozzle_id; }
void msw_rescale();
void on_filament_load(wxCommandEvent &event);
+384 -57
View File
@@ -42,6 +42,7 @@
#include <boost/log/trivial.hpp>
#include <wx/timer.h>
#include <wx/sizer.h>
#include <wx/scrolwin.h>
#include "CalibUtils.hpp"
#include "slic3r/GUI/wxExtensions.hpp"
@@ -76,6 +77,48 @@ namespace Slic3r { namespace GUI {
//#define AMS_SINGLE_CAN_SIZE wxSize(FromDIP(78), 144)
#define AMS_CANS_WINDOW_SIZE wxSize(FromDIP(264), FromDIP(174))
#define AMS_SINGLE_CAN_SIZE wxSize(FromDIP(78), FromDIP(174))
int generic_ams_layout_width(wxWindow *window, int lane_count)
{
const int base_width = window->FromDIP(264);
const int lane_width = window->FromDIP(52);
const int lane_gap = window->FromDIP(4);
if (lane_count <= 4)
return base_width;
// Lane centers are distributed as width * (index + 1) / (count + 1).
// Keep at least one card width plus the requested gap between centers.
return std::max(base_width, (lane_count + 1) * (lane_width + lane_gap));
}
std::vector<int> generic_ams_lane_centers(int lane_count, int width)
{
std::vector<int> centers;
if (lane_count <= 0)
return centers;
centers.reserve(lane_count);
for (int lane = 0; lane < lane_count; ++lane)
centers.push_back(width * (lane + 1) / (lane_count + 1));
return centers;
}
int generic_ams_preview_width(wxWindow *window, int lane_count)
{
const int base_width = window->FromDIP(52);
const int lane_width = window->FromDIP(9);
const int lane_gap = window->FromDIP(4);
if (lane_count <= 0)
return base_width;
return std::max(base_width, lane_count * lane_width + (lane_count + 1) * lane_gap);
}
bool use_generic_ams_layout(AMSModel model)
{
return model == AMSModel::GENERIC_AMS &&
(!wxGetApp().preset_bundle || !wxGetApp().preset_bundle->is_bbl_vendor());
}
bool AMSinfo::parse_ams_info(MachineObject *obj, DevAms *ams, bool remain_flag, bool humidity_flag)
{
if (!ams) return false;
@@ -170,10 +213,14 @@ void AMSinfo::parse_ext_info(MachineObject* obj, DevAmsTray tray) {
info.can_id = std::to_string(0);
this->cans.clear();
if (tray.id == std::to_string(VIRTUAL_TRAY_MAIN_ID))
try {
const int virtual_tray_id = std::stoi(tray.id);
if (virtual_tray_id >= 0 && virtual_tray_id <= VIRTUAL_TRAY_MAIN_ID)
this->nozzle_id = VIRTUAL_TRAY_MAIN_ID - virtual_tray_id;
}
catch (...) {
this->nozzle_id = 0;
else if (tray.id == std::to_string(VIRTUAL_TRAY_DEPUTY_ID))
this->nozzle_id = 1;
}
if (tray.is_tray_info_ready()) {
info.ctype = tray.ctype;
@@ -634,7 +681,7 @@ void AMSextruderImage::doRender(wxDC &dc)
}
AMSextruderImage::AMSextruderImage(wxWindow *parent, wxWindowID id, string file_name, const wxSize& size, const wxPoint &pos)
AMSextruderImage::AMSextruderImage(wxWindow *parent, wxWindowID id, std::string file_name, const wxSize& size, const wxPoint &pos)
{
wxWindow::Create(parent, id, pos, size);
SetBackgroundColour(StateColor::darkModeColorFor(*wxWHITE));
@@ -823,7 +870,7 @@ void SwitcherImage::doRender(wxDC &dc)
Layout();
}
SwitcherImage::SwitcherImage(wxWindow *parent, wxWindowID id, string file_name, const wxSize& size, const wxPoint &pos)
SwitcherImage::SwitcherImage(wxWindow *parent, wxWindowID id, std::string file_name, const wxSize& size, const wxPoint &pos)
{
wxWindow::Create(parent, id, pos, size);
SetBackgroundColour(StateColor::darkModeColorFor(*wxWHITE));
@@ -856,7 +903,11 @@ AMSextruder::AMSextruder(wxWindow *parent, wxWindowID id, int nozzle_num, const
void AMSextruder::TurnOff()
{
m_left_extruder->TurnOff();
if (m_left_extruder) m_left_extruder->TurnOff();
if (m_right_extruder) m_right_extruder->TurnOff();
for (auto *extruder : m_nozzle_extruders) {
if (extruder) extruder->TurnOff();
}
}
void AMSextruder::create(wxWindow *parent, wxWindowID id, const wxPoint &pos, const wxSize &size, int nozzle_num)
@@ -882,7 +933,7 @@ void AMSextruder::OnAmsLoading(bool load, int nozzle_id, wxColour col /*= AMS_CO
m_left_extruder->OnAmsLoading(load, col);
if (load) m_current_colur_deputy = col;
}
else if (m_nozzle_num > 1){
else if (m_nozzle_num == 2){
if (nozzle_id == MAIN_EXTRUDER_ID) {
m_right_extruder->OnAmsLoading(load, col);
if (m_current_colur != col){
@@ -896,21 +947,44 @@ void AMSextruder::OnAmsLoading(bool load, int nozzle_id, wxColour col /*= AMS_CO
}
}
}
else if (m_nozzle_num > 2) {
if (nozzle_id >= 0 && nozzle_id < static_cast<int>(m_nozzle_extruders.size())) {
m_nozzle_extruders[nozzle_id]->OnAmsLoading(load, col);
if (load) m_current_colur = col;
}
}
else {
// Generic N Nozzles
}
}
/*return true if something is updated*/
bool AMSextruder::updateNozzleNum(int nozzle_num, const string& series_name)
bool AMSextruder::updateNozzleNum(int nozzle_num, const std::string& series_name)
{
if (m_nozzle_num == nozzle_num && m_series_name == series_name) return false;
m_series_name = series_name;
m_nozzle_num = nozzle_num;
this->DestroyChildren();
m_left_extruder = nullptr;
m_right_extruder = nullptr;
m_nozzle_extruders.clear();
m_right_extruder = new AMSextruderImage(this, wxID_ANY, "right_nozzle", AMS_EXTRUDER_DOUBLE_NOZZLE_BITMAP_SIZE);
m_bitmap_sizer = new wxBoxSizer(wxHORIZONTAL);
if (m_nozzle_num == 1) {
if (MachineObject::is_series_n(m_series_name)) {
m_left_extruder = new AMSextruderImage(this, wxID_ANY, "single_nozzle_n", AMS_EXTRUDER_SINGLE_NOZZLE_N_SIZE);
} else if (MachineObject::is_series_x(m_series_name) || MachineObject::is_series_p(m_series_name)) {
m_left_extruder = new AMSextruderImage(this, wxID_ANY, "single_nozzle_xp", AMS_EXTRUDER_SINGLE_NOZZLE_XP_SIZE);
} else {
m_left_extruder = new AMSextruderImage(this, wxID_ANY, "single_nozzle_xp", AMS_EXTRUDER_SINGLE_NOZZLE_XP_SIZE);
}
wxBoxSizer* m_bitmap_sizer = new wxBoxSizer(wxHORIZONTAL);
if (m_nozzle_num >= 2)
m_left_extruder->setShowState(true);
m_bitmap_sizer->Add(m_left_extruder, 0, wxALIGN_LEFT | wxALIGN_TOP, 0);
}
else if (m_nozzle_num == 2)
{
m_right_extruder = new AMSextruderImage(this, wxID_ANY, "right_nozzle", AMS_EXTRUDER_DOUBLE_NOZZLE_BITMAP_SIZE);
m_left_extruder = new AMSextruderImage(this, wxID_ANY, "left_nozzle", AMS_EXTRUDER_DOUBLE_NOZZLE_BITMAP_SIZE);
m_left_extruder->setShowState(true);
m_right_extruder->setShowState(true);
@@ -918,25 +992,17 @@ bool AMSextruder::updateNozzleNum(int nozzle_num, const string& series_name)
m_bitmap_sizer->Add(m_right_extruder, 0, wxLEFT | wxALIGN_TOP, FromDIP(2));
m_bitmap_sizer->AddSpacer(2);
}
else if (m_nozzle_num > 2) {
for (int nozzle_id = 0; nozzle_id < m_nozzle_num; ++nozzle_id) {
auto *extruder = new AMSextruderImage(this, wxID_ANY, "single_nozzle_n", AMS_EXTRUDER_SINGLE_NOZZLE_N_SIZE);
extruder->setShowState(true);
m_nozzle_extruders.push_back(extruder);
m_bitmap_sizer->Add(extruder, 0, wxALIGN_LEFT | wxALIGN_TOP, 0);
}
}
else
{
if (MachineObject::is_series_n(m_series_name))
{
m_left_extruder = new AMSextruderImage(this, wxID_ANY, "single_nozzle_n", AMS_EXTRUDER_SINGLE_NOZZLE_N_SIZE);
}
else if(MachineObject::is_series_x(m_series_name) || MachineObject::is_series_p(m_series_name))
{
m_left_extruder = new AMSextruderImage(this, wxID_ANY, "single_nozzle_xp", AMS_EXTRUDER_SINGLE_NOZZLE_XP_SIZE);
}
else
{
m_left_extruder = new AMSextruderImage(this, wxID_ANY, "single_nozzle_xp", AMS_EXTRUDER_SINGLE_NOZZLE_XP_SIZE);
}
m_left_extruder->setShowState(true);
m_right_extruder->setShowState(false);
m_bitmap_sizer->Add(m_left_extruder, 0, wxALIGN_LEFT | wxALIGN_TOP, 0);
m_bitmap_sizer->Add(m_right_extruder, 0, wxLEFT | wxALIGN_TOP, FromDIP(3));
// Generic N Nozzles
}
SetSizer(m_bitmap_sizer);
@@ -951,6 +1017,9 @@ void AMSextruder::msw_rescale()
//m_amsSextruder->msw_rescale();
if (m_left_extruder) m_left_extruder->msw_rescale();
if (m_right_extruder) m_right_extruder->msw_rescale();
for (auto *extruder : m_nozzle_extruders) {
if (extruder) extruder->msw_rescale();
}
Layout();
Update();
Refresh();
@@ -2123,7 +2192,10 @@ AMSRoadUpPart::AMSRoadUpPart(wxWindow* parent, wxWindowID id, AMSinfo info, AMSM
m_ams_model = model;
if (m_ams_model == AMSModel::GENERIC_AMS){
create(parent, id, pos, wxSize(FromDIP(264), FromDIP(34)));
const int width = use_generic_ams_layout(m_ams_model)
? generic_ams_layout_width(this, static_cast<int>(m_amsinfo.cans.size()))
: FromDIP(264);
create(parent, id, pos, wxSize(width, FromDIP(34)));
}
else{
create(parent, id, pos, wxSize(FromDIP(78), FromDIP(34)));
@@ -2235,6 +2307,38 @@ void AMSRoadUpPart::doRender(wxDC& dc)
dc.SetPen(wxPen(AMS_CONTROL_GRAY500, 2, wxPENSTYLE_SOLID));
dc.SetBrush(wxBrush(*wxTRANSPARENT_BRUSH));
if (use_generic_ams_layout(m_ams_model)) {
const int lane_count = static_cast<int>(m_amsinfo.cans.size());
if (lane_count <= 0)
return;
const auto lane_centers = generic_ams_lane_centers(lane_count, size.x);
const int height = FromDIP(21);
for (const int lane_center : lane_centers)
dc.DrawLine(lane_center, 0, lane_center, height);
if (lane_centers.size() > 1)
dc.DrawLine(lane_centers.front(), height, lane_centers.back(), height);
dc.DrawLine(size.x / 2, height, size.x / 2, size.y);
if (m_load_step != AMSPassRoadSTEP::AMS_ROAD_STEP_NONE &&
m_load_slot_index >= 0 && m_load_slot_index < lane_count) {
const int lane_center = lane_centers[m_load_slot_index];
dc.SetPen(wxPen(_get_diff_clr(this, m_amsinfo.cans[m_load_slot_index].material_colour), 4, wxPENSTYLE_SOLID));
dc.DrawLine(lane_center, 0, lane_center, height);
dc.DrawLine(std::min(lane_center, size.x / 2), height,
std::max(lane_center, size.x / 2), height);
if (m_load_step == AMSPassRoadSTEP::AMS_ROAD_STEP_2 ||
m_load_step == AMSPassRoadSTEP::AMS_ROAD_STEP_3)
dc.DrawLine(size.x / 2, height, size.x / 2, size.y);
}
dc.SetPen(*wxTRANSPARENT_PEN);
dc.SetBrush(wxBrush(wxColour(194, 194, 194)));
dc.DrawRectangle(size.x / 2 - FromDIP(14), height - FromDIP(5), FromDIP(28), FromDIP(10));
return;
}
if ((m_ams_model == N3S_AMS || m_ams_model == EXT_AMS) && m_amsinfo.cans.size() != 4){
dc.DrawLine(((float)size.x / 2), (0), ((float)size.x / 2), (size.y));
if (m_load_step == AMSPassRoadSTEP::AMS_ROAD_STEP_2 || m_load_step == AMSPassRoadSTEP::AMS_ROAD_STEP_3){
@@ -2360,6 +2464,45 @@ void AMSRoadDownPart::UpdateRight(int nozzle_num, AMSRoadShowMode mode)
Refresh();
}
void AMSRoadDownPart::SetNozzleCount(int nozzle_num)
{
const int new_nozzle_num = std::max(1, nozzle_num);
if (m_nozzle_num == new_nozzle_num &&
(new_nozzle_num <= 2 || static_cast<int>(m_generic_road_states.size()) == new_nozzle_num)) {
return;
}
m_nozzle_num = new_nozzle_num;
if (m_nozzle_num > 2)
m_generic_road_states.resize(m_nozzle_num);
else
m_generic_road_states.clear();
Refresh();
}
void AMSRoadDownPart::SetSingleSideLayout(bool enabled, AMSPanelPos pos)
{
if (m_single_side_layout == enabled && m_single_side_pos == pos)
return;
m_single_side_layout = enabled;
m_single_side_pos = pos;
Refresh();
}
void AMSRoadDownPart::UpdateNozzle(int nozzle_id, AMSRoadShowMode mode)
{
if (nozzle_id < 0) return;
if (nozzle_id >= static_cast<int>(m_generic_road_states.size()))
m_generic_road_states.resize(nozzle_id + 1);
auto &state = m_generic_road_states[nozzle_id];
if (state.mode == mode && m_nozzle_num > 2) return;
state.mode = mode;
m_nozzle_num = std::max(m_nozzle_num, nozzle_id + 1);
Refresh();
}
void AMSRoadDownPart::OnVamsLoading(bool load, wxColour col /*= AMS_CONTROL_GRAY500*/)
{
/*m_vams_loading = load;
@@ -2389,6 +2532,17 @@ void AMSRoadDownPart::SetPassRoadColour(bool left, wxColour col)
Refresh();
}
void AMSRoadDownPart::SetPassRoadColour(int nozzle_id, wxColour col)
{
if (nozzle_id < 0) return;
if (nozzle_id >= static_cast<int>(m_generic_road_states.size()))
m_generic_road_states.resize(nozzle_id + 1);
if (m_generic_road_states[nozzle_id].road_color == col) return;
m_generic_road_states[nozzle_id].road_color = col;
Refresh();
}
void AMSRoadDownPart::SetShowMode(AMSRoadShowMode left_mode, AMSRoadShowMode right_mode)
{
if (m_left_rode_mode == left_mode && m_right_rode_mode == right_mode) { return; }
@@ -2433,8 +2587,34 @@ void AMSRoadDownPart::doRender(wxDC& dc)
/*if (m_road_color.Alpha() == 0) { dc.SetPen(wxPen(*wxWHITE, m_passroad_width, wxPENSTYLE_SOLID)); }
else { dc.SetPen(wxPen(m_road_color, m_passroad_width, wxPENSTYLE_SOLID)); }*/
dc.SetPen(wxPen(AMS_CONTROL_GRAY500, 2, wxPENSTYLE_SOLID));
if (m_nozzle_num > 2) {
const int nozzle_count = std::min(m_nozzle_num, static_cast<int>(m_generic_road_states.size()));
for (int nozzle_id = 0; nozzle_id < nozzle_count; ++nozzle_id) {
const auto &state = m_generic_road_states[nozzle_id];
if (state.mode == AMSRoadShowMode::AMS_ROAD_MODE_NONE) continue;
const int x = size.x * (nozzle_id + 1) / (nozzle_count + 1);
dc.SetPen(wxPen(AMS_CONTROL_GRAY500, 2, wxPENSTYLE_SOLID));
dc.DrawLine(x, size.y / 2, x, size.y);
if (state.road_length > 0 &&
(state.pass_road_step == AMSPassRoadSTEP::AMS_ROAD_STEP_2 ||
state.pass_road_step == AMSPassRoadSTEP::AMS_ROAD_STEP_3)) {
dc.SetPen(wxPen(_get_diff_clr(this, state.road_color), 4, wxPENSTYLE_SOLID));
dc.DrawLine(x - FromDIP(state.road_length), size.y / 2, x, size.y / 2);
}
}
return;
}
auto xpos = left_nozzle_pos.x;
if (m_left_rode_mode == AMSRoadShowMode::AMS_ROAD_MODE_NONE || m_right_rode_mode == AMSRoadShowMode::AMS_ROAD_MODE_NONE){
if (m_single_side_layout) {
dc.DrawLine(size.x / 2, 0, size.x / 2, size.y);
}
else if (
(m_left_rode_mode == AMSRoadShowMode::AMS_ROAD_MODE_NONE ||
m_right_rode_mode == AMSRoadShowMode::AMS_ROAD_MODE_NONE)){
auto length = 50;
if (m_left_rode_mode == AMSRoadShowMode::AMS_ROAD_MODE_AMS_LITE || m_right_rode_mode == AMSRoadShowMode::AMS_ROAD_MODE_AMS_LITE)
length = -13;
@@ -2465,6 +2645,9 @@ void AMSRoadDownPart::doRender(wxDC& dc)
case AMSRoadShowMode::AMS_ROAD_MODE_AMS_LITE:
dc.DrawLine(left_nozzle_pos.x, 0, left_nozzle_pos.x, size.y);
break;
case AMSRoadShowMode::AMS_ROAD_MODE_GENERIC:
// Generic N-lane geometry is intentionally left unimplemented.
break;
default:
break;
}
@@ -2492,23 +2675,41 @@ void AMSRoadDownPart::doRender(wxDC& dc)
dc.DrawLine(left_nozzle_pos.x, (size.y / 2), left_nozzle_pos.x + FromDIP(145), (size.y / 2));
dc.DrawLine(left_nozzle_pos.x + FromDIP(145), 0, left_nozzle_pos.x + FromDIP(145), (size.y / 2));
break;
case AMSRoadShowMode::AMS_ROAD_MODE_GENERIC:
// Generic N-lane geometry is intentionally left unimplemented.
break;
default:
break;
}
}
if (m_single_side_layout) {
if (m_pass_road_left_step == AMSPassRoadSTEP::AMS_ROAD_STEP_2 ||
m_pass_road_left_step == AMSPassRoadSTEP::AMS_ROAD_STEP_3) {
dc.SetPen(wxPen(_get_diff_clr(this, m_road_color[1]), 4, wxPENSTYLE_SOLID));
dc.DrawLine(size.x / 2, 0, size.x / 2, size.y);
}
return;
}
if (m_right_rode_mode != AMSRoadShowMode::AMS_ROAD_MODE_AMS_LITE){
if (m_nozzle_num == 2) {
if (m_nozzle_num == 1) {
if (m_right_rode_mode != AMSRoadShowMode::AMS_ROAD_MODE_NONE && m_left_rode_mode != AMSRoadShowMode::AMS_ROAD_MODE_NONE) {
dc.DrawLine((left_nozzle_pos.x), (size.y / 2), (right_nozzle_pos.x), (size.y / 2));
}
const auto &nozzle_pos = m_single_side_layout && m_single_side_pos == AMSPanelPos::RIGHT_PANEL
? right_nozzle_pos
: left_nozzle_pos;
dc.DrawLine(nozzle_pos.x, (size.y / 2), nozzle_pos.x, (size.y));
}
else if (m_nozzle_num == 2) {
/*dc.DrawLine(FromDIP(left_nozzle_pos.x), FromDIP(size.y / 2), FromDIP(left_nozzle_pos.x), FromDIP(size.y));
dc.DrawLine(FromDIP(right_nozzle_pos.x), FromDIP(size.y / 2), FromDIP(right_nozzle_pos.x), FromDIP(size.y));*/
dc.DrawLine((left_nozzle_pos.x), (size.y / 2), (left_nozzle_pos.x), (size.y));
dc.DrawLine((right_nozzle_pos.x), (size.y / 2), (right_nozzle_pos.x), (size.y));
}
else {
if (m_right_rode_mode != AMSRoadShowMode::AMS_ROAD_MODE_NONE && m_left_rode_mode != AMSRoadShowMode::AMS_ROAD_MODE_NONE) {
dc.DrawLine((left_nozzle_pos.x), (size.y / 2), (right_nozzle_pos.x), (size.y / 2));
}
dc.DrawLine((left_nozzle_pos.x), (size.y / 2), (left_nozzle_pos.x), (size.y));
// Generic N Nozzles
}
}
@@ -2533,10 +2734,16 @@ void AMSRoadDownPart::doRender(wxDC& dc)
dc.DrawLine(xpos, size.y / 2, xpos, size.y);*/
int x = left_nozzle_pos.x;
int len = m_right_road_length;
if (m_nozzle_num == 2) {
if (m_nozzle_num == 1) {
// Keep the single-nozzle path anchored at the left nozzle.
}
else if (m_nozzle_num == 2) {
x = right_nozzle_pos.x;
len = len - 14;
}
else {
// Generic N Nozzles
}
dc.DrawLine(((x)), (size.y / 2), x + FromDIP(len), (size.y / 2));
dc.DrawLine(x + FromDIP(len), (0), x + FromDIP(len), (size.y / 2));
dc.DrawLine((x), (size.y / 2), (x), (size.y));
@@ -2544,10 +2751,16 @@ void AMSRoadDownPart::doRender(wxDC& dc)
else{
int x = left_nozzle_pos.x;
int len = m_right_road_length;
if (m_nozzle_num == 2) {
if (m_nozzle_num == 1) {
// Keep the single-nozzle path anchored at the left nozzle.
}
else if (m_nozzle_num == 2) {
x = right_nozzle_pos.x;
len = len - 14;
}
else {
// Generic N Nozzles
}
dc.DrawLine(((x)), (size.y / 2), x + FromDIP(len), (size.y / 2));
dc.DrawLine(x + FromDIP(len), (0), x + FromDIP(len), (size.y / 2));
dc.DrawLine((x), (size.y / 2), (x), (size.y));
@@ -2571,7 +2784,7 @@ void AMSRoadDownPart::doRender(wxDC& dc)
}
void AMSRoadDownPart::UpdatePassRoad(AMSPanelPos pos, int len, AMSPassRoadSTEP step) {
if (m_nozzle_num >= 2){
if (m_nozzle_num == 2){
if (pos == AMSPanelPos::LEFT_PANEL){
if (m_left_road_length == len && m_pass_road_left_step == step){ return; }
m_left_road_length = len;;
@@ -2583,7 +2796,7 @@ void AMSRoadDownPart::UpdatePassRoad(AMSPanelPos pos, int len, AMSPassRoadSTEP s
m_pass_road_right_step = step;
}
}
else{
else if (m_nozzle_num == 1){
if (pos == AMSPanelPos::LEFT_PANEL) {
if (m_left_road_length == len && m_pass_road_left_step == step && m_right_road_length == -1) { return; }
m_left_road_length = len;
@@ -2597,6 +2810,22 @@ void AMSRoadDownPart::UpdatePassRoad(AMSPanelPos pos, int len, AMSPassRoadSTEP s
m_pass_road_right_step = step;
}
}
else {
// Generic N Nozzles
}
Refresh();
}
void AMSRoadDownPart::UpdatePassRoad(int nozzle_id, int len, AMSPassRoadSTEP step)
{
if (nozzle_id < 0) return;
if (nozzle_id >= static_cast<int>(m_generic_road_states.size()))
m_generic_road_states.resize(nozzle_id + 1);
auto &state = m_generic_road_states[nozzle_id];
if (state.road_length == len && state.pass_road_step == step) return;
state.road_length = len;
state.pass_road_step = step;
Refresh();
}
@@ -2615,7 +2844,10 @@ AMSPreview::AMSPreview(wxWindow* parent, wxWindowID id, AMSinfo amsinfo, AMSMode
{
wxWindow::Create(parent, id, pos);
if (itemType == AMSModel::GENERIC_AMS || itemType == AMSModel::AMS_LITE || itemType == AMSModel::N3F_AMS) {
create(parent, id, pos, AMS_PREV_FOUR_SIZE);
if (itemType == AMSModel::GENERIC_AMS && use_generic_ams_layout(itemType))
create(parent, id, pos, wxSize(generic_ams_preview_width(this, static_cast<int>(amsinfo.cans.size())), AMS_PREV_FOUR_SIZE.y));
else
create(parent, id, pos, AMS_PREV_FOUR_SIZE);
}
else {
create(parent, id, pos, AMS_PREV_SINGLE_SIZE);
@@ -2650,7 +2882,13 @@ void AMSPreview::UpdateInfo(AMSinfo amsinfo)
m_amsinfo = amsinfo;
m_ams_item_type = amsinfo.ams_type;
if (m_ams_item_type == AMSModel::GENERIC_AMS || m_ams_item_type == AMSModel::AMS_LITE || m_ams_item_type == AMSModel::N3F_AMS) {
if (m_ams_item_type == AMSModel::GENERIC_AMS && use_generic_ams_layout(m_ams_item_type)) {
const wxSize generic_size(generic_ams_preview_width(this, static_cast<int>(m_amsinfo.cans.size())), AMS_PREV_FOUR_SIZE.y);
SetSize(generic_size);
SetMinSize(generic_size);
SetMaxSize(generic_size);
}
else if (m_ams_item_type == AMSModel::GENERIC_AMS || m_ams_item_type == AMSModel::AMS_LITE || m_ams_item_type == AMSModel::N3F_AMS) {
SetMinSize(AMS_PREV_FOUR_SIZE);
SetMaxSize(AMS_PREV_FOUR_SIZE);
} else {
@@ -2758,8 +2996,41 @@ void AMSPreview::doRender(wxDC &dc)
dc.DrawRoundedRectangle(0, 0, size.x, size.y, FromDIP(3));
auto left = 0;
//four slot
if (m_ams_item_type != AMSModel::EXT_AMS && m_ams_item_type != AMSModel::N3S_AMS){
// Generic AMS previews use the same normalized lane centers as the card row and connector.
if (m_ams_item_type == AMSModel::GENERIC_AMS && use_generic_ams_layout(m_ams_item_type)) {
const int lane_count = static_cast<int>(m_amsinfo.cans.size());
const auto lane_centers = generic_ams_lane_centers(lane_count, size.x);
const int cube_width = FromDIP(9);
const int cube_height = FromDIP(14);
for (int lane = 0; lane < lane_count; ++lane) {
const Caninfo &can = m_amsinfo.cans[lane];
const int cube_left = lane_centers[lane] - cube_width / 2;
const int cube_top = (size.y - cube_height) / 2;
wxRect rect(cube_left, cube_top, cube_width, cube_height);
if (can.material_colour.Alpha() == 0) {
if (wxGetApp().dark_mode())
dc.DrawBitmap(m_ts_bitmap_cube_dark.bmp(), cube_left - FromDIP(1), (size.y - m_ts_bitmap_cube_dark.GetBmpHeight()) / 2);
else
dc.DrawBitmap(m_ts_bitmap_cube.bmp(), cube_left - FromDIP(1), (size.y - m_ts_bitmap_cube.GetBmpHeight()) / 2);
}
else if (can.material_state == AMSCanType::AMS_CAN_TYPE_EMPTY) {
dc.SetPen(wxPen(wxColor(0, 0, 0)));
dc.DrawLine(rect.GetRight() - FromDIP(1), rect.GetTop() + FromDIP(1),
rect.GetLeft() + FromDIP(1), rect.GetBottom() - FromDIP(1));
}
else {
wxColour color = can.material_colour;
change_the_opacity(color);
dc.SetPen(wxPen(*wxTRANSPARENT_PEN));
dc.SetBrush(wxBrush(color));
dc.DrawRoundedRectangle(rect, 2);
}
}
}
// four slot
else if (m_ams_item_type != AMSModel::EXT_AMS && m_ams_item_type != AMSModel::N3S_AMS){
left = FromDIP(8);
for (std::vector<Caninfo>::iterator iter = m_amsinfo.cans.begin(); iter != m_amsinfo.cans.end(); iter++) {
@@ -3158,10 +3429,19 @@ AmsItem::AmsItem(wxWindow *parent,AMSinfo info, AMSModel model, AMSPanelPos pos
m_panel_pos = pos;
if (m_ams_model == AMSModel::GENERIC_AMS){
wxWindow::Create(parent, wxID_ANY, wxDefaultPosition, AMS_CANS_WINDOW_SIZE);
SetSize(AMS_CANS_WINDOW_SIZE);
SetMinSize(AMS_CANS_WINDOW_SIZE);
SetMaxSize(AMS_CANS_WINDOW_SIZE);
const int lane_count = static_cast<int>(m_info.cans.size());
const int width = use_generic_ams_layout(m_ams_model) && lane_count <= 8
? generic_ams_layout_width(this, lane_count)
: FromDIP(264);
const wxSize generic_size(width, FromDIP(174));
wxWindow::Create(parent, wxID_ANY, wxDefaultPosition, generic_size);
SetSize(generic_size);
SetMinSize(generic_size);
// For large lane counts the card is a viewport, not the lane strip. Let
// its parent allocate the available width; the scrolled child retains
// the wider virtual content size.
if (!(use_generic_ams_layout(m_ams_model) && lane_count > 8))
SetMaxSize(generic_size);
}
else{
wxWindow::Create(parent, wxID_ANY, wxDefaultPosition, AMS_SINGLE_CAN_SIZE);
@@ -3187,12 +3467,43 @@ void AmsItem::create(wxWindow *parent)
if (m_ams_model != AMSModel::AMS_LITE) {
sizer_can = new wxBoxSizer(wxHORIZONTAL);
sizer_item = new wxBoxSizer(wxVERTICAL);
auto it = m_info.cans.begin();
for (; it != m_info.cans.end(); it++) {
AddCan(*it, m_can_count, m_info.cans.size(), sizer_can);
const bool generic_layout = use_generic_ams_layout(m_ams_model);
const int lane_count = static_cast<int>(m_info.cans.size());
const int content_width = generic_layout ? generic_ams_layout_width(this, lane_count) : 0;
const bool scroll_lanes = generic_layout && lane_count > 8;
const auto lane_centers = generic_layout
? generic_ams_lane_centers(lane_count, content_width)
: std::vector<int>();
m_can_parent = this;
if (scroll_lanes) {
m_can_scroll = new wxScrolledWindow(this, wxID_ANY);
m_can_scroll->SetScrollRate(FromDIP(10), 0);
m_can_scroll->SetMinSize(wxSize(FromDIP(264), FromDIP(143)));
m_can_scroll->SetBackgroundColour(StateColor::darkModeColorFor(AMS_CONTROL_DEF_LIB_BK_COLOUR));
m_can_scroll_sizer = new wxBoxSizer(wxVERTICAL);
m_can_scroll->SetSizer(m_can_scroll_sizer);
m_can_parent = m_can_scroll;
}
int previous_right = 0;
for (int can_index = 0; can_index < lane_count; ++can_index) {
if (generic_layout) {
const int left = lane_centers[can_index] - FromDIP(26);
sizer_can->AddSpacer(std::max(0, left - previous_right));
AddCan(m_info.cans[can_index], m_can_count, m_info.cans.size(), sizer_can);
previous_right = left + FromDIP(52);
}
else {
AddCan(m_info.cans[can_index], m_can_count, m_info.cans.size(), sizer_can);
}
m_can_count++;
}
it = m_info.cans.begin();
if (generic_layout)
sizer_can->AddSpacer(std::max(0, content_width - previous_right));
if (generic_layout)
sizer_can->SetMinSize(wxSize(content_width, -1));
//auto road_panel = new wxWindow(this, wxID_ANY);
//auto road_panel = new wxPanel(this, wxID_ANY);
//road_panel->SetSize(AMS_CAN_ROAD_SIZE);
@@ -3207,11 +3518,18 @@ void AmsItem::create(wxWindow *parent)
sizer_item->Add(m_ext_image, 0, wxALIGN_CENTER, 0);
}
}
m_panel_road = new AMSRoadUpPart(this, wxID_ANY, m_info, m_ams_model);
m_panel_road = new AMSRoadUpPart(m_can_parent, wxID_ANY, m_info, m_ams_model);
sizer_item->Add(sizer_can, 0, wxALIGN_CENTER_HORIZONTAL, 0);
//sizer_item->Add(m_panel_road, 0, wxALIGN_CENTER_HORIZONTAL, 0);
sizer_item->Add(m_panel_road, 1, wxEXPAND);
if (scroll_lanes) {
m_can_scroll_sizer->Add(sizer_can, 0, wxALIGN_LEFT, 0);
m_can_scroll_sizer->Add(m_panel_road, 0, wxALIGN_LEFT, 0);
sizer_item->Add(m_can_scroll, 1, wxEXPAND);
}
else {
sizer_item->Add(sizer_can, 0, wxALIGN_CENTER_HORIZONTAL, 0);
//sizer_item->Add(m_panel_road, 0, wxALIGN_CENTER_HORIZONTAL, 0);
sizer_item->Add(m_panel_road, 1, wxEXPAND);
}
SetSizer(sizer_item);
}
@@ -3235,13 +3553,17 @@ void AmsItem::create(wxWindow *parent)
Layout();
Fit();
if (m_can_scroll) {
m_can_scroll->Layout();
m_can_scroll->FitInside();
}
Thaw();
//Refresh();
}
void AmsItem::AddCan(Caninfo caninfo, int canindex, int maxcan, wxBoxSizer* sizer)
{
auto amscan = new wxWindow(this, wxID_ANY);
auto amscan = new wxWindow(m_can_parent ? m_can_parent : this, wxID_ANY);
amscan->SetSize(wxSize(FromDIP(52), FromDIP(109)));
amscan->SetMinSize(wxSize(FromDIP(52), FromDIP(109)));
@@ -3322,7 +3644,8 @@ void AmsItem::AddCan(Caninfo caninfo, int canindex, int maxcan, wxBoxSizer* size
amscan->Layout();
amscan->Fit();
sizer->Add(amscan, 0, wxUP | wxLEFT | wxRIGHT, FromDIP(5));
const int margin = use_generic_ams_layout(m_ams_model) ? 0 : FromDIP(5);
sizer->Add(amscan, 0, wxUP | wxLEFT | wxRIGHT, margin);
m_can_lib_list[caninfo.can_id] = m_panel_lib;
//m_can_road_list[caninfo.can_id] = m_panel_road;
@@ -3447,6 +3770,10 @@ void AmsItem::UpdateInfo(AMSinfo info)
}
Layout();
if (m_can_scroll) {
m_can_scroll->Layout();
m_can_scroll->FitInside();
}
}
void AmsItem::SetDefSelectCan()
+28 -6
View File
@@ -30,6 +30,7 @@
#include <wx/string.h>
#include <wx/timer.h>
#include <wx/sizer.h>
#include <wx/scrolwin.h>
#include "slic3r/GUI/DeviceCore/DevFilaSwitch.h" // Orca: DevFilaSwitch::SwitchPos for inlet-aware AMS placement
@@ -96,6 +97,7 @@ enum class AMSRoadShowMode : int {
AMS_ROAD_MODE_SINGLE,
AMS_ROAD_MODE_SINGLE_N3S,
AMS_ROAD_MODE_AMS_LITE,
AMS_ROAD_MODE_GENERIC,
AMS_ROAD_MODE_NONE
};
@@ -393,10 +395,10 @@ public:
bool m_show_state = {false};
wxColour m_colour;
ScalableBitmap m_ams_extruder;
string m_file_name;
std::string m_file_name;
bool m_ams_loading{ false };
void doRender(wxDC &dc);
AMSextruderImage(wxWindow *parent, wxWindowID id, string file_name, const wxSize& size, const wxPoint &pos = wxDefaultPosition);
AMSextruderImage(wxWindow *parent, wxWindowID id, std::string file_name, const wxSize& size, const wxPoint &pos = wxDefaultPosition);
~AMSextruderImage();
};
@@ -444,10 +446,10 @@ public:
bool m_show_state = {false};
wxColour m_colour;
ScalableBitmap m_switcher;
string m_file_name;
std::string m_file_name;
// bool m_ams_loading{ false };
void doRender(wxDC &dc);
SwitcherImage(wxWindow *parent, wxWindowID id, string file_name, const wxSize& size, const wxPoint &pos = wxDefaultPosition);
SwitcherImage(wxWindow *parent, wxWindowID id, std::string file_name, const wxSize& size, const wxPoint &pos = wxDefaultPosition);
~SwitcherImage();
};
@@ -456,7 +458,7 @@ class AMSextruder : public wxWindow
{
private:
int m_nozzle_num = -1;
string m_series_name;
std::string m_series_name;
public:
void TurnOn(wxColour col);
@@ -466,7 +468,7 @@ public:
void msw_rescale();
void has_ams(bool hams) {m_has_vams = hams; Refresh();};
void no_ams_mode(bool mode) {m_none_ams_mode = mode; Refresh();};
bool updateNozzleNum(int nozzle_num, const std::string& series_name = string());
bool updateNozzleNum(int nozzle_num, const std::string& series_name = std::string());
bool m_none_ams_mode{true};
bool m_has_vams{false};
@@ -480,6 +482,7 @@ public:
//AMSextruderImage *m_amsSextruder{nullptr};
AMSextruderImage* m_left_extruder = nullptr;
AMSextruderImage* m_right_extruder = nullptr;
std::vector<AMSextruderImage*> m_nozzle_extruders;
AMSextruder(wxWindow *parent, wxWindowID id, int nozzle_num, const wxPoint &pos = wxDefaultPosition, const wxSize &size = wxDefaultSize);
~AMSextruder();
@@ -681,11 +684,16 @@ public:
// void Update(AMSRoadDownPartMode nozzle, AMSRoadShowMode left_mode, AMSRoadShowMode right_mode, int left_len, int right_len);
void UpdateLeft(int nozzle_num, AMSRoadShowMode mode);
void UpdateRight(int nozzle_num, AMSRoadShowMode mode);
void SetNozzleCount(int nozzle_num);
void SetSingleSideLayout(bool enabled, AMSPanelPos pos = AMSPanelPos::LEFT_PANEL);
void UpdateNozzle(int nozzle_id, AMSRoadShowMode mode);
void OnVamsLoading(bool load, wxColour col = AMS_CONTROL_GRAY500);
void SetPassRoadColour(bool left, wxColour col);
void SetPassRoadColour(int nozzle_id, wxColour col);
void SetShowMode(AMSRoadShowMode left_mode, AMSRoadShowMode right_mode);
void UpdatePassRoad(AMSPanelPos pos, int len, AMSPassRoadSTEP step);
void UpdatePassRoad(int nozzle_id, int len, AMSPassRoadSTEP step);
void paintEvent(wxPaintEvent& evt);
void render(wxDC& dc);
@@ -698,6 +706,8 @@ private:
AMSRoadShowMode m_left_rode_mode = {AMSRoadShowMode::AMS_ROAD_MODE_FOUR};
AMSRoadShowMode m_right_rode_mode = {AMSRoadShowMode::AMS_ROAD_MODE_FOUR};
bool m_selected = {false};
bool m_single_side_layout = {false};
AMSPanelPos m_single_side_pos = {AMSPanelPos::LEFT_PANEL};
int m_left_road_length = {-1};
int m_right_road_length = {-1};
@@ -705,6 +715,15 @@ private:
AMSPassRoadSTEP m_pass_road_right_step = {AMSPassRoadSTEP::AMS_ROAD_STEP_NONE};
std::map<int, wxColour> m_road_color;
struct GenericRoadState
{
AMSRoadShowMode mode{AMSRoadShowMode::AMS_ROAD_MODE_NONE};
int road_length{-1};
AMSPassRoadSTEP pass_road_step{AMSPassRoadSTEP::AMS_ROAD_STEP_NONE};
wxColour road_color{AMS_CONTROL_GRAY500};
};
std::vector<GenericRoadState> m_generic_road_states;
};
/*************************************************
@@ -864,6 +883,9 @@ private:
std::map<std::string, AMSLib*> m_can_lib_list;
//std::map<std::string, AMSRoad*> m_can_road_list;
wxScrolledWindow* m_can_scroll = { nullptr };
wxBoxSizer* m_can_scroll_sizer = { nullptr };
wxWindow* m_can_parent = { nullptr };
AMSRoadUpPart* m_panel_road = { nullptr };
std::map<std::string, AMSrefresh*> m_can_refresh_list;
AMSHumidity* m_humidity = { nullptr };
+18 -3
View File
@@ -420,6 +420,14 @@ ExtruderBadge::ExtruderBadge(wxWindow* parent) : wxPanel(parent)
void ExtruderBadge::SetExtruderInfo(int extruder_id, const std::string& diameter, const NozzleVolumeType& volume_type)
{
// The badge renders exactly two extruders (left/right). A generic non-BBL printer with
// N > 2 can report an out-of-range id here (see MultiNozzleStatusTable::UpdateRackInfo);
// reject it rather than writing past the two-element lists.
if (extruder_id < 0 || extruder_id >= static_cast<int>(m_diameter_list.size())) {
BOOST_LOG_TRIVIAL(warning) << __FUNCTION__ << ": out-of-range extruder_id " << extruder_id;
return;
}
m_diameter_list[extruder_id] = diameter;
m_volume_type_list[extruder_id] = volume_type;
@@ -867,14 +875,21 @@ void MultiNozzleStatusTable::UpdateRackInfo(std::weak_ptr<DevNozzleRack> rack)
bool has_right = false;
for (auto& elem : nozzles_in_extruder) {
auto& nozzle = elem.second;
int extruder_id = nozzle.AtLeftExtruder() ? 0 : 1;
if (nozzle.AtRightExtruder())
has_right = true;
int extruder_id{};
if (wxGetApp().preset_bundle->is_bbl_vendor()) {
extruder_id = nozzle.AtLeftExtruder() ? 0 : 1;
if (nozzle.AtRightExtruder())
has_right = true;
}
else
extruder_id = nozzle.GetExtruderId();
NozzleVolumeType volume_type = DevNozzle::ToNozzleVolumeType(nozzle.m_nozzle_flow);
m_badge->SetExtruderInfo(extruder_id, format_diameter_to_str(nozzle.GetNozzleDiameter()), volume_type);
}
// TODO: Update for N extruders
m_badge->SetExtruderValid(has_right);
}
}
+3
View File
@@ -184,6 +184,9 @@ void TempInput::SetFinish()
wxCommandEvent event(wxCUSTOMEVT_SET_TEMP_FINISH);
event.SetInt(temp_type);
event.SetString(wxString::Format("%d", m_input_type));
// N-extruder temp controls all share TEMP_OF_NORMAL_TYPE, so the string payload above can't
// tell them apart; carry widget identity so the listener can find which one fired.
event.SetEventObject(this);
wxPostEvent(this->GetParent(), event);
}
+2 -1
View File
@@ -596,7 +596,8 @@ PrintParams_Legacy BBLNetworkPlugin::as_legacy(PrintParams& param)
}
// Every PrintParams field except the four the 02.08.01 series added
// (task_timelapse_use_internal, extruder_cali_manual_mode, svc_context, slicer_uid).
// (task_timelapse_use_internal, extruder_cali_manual_mode, svc_context, slicer_uid) and the
// queue_plate_id 02.08.02 appended.
PrintParams_0203 BBLNetworkPlugin::as_0203(PrintParams& param)
{
PrintParams_0203 p;
+100 -5
View File
@@ -1057,6 +1057,25 @@ int MoonrakerPrinterAgent::handle_request(const std::string& dev_id, const std::
}
}
// Tool selection is printer-specific G-code. Moonraker forwards the
// conventional T<index> command to Klipper, where the printer can
// implement logical selection or a physical toolhead attach macro.
if (cmd == "select_extruder") {
if (!json["print"].contains("extruder_index") || !json["print"]["extruder_index"].is_number_integer()) {
BOOST_LOG_TRIVIAL(error) << "MoonrakerPrinterAgent: select_extruder missing integer extruder_index, full json: " << json_str;
return BAMBU_NETWORK_ERR_INVALID_RESULT;
}
const int extruder_idx = json["print"]["extruder_index"].get<int>();
if (extruder_idx < 0) {
BOOST_LOG_TRIVIAL(error) << "MoonrakerPrinterAgent: select_extruder received negative extruder_index: " << extruder_idx;
return BAMBU_NETWORK_ERR_INVALID_RESULT;
}
send_gcode_async(dev_id, "T" + std::to_string(extruder_idx));
return BAMBU_NETWORK_SUCCESS;
}
if (cmd == "home") {
send_gcode_async(dev_id, "G28");
return BAMBU_NETWORK_SUCCESS;
@@ -1153,7 +1172,7 @@ bool MoonrakerPrinterAgent::query_printer_status(const std::string& base_url,
nlohmann::json& status,
std::string& error) const
{
std::string url = join_url(base_url, "/printer/objects/query?print_stats&virtual_sdcard&extruder&heater_bed&fan");
std::string url = join_url(base_url, "/printer/objects/query?print_stats&virtual_sdcard&toolhead&extruder&heater_bed&fan");
std::string response_body;
bool success = false;
@@ -1566,11 +1585,12 @@ void MoonrakerPrinterAgent::run_status_stream(std::string dev_id, std::string ba
}
for (const auto& name : this->available_objects) {
if (name == "extruder" || name.rfind("extruder", 0) == 0) {
const bool is_main_extruder = name == "extruder";
const bool is_additional_extruder =
name.rfind("extruder", 0) == 0 && name.size() > 8 &&
std::all_of(name.begin() + 8, name.end(), [](unsigned char c) { return std::isdigit(c) != 0; });
if (is_main_extruder || is_additional_extruder) {
subscribe_objects.insert(name);
if (name == "extruder") {
break;
}
}
}
} else {
@@ -1866,6 +1886,81 @@ nlohmann::json MoonrakerPrinterAgent::build_print_payload_locked() const
}
}
// MachineObject::parse_new_info() only parses device.extruder when the four
// legacy new-protocol fields are present. Moonraker has no equivalents, so
// empty values act as compatibility markers without enabling any features.
std::map<int, const nlohmann::json*> extruder_status;
for (const auto& item : status_cache.items()) {
int extruder_id = -1;
if (item.key() == "extruder") {
extruder_id = 0;
} else if (item.key().rfind("extruder", 0) == 0 && item.key().size() > 8 &&
std::all_of(item.key().begin() + 8, item.key().end(), [](unsigned char c) { return std::isdigit(c) != 0; })) {
try {
extruder_id = std::stoi(item.key().substr(8));
} catch (...) {
extruder_id = -1;
}
}
if (extruder_id >= 0 && item.value().is_object()) {
extruder_status.emplace(extruder_id, &item.value());
}
}
if (!extruder_status.empty()) {
auto& device_extruder = payload["print"]["device"]["extruder"];
int current_extruder_id = 0;
if (status_cache.contains("toolhead") && status_cache["toolhead"].is_object() &&
status_cache["toolhead"].contains("extruder") && status_cache["toolhead"]["extruder"].is_string()) {
const std::string active_extruder = status_cache["toolhead"]["extruder"].get<std::string>();
if (active_extruder != "extruder" && active_extruder.rfind("extruder", 0) == 0 && active_extruder.size() > 8 &&
std::all_of(active_extruder.begin() + 8, active_extruder.end(), [](unsigned char c) { return std::isdigit(c) != 0; })) {
try {
const int parsed_id = std::stoi(active_extruder.substr(8));
if (parsed_id >= 0 && parsed_id < 16 && extruder_status.count(parsed_id) != 0) {
current_extruder_id = parsed_id;
}
} catch (...) {
// Keep the main extruder as the fallback for malformed status data.
}
}
}
const int extruder_count = std::min<int>(static_cast<int>(extruder_status.size()), 0xF);
device_extruder["state"] = extruder_count | (current_extruder_id << 4);
device_extruder["info"] = nlohmann::json::array();
auto encoded_temperature = [](const nlohmann::json& status, const char* key) {
if (!status.contains(key) || !status[key].is_number()) {
return 0;
}
const double temperature = status[key].get<double>();
return std::clamp(static_cast<int>(temperature + 0.5), 0, 65535);
};
for (const auto& [extruder_id, status] : extruder_status) {
const int current_temperature = encoded_temperature(*status, "temperature");
const int target_temperature = encoded_temperature(*status, "target");
device_extruder["info"].push_back({
{"id", extruder_id},
{"filam_bak", nlohmann::json::array()},
{"info", 1 << 3}, // The configured Moonraker extruder has a toolhead.
{"temp", current_temperature | (target_temperature << 16)},
{"spre", 0},
{"snow", 0},
{"star", 0},
{"stat", 0},
{"hnow", 0},
});
}
payload["print"]["cfg"] = "";
payload["print"]["fun"] = "";
payload["print"]["aux"] = "";
payload["print"]["stat"] = "";
}
if (status_cache.contains("heater_bed") && status_cache["heater_bed"].is_object()) {
const auto& bed = status_cache["heater_bed"];
if (bed.contains("temperature") && bed["temperature"].is_number()) {
+7 -4
View File
@@ -336,6 +336,7 @@ struct PrintParams {
bool try_emmc_print;
std::string svc_context;
std::string slicer_uid;
std::string queue_plate_id;
};
struct TaskQueryParams
@@ -412,7 +413,7 @@ enum class NetworkAbi {
Unsupported, // no generation in this build can call it - never dispatch through it
Legacy, // 01.10.01: PrintParams_Legacy; send_message/send_message_to_printer take no flag
V0203, // 02.03.00: PrintParams_0203; bind takes no dev_model
Current, // 02.08.01: the layouts and signatures this build declares directly
Current, // 02.08.04: the layouts and signatures this build declares directly
};
struct NetworkLibraryVersion {
@@ -425,10 +426,12 @@ struct NetworkLibraryVersion {
};
// Every row names the generation that can call it, so a series can never be offered without a
// host-side ABI for it. Series with no generation - 02.01.01, 02.00.02 and older - must stay out;
// is_supported_network_version() is the gate that keeps them from loading.
// host-side ABI for it. Series with no generation - 02.08.01 (whose PrintParams lacks the
// queue_plate_id that 02.08.02 appended, and whose malformed bind table macOS 27 refuses to
// load), 02.01.01, 02.00.02 and older - must stay out; is_supported_network_version() is the
// gate that keeps them from loading.
static const NetworkLibraryVersion AVAILABLE_NETWORK_VERSIONS[] = {
{"02.08.01", "02.08.01", nullptr, true, nullptr, NetworkAbi::Current},
{"02.08.04", "02.08.04", nullptr, true, nullptr, NetworkAbi::Current},
{"02.03.00", "02.03.00", nullptr, false,
"An older plug-in series. Features that need newer plug-in support, such as print-failure "
"snapshots in the device error dialog, are unavailable.", NetworkAbi::V0203},
-3
View File
@@ -7,9 +7,6 @@
#include "nlohmann/json.hpp"
using namespace std;
class json_diff
{
private:
+104
View File
@@ -538,6 +538,110 @@ SCENARIO("update_diff_values_to_child_config keeps a child's values on variants
}
}
SCENARIO("update_non_diff_values_to_base_config keeps a project's changed values on variants it does not list",
"[Config][Variant]") {
std::set<std::string> no_keys;
auto variants = [](std::initializer_list<std::string> names) { return new Slic3r::ConfigOptionStrings(names); };
GIVEN("A filament base with three variants") {
Slic3r::DynamicPrintConfig base;
base.set_key_value("filament_extruder_variant",
variants({"Direct Drive Standard", "Bowden Standard", "Direct Drive High Flow"}));
base.set_deserialize_strict("nozzle_temperature", "220,220,220");
WHEN("the project was saved when the base had only its first variant") {
Slic3r::DynamicPrintConfig project;
project.set_key_value("filament_extruder_variant", variants({"Direct Drive Standard"}));
project.set_deserialize_strict("nozzle_temperature", "199");
AND_WHEN("the project lists the value as changed") {
project.update_non_diff_values_to_base_config(base, project.keys(), {"nozzle_temperature"}, "", "filament_extruder_variant",
Slic3r::filament_options_with_variant, no_keys);
THEN("the project's value applies to every variant") {
REQUIRE(project.opt_serialize("nozzle_temperature") == "199,199,199");
}
}
AND_WHEN("the project does not list the value as changed") {
project.update_non_diff_values_to_base_config(base, project.keys(), {}, "", "filament_extruder_variant",
Slic3r::filament_options_with_variant, no_keys);
THEN("the base's values replace it") {
REQUIRE(project.opt_serialize("nozzle_temperature") == "220,220,220");
}
}
}
WHEN("the project lists every variant, in another order") {
Slic3r::DynamicPrintConfig project;
project.set_key_value("filament_extruder_variant",
variants({"Bowden Standard", "Direct Drive High Flow", "Direct Drive Standard"}));
project.set_deserialize_strict("nozzle_temperature", "190,205,199");
project.update_non_diff_values_to_base_config(base, project.keys(), {"nozzle_temperature"}, "", "filament_extruder_variant",
Slic3r::filament_options_with_variant, no_keys);
THEN("each variant keeps its own value") {
REQUIRE(project.opt_serialize("nozzle_temperature") == "199,190,205");
}
}
WHEN("the project lists no variants") {
Slic3r::DynamicPrintConfig project;
project.set_deserialize_strict("nozzle_temperature", "199");
project.update_non_diff_values_to_base_config(base, project.keys(), {"nozzle_temperature"}, "", "filament_extruder_variant",
Slic3r::filament_options_with_variant, no_keys);
THEN("the project's value applies to every variant") {
REQUIRE(project.opt_serialize("nozzle_temperature") == "199,199,199");
}
}
}
GIVEN("A two-extruder printer base with two variants per extruder") {
Slic3r::DynamicPrintConfig base;
base.set_key_value("printer_extruder_variant",
variants({"Direct Drive Standard", "Direct Drive High Flow", "Direct Drive Standard", "Direct Drive High Flow"}));
base.set_key_value("printer_extruder_id", new Slic3r::ConfigOptionInts({1, 1, 2, 2}));
base.set_deserialize_strict("retraction_length", "0.8,0.8,0.8,0.8");
WHEN("the project lists only the Standard variant of each extruder") {
Slic3r::DynamicPrintConfig project;
project.set_key_value("printer_extruder_variant", variants({"Direct Drive Standard", "Direct Drive Standard"}));
project.set_key_value("printer_extruder_id", new Slic3r::ConfigOptionInts({1, 2}));
project.set_deserialize_strict("retraction_length", "1.1,2.2");
project.update_non_diff_values_to_base_config(base, project.keys(), {"retraction_length"}, "printer_extruder_id", "printer_extruder_variant",
Slic3r::printer_options_with_variant_1,
Slic3r::printer_options_with_variant_2);
THEN("each extruder's High Flow variant takes that extruder's value") {
REQUIRE(project.opt_serialize("retraction_length") == "1.1,1.1,2.2,2.2");
}
}
WHEN("the project lists only the Standard variant of each extruder, and the variant lists as changed") {
Slic3r::DynamicPrintConfig project;
project.set_key_value("printer_extruder_variant", variants({"Direct Drive Standard", "Direct Drive Standard"}));
project.set_key_value("printer_extruder_id", new Slic3r::ConfigOptionInts({1, 2}));
project.set_deserialize_strict("machine_max_speed_x", "300,100,400,150");
base.set_deserialize_strict("machine_max_speed_x", "500,200,500,200,500,200,500,200");
project.update_non_diff_values_to_base_config(base, project.keys(),
{"machine_max_speed_x", "printer_extruder_id", "printer_extruder_variant"},
"printer_extruder_id", "printer_extruder_variant",
Slic3r::printer_options_with_variant_1,
Slic3r::printer_options_with_variant_2);
THEN("the variant lists are the base's") {
REQUIRE(project.opt_serialize("printer_extruder_variant") == base.opt_serialize("printer_extruder_variant"));
REQUIRE(project.opt_serialize("printer_extruder_id") == "1,1,2,2");
}
THEN("each extruder's High Flow variant takes that extruder's pair of limits") {
REQUIRE(project.opt_serialize("machine_max_speed_x") == "300,100,300,100,400,150,400,150");
}
}
WHEN("the project lists no variants") {
Slic3r::DynamicPrintConfig project;
project.set_deserialize_strict("retraction_length", "1.1");
project.update_non_diff_values_to_base_config(base, project.keys(), {"retraction_length"}, "printer_extruder_id", "printer_extruder_variant",
Slic3r::printer_options_with_variant_1,
Slic3r::printer_options_with_variant_2);
THEN("only the first extruder's variants take the project's value") {
REQUIRE(project.opt_serialize("retraction_length") == "1.1,1.1,0.8,0.8");
}
}
}
}
// SCENARIO("DynamicPrintConfig JSON serialization", "[Config]") {
// WHEN("DynamicPrintConfig is serialized and deserialized") {
// auto now = std::chrono::high_resolution_clock::now();
@@ -799,3 +799,67 @@ TEST_CASE("A per-variant filament option read with a single value gives it to ev
config.load_from_ini_string("pressure_advance = 0.021", ForwardCompatibilitySubstitutionRule::Disable);
REQUIRE(config.option<ConfigOptionFloats>("pressure_advance")->values == std::vector<double>({0.021, 0.021, 0.021}));
}
// Machine limits (printer_options_with_variant_2) hold a (normal, silent) pair per printer variant, so the
// printer Tab's Motion ability page can edit each extruder's own limits.
TEST_CASE("set_num_extruders gives every printer variant its own pair of machine limits", "[Config]")
{
DynamicPrintConfig config = DynamicPrintConfig::full_print_config();
auto speed_x = [&config]() { return config.option<ConfigOptionFloats>("machine_max_speed_x")->values; };
SECTION("a new extruder's pair is padded with the first value, like the other variant keys") {
config.option<ConfigOptionFloats>("machine_max_speed_x")->values = {500., 200.};
config.set_num_extruders(3);
REQUIRE(config.option<ConfigOptionStrings>("printer_extruder_variant")->size() == 3);
REQUIRE(speed_x() == std::vector<double>({500., 200., 500., 500., 500., 500.}));
}
SECTION("per-extruder pairs are kept, and removing an extruder removes its pair") {
config.option<ConfigOptionFloats>("machine_max_speed_x")->values = {500., 200., 400., 150., 300., 100.};
config.set_num_extruders(3);
REQUIRE(speed_x() == std::vector<double>({500., 200., 400., 150., 300., 100.}));
config.set_num_extruders(2);
REQUIRE(speed_x() == std::vector<double>({500., 200., 400., 150.}));
}
SECTION("a printer with nozzle variants gets a pair per variant") {
// 2 extruders x 2 variants each = 4 variants
config.option<ConfigOptionStrings>("extruder_variant_list", true)->values = {"Direct Drive Standard,Direct Drive High Flow",
"Direct Drive Standard,Direct Drive High Flow"};
const std::vector<double> per_variant = {500., 200., 510., 210., 520., 220., 530., 230.};
config.option<ConfigOptionFloats>("machine_max_speed_x")->values = per_variant;
config.set_num_extruders(2);
REQUIRE(speed_x() == per_variant);
config.option<ConfigOptionFloats>("machine_max_speed_x")->values = {500., 200.};
config.set_num_extruders(2);
REQUIRE(speed_x() == std::vector<double>({500., 200., 500., 500., 500., 500., 500., 500.}));
}
}
// The device drying options hold several values per filament, as many as each filament preset gives.
TEST_CASE("The device drying options are rebuilt as each filament's values in slot order", "[Config]")
{
DynamicPrintConfig two_values, one_value, no_value;
two_values.option<ConfigOptionStrings>("filament_dev_ams_drying_ams_limitations", true)->values = {"1", "0"};
two_values.option<ConfigOptionFloats>("filament_dev_ams_drying_temperature", true)->values = {45., 45., 55., 55.};
one_value.option<ConfigOptionStrings>("filament_dev_ams_drying_ams_limitations", true)->values = {"1"};
one_value.option<ConfigOptionFloats>("filament_dev_ams_drying_temperature", true)->values = {65., 65., 75., 75.};
// values a project stored for three other filaments
DynamicPrintConfig config;
config.option<ConfigOptionStrings>("filament_dev_ams_drying_ams_limitations", true)->values = {"0", "0", "0"};
config.option<ConfigOptionFloats>("filament_dev_chamber_drying_time", true)->values = {12., 8., 12.};
set_filament_dev_options(config, {&two_values, &one_value, &two_values});
REQUIRE(config.option<ConfigOptionStrings>("filament_dev_ams_drying_ams_limitations")->values ==
std::vector<std::string>({"1", "0", "1", "1", "0"}));
REQUIRE(config.option<ConfigOptionFloats>("filament_dev_ams_drying_temperature")->values ==
std::vector<double>({45., 45., 55., 55., 65., 65., 75., 75., 45., 45., 55., 55.}));
// an option no filament defines keeps the stored values
REQUIRE(config.option<ConfigOptionFloats>("filament_dev_chamber_drying_time")->values == std::vector<double>({12., 8., 12.}));
// a filament without the option takes the option's default
set_filament_dev_options(config, {&two_values, &no_value});
REQUIRE(config.option<ConfigOptionStrings>("filament_dev_ams_drying_ams_limitations")->values == std::vector<std::string>({"1", "0", ""}));
}
@@ -6228,8 +6228,9 @@ TEST_CASE("A per-variant project value maps onto its base preset's variant layou
base_finder(&base, calls));
CHECK(config.option<ConfigOptionStrings>("print_extruder_variant")->values ==
std::vector<std::string>{"Direct Drive Standard", "Direct Drive High Flow"});
// The listed key keeps the project's Standard value and takes High Flow from the base.
check_double_vector(config.option<ConfigOptionFloats>("outer_wall_speed")->values, {100., 300.});
// The listed key keeps the project's Standard value, and High Flow, which the project does not
// list, takes it too, as a user preset's value does.
check_double_vector(config.option<ConfigOptionFloats>("outer_wall_speed")->values, {100., 100.});
check_double_vector(config.option<ConfigOptionFloats>("inner_wall_speed")->values, {250., 350.});
}
+1
View File
@@ -5,6 +5,7 @@ add_executable(${_TEST_NAME}_tests
test_config_value_formatter.cpp
test_creality_cfs_match.cpp
test_dev_mapping.cpp
test_dev_extder_system.cpp
test_filament_bitmap_utils.cpp
test_device_progress.cpp
test_device_manager_integration.cpp
@@ -0,0 +1,86 @@
// Match the include environment that libslic3r_gui TUs get from pchheader.hpp: Windows.h with
// WIN32_LEAN_AND_MEAN/NOMINMAX must come first so rpcndr.h's `byte` is processed before <cstddef>
// makes std::byte a competing candidate (otherwise the Windows COM headers pulled in via
// DeviceManager.hpp error with an ambiguous `byte`). wx/timer.h must precede DeviceManager.hpp,
// which includes DeviceErrorDialog.hpp (uses wxTimerEvent) before its own wx/timer.h include.
#include "catch2/catch_message.hpp"
#include <string>
#include <utility>
#include <vector>
#ifdef WIN32
#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
#endif
#ifndef NOMINMAX
#define NOMINMAX
#endif
#include <Windows.h>
#endif
#include <catch2/catch_test_macros.hpp>
#include <catch2/catch_all.hpp>
#include <wx/timer.h>
#include "slic3r/GUI/DeviceManager.hpp"
#include "slic3r/GUI/DeviceCore/DevExtruderSystem.h"
#include <nlohmann/json.hpp>
using json = nlohmann::json;
using namespace Slic3r;
TEST_CASE("Extruder state decodes a generic toolhead set", "[DevExtderSystem]")
{
MachineObject obj(nullptr, nullptr, "test", "test_dev", "127.0.0.1");
// device.extruder.state packing: count 0..3, current 4..7, target 8..11,
// switch 12..14, loading 15..18, busy 19.
const int extruder_count = 4;
const int current_extruder = 2;
const int switch_state = ES_BUSY;
const int loading_extruder = 1;
const int state = extruder_count | (current_extruder << 4) | (3 << 8) |
(switch_state << 12) | (loading_extruder << 15) | (1 << 19);
json extruder = { {"state", state}, {"info", json::array()} };
for (int id = 0; id < extruder_count; ++id) {
// info: bit 1 has filament, bit 3 nozzle present.
// temp: current 0..15, target 16..31. spre/snow/star: slot 0..7, ams 8..15.
extruder["info"].push_back({
{"id", id},
{"filam_bak", json::array({id})},
{"info", (id % 2 == 0 ? 2 : 0) | 8},
{"temp", (200 + id) | ((250 + id) << 16)},
{"spre", 10 << 8},
{"snow", id | ((20 + id) << 8)},
{"star", 30 << 8},
{"stat", 0},
{"hnow", id},
});
}
ExtderSystemParser::ParseV2_0(extruder, obj.GetExtderSystem());
REQUIRE(obj.GetExtderSystem()->GetTotalExtderCount() == extruder_count);
CHECK(obj.GetExtderSystem()->GetCurrentExtderId() == current_extruder);
CHECK(static_cast<int>(obj.GetExtderSystem()->GetSwitchState()) == switch_state);
CHECK(obj.GetExtderSystem()->GetLoadingExtderId() == loading_extruder);
CHECK(obj.GetExtderSystem()->IsBusyLoading());
for (int id = 0; id < extruder_count; ++id) {
const auto ext = obj.GetExtderSystem()->GetExtderById(id);
REQUIRE(ext.has_value());
INFO("extruder " << id);
CHECK(ext->GetExtId() == id);
CHECK(ext->GetCurrentTemp() == 200 + id);
CHECK(ext->GetTargetTemp() == 250 + id);
CHECK(ext->HasFilamentInExt() == (id % 2 == 0));
CHECK(ext->GetFilamBackup() == std::vector<int>{id});
CHECK(ext->GetSlotPre().ams_id == "10");
CHECK(ext->GetSlotNow().ams_id == std::to_string(20 + id));
CHECK(ext->GetSlotNow().slot_id == std::to_string(id));
CHECK(ext->GetSlotTarget().ams_id == "30");
CHECK(ext->GetNozzleId() == id);
}
}
+159
View File
@@ -3,6 +3,8 @@
// makes std::byte a competing candidate (otherwise the Windows COM headers pulled in via
// DeviceManager.hpp error with an ambiguous `byte`). wx/timer.h must precede DeviceManager.hpp,
// which includes DeviceErrorDialog.hpp (uses wxTimerEvent) before its own wx/timer.h include.
#include <string>
#include <utility>
#ifdef WIN32
#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
@@ -17,6 +19,7 @@
#include "slic3r/GUI/DeviceCore/DevDefs.h"
#include "libslic3r/ProjectTask.hpp"
#include <vector>
#include <set>
#include <catch2/catch_message.hpp>
#include <catch2/catch_all.hpp>
@@ -65,6 +68,162 @@ TEST_CASE("AMS tray placeholder state follows the latest status", "[DevFilaSyste
CHECK_FALSE(tray->is_slot_placeholder);
}
TEST_CASE("Agent filament slots preserve every extruder and AMS binding", "[DevFilaSystem]")
{
MachineObject obj(nullptr, nullptr, "test", "test_dev", "127.0.0.1");
const json filament = json::parse(R"({
"units": [
{ "id": "0", "extruder": 0, "slots": [
{ "index": 0, "loaded": true, "material": "PLA", "color": "FF5733FF" },
{ "index": 1, "loaded": true, "material": "PLA", "color": "33C1FFFF" },
{ "index": 2, "loaded": true, "material": "PLA", "color": "8D33FFFF" },
{ "index": 3, "loaded": false, "material": "", "color": "00000000" }
] },
{ "id": "1", "extruder": 1, "slots": [
{ "index": 0, "loaded": true, "material": "ASA", "color": "33C1FFFF" },
{ "index": 1, "loaded": true, "material": "ASA", "color": "8D33FFFF" },
{ "index": 2, "loaded": true, "material": "ASA", "color": "FF5733FF" },
{ "index": 3, "loaded": false, "material": "", "color": "00000000" }
] },
{ "id": "2", "extruder": 2, "slots": [
{ "index": 0, "loaded": true, "material": "ABS", "color": "8D33FFFF" },
{ "index": 1, "loaded": true, "material": "ABS", "color": "FF5733FF" },
{ "index": 2, "loaded": true, "material": "ABS", "color": "33C1FFFF" },
{ "index": 3, "loaded": false, "material": "", "color": "00000000" }
] },
{ "id": "3", "extruder": 3, "slots": [
{ "index": 0, "loaded": true, "material": "PETG", "color": "FF5733FF" },
{ "index": 1, "loaded": true, "material": "PETG", "color": "33C1FFFF" },
{ "index": 2, "loaded": true, "material": "PETG", "color": "8D33FFFF" },
{ "index": 3, "loaded": false, "material": "", "color": "00000000" }
] }
],
"external": [
{ "extruder": 3, "loaded": true, "material": "PETG", "preset_id": "P3" },
{ "extruder": 0, "loaded": true, "material": "PLA", "preset_id": "P0" },
{ "extruder": 2, "loaded": false, "material": "ABS", "preset_id": "P2" },
{ "extruder": 1, "loaded": true, "material": "ASA", "preset_id": "P1" }
]
})");
DevFilaSystemParser::ParseAgentFilament(filament, &obj, obj.GetFilaSystem().get());
REQUIRE(obj.vt_slot.size() == 4);
CHECK(obj.vt_slot[0].id == "255");
CHECK(obj.vt_slot[1].id == "254");
CHECK(obj.vt_slot[2].id == "253");
CHECK(obj.vt_slot[3].id == "252");
CHECK(obj.vt_slot[0].setting_id == "P0");
CHECK(obj.vt_slot[1].setting_id == "P1");
CHECK(obj.vt_slot[2].setting_id == "P2");
CHECK(obj.vt_slot[3].setting_id == "P3");
REQUIRE(obj.GetFilaSystem()->GetAmsCount() == 4);
for (int extruder = 0; extruder < 4; ++extruder) {
const auto& ams = obj.GetFilaSystem()->GetAmsList().at(std::to_string(extruder));
CHECK(ams->GetExtruderId() == extruder);
CHECK(ams->GetBindedExtruderSet() == std::set<int>{extruder});
REQUIRE(ams->GetTrays().size() == 4);
CHECK(ams->GetTrays().at("0")->color != ams->GetTrays().at("1")->color);
CHECK(ams->GetTrays().at("1")->color != ams->GetTrays().at("2")->color);
CHECK_FALSE(ams->GetTrays().at("3")->is_exists);
}
CHECK(obj.contains_tray("253", "0"));
CHECK(obj.get_tray("253", "0").setting_id == "P2");
CHECK(obj.GetFilaSystem()->GetExtruderIdByAmsId("253") == 2);
const auto tray_index_map = obj.GetFilaSystem()->GetTrayIndexMap();
REQUIRE(tray_index_map.count(252) == 1);
CHECK(tray_index_map.at(252).first == 252);
CHECK(tray_index_map.at(252).second == 0);
}
TEST_CASE("Generic AMS tray index map uses cumulative lane counts", "[DevMapping]")
{
MachineObject obj(nullptr, nullptr, "test", "test_dev", "127.0.0.1");
// A well-formed generic payload pairs "units" with "external". An empty external array resets
// vt_slot, clearing the default virtual tray (255) MachineObject's constructor seeds; otherwise
// that phantom external tray lands in GetTrayIndexMap and inflates the map size.
json filament = { {"units", json::array()}, {"external", json::array()} };
for (int ams_id = 0; ams_id < 2; ++ams_id) {
json unit = {
{"id", std::to_string(ams_id)},
{"extruder", ams_id},
{"slots", json::array()}
};
for (int slot_id = 0; slot_id < 5; ++slot_id)
unit["slots"].push_back({{"index", slot_id}, {"loaded", false}});
filament["units"].push_back(std::move(unit));
}
DevFilaSystemParser::ParseAgentFilament(filament, &obj, obj.GetFilaSystem().get());
const auto tray_index_map = obj.GetFilaSystem()->GetTrayIndexMap();
REQUIRE(tray_index_map.size() == 10);
CHECK(tray_index_map.at(0).first == 0);
CHECK(tray_index_map.at(0).second == 0);
CHECK(tray_index_map.at(4).first == 0);
CHECK(tray_index_map.at(4).second == 4);
CHECK(tray_index_map.at(5).first == 1);
CHECK(tray_index_map.at(5).second == 0);
CHECK(tray_index_map.at(9).first == 1);
CHECK(tray_index_map.at(9).second == 4);
}
TEST_CASE("Generic tray index map handles unequal per-unit slot counts", "[DevMapping]")
{
MachineObject obj(nullptr, nullptr, "test", "test_dev", "127.0.0.1");
// An "external" array must accompany "units" so vt_slot is reset (see the cumulative test above).
json filament = { {"units", json::array()}, {"external", json::array()} };
const std::vector<int> slot_counts = {4, 12};
for (int ams_id = 0; ams_id < static_cast<int>(slot_counts.size()); ++ams_id) {
json unit = { {"id", std::to_string(ams_id)}, {"extruder", ams_id}, {"slots", json::array()} };
for (int slot_id = 0; slot_id < slot_counts[ams_id]; ++slot_id)
unit["slots"].push_back({{"index", slot_id}, {"loaded", false}});
filament["units"].push_back(std::move(unit));
}
DevFilaSystemParser::ParseAgentFilament(filament, &obj, obj.GetFilaSystem().get());
const auto tray_index_map = obj.GetFilaSystem()->GetTrayIndexMap();
REQUIRE(tray_index_map.size() == 16);
// Unit 0 occupies global lanes 0..3; unit 1 continues at 4..15 because the counter advances
// by the reported tray count, not by a fixed four-slot stride.
CHECK(tray_index_map.at(0).first == 0);
CHECK(tray_index_map.at(0).second == 0);
CHECK(tray_index_map.at(3).first == 0);
CHECK(tray_index_map.at(3).second == 3);
CHECK(tray_index_map.at(4).first == 1);
CHECK(tray_index_map.at(4).second == 0);
CHECK(tray_index_map.at(15).first == 1);
CHECK(tray_index_map.at(15).second == 11);
}
TEST_CASE("A generic AMS unit reports its reported tray count", "[DevFilaSystem]")
{
MachineObject obj(nullptr, nullptr, "test", "test_dev", "127.0.0.1");
json filament = { {"units", json::array()}, {"external", json::array()} };
const std::vector<int> slot_counts = {8, 1};
for (int ams_id = 0; ams_id < static_cast<int>(slot_counts.size()); ++ams_id) {
json unit = { {"id", std::to_string(ams_id)}, {"extruder", ams_id}, {"slots", json::array()} };
for (int slot_id = 0; slot_id < slot_counts[ams_id]; ++slot_id)
unit["slots"].push_back({{"index", slot_id}, {"loaded", false}});
filament["units"].push_back(std::move(unit));
}
DevFilaSystemParser::ParseAgentFilament(filament, &obj, obj.GetFilaSystem().get());
// Non-Bambu units are not capped at the four-slot Bambu contract; the reported tray count wins.
const auto& ams_list = obj.GetFilaSystem()->GetAmsList();
REQUIRE(ams_list.at("0")->GetSlotCount() == 8);
REQUIRE(ams_list.at("1")->GetSlotCount() == 1);
}
TEST_CASE("Switch-bound AMS trays map to the left extruder", "[DevMapping]")
{
MachineObject obj(nullptr, nullptr, "test", "test_dev", "127.0.0.1");
+36 -29
View File
@@ -78,31 +78,34 @@ TEST_CASE("Series and managed classification", "[NetworkVersions]")
TEST_CASE_METHOD(PluginFolderFixture, "Managed builds fold into the series; customs are surfaced", "[NetworkVersions]")
{
add_plugin("02.08.01.55"); // managed, same series -> folded into the 02.08.01 row
add_plugin("02.08.04.60"); // managed, same series -> folded into the 02.08.04 row
add_plugin("02.09.00.10"); // managed, unknown series -> not listed
add_plugin("02.08.01.55"); // managed, series this build no longer has an ABI for -> not listed
add_plugin("02.03.00.62"); // managed, older whitelisted series -> folded into 02.03.00
add_plugin("02.01.01.52"); // managed, series with no ABI in this build -> not listed
add_plugin("02.08.01_custom"); // custom, whitelisted series -> listed under it
add_plugin("02.08.01.52-dev"); // custom (dash-suffixed), whitelisted series -> listed
add_plugin("02.08.04_custom"); // custom, whitelisted series -> listed under it
add_plugin("02.08.04.52-dev"); // custom (dash-suffixed), whitelisted series -> listed
auto versions = get_all_available_versions();
// The specific managed build never gets its own row - the series represents it.
REQUIRE(count_version(versions, "02.08.01.55") == 0);
REQUIRE(count_version(versions, "02.08.01") == 1);
REQUIRE(count_version(versions, "02.08.04.60") == 0);
REQUIRE(count_version(versions, "02.08.04") == 1);
REQUIRE(count_version(versions, "02.09.00.10") == 0);
REQUIRE(count_version(versions, "02.08.01.55") == 0);
REQUIRE(count_version(versions, "02.08.01") == 0);
REQUIRE(count_version(versions, "02.03.00.62") == 0);
REQUIRE(count_version(versions, "02.03.00") == 1);
REQUIRE(count_version(versions, "02.01.01.52") == 0);
// Custom-named builds are distinct files kept under their own name.
REQUIRE(count_version(versions, "02.08.01_custom") == 1);
REQUIRE(count_version(versions, "02.08.01.52-dev") == 1);
REQUIRE(count_version(versions, "02.08.04_custom") == 1);
REQUIRE(count_version(versions, "02.08.04.52-dev") == 1);
// Newest series first, its customs nested under it (suffix sort: "" < ".52-dev" < "_custom"),
// then older series, legacy last.
REQUIRE(versions[0].version == "02.08.01");
REQUIRE(versions[1].version == "02.08.01.52-dev");
REQUIRE(versions[2].version == "02.08.01_custom");
REQUIRE(versions[0].version == "02.08.04");
REQUIRE(versions[1].version == "02.08.04.52-dev");
REQUIRE(versions[2].version == "02.08.04_custom");
REQUIRE(versions[3].version == "02.03.00");
REQUIRE(versions.back().version == BAMBU_NETWORK_AGENT_VERSION_LEGACY);
@@ -111,9 +114,9 @@ TEST_CASE_METHOD(PluginFolderFixture, "Managed builds fold into the series; cust
REQUIRE_FALSE(versions[3].is_latest);
// Customs sort/render nested under their series (non-empty suffix, base = the series).
REQUIRE(versions[1].base_version == "02.08.01");
REQUIRE(versions[1].base_version == "02.08.04");
REQUIRE_FALSE(versions[1].suffix.empty());
REQUIRE(versions[2].base_version == "02.08.01");
REQUIRE(versions[2].base_version == "02.08.04");
REQUIRE_FALSE(versions[2].suffix.empty());
// "(Latest)" is the series row, never a nested custom build.
@@ -123,20 +126,20 @@ TEST_CASE_METHOD(PluginFolderFixture, "Managed builds fold into the series; cust
REQUIRE_FALSE(versions[2].is_latest);
// The stored default that drives download and update-check decisions is now the series.
REQUIRE(std::string(get_latest_network_version()) == "02.08.01");
REQUIRE(std::string(get_latest_network_version()) == "02.08.04");
}
TEST_CASE_METHOD(PluginFolderFixture, "Only the loaded series is marked installed", "[NetworkVersions]")
{
add_plugin("02.08.01.55");
add_plugin("02.08.01_custom");
add_plugin("02.08.04.60");
add_plugin("02.08.04_custom");
// The loaded plug-in reports its full build (02.08.01.55); the series row is what gets marked.
// The loaded plug-in reports its full build (02.08.04.60); the series row is what gets marked.
{
auto versions = get_all_available_versions("02.08.01.55");
auto versions = get_all_available_versions("02.08.04.60");
int marked = 0;
for (const auto& info : versions)
if (info.is_loaded) { ++marked; REQUIRE(info.version == "02.08.01"); }
if (info.is_loaded) { ++marked; REQUIRE(info.version == "02.08.04"); }
REQUIRE(marked == 1);
}
@@ -152,10 +155,10 @@ TEST_CASE_METHOD(PluginFolderFixture, "Only the loaded series is marked installe
// A loaded custom build matches its own row, never the bare series.
{
auto versions = get_all_available_versions("02.08.01_custom");
auto versions = get_all_available_versions("02.08.04_custom");
int marked = 0;
for (const auto& info : versions)
if (info.is_loaded) { ++marked; REQUIRE(info.version == "02.08.01_custom"); }
if (info.is_loaded) { ++marked; REQUIRE(info.version == "02.08.04_custom"); }
REQUIRE(marked == 1);
}
@@ -167,11 +170,11 @@ TEST_CASE_METHOD(PluginFolderFixture, "Only the loaded series is marked installe
TEST_CASE("Only whitelisted series pass the load gate", "[NetworkVersions]")
{
// Each whitelisted series, its builds, and custom-named builds of that series.
REQUIRE(is_supported_network_version("02.08.01"));
REQUIRE(is_supported_network_version("02.08.01.52"));
REQUIRE(is_supported_network_version("02.08.01.55"));
REQUIRE(is_supported_network_version("02.08.01_custom"));
REQUIRE(is_supported_network_version("02.08.01.52-dev"));
REQUIRE(is_supported_network_version("02.08.04"));
REQUIRE(is_supported_network_version("02.08.04.52"));
REQUIRE(is_supported_network_version("02.08.04.60"));
REQUIRE(is_supported_network_version("02.08.04_custom"));
REQUIRE(is_supported_network_version("02.08.04.52-dev"));
REQUIRE(is_supported_network_version("02.03.00"));
REQUIRE(is_supported_network_version("02.03.00.62"));
REQUIRE(is_supported_network_version("02.03.00.70"));
@@ -179,6 +182,9 @@ TEST_CASE("Only whitelisted series pass the load gate", "[NetworkVersions]")
REQUIRE(is_supported_network_version(BAMBU_NETWORK_AGENT_VERSION_LEGACY));
// Series whitelisted by previous Orca releases that no generation here can call.
REQUIRE_FALSE(is_supported_network_version("02.08.01"));
REQUIRE_FALSE(is_supported_network_version("02.08.01.55"));
REQUIRE_FALSE(is_supported_network_version("02.08.01_custom"));
REQUIRE_FALSE(is_supported_network_version("02.01.01.52"));
REQUIRE_FALSE(is_supported_network_version("02.00.02.50"));
@@ -198,9 +204,9 @@ TEST_CASE("Each version resolves to the ABI generation that can call it", "[Netw
{
// The generation is keyed on the series, so every build of a series - including the
// custom-named ones - resolves to the same one.
CHECK(network_plugin_abi("02.08.01") == NetworkAbi::Current);
CHECK(network_plugin_abi("02.08.01.55") == NetworkAbi::Current);
CHECK(network_plugin_abi("02.08.01.52-dev") == NetworkAbi::Current);
CHECK(network_plugin_abi("02.08.04") == NetworkAbi::Current);
CHECK(network_plugin_abi("02.08.04.60") == NetworkAbi::Current);
CHECK(network_plugin_abi("02.08.04.52-dev") == NetworkAbi::Current);
CHECK(network_plugin_abi("02.03.00") == NetworkAbi::V0203);
CHECK(network_plugin_abi("02.03.00.62") == NetworkAbi::V0203);
CHECK(network_plugin_abi("02.03.00_custom") == NetworkAbi::V0203);
@@ -208,6 +214,7 @@ TEST_CASE("Each version resolves to the ABI generation that can call it", "[Netw
// Anything the load gate rejects must dispatch through nothing at all, rather than
// defaulting to a layout it does not share.
CHECK(network_plugin_abi("02.08.01.55") == NetworkAbi::Unsupported);
CHECK(network_plugin_abi("02.01.01.52") == NetworkAbi::Unsupported);
CHECK(network_plugin_abi("02.00.02.50") == NetworkAbi::Unsupported);
CHECK(network_plugin_abi("02.09.00.10") == NetworkAbi::Unsupported);
@@ -234,7 +241,7 @@ TEST_CASE_METHOD(PluginFolderFixture, "Legacy series never adopts discovered bui
// With nothing else on disk, the series holds "(Latest)" even though its library is
// not installed.
for (const auto& info : versions) {
if (info.version == "02.08.01") {
if (info.version == "02.08.04") {
REQUIRE(info.is_latest);
REQUIRE_FALSE(info.is_loaded);
}