mirror of
https://github.com/Klipper4FlashForge/firmware.git
synced 2026-10-22 21:32:16 -05:00
_FF_PREFLIGHT now runs for every job origin, not only SDCARD_PRINT_FILE. It was gated on that origin, and prepare_on_m23 defaults to 0, so an M23-started print got neither the prepare nor the gate -- skipping the check that exists to stop the nozzle being driven ~3 mm into the plate. The gate only raises; it moves and heats nothing, so no origin is a reason to skip it. The prepare_on_m23 comment is corrected too: it was written for FlashForge's touchscreen preparing before M23, and that binary is replaced. _FF_JOB.ended is now cleared when a job starts rather than only when FF_AFTER_PRINT_END runs. It exists so a slicer footer's END_PRINT cannot make the exit block run twice, but only ff_print clears it, and only for a job it announced (self.active is armed after before_macro succeeds). So an END_PRINT or CANCEL_PRINT typed at the console while idle latched it -- and the NEXT print silently skipped its whole exit block: hotends and bed left at target, tool never docked, Z offset never reset, idle timeout left at 864000 s. CANCEL_PRINT calls END_PRINT, and both shipped gcode files call it directly, so this was reachable without touching a console at all. END_PRINT's SET_GCODE_OFFSET Z=0 MOVE=1 moved outside the homed_axes guard written two lines above it. MOVE=1 makes gcode_move issue a real Z move and cartesian.check_move refuses one on an unhomed Z, so a failed G28 Z earlier in the job made END_PRINT raise there -- skipping M18, the idle-timeout reset and the ended flag, which then let FF_AFTER_PRINT_END run the block a second time. Unhomed now clears the offset with MOVE=0. Calibration left the Z frame broken. _two_pass zeroes the G-code offsets to probe in raw coordinates and never restores them, and RELEASE defaults to 0, so TOOL_OFFSET_CALIBRATE ended with a tool mounted and Z=0 at the eddy plane, ~3.2 mm below the bed. Worse, ff_toolchange._z_tool_term still held the pre-calibration term, so the next grab computed its babystep against something no longer applied and propagated the zeroing instead of curing it. New ff_toolchange.reapply_offsets_after_external_zero() resets the term and re-applies the mounted tool's offsets; both calibration commands call it. GRAB=0 was silently defeated. It means the operator mounted the tool by hand, but the plate check parks whatever is mounted to measure an empty carriage -- so it undid the hand-mount and stored the bare carriage as that tool's nozzle position, out by the full gap in the crash direction, with the gap guard unable to catch it before a station_z exists. It now refuses with the two ways out rather than doing the wrong thing quietly. 54 passed. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>