Skip to content

[fix]: Updates so that Snapmaker U1 pause/resume and power loss resume works correctly with AFC#728

Merged
jimmyjon711 merged 3 commits into
AFCProject:DEVfrom
jimmyjon711:u1_pause_resume_power_loss
Jun 6, 2026
Merged

[fix]: Updates so that Snapmaker U1 pause/resume and power loss resume works correctly with AFC#728
jimmyjon711 merged 3 commits into
AFCProject:DEVfrom
jimmyjon711:u1_pause_resume_power_loss

Conversation

@jimmyjon711

@jimmyjon711 jimmyjon711 commented Jun 2, 2026

Copy link
Copy Markdown
Member

Major Changes in this PR

Before these changes U1 printer would select the wrong toolhead when resuming from pause or heat the wrong toolhead when resuming from power loss. These fixes allow pause/resume and power loss resume to work correctly when having more that 4 T(n) macros defined by AFC.

  • Updated CHANGE_TOOL method to call original T(n) if A(n) param is passed in.
  • Updated M104/M109 macro to heat hot end based off T(n) index if A(n) param was passed in.

How the changes in this PR are tested

Tested on my U1 printer.

  • Reordered T(n) so T0 was not extruder, T1 was not extruder1 etc and pause print. Once resumed correct toolhead was heated and resume worked correctly.
  • Caused klipper to crash by unplugging an attached boxturtle, verified that printer was successfully able to resume print with correct toolhead after crash happened.

2.4 Printer, Snapmaker Detected not printing out in console during PREP:
image

Snapmaker U1, Snapmaker Printer Decteted printing out in console during PREP:
image

PR Checklist: (Checked-off items are either done or do not apply to this PR)

  • I have performed a self-review of my code
  • CHANGELOG.md is updated (if end-user facing)
  • Documentation updated in AT-Documentation repository
  • Sent notification to software-design/software-discussions channel (as appropriate) requesting review
  • If this PR address a GitHub issue, the issue number is referenced in the PR description

NOTE: GitHub Copilot may be used for automated code reviews, however as it is an automated system, it's suggestions
may not be correct. Please do not rely on it to catch all issues. Please review any suggestions it makes and use your
own judgement to determine if they are correct.

Summary by CodeRabbit

  • New Features

    • Detect and specially handle Snapmaker U1-style printers to improve tool-change and temperature handling.
  • Bug Fixes

    • Fixed stepper homing debug logging and improved exception reporting during homing.
  • Behaviour / UX

    • Prep flow now logs when a Snapmaker printer is detected.
  • Tests

    • Expanded tests for Snapmaker-specific paths and tool-change scenarios.
  • Documentation

    • Changelog updated with Snapmaker support entry.

… correctly

- Updated CHANGE_TOOL method to call original T(n) if A(n) param is passed in.
- Updated M104/M109 macro to heat hot end based off T(n) index if A(n) param
  was passed in.
@coderabbitai

coderabbitai Bot commented Jun 2, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Repository UI (base), Organization UI (inherited)

Review profile: CHILL

Plan: Pro Plus

Run ID: c9c7038d-b254-40ff-8938-b3dd20595eb0

📥 Commits

Reviewing files that changed from the base of the PR and between 87a0c20 and cd0eb64.

📒 Files selected for processing (1)
  • tests/test_AFC.py
🚧 Files skipped from review as they are similar to previous changes (1)
  • tests/test_AFC.py

📝 Walkthrough

Walkthrough

AFC now detects Snapmaker printers via klippy.Printer, special-cases CHANGE_TOOL and M109 commands carrying an A parameter to dispatch ready-gcode handlers or target extruders directly, moves a PREP version-print call, updates tests/mocks, and fixes a stepper homing log format.

Changes

Snapmaker U1 Support

