mirror of
https://github.com/openglow-org/forgefirm.git
synced 2026-10-19 02:51:42 -07:00
setup.focus: the head's place judged against the stops its manual home declared, read from the home anchor
head_at_home placed the gantry stops at minus manual_home_x and manual_home_y as the settings read at the burn. A manual home reads them once: it declares the head at the stops at minus the offsets, and with an offset it moves the head to X0 Y0, that far in front of them. A write of the offsets after the home restarts no controller and keeps the X/Y reference. So a home made with 5 and 3, the offsets then cleared, had the head at X0 Y0 judged home 5 and 3 mm in front of the stops, and the burn started: the false PASS2172ae4set out to close. The other way round, offsets set after a home made with none failed a head that was at the stops. The home anchor holds the stops as the home declared them: /status reports the anchor's X and Y plus the step counters. head_at_home now takes the stops from the X and Y of the anchor (baseline.ANCHOR_PATH) when /status reports X and Y referenced by a manual home, and judges the head against them within HOME_TOL_MM. An anchor that is gone, or that is not a manual home of X and Y, fails the test before any start. The settings are read for the remedy alone. With an offset, the remedy names how the push ends: release the motors, push the head against the stops, and end the release with Energize motors ($ME), not with Set home here, which moves the head off the stops to X0 Y0 again. The remedy before said only to release the motors and push the head, and a release left standing makes the burn ask home-push, which the test does not answer. Without an offset it stays "home it there". The judge only refuses more: no energy or motion path changes. Proof: tests/test_setup_burns.py, the Unit tests step of forgetest-ci.yml (python -B -m unittest discover -s tests), 24 tests OK on Windows with python 3.14 and on Ubuntu 24.04 with python 3.12. BurnRunTests plants the anchor of a home with no offset beside the fake's /status, and test_a_manual_home_offset_puts_the_stops_off_x0_y0 plants the anchor of each home: a home made with 5 and 3 fails the head at X0 Y0 and passes it at X-5 Y-3; the offsets cleared after that home still fail the head at X0 Y0 before any start (no wizard start, no lever); the offsets set after a home made with none pass the head at X0 Y0; an anchor that is gone, or that reads as the startup's Z-only reference, fails before any start. That last pair replaces the leg where an offset that did not read as a number failed: the stops no longer come from the settings, and the rule that leg held (no stops placed, nothing starts) holds on the anchor. test_setup_dark.py (21) and test_manifest.py (39, with the guard on the suite's package imports, which setup_burns.py's import of baseline.py passes) pass. Negative controls on scratch copies: head_at_home as it was fails the case at the offsets cleared after the home (Failed not raised), and so do the stops read from the settings; an anchor that cannot be read taken as X0 Y0 fails it at the anchor that is gone; an anchor of any axes and any source fails it at the startup anchor. A probe of the failure text: with the offsets 5 and 3 it names Energize motors ($ME), not Set home here; with none it says "home it there". pyflakes reports no undefined name. Acceptance: setup.focus's fingerprint moves (setup_burns.py's shared text and the test's own description); the other 42 do not move (source_sha compared before and after). baseline.py is a shared judge already, in every hash. Against the campaign this series already needs, that adds no run:850498fmoved all 43 once.
This commit is contained in:
@@ -21,7 +21,7 @@ import unittest
|
||||
|
||||
sys.path.insert(0, os.path.dirname(__file__))
|
||||
import helpers # noqa: E402
|
||||
from forgetest import catalog # noqa: E402
|
||||
from forgetest import baseline, catalog # noqa: E402
|
||||
from forgetest.runner import Aborted, Context, Failed, Run # noqa: E402
|
||||
from forgetest.suite import setup_burns, setup_dark # noqa: E402
|
||||
|
||||
@@ -291,14 +291,19 @@ class BurnRunTests(unittest.TestCase):
|
||||
os.environ["GF_SYSFS_ROOT"] = self.sysfs + "/"
|
||||
self.fake = helpers.FakeForgectrl().start()
|
||||
# the head at its home against the gantry stops, X and Y referenced
|
||||
# by a manual home (the fake's pos is X0 Y0 Z0)
|
||||
# by a manual home with no offset (the fake's pos is X0 Y0 Z0), its
|
||||
# anchor declaring the stops at X0 Y0
|
||||
self.fake.state["status"].update({"homed": True, "homed_axes": 7, "home_source": "manual"})
|
||||
self.saved_anchor = baseline.ANCHOR_PATH
|
||||
baseline.ANCHOR_PATH = os.path.join(self.sysfs, "grblhal.homed")
|
||||
self.anchor("0.000 0.000 0.000 7 manual")
|
||||
self.daemon = ScriptedDaemon(self.fake)
|
||||
self.poll = setup_dark.POLL_S
|
||||
setup_dark.POLL_S = 0.01
|
||||
|
||||
def tearDown(self):
|
||||
setup_dark.POLL_S = self.poll
|
||||
baseline.ANCHOR_PATH = self.saved_anchor
|
||||
self.fake.stop()
|
||||
if self.saved_root is None:
|
||||
os.environ.pop("GF_SYSFS_ROOT", None)
|
||||
@@ -312,6 +317,12 @@ class BurnRunTests(unittest.TestCase):
|
||||
with open(path, "w") as f:
|
||||
f.write(value + "\n")
|
||||
|
||||
def anchor(self, line):
|
||||
"""The controller's home anchor as a home writes it: X Y Z, the
|
||||
axes it references, and what set it."""
|
||||
with open(baseline.ANCHOR_PATH, "w") as f:
|
||||
f.write(line + "\n")
|
||||
|
||||
def run_test(self, tid):
|
||||
t = catalog.load_suite()[tid]
|
||||
run = self.run = Run("test", t.id, t.title) # kept for a run that fails
|
||||
@@ -451,11 +462,13 @@ class BurnRunTests(unittest.TestCase):
|
||||
self.assert_found()
|
||||
|
||||
def test_a_manual_home_offset_puts_the_stops_off_x0_y0(self):
|
||||
# manual_home_x 5 and manual_home_y 3: a manual home declares the
|
||||
# stops X-5 Y-3 and leaves the head at X0 Y0, in front of them,
|
||||
# where the wizard does not draw from. X0 Y0 fails before any start,
|
||||
# with no controller lever touched; the head at the stops is home.
|
||||
# A manual home made with manual_home_x 5 and manual_home_y 3
|
||||
# declares the stops X-5 Y-3 (its anchor) and leaves the head at
|
||||
# X0 Y0, in front of them, where the wizard does not draw from. X0
|
||||
# Y0 fails before any start, with no controller lever touched; the
|
||||
# head at the stops is home.
|
||||
self.fake.state["settings"].update({"manual_home_x": "5", "manual_home_y": "3"})
|
||||
self.anchor("-5.000 -3.000 0.000 7 manual")
|
||||
with self.assertRaises(Failed) as cm:
|
||||
self.run_test("setup.focus")
|
||||
self.assertIn("not at its home", str(cm.exception))
|
||||
@@ -467,12 +480,34 @@ class BurnRunTests(unittest.TestCase):
|
||||
run = self.run_test("setup.focus")
|
||||
self.assertTrue(run.evidence["head_at_start"]["judged"])
|
||||
self.assertEqual(self.daemon.order, ["laser.focus"])
|
||||
# an offset that does not read as a number places no stops: nothing starts
|
||||
self.fake.state["settings"]["manual_home_x"] = "five"
|
||||
# The offsets cleared after that home leave its reference as it is:
|
||||
# the stops are still X-5 Y-3, and the head at X0 Y0 still fails
|
||||
# before any start.
|
||||
for k in ("manual_home_x", "manual_home_y"):
|
||||
self.fake.state["settings"].pop(k)
|
||||
self.fake.state["status"]["pos"] = {"x": 0.0, "y": 0.0, "z": 0.0}
|
||||
with self.assertRaises(Failed) as cm:
|
||||
self.run_test("setup.focus")
|
||||
self.assertIn("manual_home_x reads 'five'", str(cm.exception))
|
||||
self.assertIn("not at its home", str(cm.exception))
|
||||
self.assertEqual(self.daemon.order, ["laser.focus"])
|
||||
# The offsets set after a home made with none: the head at X0 Y0 is
|
||||
# at the stops that home declared, and the burn runs.
|
||||
self.fake.state["settings"].update({"manual_home_x": "5", "manual_home_y": "3"})
|
||||
self.anchor("0.000 0.000 0.000 7 manual")
|
||||
run = self.run_test("setup.focus")
|
||||
self.assertEqual(run.evidence["head_at_start"]["stops"], {"x": 0.0, "y": 0.0})
|
||||
self.assertEqual(self.daemon.order, ["laser.focus", "laser.focus"])
|
||||
# An anchor that is gone, or that is not the manual home /status
|
||||
# reports, places no stops: nothing starts.
|
||||
os.unlink(baseline.ANCHOR_PATH)
|
||||
with self.assertRaises(Failed) as cm:
|
||||
self.run_test("setup.focus")
|
||||
self.assertIn("home anchor", str(cm.exception))
|
||||
self.anchor("0.000 0.000 0.000 4 startup")
|
||||
with self.assertRaises(Failed) as cm:
|
||||
self.run_test("setup.focus")
|
||||
self.assertIn("home anchor", str(cm.exception))
|
||||
self.assertEqual(self.daemon.order, ["laser.focus", "laser.focus"])
|
||||
|
||||
def test_an_unreferenced_head_runs_on_the_operators_word(self):
|
||||
# A fresh controller's counters-only position places nothing: the
|
||||
|
||||
Reference in New Issue
Block a user