Every MIDI link by wired address: flag links joined by name, hold MIDI moves them, refuse non-wired addresses
Tracks had joined NA Ingest, Lighting PC and Studio B by Bonjour name: macOS picks the network each time, so a reconnect could land on Wi-Fi or Dante. The wired check now also names a link joined by name; holding MIDI moves it to the wired address its own MIDI check names (a hold only: auto-fix never moves a working link). Reconnects prefer a wired address over a Wi-Fi or by-name one they last saw. The settings refuse a MIDI link whose address is a name or off the wired network, and the add-a-link screen fills in the wired address. Studio B's link now has its wired address, and Tracks' link to NA Ingest is watched (TRK INGEST). All links on the four Macs were moved and checked: wired IPv4, running. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
+64
-9
@@ -530,17 +530,53 @@ def check_midi_wired(chk, snap, ctx):
|
||||
for host, sessions in groups:
|
||||
for sname, sess in (sessions or {}).items():
|
||||
for p in sess.get("peers", []):
|
||||
name = p["name"].upper()
|
||||
name = name if len(name) <= 14 else name[:13].rstrip() + "."
|
||||
if by_name(p.get("address")):
|
||||
# Joined from the Bonjour list: on a Mac with Wi-Fi and Dante as well, macOS may pick
|
||||
# any of them, now or at the next reconnect. Hold MIDI joins it at its wired address.
|
||||
out.append(("%s|%s|%s" % (host, sname, p["name"]), Result(ERR, "%s BY NAME" % name, named=True)))
|
||||
continue
|
||||
ip = peer_ip(p.get("address"))
|
||||
if ip is None or ip.is_loopback or any(ip in n for n in nets) or (p["name"], str(ip)) in seen:
|
||||
continue
|
||||
seen.add((p["name"], str(ip)))
|
||||
name = p["name"].upper()
|
||||
name = name if len(name) <= 14 else name[:13] + "."
|
||||
out.append(("%s|%s|%s" % (host, sname, p["name"]),
|
||||
Result(ERR, "%s NOT WIRED" % name, named=True)))
|
||||
return out or [("wired", Result(OK))]
|
||||
|
||||
|
||||
def by_name(address):
|
||||
"""A peer joined by its Bonjour name ("\tNA Ingest\tlocal."): macOS picks the network each time."""
|
||||
return str(address or "").startswith("\t")
|
||||
|
||||
|
||||
def wired_nets(cfg):
|
||||
"""The wired network(s) the MIDI key's wired check names (empty: no rule)."""
|
||||
nets = []
|
||||
for t in all_tiles(cfg):
|
||||
for c in t.get("checks", []):
|
||||
if c.get("type") == "midi_wired":
|
||||
nets += [ipaddress.ip_network(n, strict=False) for n in c.get("nets") or []]
|
||||
return nets
|
||||
|
||||
|
||||
def pick_midi_address(nets, last, configured):
|
||||
"""Where a MIDI reconnect joins a peer: where it was last seen, unless that was off the wired
|
||||
network or a Bonjour name and the settings name a wired address (2026-10-20: a reconnect must
|
||||
never go back to a Wi-Fi copy)."""
|
||||
def wired(a):
|
||||
ip = peer_ip(a)
|
||||
return ip is not None and any(ip in n for n in nets)
|
||||
if not nets:
|
||||
return last or configured
|
||||
if last and wired(last):
|
||||
return last
|
||||
if configured and wired(configured):
|
||||
return configured
|
||||
return last or configured
|
||||
|
||||
|
||||
def check_mdns(chk, snap, ctx):
|
||||
if snap.mdns_time and chk["instance"] not in snap.mdns:
|
||||
return [("mdns", Result(ERR, chk.get("name", chk["instance"]) + " GONE", named=True))]
|
||||
@@ -1374,10 +1410,27 @@ class Engine:
|
||||
self.deck_ips[chk["conn"]] = ip
|
||||
chk = dict(chk, ip=ip)
|
||||
target = chk["conn"]
|
||||
if fix_kind == "midi_connect":
|
||||
# Reconnect the peer where it was last seen (or the config's address).
|
||||
if fix_kind == "midi_connect" and chk["type"] == "midi_wired":
|
||||
# A peer off the wired network or joined by name: join it again at the wired
|
||||
# address its own MIDI check names. One no check names (ProdDash's own) is left
|
||||
# to its program: the key says which.
|
||||
if errors_only:
|
||||
continue # moving a working link drops it a second: a hold only
|
||||
host, session, peer = (sub.split("|", 2) + ["", ""])[:3]
|
||||
nets = wired_nets(self.cfg)
|
||||
want = next((c.get("address") for t2 in all_tiles(self.cfg) for c in t2.get("checks", [])
|
||||
if c.get("type") == "midi_peer" and c.get("host", "") == host and c.get("session") == session
|
||||
and c.get("peer") == peer and peer_ip(c.get("address")) is not None
|
||||
and any(peer_ip(c["address"]) in n for n in nets)), None)
|
||||
if not want:
|
||||
continue
|
||||
chk = dict({"host": host} if host else {}, type="midi_peer", session=session, peer=peer, address=want, repoint=True)
|
||||
target = "|".join((host, session, peer))
|
||||
elif fix_kind == "midi_connect":
|
||||
# Reconnect the peer where it was last seen, or at the config's address when that
|
||||
# was off the wired network.
|
||||
key = (chk.get("host", ""), chk["session"], chk["peer"])
|
||||
address = snap.peer_addr.get(key) or chk.get("address")
|
||||
address = pick_midi_address(wired_nets(self.cfg), snap.peer_addr.get(key), chk.get("address"))
|
||||
if not address:
|
||||
continue
|
||||
chk = dict(chk, address=address)
|
||||
@@ -1903,7 +1956,8 @@ class Engine:
|
||||
return None
|
||||
chk = dict(chk, ip=chk.get("ip") or snap.var(chk["conn"], "ip") or self.deck_ips.get(chk["conn"]))
|
||||
if fix.get("kind") == "midi_connect":
|
||||
address = snap.peer_addr.get((chk.get("host", ""), chk["session"], chk["peer"])) or chk.get("address")
|
||||
address = pick_midi_address(wired_nets(self.cfg), snap.peer_addr.get((chk.get("host", ""), chk["session"], chk["peer"])),
|
||||
chk.get("address"))
|
||||
if not address:
|
||||
log.info("TEST %s: no address known for %s", key_id, chk["peer"])
|
||||
return None
|
||||
@@ -2711,9 +2765,10 @@ def execute_fix(cfg, fix_id, chk):
|
||||
if kind == "midi_connect":
|
||||
# Like Connect in Audio MIDI Setup: add the peer back to the session it dropped out of.
|
||||
args = (chk["session"], chk["peer"], chk["address"])
|
||||
repoint = bool(chk.get("repoint"))
|
||||
if chk.get("host"):
|
||||
r = subprocess.run(ssh_cmd(cfg, chk["host"]) + ["/usr/bin/python3", "-"],
|
||||
input=remote_midi_script("ns['connect_peer'](%r, %r, %r)" % args),
|
||||
input=remote_midi_script("ns['connect_peer'](%r, %r, %r, repoint=%r)" % (args + (repoint,))),
|
||||
capture_output=True, text=True, timeout=30)
|
||||
result = parse_remote_midi(r.stdout) if r.returncode == 0 else None
|
||||
if result is None:
|
||||
@@ -2722,10 +2777,10 @@ def execute_fix(cfg, fix_id, chk):
|
||||
return False
|
||||
else:
|
||||
import coremidi
|
||||
result = coremidi.connect_peer(*args)
|
||||
result = coremidi.connect_peer(*args, repoint=repoint)
|
||||
log.info("FIX %s -> %s at %s, session %s%s: %s", label, chk["peer"], chk["address"], chk["session"],
|
||||
(" on " + chk["host"]) if chk.get("host") else "", result)
|
||||
return result in ("asked", "already", "kicked")
|
||||
return result in ("asked", "already", "kicked", "moved")
|
||||
if kind == "netgear_poe":
|
||||
# Netgear AV switch on the Dante network: power-cycle one PoE port (netgear.py).
|
||||
import netgear
|
||||
|
||||
Reference in New Issue
Block a user