Wiring view: every machine and what connects it (Companion, MIDI, Dante, watchdog, web, Art-Net), hide types or single links, scripts per computer
RESI LIVE NOT REC follows the Resi auto-record service (custom autorec_status, not during its REC hold; stale heartbeat = no verdict); INGEST watches AUTO REC (running, heartbeat, Resi reachable), hold restarts it. Probes list each computer's scripts by file name only. HyperDeck addresses read from bonjourHost. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
+36
-18
@@ -76,7 +76,8 @@ def healthy_snapshot(now):
|
||||
snap.ping = {"10.1.3.227": 0, "10.6.7.13": 0, "10.1.0.1": 0}
|
||||
snap.link = True
|
||||
snap.launchd = {"org.waterschurch.resi-propresenter": 884, "org.waterschurch.lobby-captions": 900,
|
||||
"org.waterschurch.lobby-captions-display": 901, "org.waterschurch.digico-relay": 902}
|
||||
"org.waterschurch.lobby-captions-display": 901, "org.waterschurch.digico-relay": 902,
|
||||
"org.waterschurch.resi-autorec": 903}
|
||||
snap.launchd_time = now
|
||||
snap.procs = {"/Applications/ProdDash.app/Contents/MacOS/ProdDash": True}
|
||||
snap.remote = {"studiob\0pgrep -xq 'Pro Tools'": True} # the failover's check has a verdict
|
||||
@@ -488,12 +489,14 @@ class TestChecks(unittest.TestCase):
|
||||
db.execute("CREATE TABLE instances (id STRING, value STRING)")
|
||||
rows = [("a", {"label": "Axient_1", "config": {"host": "10.1.1.101", "port": 2202}}),
|
||||
("b", {"label": "OBS-Youtube", "config": {"host": " 10.1.9.126 "}}),
|
||||
("c", {"label": "no-host", "config": {}}), ("d", "not json")]
|
||||
("c", {"label": "no-host", "config": {}}), ("d", "not json"),
|
||||
("e", {"label": "HyperDeck_1", "config": {"bonjourHost": "10.1.10.11:9993"}})]
|
||||
db.executemany("INSERT INTO instances VALUES (?, ?)",
|
||||
[(i, v if isinstance(v, str) else json.dumps(v)) for i, v in rows])
|
||||
db.commit()
|
||||
db.close()
|
||||
self.assertEqual(sp.companion_conn_hosts(path), {"Axient_1": "10.1.1.101", "OBS-Youtube": "10.1.9.126"})
|
||||
self.assertEqual(sp.companion_conn_hosts(path), {"Axient_1": "10.1.1.101", "OBS-Youtube": "10.1.9.126",
|
||||
"HyperDeck_1": "10.1.10.11"})
|
||||
self.assertEqual(sp.companion_conn_hosts(os.path.join(d, "missing.sqlite")), {})
|
||||
|
||||
def test_fx_lights_midi_on_the_tracks_mac(self):
|
||||
@@ -1517,43 +1520,58 @@ class TestFixes(unittest.TestCase):
|
||||
self.assertEqual(self.plan("rec"), [("enable", "HyperDeck_1")])
|
||||
self.assertEqual(self.plan("x32"), [("enable", "x32")])
|
||||
|
||||
def autorec(self, status, hb=None):
|
||||
"""What the Resi auto-record service publishes (custom variables)."""
|
||||
self.snap.vars[("custom", "autorec_status")] = status
|
||||
self.snap.vars[("custom", "autorec_hb")] = str(int(SUNDAY + 100000 if hb is None else hb))
|
||||
|
||||
def test_resi_live_and_no_deck_recording_is_red_and_a_hold_starts_them(self):
|
||||
# 2026-10-27: "notify me if we are not recording on the hyperdecks, but resi is playing"
|
||||
self.snap.vars[("resi-studio-cues", "playback_state")] = "PLAYING"
|
||||
self.autorec("AUTO REC\nLIVE, NOT\nRECORDING")
|
||||
self.snap.vars[("HyperDeck_2", "slotId")] = "-" # optional deck, no disk: not in use
|
||||
self.snap.vars[("HyperDeck_2", "recordingTime")] = "--:--:--"
|
||||
tiles, _ = run_steps(self.engine, self.snap, SUNDAY, 30)
|
||||
self.assertEqual(level(tiles["hd1"]), "ok") # not yet: Resi just started
|
||||
tiles, _ = run_steps(self.engine, self.snap, SUNDAY + 30, 40)
|
||||
tiles, _ = run_steps(self.engine, self.snap, SUNDAY, 10)
|
||||
self.assertEqual(level(tiles["hd1"]), "ok") # not yet: give it a moment
|
||||
tiles, _ = run_steps(self.engine, self.snap, SUNDAY + 10, 30)
|
||||
self.assertEqual(tiles["hd1"]["x"], "HD1\nRESI LIVE NOT REC")
|
||||
self.assertEqual(level(tiles["rec"]), "err")
|
||||
self.assertEqual(level(tiles["hd2"]), "off")
|
||||
self.snap.vars[("HyperDeck_3", "status")] = "Record" # once one records, the others are NOT REC
|
||||
tiles, _ = run_steps(self.engine, self.snap, SUNDAY + 70, 4)
|
||||
self.autorec("AUTO REC\nRECORDING\n9:30 AM")
|
||||
tiles, _ = run_steps(self.engine, self.snap, SUNDAY + 40, 4)
|
||||
self.assertEqual(tiles["hd1"]["x"], "HD1\nNOT REC")
|
||||
started = []
|
||||
self.engine.record_executor = lambda ip, name: started.append((ip, name)) or True
|
||||
self.engine.run_fixes("rec", self.snap, None, SUNDAY + 80)
|
||||
self.engine.run_fixes("rec", self.snap, None, SUNDAY + 50)
|
||||
time.sleep(0.2)
|
||||
self.assertEqual([ip for ip, _ in started], ["10.1.10.11", "10.1.10.14", "10.1.10.15", "10.1.10.16"])
|
||||
self.assertTrue(started[0][1].startswith("CAM3-") and started[0][1].endswith("-"))
|
||||
self.assertTrue(started[3][1].startswith("PGM4-"))
|
||||
self.assertEqual(self.engine.notes["rec"]["text"], "STARTING\nREC")
|
||||
|
||||
def test_resi_paused_or_its_module_down_is_not_live(self):
|
||||
self.snap.vars[("resi-studio-cues", "playback_state")] = "PAUSED"
|
||||
tiles, _ = run_steps(self.engine, self.snap, SUNDAY, 70)
|
||||
def test_not_live_while_auto_rec_waits_or_is_stuck(self):
|
||||
self.autorec("AUTO REC\nREC AT\n9:20 AM") # live early: REC held until 10 min before
|
||||
tiles, _ = run_steps(self.engine, self.snap, SUNDAY, 40)
|
||||
self.assertEqual(level(tiles["hd1"]), "ok")
|
||||
self.snap.vars[("resi-studio-cues", "playback_state")] = "PLAYING"
|
||||
self.snap.conns["resi-studio-cues"]["category"] = "error"
|
||||
run_steps(self.engine, self.snap, SUNDAY + 70, 70)
|
||||
self.autorec("AUTO REC\nLIVE, NOT\nRECORDING", hb=SUNDAY - 600) # a stale verdict counts for nothing...
|
||||
tiles, _ = run_steps(self.engine, self.snap, SUNDAY + 40, 200)
|
||||
self.assertFalse(self.engine.resi_live)
|
||||
self.assertEqual(tiles["ingest"]["x"], "INGEST\nAUTO REC STUCK") # ...and says the service is stuck
|
||||
ran = []
|
||||
self.engine.executor = lambda cfg, fix_id, chk: ran.append(chk.get("conn")) or True
|
||||
self.engine.executor = lambda cfg, fix_id, chk: ran.append(fix_id) or True
|
||||
self.engine.record_executor = lambda ip, name: self.fail("must not record when Resi is not live")
|
||||
self.engine.run_fixes("hd1", self.snap, None, SUNDAY + 150) # the usual hold: reconnect
|
||||
self.engine.run_fixes("ingest", self.snap, None, SUNDAY + 250) # the hold restarts the service
|
||||
time.sleep(0.2)
|
||||
self.assertEqual(ran, ["HyperDeck_1"])
|
||||
self.assertEqual(ran, ["autorec"])
|
||||
|
||||
def test_auto_rec_not_running_or_without_resi(self):
|
||||
del self.snap.launchd["org.waterschurch.resi-autorec"]
|
||||
tiles, _ = run_steps(self.engine, self.snap, SUNDAY, 6)
|
||||
self.assertEqual(tiles["ingest"]["x"], "INGEST\nAUTO REC NOT LOADED")
|
||||
self.snap.launchd["org.waterschurch.resi-autorec"] = 903
|
||||
self.autorec("AUTO REC\nRESI ERROR")
|
||||
tiles, _ = run_steps(self.engine, self.snap, SUNDAY + 6, 200) # amber only once it lasts 3 minutes
|
||||
self.assertEqual((tiles["ingest"]["x"], level(tiles["ingest"])), ("INGEST\nAUTO REC NO RESI", "warn"))
|
||||
|
||||
def test_not_recording_is_not_fixed_by_a_reboot(self):
|
||||
for i in (1, 3):
|
||||
|
||||
Reference in New Issue
Block a user