mirror of
https://github.com/openglow-org/forgefirm.git
synced 2026-09-27 16:51:12 -07:00
Stream and motion robustness: harness rules, catalog tests, the hand-back's counter scale
The GRBL driver's stream engine and its motion envelope change (grblHAL-glowforge: the shipper writes outside the lock, a clamp inside an armed window faults, the X/Y soft limits follow the home, the machine's settings are pinned, the homing keys are clamped). This commit carries the host rules and the catalog tests that hold them; the driver commit follows, because its CI fetches these harnesses unpinned. scripts/bench/laser_stream_test.py: - Rule 28: a 300 ms producer stall while armed faults the stream with ALARM:17, the kernel sees no step burst (at most the planned steps per 100 ticks), the stream ends dark, the latch sideband ends on the lock. The same stall unarmed is a warning: the move completes with every step, the clamp visible as the burst the kernel counts. - Rule 29: a 300 ms stall of the sink's write leaves the producer on pace: no clamp, every step, lit through, dark at the end. - The stand-in engine takes GFSINK_STALL_MS and GFSINK_WRITE_STALL_MS, null-sink only. scripts/bench/z_envelope_test.py: - Rule 10: homed (a gfcloud home), a program move past X max, Y max or the near edge alarms with ALARM:2 before any motion, a jog past the bed is refused with error 15, a move inside the bed runs, and a $20 write keeps the limits. The core repeats the last error for the line after a refused jog until an empty line clears it, so the rule sends one. forgetest/forgetest/suite/motion.py: - motion.soft-limits (kind auto, no emission): homes through the cloud suite's gfhome homing when the machine is not homed, then the three refusals (ALARM:2, the kernel counters still), the refused jog, the inside move, and the return to the corner read at rest. - motion.deadman phase 2b: a 100 ms SIGSTOP mid-move, inside the kernel's queue: no underrun, the controller's log warns of the clamped late events, the move completes with every step (read at rest), the latch stays locked. The armed clamp is proven on the host (rule 28). forgetest/forgetest/suite/cloud.py: - gfhome_homing drains the driver's answer to $H once the session ends: it sits behind the status reports and passed for the reply to the caller's next command (a setting read as None). forgetest/forgetest/baseline.py: - The hand-back reads the position counters at the kernel's own microstep mode (cnc/x_mode, read before the sysfs restore puts the settings' mode back). At the x8 constant, an x32 machine's 30 mm read as 120 mm, beyond the return bound, and the displaced head was left in place. The dead band scales the same way. tests/test_baseline.py holds both. Proof. Host: stream rules 1 to 29 and z_envelope rules 1 to 10 against the null-sink driver, the forgetest unit tests. Bench reference: motion.soft-limits passed (X 495 and Y 279 refused with ALARM:2 and 0.000 mm of kernel motion, X-1 the same, the jog error:15, the inside move ran, back at the corner 0.0/0.0 mm); motion.deadman passed with the new phase (92 late events clamped, max behind 92.3 ms, no underrun, 30.0 mm counted, latch locked) and the hand-back jogged the head back under the x32 scale.
This commit is contained in:
@@ -126,6 +126,21 @@ class BaselineTests(unittest.TestCase):
|
||||
self.assertTrue(items["position"].action.startswith("unrestorable"), items["position"].action)
|
||||
self.assertEqual(items["position"].found, [1000, 0, 0])
|
||||
|
||||
def test_the_counters_are_read_at_the_modes_scale(self):
|
||||
# 6400 steps is 30 mm at x32 (a return within the bound; on the
|
||||
# host it stops at the missing controller) and 120 mm at x8
|
||||
# (beyond the bound). Read at the x8 scale, an x32 machine's
|
||||
# displaced head is never jogged back.
|
||||
b = self.bl()
|
||||
cap = b.capture()
|
||||
self._attr("cnc/x_mode", "32")
|
||||
self._pos(6400, 0, 0)
|
||||
items = {x.item: x for x in b.enforce("post", captured=cap)}
|
||||
self.assertEqual(items["position"].action, "unrestorable: no running GRBL controller")
|
||||
self._attr("cnc/x_mode", "8")
|
||||
items = {x.item: x for x in b.enforce("post", captured=cap)}
|
||||
self.assertEqual(items["position"].action, "unrestorable: 120.0/0.0 mm exceeds 100 mm")
|
||||
|
||||
def test_a_held_controller_is_reset_before_the_return_jog(self):
|
||||
# a pause test that failed while held leaves the controller in
|
||||
# Hold, which refuses a jog: the baseline resets out of it first
|
||||
@@ -434,6 +449,13 @@ class PositionDeadbandTests(unittest.TestCase):
|
||||
self.assertFalse(baseline.position_quantized([0, 0, 0], [over, 0, 0]))
|
||||
self.assertFalse(baseline.position_quantized([0, 0, 0], [0, over, 0]))
|
||||
|
||||
def test_the_dead_band_scales_with_the_counters(self):
|
||||
# the same step count is a quarter of the distance at x32
|
||||
over = int(baseline.POSITION_DEADBAND_MM * baseline.XY_STEPS_PER_MM) + 2
|
||||
x32 = baseline.XY_STEPS_PER_MM_OF[32]
|
||||
self.assertTrue(baseline.position_quantized([0, 0, 0], [over, 0, 0], x32))
|
||||
self.assertFalse(baseline.position_quantized([0, 0, 0], [4 * over, 0, 0], x32))
|
||||
|
||||
def test_z_is_exact_because_the_return_never_moves_it(self):
|
||||
self.assertFalse(baseline.position_quantized([0, 0, 0], [0, 0, 1]))
|
||||
|
||||
|
||||
Reference in New Issue
Block a user