diff --git a/.idea/workspace.xml b/.idea/workspace.xml
index 24dd257..65da30f 100644
--- a/.idea/workspace.xml
+++ b/.idea/workspace.xml
@@ -5,15 +5,9 @@
-
-
-
-
-
-
-
-
+
+
@@ -56,32 +50,30 @@
- {
+ "keyToString": {
+ "ASKED_ADD_EXTERNAL_FILES": "true",
+ "Python.12.executor": "Run",
+ "Python.123.executor": "Run",
+ "Python.Main.executor": "Run",
+ "Python.tidevice_entry.executor": "Run",
+ "RunOnceActivity.ShowReadmeOnStart": "true",
+ "RunOnceActivity.TerminalTabsStorage.copyFrom.TerminalArrangementManager.252": "true",
+ "RunOnceActivity.git.unshallow": "true",
+ "SHARE_PROJECT_CONFIGURATION_FILES": "true",
+ "git-widget-placeholder": "main",
+ "javascript.nodejs.core.library.configured.version": "20.17.0",
+ "javascript.nodejs.core.library.typings.version": "20.17.58",
+ "last_opened_file_path": "F:/company code/AI item/20250820/iOSAI",
+ "node.js.detected.package.eslint": "true",
+ "node.js.detected.package.tslint": "true",
+ "node.js.selected.package.eslint": "(autodetect)",
+ "node.js.selected.package.tslint": "(autodetect)",
+ "nodejs_package_manager_path": "npm",
+ "settings.editor.selected.configurable": "preferences.editor.code.editing",
+ "vue.rearranger.settings.migration": "true"
}
-}]]>
+}
@@ -150,52 +142,6 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
@@ -221,18 +167,15 @@
-
-
-
+
-
-
+
@@ -297,7 +240,6 @@
-
@@ -367,18 +309,15 @@
+
-
-
-
-
-
-
+
-
-
+
+
+
\ No newline at end of file
diff --git a/Entity/DeviceModel.py b/Entity/DeviceModel.py
index 8590b2c..d0108bf 100644
--- a/Entity/DeviceModel.py
+++ b/Entity/DeviceModel.py
@@ -15,7 +15,7 @@ class DeviceModel(object):
self.scale = scale
# 1 添加 2删除
self.type = type
-
+ self.ready = False
# 转字典
def toDict(self):
diff --git a/Module/DeviceInfo.py b/Module/DeviceInfo.py
index 05116b7..52dc466 100644
--- a/Module/DeviceInfo.py
+++ b/Module/DeviceInfo.py
@@ -34,6 +34,9 @@ class Deviceinfo(object):
self._port_pool: List[int] = [] # 端口回收池
self._port_in_use: set[int] = set() # 正在使用的端口
+ # 🔥1. 启动 WDA 健康检查线程
+ threading.Thread(target=self._wda_health_checker, daemon=True).start()
+
# region iproxy 初始化
try:
self.iproxy_path = self._iproxy_path()
@@ -124,7 +127,40 @@ class Deviceinfo(object):
time.sleep(1)
- # endregion
+ # 🔥2. WDA 健康检查
+ def _wda_health_checker(self):
+ while True:
+ time.sleep(1)
+ print(len(self.deviceModelList))
+ with self._lock:
+ online = [m for m in self.deviceModelList if m.ready] # ← 只检查就绪的
+ print(len(online))
+ for model in online:
+ udid = model.deviceId
+ if not self._wda_ok(udid):
+ LogManager.warning(f"WDA 异常,重启通道:{udid}", udid)
+ with self._lock:
+ self._remove_model(udid)
+ self.connectDevice(udid)
+
+ # 🔥3. 真正做 health-check 的地方
+ def _wda_ok(self, udid: str) -> bool:
+ """返回 True 表示 WDA 活着,False 表示已死"""
+ try:
+ # 用 2 秒超时快速探测
+ c = wda.USBClient(udid, 8100)
+ # 下面这句就是“xctest launched but check failed” 的触发点
+ # 如果 status 里返回了 WebDriverAgent 运行信息就认为 OK
+ st = c.status()
+ if st.get("state") != "success":
+ return False
+ # 你也可以再苛刻一点,多做一次 /wda/healthcheck
+ # c.http.get("/wda/healthcheck")
+ return True
+ except Exception as e:
+ # 任何异常(连接拒绝、超时、json 解析失败)都认为已死
+ LogManager.error(f"WDA health-check 异常:{e}", udid)
+ return False
# region ===================== 增删改查唯一入口(线程安全) =====================
def _has_model(self, udid: str) -> bool:
@@ -133,6 +169,7 @@ class Deviceinfo(object):
def _add_model(self, model: DeviceModel):
if model.deviceId in self._model_index:
return # 防重复
+ model.ready = True
self.deviceModelList.append(model)
self._model_index[model.deviceId] = model
try:
@@ -188,6 +225,7 @@ class Deviceinfo(object):
# region ===================== 端口分配与回收 =====================
def _alloc_port(self) -> int:
+ print(self.screenProxy)
if self._port_pool:
port = self._port_pool.pop()
else:
@@ -207,9 +245,6 @@ class Deviceinfo(object):
if not self.is_device_trusted(udid):
LogManager.warning("设备未信任,跳过 WDA 启动", udid)
return
- if self._has_model(udid):
- LogManager.warning("设备已存在,跳过重复连接", udid)
- return
try:
d = wda.USBClient(udid, 8100)