laser_lifecycle_test: a travel job takes the button toggle and lands on its target

Rule 7 proved the button's pause/resume toggle on an armed job only, and
nothing checked that the move resumed from the hold reaches its target.
The catalog's motion.button-hold-resume held both on the machine with a
travel job; it can leave the catalog once the host does.

- test_button_pause_resume, after its armed half, runs the catalog's
  job: M5, G91, G1 X40 F300 (8 s). It must reach Run without asking for
  the arm press; a press holds it part-way; a second press resumes it to
  Idle within 0.05 mm of +40.
- Rule 7's line in the header and the case's docstring say so.

Proof: the case passes on a null-sink build of the grblHAL-glowforge
working tree under Linux (the armed half as before; the travel job held
at 8.747 mm and resumed to 39.999 mm). A control, the same job held and
soft-reset instead of resumed, ends at 8.747 mm, which the landing check
fails. pyflakes is clean.

Acceptance: none. The harness runs in grblHAL-glowforge's CI.
This commit is contained in:
ScottW514
2026-10-13 18:42:23 -04:00
parent 67ac8f4064
commit a0a4edb322
+39 -2
View File
@@ -26,7 +26,8 @@ reported messages:
soft reset, no alarm; latch relocked, never armed)
7. outside the arm wait the button is the pause/resume toggle: a press
while running feed-holds, a press while held resumes; the arming
press itself is not a pause press
press itself is not a pause press; a travel job (no laser, no arm
wait) takes the same toggle, and its resumed move lands on its target
8. the lid or the interlock loop opening mid-job cancels the job (the
default lid_policy): reason reported, armed window closed, a soft
reset with the position kept (no alarm), the head returns to the
@@ -936,7 +937,8 @@ def start_armed_move(s, tag, gcode="G1 X30 F60"):
def test_button_pause_resume():
"""Rule 7: outside the arm wait the button is the pause/resume toggle -
a press while running is a feed hold, a press while held is a cycle
start; the arming press itself is not a pause press."""
start; the arming press itself is not a pause press. A travel job takes
the same toggle, and its resumed move lands on its target."""
s = Session("button-toggle", disarm_s=60, switches=SW_CLOSED)
try:
start_armed_move(s, "button-toggle")
@@ -958,6 +960,41 @@ def test_button_pause_resume():
finally:
s.close()
# The same toggle on a travel job: no laser, so no arm wait, and the
# move resumed from the hold lands where it was programmed to.
s = Session("button-toggle-travel", disarm_s=60, switches=SW_CLOSED)
try:
x0 = s.mpos_x()
if x0 is None:
fail("[button-toggle-travel] no position in the status report")
s.send_raw("M5")
s.send_raw("G91")
s.send_raw("G1 X40 F300") # 8 s
if not s.wait_state("Run", 5):
fail("[button-toggle-travel] the travel job never reported Run")
if s.count(PROMPT):
fail("[button-toggle-travel] a job with no laser asked for the arm press")
time.sleep(1.0)
s.press_button()
if not s.wait_state("Hold", 3):
fail("[button-toggle-travel] a press while running did not feed-hold (state %s)" % s.state())
held = s.mpos_x()
if held is None or not x0 < held < x0 + 40.0:
fail("[button-toggle-travel] held at X %s, not part-way from %s" % (held, x0))
time.sleep(0.5)
s.press_button()
if not s.wait_state("Run", 3):
fail("[button-toggle-travel] a press while held did not resume (state %s)" % s.state())
if not s.wait_state("Idle", 15):
fail("[button-toggle-travel] the resumed move did not complete (state %s)" % s.state())
x1 = s.mpos_x()
if x1 is None or abs(x1 - x0 - 40.0) > 0.05:
fail("[button-toggle-travel] the resumed move ended at X %s, not %.3f" % (x1, x0 + 40.0))
print("PASS [button-toggle-travel]: a travel job held at %.3f mm and resumed to its target, "
"%.3f mm" % (held - x0, x1 - x0))
finally:
s.close()
def test_lid_cancels_and_returns():
"""Rule 8 (lid_policy = cancel, the default): the lid opening mid-job