The iPhone USB source gave up permanently if the first connect_device() failed (app not streaming on :7000 at startup -- phone locked, app not launched/crashed, USB hiccup): start() returned False and the read thread (which holds the reconnect loop) never ran. Auto-reconnect only covered a mid-session drop, never a startup race. Now start() launches the thread regardless and _run reconnects from the top of its loop, so the initial connect retries with backoff exactly like a mid-session drop -- the source connects whenever the iPhone app appears, no data_only_viz restart needed. Test: tests/test_iphone_usb_reconnect.py (start() keeps retrying when no device). The 2 unrelated pre-existing collection errors are unchanged.
This commit is contained in:
@@ -91,12 +91,17 @@ class IphoneUSBSource:
|
||||
self._opened = False
|
||||
|
||||
def start(self) -> bool:
|
||||
# Try an immediate connect, but do NOT give up if the iPhone app isn't
|
||||
# streaming yet: start the read thread regardless. _run retries the
|
||||
# connect (with backoff) until the app appears, so a startup race (app
|
||||
# not ready / phone locked at launch) recovers the same way a mid-session
|
||||
# drop does -- no data_only_viz restart needed.
|
||||
sock = connect_device()
|
||||
if sock is None:
|
||||
LOG.error("iphone usb: no device / connect failed")
|
||||
return False
|
||||
self._sock = sock
|
||||
self._opened = True
|
||||
LOG.warning("iphone usb: no device yet -- thread will keep retrying")
|
||||
else:
|
||||
self._sock = sock
|
||||
self._opened = True
|
||||
self._thread = threading.Thread(
|
||||
target=self._run, args=(sock,), name="iphone_usb_src", daemon=True)
|
||||
self._thread.start()
|
||||
@@ -107,6 +112,13 @@ class IphoneUSBSource:
|
||||
|
||||
def _run(self, sock) -> None:
|
||||
while not self._stop.is_set():
|
||||
if sock is None: # no connection yet (startup or post-drop)
|
||||
sock = self._reconnect()
|
||||
if sock is None: # release() was called while waiting
|
||||
break
|
||||
self._sock = sock
|
||||
self._opened = True
|
||||
LOG.info("iphone usb connected")
|
||||
try:
|
||||
for tag, pid, payload in iter_frames(sock):
|
||||
if self._stop.is_set():
|
||||
@@ -161,15 +173,9 @@ class IphoneUSBSource:
|
||||
with self._lock:
|
||||
self._frame = None
|
||||
self._opened = False
|
||||
if self._stop.is_set():
|
||||
break
|
||||
LOG.info("iphone usb stream ended — reconnecting...")
|
||||
sock = self._reconnect()
|
||||
if sock is None:
|
||||
break
|
||||
self._sock = sock
|
||||
self._opened = True
|
||||
LOG.info("iphone usb reconnected")
|
||||
sock = None # forces the reconnect at the loop top
|
||||
if not self._stop.is_set():
|
||||
LOG.info("iphone usb stream ended -- reconnecting...")
|
||||
self._opened = False
|
||||
|
||||
def _reconnect(self):
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
"""The iPhone USB source must keep retrying the initial connect, not give up
|
||||
when the iPhone app isn't streaming yet. Auto-reconnect must cover a startup
|
||||
race (app not ready when data_only_viz starts), not only a mid-session drop."""
|
||||
import time
|
||||
|
||||
import data_only_viz.iphone_usb_source as m
|
||||
|
||||
|
||||
def test_start_keeps_retrying_when_no_device(monkeypatch):
|
||||
calls = {"n": 0}
|
||||
|
||||
def fake_connect():
|
||||
calls["n"] += 1
|
||||
return None # device never available during the test
|
||||
|
||||
monkeypatch.setattr(m, "connect_device", fake_connect)
|
||||
monkeypatch.setattr(m, "iter_frames", lambda s: iter(()))
|
||||
src = m.IphoneUSBSource()
|
||||
try:
|
||||
assert src.start() is True # does NOT give up on initial connect failure
|
||||
time.sleep(0.25) # let the read thread attempt a few reconnects
|
||||
assert calls["n"] >= 2, (
|
||||
"start() should keep retrying; connect_device called %d time(s)" % calls["n"])
|
||||
finally:
|
||||
src.release()
|
||||
Reference in New Issue
Block a user