阶段2: USB 远程 adb server 打通(220 host 网络 5037 全接口监听,无需改 220)+ 自建远程看屏(MJPEG 流 + u2 触控 tap/swipe/key/text,ws-scrcpy 不在 npm 改自建)
This commit is contained in:
+57
-32
@@ -1,9 +1,9 @@
|
||||
"""设备生命周期 + 通用 Worker 基类 + 全局状态注册表 + 心跳看门狗。
|
||||
|
||||
STFDevice — 单设备生命周期:acquire(占用+adb连接)→ release(断开+释放)
|
||||
BaseWorker — 通用 worker 基类:封装 STF 生命周期 + u2 连接 + 状态上报 + stop
|
||||
STFDevice — 单设备生命周期:acquire(直连/远程 server)→ release
|
||||
BaseWorker — 通用 worker 基类:封装设备生命周期 + u2 连接 + 状态上报 + stop
|
||||
+ 生命周期钩子(setup/teardown/on_error) + 心跳上报
|
||||
子类只需实现 run_task(d),可选实现钩子,不用管 STF/adb/状态/异常
|
||||
子类只需实现 run_task(d),可选实现钩子,不用管 adb/状态/异常
|
||||
_WORKERS — 全局状态注册表,供 web_server 读取实时状态
|
||||
_watchdog — 心跳看门狗线程,检测 worker 卡死
|
||||
|
||||
@@ -16,10 +16,11 @@ from concurrent.futures import ThreadPoolExecutor, TimeoutError as FuturesTimeou
|
||||
|
||||
import uiautomator2 as u2
|
||||
|
||||
from config import USB_ADB_HOST, USB_ADB_PORT
|
||||
from core.logger import get_logger
|
||||
from core.stf_client import DeviceOfflineError, STFError
|
||||
from core import device_pool
|
||||
from .adb_helper import adb_connect, adb_disconnect
|
||||
from .adb_helper import adb_connect, _adb_remote
|
||||
|
||||
_log = get_logger("core.worker")
|
||||
|
||||
@@ -35,15 +36,15 @@ _U2_INFO_TIMEOUT = 10
|
||||
class STFDevice:
|
||||
"""单设备生命周期:acquire(直连)→ release(无操作)。
|
||||
|
||||
阶段 1:已摘除 STF occupy/release——单实例互斥由 TaskManager._running 保证,
|
||||
设备健康由 device_pool(本地 adb 在线状态)判断。保留 STF 引用仅为
|
||||
非 IP:port(USB 序列号)设备的 remoteConnect 桥接,阶段 2 换成 220 adb server。
|
||||
阶段 2:已摘除 STF occupy/release 与 remoteConnect 桥接——
|
||||
单实例互斥由 TaskManager._running 保证;非 IP:port(USB 序列号)设备
|
||||
通过 220 的远程 adb server(-H/-P)驱动,不再依赖 STF。
|
||||
"""
|
||||
|
||||
def __init__(self, stf_client=None, serial=None):
|
||||
self.stf = stf_client # 仅 USB 桥接用(阶段 2 移除)
|
||||
def __init__(self, serial=None):
|
||||
self.serial = serial
|
||||
self.remote_adb_url = None
|
||||
self.remote_adb_url = None # IP:5555 设备:设备地址
|
||||
self.usb_server = None # USB 设备:远程 adb server 地址(host:port)
|
||||
|
||||
def acquire(self, serial=None):
|
||||
self.serial = serial or self.serial or self._pick_free()
|
||||
@@ -51,18 +52,21 @@ class STFDevice:
|
||||
|
||||
# 直连优先:serial 本身是 IP:5555(如 Tailscale 网络设备)时直接 adb connect。
|
||||
# 只 connect、绝不 disconnect/kill-server(红线),不影响 STF provider 的连接。
|
||||
# 仅当 serial 不是 IP:port(如 USB 序列号)时才走 STF 桥接(阶段 2 替换)。
|
||||
if ":" in self.serial:
|
||||
self.remote_adb_url = self.serial
|
||||
_log.info(f"[{self.serial}] 直连设备: {self.remote_adb_url}")
|
||||
if not adb_connect(self.remote_adb_url):
|
||||
raise STFError(f"adb connect 失败: {self.remote_adb_url}", "offline")
|
||||
else:
|
||||
if self.stf is None:
|
||||
raise STFError(f"USB 设备 {self.serial} 暂不支持(未启用 STF 桥接)", "offline")
|
||||
self.remote_adb_url = self.stf.remote_connect(self.serial)
|
||||
_log.info(f"[{self.serial}] STF 桥接: {self.remote_adb_url}")
|
||||
|
||||
if not adb_connect(self.remote_adb_url):
|
||||
raise STFError(f"adb connect 失败: {self.remote_adb_url}", "offline")
|
||||
# USB 设备:插在 220 上,本机 adb 看不到,经远程 adb server 驱动。
|
||||
# 无需 adb connect——设备已在远程 server 上,直接校验可见性。
|
||||
self.usb_server = f"{USB_ADB_HOST}:{USB_ADB_PORT}"
|
||||
_log.info(f"[{self.serial}] USB 设备,远程 adb server: {self.usb_server}")
|
||||
out = _adb_remote(USB_ADB_HOST, USB_ADB_PORT, "devices") or ""
|
||||
seen = {line.split()[0] for line in out.splitlines()[1:]
|
||||
if len(line.split()) >= 2}
|
||||
if self.serial not in seen:
|
||||
raise STFError(f"USB 设备 {self.serial} 不在远程 adb server", "offline")
|
||||
time.sleep(2)
|
||||
|
||||
def _pick_free(self):
|
||||
@@ -72,15 +76,8 @@ class STFDevice:
|
||||
return free[0]
|
||||
|
||||
def release(self):
|
||||
if not self.serial:
|
||||
return
|
||||
# 直连模式(remote_adb_url == serial):不 disconnect(红线,STF 共享 transport)
|
||||
if self.remote_adb_url and self.remote_adb_url != self.serial:
|
||||
adb_disconnect(self.remote_adb_url)
|
||||
try:
|
||||
self.stf.remote_disconnect(self.serial)
|
||||
except Exception:
|
||||
pass
|
||||
# 直连模式:不 disconnect(红线,STF 共享 transport);USB:无连接可断
|
||||
pass
|
||||
|
||||
|
||||
# ================== 全局 worker 状态注册表(供 web_server 读取) ==================
|
||||
@@ -349,15 +346,20 @@ class BaseWorker(threading.Thread):
|
||||
# ================== 基类主循环(不要重写) ==================
|
||||
def run(self):
|
||||
"""基类主循环:不要重写。子类实现 run_task + 可选钩子。"""
|
||||
device = STFDevice(self.stf, serial=self.serial)
|
||||
device = STFDevice(serial=self.serial)
|
||||
_update_status(self.serial, status="connecting", stf_occupied=False,
|
||||
last_error="", remote_adb_url="", model="",
|
||||
last_error="", remote_adb_url="", usb_server="", model="",
|
||||
current_action="")
|
||||
try:
|
||||
device.acquire()
|
||||
remote = device.remote_adb_url
|
||||
# u2.connect 可能 hang 住(atx-agent 无响应),用线程池+超时保护
|
||||
self.d = self._u2_connect_with_timeout(remote)
|
||||
usb_server = device.usb_server
|
||||
# u2.connect 可能 hang 住(atx-agent 无响应),用线程池+超时保护;
|
||||
# USB 设备(无 IP:5555)经远程 adb server 建 u2 连接
|
||||
if usb_server:
|
||||
self.d = self._u2_connect_usb_with_timeout(usb_server)
|
||||
else:
|
||||
self.d = self._u2_connect_with_timeout(remote)
|
||||
if self.d is None:
|
||||
raise STFError(f"u2.connect 超时 {_U2_CONNECT_TIMEOUT}s,atx-agent 无响应", "offline")
|
||||
_log.info(f"[{self.serial}] u2 连接成功")
|
||||
@@ -369,7 +371,8 @@ class BaseWorker(threading.Thread):
|
||||
except Exception:
|
||||
pass
|
||||
_update_status(self.serial, status="running", stf_occupied=True,
|
||||
remote_adb_url=remote, model=model)
|
||||
remote_adb_url=remote or "", usb_server=usb_server or "",
|
||||
model=model)
|
||||
|
||||
# setup 钩子
|
||||
try:
|
||||
@@ -435,6 +438,28 @@ class BaseWorker(threading.Thread):
|
||||
except FuturesTimeout:
|
||||
_log.error(f"[{self.serial}] u2.connect 超时 {_U2_CONNECT_TIMEOUT}s")
|
||||
return None
|
||||
|
||||
def _u2_connect_usb_with_timeout(self, server):
|
||||
"""USB 设备:经远程 adb server(220)建 u2 连接,带超时保护。
|
||||
|
||||
u2 3.7 底层 adbutils 支持远程 adb server:AdbClient(host, port).device(serial),
|
||||
再交给 u2.connect(AdbDevice)(与本地连接同语义,无需 adb connect)。
|
||||
"""
|
||||
host, _, port = server.partition(":")
|
||||
with ThreadPoolExecutor(max_workers=1) as pool:
|
||||
def _connect():
|
||||
from adbutils import AdbClient
|
||||
dev = AdbClient(host=host, port=int(port)).device(self.serial)
|
||||
return u2.connect(dev)
|
||||
fut = pool.submit(_connect)
|
||||
try:
|
||||
return fut.result(timeout=_U2_CONNECT_TIMEOUT)
|
||||
except FuturesTimeout:
|
||||
_log.error(f"[{self.serial}] u2 远程连接超时 {_U2_CONNECT_TIMEOUT}s")
|
||||
return None
|
||||
except Exception as e:
|
||||
_log.error(f"[{self.serial}] u2 远程连接失败: {e}")
|
||||
return None
|
||||
except Exception as e:
|
||||
_log.error(f"[{self.serial}] u2.connect 异常: {e}")
|
||||
return None
|
||||
|
||||
Reference in New Issue
Block a user