diff --git a/core/device_discovery.py b/core/device_discovery.py index 410dfe9..60cde19 100644 --- a/core/device_discovery.py +++ b/core/device_discovery.py @@ -22,7 +22,7 @@ from config import (USB_ADB_HOST, DISCOVERY_PORT, DISCOVERY_SUBNETS, DISCOVERY_INTERVAL) from core.adb_helper import _adb, adb_connect_light from core.logger import get_logger -from core.models import db, PendingDevice +from core.models import db, Device, PendingDevice _log = get_logger("core.disc") @@ -220,8 +220,14 @@ def scan_once(manual=False): first_seen=now, last_seen=now)) added += 1 db.session.commit() + # 6. 正式池断联设备自动重连:adb connect 会因 WiFi 波动/设备重启/ + # adb 服务重启而断开——扫描线程每轮顺带重试(幂等轻量,内部 + # 全局锁串行),连上即恢复在线,无需人工干预。pending 池是给 + # 「未授权新设备」的,正式池设备断联不进 pending,而是自动重连。 + back = _reconnect_offline(configured) _log.info(f"发现: 探测开放 {len(open_ips)} 台,可连 {len(verified)} 台," - f"新增待连接 {added} 台") + f"新增待连接 {added} 台" + + (f",自动重连恢复 {len(back)} 台 {back}" if back else "")) _last_scan = (now, len(open_ips), len(verified), added) _last_error = "" return True, {"found": len(open_ips), "verified": len(verified), "new": added} @@ -240,6 +246,39 @@ def device_pool_list_configured(): return device_pool.list_configured() +# ================== 正式池断联设备:自动重连 ================== +def _reconnect_offline(configured): + """对正式池中断联的网络设备逐个 adb 重连,返回恢复的 serial 列表。 + + 只重连网络设备(IP:5555;USB 设备插着就在,无需 connect)。 + 幂等轻量:内部 adb 全局锁串行,失败静默(下轮扫描再试)。 + """ + online_now = _parse_adb_devices(_adb("devices")) + targets = [s for s in (configured or []) + if ":" in s and s not in online_now] + if not targets: + return [] + for serial in targets: + adb_connect_light(serial) + online_after = _parse_adb_devices(_adb("devices")) + return [s for s in targets if s in online_after] + + +def list_pool_offline(): + """正式设备池中断联的设备(serial + 型号 + 备注名),面板展示用。 + + 断联设备仍是正式池成员(不删除、不进 pending)——pending 是给未授权 + 新设备的;它们由扫描线程每轮自动重连,也可前端手动立即重连。 + """ + online = _parse_adb_devices(_adb("devices")) + with _ctx(): + rows = [d.to_dict() for d in Device.query.filter_by(enabled=True) + .order_by(Device.serial).all()] + return [{"serial": r["serial"], "model": r.get("model") or "", + "name": r.get("name") or ""} + for r in rows if r["serial"] not in online] + + # ================== 定时扫描线程 ================== def _discovery_loop(): """定时扫描 daemon 线程。每轮重读配置(开关/周期即时生效)。 diff --git a/static/admin/tools.js b/static/admin/tools.js index d81aaf2..1d1a714 100644 --- a/static/admin/tools.js +++ b/static/admin/tools.js @@ -81,6 +81,29 @@ function loadDiscovery(){ +''; }).join(''); } + // 正式池断联设备:仍在设备池(不删除),扫描线程自动重连,可手动立即重连 + const offTb=document.getElementById('tb-pool-offline'); + if(offTb){ + const off=(r.pool_offline||[]); + if(!off.length){ + offTb.innerHTML='全部在线'; + }else{ + offTb.innerHTML=off.map(x=>{ + return ''+esc(x.serial)+'' + +''+esc(x.model||'-')+''+esc(x.name||'')+'' + +'断联·自动重连中' + +''; + }).join(''); + } + } + }); +} +function reconnectPoolDev(serial){ + apiPost('/api/devices/discovery/reconnect',{serial}).then(r=>{ + if(r&&r.ok){ + showToast('重连已启动(约 5-15 秒生效)','success'); + setTimeout(loadDiscovery,8000); + }else showToast('重连失败: '+((r&&r.error)||''),'error'); }); } function toggleDiscovery(checked){ diff --git a/templates/admin/monitor.html b/templates/admin/monitor.html index fcc172a..6d81020 100644 --- a/templates/admin/monitor.html +++ b/templates/admin/monitor.html @@ -766,6 +766,15 @@ body{background:var(--bg);font-family:var(--body);color:var(--text);font-size:14
扫描局域网/Tailscale 网段中开放 adb 5555 的设备进入待连接池; 确认后才加入正式设备池(不自动连接、不参与任务调度)。未授权设备(手机未接受 RSA 弹窗)不会出现。
+ +
+
设备池断联设备 + (仍在设备池内,扫描线程每轮自动重连;也可手动立即重连)
+ + + +
serial型号备注状态操作
全部在线
+