From a0a4edb322a6210d104f83805b82999bb5a4c96c Mon Sep 17 00:00:00 2001 From: ScottW514 Date: Tue, 13 Oct 2026 18:42:23 -0400 Subject: [PATCH] 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. --- scripts/bench/laser_lifecycle_test.py | 41 +++++++++++++++++++++++++-- 1 file changed, 39 insertions(+), 2 deletions(-) diff --git a/scripts/bench/laser_lifecycle_test.py b/scripts/bench/laser_lifecycle_test.py index cc972a3..f4cdc3f 100644 --- a/scripts/bench/laser_lifecycle_test.py +++ b/scripts/bench/laser_lifecycle_test.py @@ -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