Files
Oleksandr ShyshatskyiandClaude Opus 5 b55461cd6b Cancelling an object leaves the SD reader with nothing to pace it
EXCLUDE_OBJECT installs the ExcludeObject move transform, and inside a
cancelled object _ignore_move updates Python state without ever calling
next_transform.move(). Nothing reaches the toolhead, so the lookahead
buffer that normally blocks gcode.run_script never fills, and
virtual_sdcard.work_handler runs on its own yields alone -- one
reactor.pause per 8192-byte read, about 330 lines apart. The eboard's
soft-PWM refresh misses its deadline and the MCU shuts down with
"Timer too close".

Measured on a Creator 5 Pro: 37 s from "Excluding object" to the
shutdown, with 1 Hz status queries answered in 1.30-1.78 s throughout
while the main MCU sat at mcu_awake=0.000 and gcodein=0.

EXCLUDE_OBJECT_DEFINE is not the cost. The line costs 115 ms through the
live klippy and nothing thereafter; three prints of the same file
without a cancel complete with bytes_retransmit=0. Removing it removes
the object there is to cancel, not the work.

tools/gcode-bench holds the harnesses, including the shlex fast path
that takes that DEFINE from 91.6 ms to 5.9 ms on the printer -- worth
having for large plates, but not this bug.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AVUMfbNuKUWWV32PBcydwb
2026-08-31 15:25:51 +03:00

25 lines
985 B
Python
Executable File

#!/usr/bin/env python3
# Generate a synthetic EXCLUDE_OBJECT_DEFINE block of the shape a slicer emits.
import argparse, json, math, sys
def polygon(cx, cy, r, n):
return [[round(cx + r * math.cos(2*math.pi*i/n), 3),
round(cy + r * math.sin(2*math.pi*i/n), 3)] for i in range(n)]
def block(nobj, npoints, name_len=24):
out = []
for i in range(nobj):
cx, cy = 20.0 + (i % 10) * 20.0, 20.0 + (i // 10) * 20.0
name = ("PART_%d_" % i).ljust(name_len, 'X')[:max(name_len, 8)]
poly = json.dumps(polygon(cx, cy, 8.0, npoints), separators=(',', ' '))
out.append('EXCLUDE_OBJECT_DEFINE NAME=%s CENTER=%g,%g POLYGON=%s'
% (name, cx, cy, poly))
return out
if __name__ == '__main__':
p = argparse.ArgumentParser()
p.add_argument('--objects', type=int, default=25)
p.add_argument('--points', type=int, default=64)
a = p.parse_args()
sys.stdout.write('\n'.join(block(a.objects, a.points)) + '\n')