mirror of
https://github.com/openglow-org/forgefirm.git
synced 2026-09-27 08:41:13 -07:00
A test that does not hand the machine back fails
The baseline has always examined the machine after every run and recorded what the run left behind. It did nothing else with it: the leftovers went to the log and the evidence, and the test still reported PASS. So a check could measure correctly, walk away with the machine in a state nobody chose, and be recorded green. That is how the purge fan came to be left off by the airflow check. The leftover was not even watched, but had it been, it would have been noted and the test would have passed anyway, and an operator would still have met the airflow hold at their first fire. A post-run leftover now fails the run. One the baseline put back fails it too: the restore is the bench cleaning up after a defect, not the defect's absence. The message names what was left. The baseline watches the head as well as the motion side now: purge air on, which is how the machine idles, and the lens motor at its hold current in half step, which the lens checks and the sheet cards take and must hand back. The airflow check proves the machine is whole rather than merely measured: afterward the purge fan must read commanded-on and must draw above the floor the check itself just wrote. The pin takes forgectrl 0.1.13 (a2d73ef), which restores the idle posture after a diagnostic, fixes the lens session's takeover flag, and gives the setup a Download logs button, since the panel's Logs tab is unreachable until the setup is complete. Expect this to find things. A test that has been handing the machine back imperfectly has been passing until now, and the first campaign under the rule is where that shows.
This commit is contained in:
@@ -65,6 +65,21 @@ FIXED_SYSFS = [
|
|||||||
("head/white_led", "0"),
|
("head/white_led", "0"),
|
||||||
("thermal/heater_pwm", "0"),
|
("thermal/heater_pwm", "0"),
|
||||||
("thermal/tec_on", "0"),
|
("thermal/tec_on", "0"),
|
||||||
|
# Purge air runs continuously, as on the factory machine: the engine
|
||||||
|
# turns it on at start and only a listening (the quiet hold) switches
|
||||||
|
# it off, which puts it back on release. A check that switches it off
|
||||||
|
# to measure it and forgets leaves the machine one job away from an
|
||||||
|
# airflow hold mid-cut, judged against the floor that same check just
|
||||||
|
# wrote, and nothing notices until the daemon restarts: the engine's
|
||||||
|
# idle phase never re-applies its own duties. The airflow check did
|
||||||
|
# exactly that, and it reached an operator's first fire.
|
||||||
|
("head/purge_air", "1"),
|
||||||
|
# The lens motor at rest: hold current, half step. The lens checks and
|
||||||
|
# the sheet cards take it to the run current in full or half step and
|
||||||
|
# must hand it back, or the motor sits hot and the next reference
|
||||||
|
# starts from a state nobody chose.
|
||||||
|
("head/z_current", "1"),
|
||||||
|
("head/z_mode", "1"),
|
||||||
]
|
]
|
||||||
|
|
||||||
# The subset the GRBL controller writes at its start: checked and restored
|
# The subset the GRBL controller writes at its start: checked and restored
|
||||||
|
|||||||
@@ -994,7 +994,21 @@ class Runner:
|
|||||||
run.log(traceback.format_exc().rstrip())
|
run.log(traceback.format_exc().rstrip())
|
||||||
self.fixture_release(run)
|
self.fixture_release(run)
|
||||||
try:
|
try:
|
||||||
self._baseline_post(run, captured)
|
left = self._baseline_post(run, captured)
|
||||||
|
# A test hands the machine back as it found it. Anything left
|
||||||
|
# behind was recorded here and nothing more, so a check that
|
||||||
|
# measured correctly and walked away with the machine in a
|
||||||
|
# state nobody chose still passed: that is how the airflow
|
||||||
|
# check came to leave the purge fan off, and an operator met
|
||||||
|
# it at their first fire instead of the bench meeting it here.
|
||||||
|
# The pass is now conditional on the machine being whole. A
|
||||||
|
# leftover the baseline put back still fails: the restore is
|
||||||
|
# the bench cleaning up after a defect, not the defect's
|
||||||
|
# absence.
|
||||||
|
if left and result == _campaign.PASS:
|
||||||
|
result = _campaign.FAIL
|
||||||
|
message = ("the machine was not handed back as found: %s"
|
||||||
|
% "; ".join(str(x) for x in left))
|
||||||
except Exception as e: # noqa: BLE001 - never lose the result over the cleanup
|
except Exception as e: # noqa: BLE001 - never lose the result over the cleanup
|
||||||
run.log("baseline: post pass errored: %s: %s" % (type(e).__name__, e))
|
run.log("baseline: post pass errored: %s: %s" % (type(e).__name__, e))
|
||||||
duration = int(time.time() - run.started)
|
duration = int(time.time() - run.started)
|
||||||
|
|||||||
@@ -167,7 +167,8 @@ def check_sensors(ctx):
|
|||||||
"for 35 s, the purge is measured on and off, and five settings are written: the "
|
"for 35 s, the purge is measured on and off, and five settings are written: the "
|
||||||
"four floors at 55 percent of steady and the grace from the slowest spin-up. Each "
|
"four floors at 55 percent of steady and the grace from the slowest spin-up. Each "
|
||||||
"fan must read at least 1000 rpm. The five settings are put back as found at the "
|
"fan must read at least 1000 rpm. The five settings are put back as found at the "
|
||||||
"end and the controller must be running again.")
|
"end, the purge fan is on again and drawing over the floor the check just wrote, "
|
||||||
|
"and the controller must be running again.")
|
||||||
def check_airflow(ctx):
|
def check_airflow(ctx):
|
||||||
keys = ["cool_tach_exhaust_min_rpm", "cool_tach_intake_min_rpm", "cool_tach_air_assist_min_rpm",
|
keys = ["cool_tach_exhaust_min_rpm", "cool_tach_intake_min_rpm", "cool_tach_air_assist_min_rpm",
|
||||||
"cool_purge_min_current", "cool_fan_grace_s"]
|
"cool_purge_min_current", "cool_fan_grace_s"]
|
||||||
@@ -186,6 +187,20 @@ def check_airflow(ctx):
|
|||||||
for k in keys:
|
for k in keys:
|
||||||
ctx.check(s.get(k) == floors.get(k), "%s reads %r, the check wrote %r", k, s.get(k), floors.get(k))
|
ctx.check(s.get(k) == floors.get(k), "%s reads %r, the check wrote %r", k, s.get(k), floors.get(k))
|
||||||
ctx.log("floors: %s", json.dumps(floors))
|
ctx.log("floors: %s", json.dumps(floors))
|
||||||
|
# The check switches purge air off to read its off current. Leaving
|
||||||
|
# it off costs the next job an airflow hold mid-cut, judged against
|
||||||
|
# the very floor written above, and nothing puts it back until the
|
||||||
|
# daemon restarts: the engine's idle phase never re-applies its own
|
||||||
|
# duties. Prove the machine is whole, not just measured.
|
||||||
|
purge_on = ctx.sysfs("head/purge_air")
|
||||||
|
ctx.check(purge_on == "1", "purge air reads %r after the check, expected 1 (on)", purge_on)
|
||||||
|
gate = ((ctx.forgectrl.get("/cool/status")[1] or {}).get("fan_gates") or {}).get("purge") or {}
|
||||||
|
ctx.evidence["purge_after"] = {"purge_air": purge_on, "gate": gate}
|
||||||
|
ctx.log("purge after the check: commanded %s, drawing %s against the %s floor",
|
||||||
|
purge_on, gate.get("reading"), gate.get("floor"))
|
||||||
|
ctx.check((gate.get("reading") or 0) >= (gate.get("floor") or 0),
|
||||||
|
"purge draws %s, under the %s floor the check just wrote: the next job would be held",
|
||||||
|
gate.get("reading"), gate.get("floor"))
|
||||||
ok = ctx.wait_for(lambda: (ctx.forgectrl.get("/mode")[1] or {}).get("controller") == "running", 60)
|
ok = ctx.wait_for(lambda: (ctx.forgectrl.get("/mode")[1] or {}).get("controller") == "running", 60)
|
||||||
ctx.check(ok is not None, "the controller did not come back after the check")
|
ctx.check(ok is not None, "the controller did not come back after the check")
|
||||||
|
|
||||||
|
|||||||
@@ -2,5 +2,5 @@
|
|||||||
# only SRCREV and PV here - the image manifest leaves *-pin.inc out of the
|
# only SRCREV and PV here - the image manifest leaves *-pin.inc out of the
|
||||||
# layer content hash because the component entry already identifies the
|
# layer content hash because the component entry already identifies the
|
||||||
# pinned source (forgefirm-image-manifest.bbclass).
|
# pinned source (forgefirm-image-manifest.bbclass).
|
||||||
SRCREV = "468ee215dd1148b92c979decacecc41aa6a75ccf"
|
SRCREV = "a2d73efeb9af376e122e0877a4988d92d021cfa7"
|
||||||
PV = "0.1.12"
|
PV = "0.1.13"
|
||||||
|
|||||||
Reference in New Issue
Block a user