优化杀死iproxy逻辑
This commit is contained in:
12
.idea/workspace.xml
generated
12
.idea/workspace.xml
generated
@@ -5,18 +5,8 @@
|
|||||||
</component>
|
</component>
|
||||||
<component name="ChangeListManager">
|
<component name="ChangeListManager">
|
||||||
<list default="true" id="eceeff5e-51c1-459c-a911-d21ec090a423" name="Changes" comment="20250904-初步功能已完成">
|
<list default="true" id="eceeff5e-51c1-459c-a911-d21ec090a423" name="Changes" comment="20250904-初步功能已完成">
|
||||||
<change afterPath="$PROJECT_DIR$/resources/133bffc17635b7a3bd709492b7a519d96710a4a2/bgv.png" afterDir="false" />
|
|
||||||
<change afterPath="$PROJECT_DIR$/resources/3157d8bd35c230012cb3640d2f26ffd0b61a10d1/bgv.png" afterDir="false" />
|
|
||||||
<change afterPath="$PROJECT_DIR$/resources/f2fb0c352df29415f69708d90a9daba702ae4917/bgv.png" afterDir="false" />
|
|
||||||
<change beforePath="$PROJECT_DIR$/.idea/iOSAI.iml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/iOSAI.iml" afterDir="false" />
|
|
||||||
<change beforePath="$PROJECT_DIR$/.idea/misc.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/misc.xml" afterDir="false" />
|
|
||||||
<change beforePath="$PROJECT_DIR$/.idea/workspace.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/workspace.xml" afterDir="false" />
|
<change beforePath="$PROJECT_DIR$/.idea/workspace.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/workspace.xml" afterDir="false" />
|
||||||
<change beforePath="$PROJECT_DIR$/Entity/ResultData.py" beforeDir="false" afterPath="$PROJECT_DIR$/Entity/ResultData.py" afterDir="false" />
|
<change beforePath="$PROJECT_DIR$/Module/DeviceInfo.py" beforeDir="false" afterPath="$PROJECT_DIR$/Module/DeviceInfo.py" afterDir="false" />
|
||||||
<change beforePath="$PROJECT_DIR$/Module/FlaskService.py" beforeDir="false" afterPath="$PROJECT_DIR$/Module/FlaskService.py" afterDir="false" />
|
|
||||||
<change beforePath="$PROJECT_DIR$/Module/FlaskSubprocessManager.py" beforeDir="false" afterPath="$PROJECT_DIR$/Module/FlaskSubprocessManager.py" afterDir="false" />
|
|
||||||
<change beforePath="$PROJECT_DIR$/Module/Main.py" beforeDir="false" afterPath="$PROJECT_DIR$/Module/Main.py" afterDir="false" />
|
|
||||||
<change beforePath="$PROJECT_DIR$/Utils/LogManager.py" beforeDir="false" afterPath="$PROJECT_DIR$/Utils/LogManager.py" afterDir="false" />
|
|
||||||
<change beforePath="$PROJECT_DIR$/Utils/ThreadManager.py" beforeDir="false" afterPath="$PROJECT_DIR$/Utils/ThreadManager.py" afterDir="false" />
|
|
||||||
</list>
|
</list>
|
||||||
<option name="SHOW_DIALOG" value="false" />
|
<option name="SHOW_DIALOG" value="false" />
|
||||||
<option name="HIGHLIGHT_CONFLICTS" value="true" />
|
<option name="HIGHLIGHT_CONFLICTS" value="true" />
|
||||||
|
|||||||
@@ -4,12 +4,12 @@ import signal
|
|||||||
import sys
|
import sys
|
||||||
import time
|
import time
|
||||||
from concurrent.futures import ThreadPoolExecutor, as_completed
|
from concurrent.futures import ThreadPoolExecutor, as_completed
|
||||||
|
|
||||||
import wda
|
|
||||||
import threading
|
|
||||||
import subprocess
|
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import List, Dict, Optional
|
from typing import List, Dict, Optional
|
||||||
|
import threading
|
||||||
|
import subprocess
|
||||||
|
|
||||||
|
import wda
|
||||||
from tidevice import Usbmux, ConnectionType
|
from tidevice import Usbmux, ConnectionType
|
||||||
from tidevice._device import BaseDevice
|
from tidevice._device import BaseDevice
|
||||||
from Entity.DeviceModel import DeviceModel
|
from Entity.DeviceModel import DeviceModel
|
||||||
@@ -23,10 +23,8 @@ class Deviceinfo(object):
|
|||||||
"""设备生命周期管理:以 deviceModelList 为唯一真理源"""
|
"""设备生命周期管理:以 deviceModelList 为唯一真理源"""
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
...
|
# ✅ 连接线程池(最大 6 并发)
|
||||||
# ✅ 新增:连接线程池(最大 6 并发)
|
|
||||||
self._connect_pool = ThreadPoolExecutor(max_workers=6)
|
self._connect_pool = ThreadPoolExecutor(max_workers=6)
|
||||||
...
|
|
||||||
|
|
||||||
if os.name == "nt":
|
if os.name == "nt":
|
||||||
self._si = subprocess.STARTUPINFO()
|
self._si = subprocess.STARTUPINFO()
|
||||||
@@ -44,11 +42,14 @@ class Deviceinfo(object):
|
|||||||
|
|
||||||
self._lock = threading.Lock()
|
self._lock = threading.Lock()
|
||||||
self._model_index: Dict[str, DeviceModel] = {} # udid -> model
|
self._model_index: Dict[str, DeviceModel] = {} # udid -> model
|
||||||
# ✅ 1. 失踪时间戳记录(替代原来的 miss_count)
|
# ✅ 失踪时间戳记录(替代原来的 miss_count)
|
||||||
self._last_seen: Dict[str, float] = {}
|
self._last_seen: Dict[str, float] = {}
|
||||||
self._port_pool: List[int] = []
|
self._port_pool: List[int] = []
|
||||||
self._port_in_use: set[int] = set()
|
self._port_in_use: set[int] = set()
|
||||||
|
|
||||||
|
# ✅ 新增:全局 iproxy 进程注册表 udid -> Popen
|
||||||
|
self._iproxy_registry: Dict[str, subprocess.Popen] = {}
|
||||||
|
|
||||||
# region iproxy 初始化(原逻辑不变)
|
# region iproxy 初始化(原逻辑不变)
|
||||||
try:
|
try:
|
||||||
self.iproxy_path = self._iproxy_path()
|
self.iproxy_path = self._iproxy_path()
|
||||||
@@ -76,6 +77,9 @@ class Deviceinfo(object):
|
|||||||
args = [str(self.iproxy_path), "-u", udid, str(local_port), str(remote_port)]
|
args = [str(self.iproxy_path), "-u", udid, str(local_port), str(remote_port)]
|
||||||
p = subprocess.Popen(args, **self._popen_kwargs)
|
p = subprocess.Popen(args, **self._popen_kwargs)
|
||||||
|
|
||||||
|
# ✅ 注册到全局表
|
||||||
|
self._iproxy_registry[udid] = p
|
||||||
|
|
||||||
def _pipe_to_log(name: str, stream):
|
def _pipe_to_log(name: str, stream):
|
||||||
try:
|
try:
|
||||||
for line in iter(stream.readline, ''):
|
for line in iter(stream.readline, ''):
|
||||||
@@ -127,6 +131,13 @@ class Deviceinfo(object):
|
|||||||
for udid in need_remove:
|
for udid in need_remove:
|
||||||
self._remove_model(udid)
|
self._remove_model(udid)
|
||||||
|
|
||||||
|
# ✅ 实时清理孤儿 iproxy(原 10 秒改为每次循环)
|
||||||
|
self._cleanup_orphan_iproxy()
|
||||||
|
|
||||||
|
# ✅ 设备全空时核平所有 iproxy
|
||||||
|
if not self.deviceModelList:
|
||||||
|
self._kill_all_iproxy()
|
||||||
|
|
||||||
# 2. 发现新设备 → 并发连接
|
# 2. 发现新设备 → 并发连接
|
||||||
with self._lock:
|
with self._lock:
|
||||||
new_udids = [d.udid for d in lists
|
new_udids = [d.udid for d in lists
|
||||||
@@ -146,7 +157,7 @@ class Deviceinfo(object):
|
|||||||
time.sleep(1)
|
time.sleep(1)
|
||||||
|
|
||||||
# ------------------------------------------------------------------
|
# ------------------------------------------------------------------
|
||||||
# ✅ 3. USB 层枚举 SN(跨平台)
|
# ✅ USB 层枚举 SN(跨平台)
|
||||||
# ------------------------------------------------------------------
|
# ------------------------------------------------------------------
|
||||||
def _usb_enumerate_sn(self) -> set[str]:
|
def _usb_enumerate_sn(self) -> set[str]:
|
||||||
try:
|
try:
|
||||||
@@ -155,7 +166,32 @@ class Deviceinfo(object):
|
|||||||
except Exception:
|
except Exception:
|
||||||
return set()
|
return set()
|
||||||
|
|
||||||
# ===================== 以下代码与原文件完全一致 =====================
|
# ----------------------------------------------------------
|
||||||
|
# ✅ 清理孤儿 iproxy
|
||||||
|
# ----------------------------------------------------------
|
||||||
|
def _cleanup_orphan_iproxy(self):
|
||||||
|
live_udids = set(self._model_index.keys())
|
||||||
|
for udid, proc in list(self._iproxy_registry.items()):
|
||||||
|
if udid not in live_udids:
|
||||||
|
LogManager.warning(f"发现孤儿 iproxy 进程,UDID 不在线:{udid},正在清理")
|
||||||
|
self._terminate_proc(proc)
|
||||||
|
self._iproxy_registry.pop(udid, None)
|
||||||
|
|
||||||
|
# ----------------------------------------------------------
|
||||||
|
# ✅ 核平所有 iproxy(Windows / macOS 通用)
|
||||||
|
# ----------------------------------------------------------
|
||||||
|
def _kill_all_iproxy(self):
|
||||||
|
try:
|
||||||
|
if os.name == "nt":
|
||||||
|
subprocess.run(["taskkill", "/F", "/IM", "iproxy.exe"], check=False)
|
||||||
|
else:
|
||||||
|
subprocess.run(["pkill", "-f", "iproxy"], check=False)
|
||||||
|
self._iproxy_registry.clear()
|
||||||
|
LogManager.info("已强制清理所有 iproxy 进程")
|
||||||
|
except Exception as e:
|
||||||
|
LogManager.warning(f"强制清理 iproxy 失败:{e}")
|
||||||
|
|
||||||
|
# -------------------- 以下代码与原文件完全一致 --------------------
|
||||||
def _wda_health_checker(self):
|
def _wda_health_checker(self):
|
||||||
while True:
|
while True:
|
||||||
time.sleep(1)
|
time.sleep(1)
|
||||||
@@ -229,6 +265,11 @@ class Deviceinfo(object):
|
|||||||
print(f"【删】待杀进程数 count={len(to_kill)} udid={udid}")
|
print(f"【删】待杀进程数 count={len(to_kill)} udid={udid}")
|
||||||
LogManager.method_info(f"【删】待杀进程数 count={len(to_kill)} udid={udid}", method="device_count")
|
LogManager.method_info(f"【删】待杀进程数 count={len(to_kill)} udid={udid}", method="device_count")
|
||||||
|
|
||||||
|
# ✅ 先清理注册表中的 iproxy
|
||||||
|
iproxy_proc = self._iproxy_registry.pop(udid, None)
|
||||||
|
if iproxy_proc:
|
||||||
|
self._terminate_proc(iproxy_proc)
|
||||||
|
|
||||||
for idx, item in enumerate(to_kill, 1):
|
for idx, item in enumerate(to_kill, 1):
|
||||||
print(f"【删】杀进程 {idx}/{len(to_kill)} pid={item.get('target').pid} udid={udid}")
|
print(f"【删】杀进程 {idx}/{len(to_kill)} pid={item.get('target').pid} udid={udid}")
|
||||||
LogManager.method_info(f"【删】杀进程 {idx}/{len(to_kill)} pid={item.get('target').pid} udid={udid}", method="device_count")
|
LogManager.method_info(f"【删】杀进程 {idx}/{len(to_kill)} pid={item.get('target').pid} udid={udid}", method="device_count")
|
||||||
@@ -335,13 +376,14 @@ class Deviceinfo(object):
|
|||||||
if not self._spawn_iproxy:
|
if not self._spawn_iproxy:
|
||||||
LogManager.error("iproxy 启动器未就绪", udid)
|
LogManager.error("iproxy 启动器未就绪", udid)
|
||||||
return None
|
return None
|
||||||
while self._port_in_use and self._is_port_open(port):
|
for attempt in range(5):
|
||||||
|
if not self._is_port_open(port):
|
||||||
|
break
|
||||||
|
LogManager.warning(f"端口 {port} 仍被占用,第 {attempt+1} 次重试释放", udid)
|
||||||
pid = self._get_pid_by_port(port)
|
pid = self._get_pid_by_port(port)
|
||||||
if pid and pid != os.getpid():
|
if pid and pid != os.getpid():
|
||||||
LogManager.warning(f"端口 {port} 仍被 PID {pid} 占用,尝试释放", udid)
|
|
||||||
self._kill_pid_gracefully(pid)
|
self._kill_pid_gracefully(pid)
|
||||||
else:
|
time.sleep(0.2)
|
||||||
break
|
|
||||||
try:
|
try:
|
||||||
p = self._spawn_iproxy(udid, port, 9100)
|
p = self._spawn_iproxy(udid, port, 9100)
|
||||||
self._port_in_use.add(port)
|
self._port_in_use.add(port)
|
||||||
@@ -405,4 +447,4 @@ class Deviceinfo(object):
|
|||||||
for p in candidates:
|
for p in candidates:
|
||||||
if p.exists():
|
if p.exists():
|
||||||
return p
|
return p
|
||||||
raise FileNotFoundError(f"iproxy not found, tried: {[str(c) for c in candidates]}")
|
raise FileNotFoundError(f"iproxy not found, tried: {[str(c) for c in candidates]}")
|
||||||
Binary file not shown.
Reference in New Issue
Block a user