Layer / File(s) Summary
Detection & imports
extras/AFC.py
Add imports for cached_property and klippy.Printer; remove SNAPMAKER_TRAPQ_APPEND_LEN; add @cached_property snapmaker_printer probing klippy.Printer.get_snapmaker_config_dir.
PREP flow integration
extras/AFC_prep.py
Move print_version(console_only=True) later in PREP and log “Snapmaker Printer Detected” when self.afc.snapmaker_printer is truthy.
cmd_CHANGE_TOOL Snapmaker handling
extras/AFC.py
When CHANGE_TOOL includes A and Snapmaker mode is enabled, derive renamed handler (e.g., _T0), verify existence in self.gcode.ready_gcode_handlers, call run_script_from_command with A=<value>, and return early.
cmd_AFC_M109 Snapmaker handling
extras/AFC.py
Parse A in M109; when present and Snapmaker mode is enabled, select extruder target by T (direct extruder or extruder{n}) instead of lane-to-extruder mapping.
Homing log fix
extras/AFC_stepper.py
Reformat homing success debug log into a correct multi-line f-string and set steps_moved/dist_mm to zero on exception paths before logging.
Tests & mocks
tests/conftest.py, tests/test_AFC.py
Add stub Printer to klippy mock, import/wire Printer in tests, update CHANGE_TOOL fixture, add cmd_CHANGE_TOOL and snapmaker property tests, and complete LONG speed-mode assertion.
CHANGELOG
CHANGELOG.md
Add 2026-06-02 entry describing Snapmaker U1 pause/resume and power-loss resume support.

Sequence Diagram(s)

sequenceDiagram
  participant GcodeSource as G-code source
  participant AFC as afc.cmd_CHANGE_TOOL
  participant Klippy as klippy.Printer
  participant Handlers as gcode.ready_gcode_handlers
  GcodeSource->>AFC: CHANGE_TOOL A=<val>
  AFC->>Klippy: probe get_snapmaker_config_dir (snapmaker_printer)
  Klippy-->>AFC: present / absent
  AFC->>Handlers: lookup renamed handler (e.g., _T0)
  Handlers-->>AFC: handler exists
  AFC->>Handlers: run_script_from_command with A=<val>
  AFC-->>GcodeSource: return early (skip normal tool-change)
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

