Repository navigation
Conversation
|
Thanks, the phase description matches the code and the architecture is the right shape. Four things to settle before you build:
One more constraint: please keep ace.py untouched. Put the ACE_BG_WAIT handler and the phase accessor in ace_bg_swap.py and reach the park helpers via self.printer.lookup_object('ace', None) with a getattr fallback, the same way cmd_ACE_BG_SET_HEAD reaches _wt_persist. ace.py is being split into mixins on the dev branch right now, so any edit there would have to be re-ported by hand. |
|
Thanks for the thorough feedback and clear constraints @decay71. Here is the settled plan addressing each point:
I'm proceeding incrementally on |
Implementation & Testing Roadmap
I'll be tackling Milestones 1 through 5 on hardware first using the bench harness to verify real timing and motion on the machine. @decay71 if you'd like to collaborate or take on Milestone 6 (the post-processor stamping and stats alignment) in parallel, please feel free! Otherwise, I'm happy to draft that as well once the Klipper command behavior is proven on hardware. |
decay71
left a comment
There was a problem hiding this comment.
Thanks, milestone 1 looks right: a pass-through skeleton can't stall a print. Four notes:
- Timeout: 15-20 s is too short for PULL on the current engine. With a pre-prime configured, PULL contains the pre-prime push and a waittemp down to the pull temperature, which is passive cooling on a parked head without a fan; custom tip-form tables can carry waittemp steps too. Please size the timeout to cover the remaining PULL work rather than a fixed 15-20 s, otherwise the wait mostly expires and aborts as today.
- Please move BG_WAIT_SPEC.md into the PR description (or under multiace/); a file in the repo root would end up in the public tree.
- Please drop the version bump and the "Oct 22 2026 | Author" comment on BG_SWAP_VERSION; we keep names and dates out of the code and set the version on our side. You will of course be credited in the README.
- Small one in the spec: the wait is not about head collisions, it keeps the docked head's cold pull from being aborted.
Milestone 6 is yours too, unless you think it needs me: I am deep in the new driver code right now, which is meant to bring in other devices later. It should be done in time for the next release.
…gle and transition logging - Implement [gcode_macro TEST_BG_SWAP_BENCH] in ace.cfg supporting ACTIVE_HEAD, TARGET_HEAD, TARGET_SLOT, and SIMULATE=0|1. - Add centralized _set_state() and phase(head) helpers in ace_bg_swap.py for live auto-printed console phase transitions. - Fix load_enabled parser in ace_bg_swap.py to support 'transport' alongside boolean representations without config errors. - Comment out macro_layout_enabled and [ace_macro_layout] in ace.cfg to prevent Klipper startup errors on older firmware. - Mark Milestone 2 subtasks 2.1-2.4 complete in BG_WAIT_SPEC.md.
…UPT macro - Register ACE_BG_AWAIT_PHASE command in ace_bg_swap.py for non-blocking phase synchronization. - Implement [gcode_macro TEST_BG_SWAP_INTERRUPT] in ace.cfg to test interrupting background swaps at arbitrary phases (HEAT, PULL, RETRACT, BOOKKEEP, LD_FEED, LD_PRIME, DONE). - Support configurable mid-phase DELAY parameter before triggering arrival pickup.
…s in TEST_BG_SWAP_INTERRUPT
…otes with next session pickup
- Add PHASE_ORDER monotonic ranking in AceBgSwap to match target phase or any subsequent phase. - Prevent 120s timeout on transient phases like BOOKKEEP (<1ms duration). - Document combiner/splitter clearance math and 1050mm resolution in BG_WAIT_DEV_NOTES.md.
|
Hey @decay71 Quick update: Milestone 2 is complete, hardware-verified, and pushed to If you want to test the new harness on hardware, here is a cool test you can run:
(Note: We also added I will go back through your comments next, try to address them in the next commit, and then I will dive into Milestone 3 ( |
decay71
left a comment
There was a problem hiding this comment.
Thanks for the harness, the phase ladder makes the interruption cases easy to reproduce. A few things before this goes further:
- load_enabled: transport now maps to a full background load. Transport mode only exists in the 1.30b engine (your printer runs the 1.30b beta); 1.27b does not know it, which is why Klipper halted on your config. Treating it as true gives everyone who set transport the full dock purge again, which is exactly what transport mode avoids. Please drop the parser change and use true/false on this base; the transport case comes with the next release.
- ace.cfg: your local macro layout setting slipped in (macro_layout_enabled and [ace_macro_layout] are commented out, which would switch the layout off for everyone). The test macros were also inserted between "[gcode_macro ACEG__List] gcode:" and its body, so ACEG__List is now empty and its ACE_LIST line ends up at the end of TEST_BG_SWAP_INTERRUPT. And macros that pick heads and force a background swap should not ship in the default config; please keep them in a separate file for testing.
- _set_state sends every phase transition to the console. That adds 8 to 10 lines per background swap, and the console pipe already overflows in swap bursts; please log the transitions to klippy.log only (logging.info).
- BG_WAIT_DEV_NOTES.md reads as a working log ("next session pickup"); please keep that in your fork. The spec can stay in multiace/docs/.
… feedback - Revert BG_SWAP_VERSION to 'v0.9' and remove date/author attribution comment. - Clarify in BG_WAIT_SPEC.md that wait prevents premature cold pull aborts. - Update Milestone 5.2 in spec to dynamically size timeout for remaining PULL work.
…d test macros
- Restore multiace/config/extended/ace.cfg to upstream defaults (re-enable macro_layout, restore ACEG__List body).
- Relocate TEST_BG_SWAP_BENCH and TEST_BG_SWAP_INTERRUPT into dedicated test_bg_swap.cfg.
- Revert load_enabled in ace_bg_swap.py to config.getboolean('load_enabled', True).
- Log state transitions to logging.info only to avoid console pipe flooding.
- Untrack working dev notes from git tree while preserving on disk via .git/info/exclude.
…etpoint query, and telemetry tracking - Implement is_wait_eligible(head) gating helper: True on PULL or late HEAT ((tgt - cur) <= 15.0C). - Add dynamic extruder heater query without hardcoded temperatures. - Track operation, heat, and pull elapsed durations in AceBgSwap. - Integrate DEBUG=1 diagnostic telemetry in ACE_BG_WAIT command. - Add TEST_BG_WAIT_GATING test macro in test_bg_swap.cfg. - Mark Milestone 3 subtasks 3.1-3.4 complete in BG_WAIT_SPEC.md.
|
@decay71 integrated your comments from milestones 1 and 2 in the two latest commits. I did make a test harness debugging mode which writes to the console during live testing so I can see how things line up. This option is off by default and everything is still written to the logs. Please let me know if anything else stands out. Might be able to test Milestone 3 which is the big one (i.e. waiting during the late heating/tip forming) 🤞 |
|
Hi, now printing yourtest file on 4 Aces and color copies enabled with my dev version, still 125 swaps. |
🥳 The swaps will be the same but the time savings could change. For my test file I have 3 colors swapping on one ACE. |
…d restore - Implement _snapshot_active_position() capturing toolhead XYZ, speed, coordinate modes, and E baseline. - Implement _park_at_discard() with +2mm Z-hop and MOVE_TO_DISCARD_FILAMENT_POSITION. - Implement _restore_active_position() restoring XY approach, Z landing, and speed. - Hook parking into cmd_ACE_BG_WAIT when eligible (with FORCE_PARK=1 and DWELL parameters for bench testing). - Add TEST_BG_WAIT_PARK_RESTORE macro in test_bg_swap.cfg. - Mark Milestone 4 subtasks 4.1-4.4 complete in BG_WAIT_SPEC.md.
|
I don‘t think so, these are color swaps not toolchanges, and with 4 instead of 2 aces there should be less. But depends on the model. Stats tomorrow! |
…with dynamic timeout and target RETRACT release - Implement reactor polling loop in cmd_ACE_BG_WAIT holding arrival head at discard position. - Sized dynamic timeout budget to cover remaining PULL work without premature aborts. - Release hold immediately once background op reaches RETRACT (or subsequent/terminal phases). - Graceful fail-open fallback: restores coordinates cleanly on timeout or failure. - Add TEST_BG_WAIT_HOLD end-to-end test macro in test_bg_swap.cfg. - Mark Milestone 5 subtasks 5.1-5.3 complete in BG_WAIT_SPEC.md.
…abort synchronization on ineligibility - Add self._abort_requested signaling to immediately kill background greenlets when ineligible. - Zero nozzle heater immediately upon ineligibility and await clean exit before returning. - Tune dynamic thermal gating threshold to <= 25.0C (covering ~4-5s of remaining heat). - Update BG_WAIT_SPEC.md with 25.0C setpoint.
|
Success on ABORTING to inline swap if T0 is issued during early heating stage (~<25s)
Success on WAITING for bg swap to finish and parking active head before T0 can execute (vid below)
https://github.com/user-attachments/assets/b12f8b99-c5b2-4b62-bd50-559470fe0502 |
- Track background-swapped heads (bg_waiting_heads) in rewrite_head_mode_to_file. - Stamp ACE_BG_WAIT HEAD=n immediately prior to arrival T<n> line. - Discard from waiting set once arrival T is reached. - Mark Milestone 6 subtasks 6.1-6.3 complete in BG_WAIT_SPEC.md.
|
Looks great, congrats! Thanks for all the testing and the clear write-ups, it makes this easy to follow. Curious about the final time compared to your earlier runs.
This is fixed in #196, merged into main now: the bg op hands the heater back to the slicer's own request instead of 0, and during a print a target below 170 is replaced by the last print temperature of that head. With both, the swap stays at your print temperature except for the very first swap of a head. Merge main into your #186 branch to get it. You will get a small conflict in ace_bg_swap.py at the HEAT and LD_FEED state lines: keep your _set_state(...) call and the new _heat_claim(...) / _set_heat(...) lines below it. |
…ump PP_FORMAT_VERSION to 5
2e364e6 to
53acde8
Compare
…ownership fix)
Thank you! Excited to see how the tests go. Thank you for your guidance; it kept the development very focused and fast by premptively solving potenital issues =)
Right. The early-heat abort on arrival takes ~0.1s to release the engine, and with your latest feedback now integrated, it preserves the preheat progress so the arrival inline swap doesn't restart from cold. I haven't parsed the full logs from the current run yet, but it might be in the logs I already uploaded? Future Work: Engine SerializationTo capture the engine serialization question for future work, I opened Feature Request #197 to track decoupled per-head motion queues ( 'ACE_BG_WAIT` per Head ParameterizationIn Python,
|
decay71
left a comment
There was a problem hiding this comment.
Read through 53acde8 and the merge, the #196 conflict is resolved the right way. Two small things:
- _check_docked now says "ABORTED by arrival wait" for a real pick too, so the log no longer tells the two apart. Please keep the old "was PICKED mid-sequence" text for a pick and use the new one only when _abort_requested fired.
- Without DEBUG, ACE_BG_WAIT still sends its verdict to the console on every arrival, right in the swap burst. Please log it to klippy.log only and keep the console line for DEBUG=1, same as _set_state.
I addressed both of these issues. See attached log and commits above. I also noticed a bug in #196 which I commented in that PR. |
…pick A full bg swap that is still priming when the T arrives kept extruding while the stock macro carried the head to the print: the engine's interlock (_check_docked) only sees the extruder ACTIVATION, which stock performs ~2.4 s after lifting the head out of the dock, and only between prime chunks. Field log: pick 13:34:26, activation 13:34:28.6, engine notice 13:34:29.8 - 3.5 s of prime at 4 mm/s plus no anti-ooze retract, the blob in front of the wipe tower. ace.py wraps T0-T3: before the stock handler runs, _notify_bg_pick tells the engine (request_pick_abort) and waits up to BG_PICK_NOTICE_WAIT for the handover. The engine stops at its next move boundary while the head is still docked: the prime loop ends cleanly, sets its anti-ooze cushion and records the missing purge for the arrival pick-check; every other polling phase (HEAT, PULL, LD_FEED, LD_GRIP) takes its existing pick-abort path. The bulk retract stays untouchable, a notice outside the polling phases is ignored and the T runs as before. An older engine without the hook, or no engine, is a no-op. Unit tests for both halves (tests/unit/test_bg_pick_notice.py).
|
Hey @decay71, I saw your note and merged Here's what's included in these latest commits:
It's late, so I haven't deployed/tested these to my machine yet, but I wanted to push the commits here so you have visibility into the implementation in your morning. I'll try to test tmrw!" |
decay71
left a comment
There was a problem hiding this comment.
Hi
RESTORE_POS=0 is right, and my point 3 from the first review was wrong: I assumed the swap after the T saves the position from before the T. The stock pick always ends at the dock line, and your log shows it: the wait returned to X=15 Y=208-236, and the swap still saved X=43.80 Y=250.00 every time. So the trip back was pure time.
Four small things:
- With RESTORE_POS=0 please still restore the feedrate and the coordinate mode, only skip the travel. The park leaves F600, and both the stock T and the swap carry the speed they find into the print.
- The LD_GRIP/LD_PRIME handover in ACE_BG_WAIT does what #198 already does at the T, two lines later, so it can go. (The "anti-ooze cushion" wording also only fits LD_PRIME; LD_GRIP takes the normal abort path.)
- For diagnostics we keep only the config line, without a runtime setter: the debug option and DEBUG=1 are enough, please drop ACE_BG_DEBUG.
- BG_WAIT_DEV_NOTES.md is back in the PR, please keep it in your fork.
Relates to #172
Objective
Allow the toolhead background swap sequence to wait during Phase 1 (cold pull/tip forming) rather than aborting the background swap and restarting inline when parked time is short.
Background Swap Phases (Reference)
Phase 1: Hotend Prep & Cold Pull (~70–85s)
MOVE_TO_DISCARD_FILAMENT_POSITION) and wait for Phase 1 to finish so the pre-prime and tip forming are not discarded and repeated.Phase 2: Bulk Retract (~15–20s)
Phase 3: Transport / Pre-Staging (~15–20s)
Phase 4: Dock Purge & Anti-Ooze (Full Mode Only)
Proposed Architecture
G-Code Post-Processor (
multiace/tools/post_process_virtual_toolheads.py):rewrite_head_mode_to_file.Tcommands where the incoming head is currently performing a background operation.ACE_BG_WAIT HEAD=nimmediately prior to the arrivalTtool change macro.Wait Logic & State Engine (
multiace/klipper/extras/ace_bg_swap.py&ace.py):ACE_BG_WAIT HEAD=n [TIMEOUT=x].is_busy(head):Nozzle Safety / Anti-Ooze:
MOVE_TO_DISCARD_FILAMENT_POSITIONbefore entering the wait state.Invariants Maintained:
@decay71 Opening this draft early as requested to review the hook locations and architecture before diving into full implementation. Does this setup match what you had in mind?