READY: LIGHT TIMING (flash and FX cues against the drum hits); auto-fix on for testing

- LIGHT TIMING: every blinder flash / FX cue is timed against the Drums (Live) stem under it
  (deactivated clips count) on the Tracks Mac; cues a strong hit is more than 25 ms away from are named
  with song and bar
- Settings Overview: "Auto-fix on for testing" for 15 min to 2 h, outside the hours and over a pause;
  the AUTO-FIX key says TESTING, and it goes back to the hours by itself

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-22 10:18:46 -04:00
co-authored by Claude Opus 5.5
parent f179df59f1
commit d4e4092a9d
9 changed files with 300 additions and 19 deletions
+32 -7
View File
@@ -817,7 +817,7 @@ def check_ready_manual(chk, snap, ctx):
# What readiness.py looks at (check type "ready") and what a "ready" fix can do.
READY_ITEMS = ("tracks", "mix_foh", "lights", "keys", "tune", "routing", "ears", "prop", "timecode", "backgrounds", "led_cross", "lineup", "mics",
READY_ITEMS = ("tracks", "mix_foh", "lights", "light_timing", "keys", "tune", "routing", "ears", "prop", "timecode", "backgrounds", "led_cross", "lineup", "mics",
"track_bundle", "prop_bundle")
READY_ACTIONS = ("recheck", "lineup_sync", "led_cross", "copy_tracks", "ltc_to_pco", "ableton_mix")
@@ -1132,6 +1132,7 @@ class ServiceMode:
self.override, self.last_sched = saved.get("override"), saved.get("last_sched")
self.edge = saved.get("edge")
self.hold = bool(saved.get("hold")) # a pause that lasts through the hours, not just to the next edge
self.test = bool(saved.get("test")) # AUTO-FIX turned on for testing: on until its edge, then the hours again
def scheduled(self, now):
return in_windows(self.windows, now)
@@ -1153,6 +1154,12 @@ class ServiceMode:
self._save()
return sched
return False
if self.test:
if self.edge is None or now >= self.edge:
self.override, self.test, self.edge, self.last_sched = None, False, None, sched
self._save()
return sched
return True
if self.override is not None and self.edge is None:
self.edge = self.next_change(now) # an override saved before edges were kept
self._save()
@@ -1168,7 +1175,7 @@ class ServiceMode:
Looked up once a minute."""
if self.override is None:
return None
if self.hold:
if self.hold or self.test:
return self.edge
minute = int(now // 60)
cached = getattr(self, "_back", None)
@@ -1191,7 +1198,7 @@ class ServiceMode:
self.last_sched = sched
self.override = None if value == sched else value
self.edge = self.next_change(now) if self.override is not None else None
self.hold = False
self.hold = self.test = False
self._save()
def pause(self, now):
@@ -1199,13 +1206,19 @@ class ServiceMode:
(2026-10-21: a press on Saturday night used to do nothing for Sunday)."""
sched = self.scheduled(now)
start = now if sched else self.next_change(now)
self.override, self.hold, self.last_sched = False, True, sched
self.override, self.hold, self.test, self.last_sched = False, True, False, sched
self.edge = self.next_change(start) if start else None
self._save()
def resume(self, now):
"""Back to the hours."""
self.override, self.hold, self.edge, self.last_sched = None, False, None, self.scheduled(now)
self.override, self.hold, self.test, self.edge, self.last_sched = None, False, False, None, self.scheduled(now)
self._save()
def test_on(self, now, minutes):
"""On for testing, whatever the hours, for `minutes` (5 to 240); then back to the hours by itself."""
self.override, self.hold, self.test, self.last_sched = True, False, True, self.scheduled(now)
self.edge = now + max(5, min(240, int(minutes))) * 60
self._save()
@property
@@ -1213,7 +1226,8 @@ class ServiceMode:
return self.hold and self.override is False
def _save(self):
self.state.set(self.name, {"override": self.override, "last_sched": self.last_sched, "edge": self.edge, "hold": self.hold})
self.state.set(self.name, {"override": self.override, "last_sched": self.last_sched, "edge": self.edge, "hold": self.hold,
"test": self.test})
def until(self, now):
"""When the state shown now next changes by itself (an override's end, else the schedule's next edge),
@@ -2379,6 +2393,8 @@ def render(engine, service_on):
when = ("" if same_day else datetime.fromtimestamp(until).strftime("%a ").upper()) + clock12(until)
if engine.autofix.paused:
text = "%s\nPAUSED" % tile["label"] + ("\nTIL " + when if when else "")
elif engine.autofix.test and on:
text = "%s\nTESTING" % tile["label"] + ("\nTIL " + when if when else "")
elif on:
text = "%s\nON" % tile["label"] + ("\nTIL " + when if when else "")
else:
@@ -3751,7 +3767,8 @@ def snapshot_summary(snap, engine, now):
"service_on": engine.service.on(now),
"alarms_override": engine.service.override,
"auto_fix": {"on": engine.auto_on, "net_wait": engine.auto_paused, "split": engine.split,
"switch": engine.autofix.on(now), "override": engine.autofix.override, "paused": engine.autofix.paused},
"switch": engine.autofix.on(now), "override": engine.autofix.override, "paused": engine.autofix.paused,
"test_until": engine.autofix.edge if engine.autofix.test else None},
"bypass": {tid: {"since": b.get("since")} for tid, b in engine.bypass.items()},
"failover": dict(engine.failed),
"burst": engine.burst and {"start": engine.burst["start"], "conns": sorted(engine.burst["conns"]),
@@ -3797,6 +3814,14 @@ def handle_commands(text, engine, comp, snap, now):
target = {"on": True, "off": False}.get(arg, not engine.service.on(now))
engine.service.set(target, now)
log.info("SERVICE %s (button)", "ON" if target else "OFF")
elif verb == "autofix" and arg == "test":
# The settings page: "autofix test <minutes>" turns auto-fix on now, outside its hours, to try it out.
mins = int(words[2]) if len(words) > 2 and words[2].isdigit() else 60
engine.autofix.test_on(now, mins)
log.info("AUTO-FIX ON FOR TESTING until %s", datetime.fromtimestamp(engine.autofix.edge).strftime("%H:%M"))
if engine.autolog:
engine.autolog.write(now, "PANEL", "auto-fix on for testing (settings page) until %s"
% datetime.fromtimestamp(engine.autofix.edge).strftime("%H:%M"))
elif verb == "autofix":
# The AUTO-FIX key pauses and resumes; "off"/"pause" and "on"/"auto"/"resume" say which (NA's ALL AUTO-FIX key).
pause = {"off": True, "pause": True, "on": False, "auto": False, "resume": False}.get(arg, not engine.autofix.paused)