Suggested reviewers

  • kekiefer
  • ejsears

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@extras/AFC_stepper.py`:
- Around line 724-725: The debug log at logger.debug in the homing routine can
reference dist_mm and steps_moved even if the metric calculation (lines
~711-721) raises, causing UnboundLocalError; to fix, initialize or set safe
fallback values for dist_mm and steps_moved before the try/except (or assign
them in the except handler) so they are always defined when logging in the
homing method (referencing the homing method, the logger.debug call, and
variables dist_mm, steps_moved, end_ts, start_ts); make sure the fallback values
meaningfully indicate a failed metric calc (e.g., None or -1) so the log remains
informative.

In `@extras/AFC.py`:
- Around line 2442-2450: The self.tools.get() call on the line where
snapmaker_param_a is not None and self.snapmaker_printer is true currently falls
back to self.toolhead.get_extruder() if the extruder_name is not found, which
silently uses the active extruder and recreates the wrong-hotend behavior.
Instead of providing a default fallback with self.toolhead.get_extruder(), make
the code fail fast by removing the default parameter and either directly raise
an error if the extruder_name is not in self.tools or handle the missing tool
configuration explicitly to prevent silent fallback to the active extruder.

In `@tests/conftest.py`:
- Around line 192-196: The klippy test helper _make_klippy_ready_mock should not
set klippy.Printer = MagicMock() because hasattr(Printer,
"get_snapmaker_config_dir") will be truthy on a MagicMock; instead define
klippy.Printer as a simple concrete class or real Printer type that only exposes
the real attributes (e.g., class DummyPrinter: pass) so hasattr(DummyPrinter,
"get_snapmaker_config_dir") behaves correctly, update the mock factory to assign
that class and keep mod.message_ready; also remove or rename the duplicated
test_check_for_snapmaker_signature in TestCheckForSnapmakerSignature so the
non-Snapmaker test isn’t overwritten and both cases of
extras.AFC::_check_for_snapmaker_signature are covered.

In `@tests/test_AFC.py`:
- Around line 2391-2404: The two test methods both named
test_check_for_snapmaker_signature_false collide so the "false" case is
overwritten; rename one (e.g. change the second to
test_check_for_snapmaker_signature_true) so both run, keeping the second's setup
that sets Printer.get_snapmaker_config_dir = MagicMock(), calling obj =
_make_afc(), invoking obj._check_for_snapmaker_signature(), and asserting
obj.snapmaker_printer is True; ensure the first retains the deletion of
Printer.get_snapmaker_config_dir and asserts not obj.snapmaker_printer.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI (base), Organization UI (inherited)

Review profile: CHILL

Plan: Pro Plus

Run ID: 43761fe0-22d9-4945-b68d-433e151ad225

📥 Commits

Reviewing files that changed from the base of the PR and between dd6217d and e157cce.

📒 Files selected for processing (5)
  • extras/AFC.py
  • extras/AFC_prep.py
  • extras/AFC_stepper.py
  • tests/conftest.py
  • tests/test_AFC.py

Comment thread extras/AFC_stepper.py
Comment thread extras/AFC.py
Comment thread tests/conftest.py
Comment thread tests/test_AFC.py Outdated
- Updated unit tests
- Updated issues pointed out by coderabbit
- Updating changelog

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
tests/test_AFC.py (1)

1371-1394: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Isolate Printer monkeypatching per test.

These cases mutate Printer.get_snapmaker_config_dir on the shared mock class and rely on later tests to clean it up. That makes the Snapmaker assertions order-dependent and can leak state across the module.

Suggested fix
 class TestCmdChangeTool_SnapmakerPath:
-    def test_setting_A_param(self):
+    def test_setting_A_param(self, monkeypatch):
         obj, _, _ = _make_afc_for_change_tool()
-        setattr(Printer, "get_snapmaker_config_dir", self.get_snapmaker_config_dir)
+        monkeypatch.setattr(
+            Printer, "get_snapmaker_config_dir", self.get_snapmaker_config_dir, raising=False
+        )
         gcmd = self._make_gcmd()
         obj.gcode = MagicMock()
         obj.gcode.ready_gcode_handlers = {"_T0": MagicMock()}
         ret = obj.cmd_CHANGE_TOOL(gcmd)
         obj.gcode.run_script_from_command.assert_called_once()
     
-    def test_setting_A_param_snapmaker_false(self):
+    def test_setting_A_param_snapmaker_false(self, monkeypatch):
         obj, _, _ = _make_afc_for_change_tool()
         obj.function.check_homed.return_value = False
-        if hasattr(Printer, "get_snapmaker_config_dir"):
-            delattr(Printer, "get_snapmaker_config_dir")
+        monkeypatch.delattr(Printer, "get_snapmaker_config_dir", raising=False)
         gcmd = self._make_gcmd()
         obj.gcode = MagicMock()
         obj.gcode.ready_gcode_handlers = {"_T0": MagicMock()}
         ret = obj.cmd_CHANGE_TOOL(gcmd)
         obj.gcode.run_script_from_command.assert_not_called()
         assert not ret
 
-    def test_setting_A_param_not_in_ready_gcode_handlers(self):
+    def test_setting_A_param_not_in_ready_gcode_handlers(self, monkeypatch):
         obj, _, _ = _make_afc_for_change_tool()
         obj.function.check_homed.return_value = False
-        setattr(Printer, "get_snapmaker_config_dir", self.get_snapmaker_config_dir)
+        monkeypatch.setattr(
+            Printer, "get_snapmaker_config_dir", self.get_snapmaker_config_dir, raising=False
+        )
         obj.function.check_homed.return_value = False
         gcmd = self._make_gcmd("T5")
         obj.gcode = MagicMock()
         obj.gcode.ready_gcode_handlers = {"_T0": MagicMock()}
         ret = obj.cmd_CHANGE_TOOL(gcmd)
@@
 class TestCheckForSnapmakerSignature:
-    def test_check_snapmaker_printer_property_false(self):
+    def test_check_snapmaker_printer_property_false(self, monkeypatch):
         obj = _make_afc()
         obj.printer = Printer
-        if hasattr(Printer, "get_snapmaker_config_dir"):
-            delattr(Printer, "get_snapmaker_config_dir")
+        monkeypatch.delattr(Printer, "get_snapmaker_config_dir", raising=False)
         assert not obj.snapmaker_printer
     
-    def test_check_snapmaker_printer_property_true(self):
+    def test_check_snapmaker_printer_property_true(self, monkeypatch):
         obj = _make_afc()
         obj.printer = Printer
-        setattr(Printer, "get_snapmaker_config_dir", True)
+        monkeypatch.setattr(Printer, "get_snapmaker_config_dir", True, raising=False)
         assert obj.snapmaker_printer

Also applies to: 2399-2406

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/test_AFC.py` around lines 1371 - 1394, The tests are mutating
Printer.get_snapmaker_config_dir on a shared class, causing order-dependent
leaks; update each test that sets or deletes Printer.get_snapmaker_config_dir
(e.g., test_setting_A_param_snapmaker_false and the similar block around lines
referenced) to isolate the monkeypatch by restoring the original attribute after
the test (or use the test framework's monkeypatch fixture to set/undo it
automatically); specifically, capture the original = getattr(Printer,
"get_snapmaker_config_dir", sentinel) before changing it, perform the
setattr/delattr and assertions inside the test, then in a finally block restore
the original (setattr if original is not sentinel, else delattr), or replace
with monkeypatch.setattr/monkeypatch.delenv-equivalent so no state leaks between
tests.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@tests/test_AFC.py`:
- Around line 1371-1394: The tests are mutating Printer.get_snapmaker_config_dir
on a shared class, causing order-dependent leaks; update each test that sets or
deletes Printer.get_snapmaker_config_dir (e.g.,
test_setting_A_param_snapmaker_false and the similar block around lines
referenced) to isolate the monkeypatch by restoring the original attribute after
the test (or use the test framework's monkeypatch fixture to set/undo it
automatically); specifically, capture the original = getattr(Printer,
"get_snapmaker_config_dir", sentinel) before changing it, perform the
setattr/delattr and assertions inside the test, then in a finally block restore
the original (setattr if original is not sentinel, else delattr), or replace
with monkeypatch.setattr/monkeypatch.delenv-equivalent so no state leaks between
tests.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI (base), Organization UI (inherited)

Review profile: CHILL

Plan: Pro Plus

Run ID: 5494ad22-dbfb-40c9-8bde-82f73ae33495

📥 Commits

Reviewing files that changed from the base of the PR and between e157cce and 87a0c20.

📒 Files selected for processing (5)
  • CHANGELOG.md
  • extras/AFC.py
  • extras/AFC_stepper.py
  • tests/conftest.py
  • tests/test_AFC.py
✅ Files skipped from review due to trivial changes (1)
  • CHANGELOG.md
🚧 Files skipped from review as they are similar to previous changes (2)
  • tests/conftest.py
  • extras/AFC_stepper.py

@jimmyjon711 jimmyjon711 requested review from ejsears and kekiefer June 3, 2026 01:16
@jimmyjon711 jimmyjon711 merged commit 5be711d into AFCProject:DEV Jun 6, 2026
5 checks passed
@jimmyjon711 jimmyjon711 deleted the u1_pause_resume_power_loss branch June 6, 2026 17:06
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant