合并代码
This commit is contained in:
@@ -174,57 +174,54 @@ class Deviceinfo(object):
|
||||
try:
|
||||
self.manager.send(model.toDict())
|
||||
except Exception as e:
|
||||
LogManager.warning(f"发送上线事件失败:{e}", model.deviceId)
|
||||
LogManager.info(f"设备上线,当前在线数:{len(self.deviceModelList)}", model.deviceId)
|
||||
LogManager.warning(f"{model.deviceId} 发送上线事件失败:{e}")
|
||||
LogManager.info(f"{model.deviceId} 设备上线,当前在线数:{len(self.deviceModelList)}")
|
||||
|
||||
# 删除设备
|
||||
def _remove_model(self, udid: str):
|
||||
print("进入删除方法")
|
||||
"""线程安全:把设备从内存、端口、iproxy 完全抹掉"""
|
||||
model = self._model_index.pop(udid, None)
|
||||
print(model)
|
||||
if not model:
|
||||
print("没有找到model")
|
||||
LogManager.error(f"没有找到需要删除的设备 {udid}")
|
||||
return
|
||||
model.type = 2
|
||||
|
||||
# 内存结构删除
|
||||
# 1. 内存列表删除
|
||||
try:
|
||||
idx = self.deviceModelList.index(model)
|
||||
self.deviceModelList.pop(idx)
|
||||
self.deviceModelList.remove(model)
|
||||
except ValueError:
|
||||
print("有错误了")
|
||||
pass
|
||||
|
||||
# 端口回收(关键)
|
||||
# 2. 端口回收
|
||||
self._free_port(model.screenPort)
|
||||
|
||||
# 清理 iproxy
|
||||
survivors = [item for item in self.pidList if item.get("id") != udid]
|
||||
# 3. 原子化清理 iproxy(确保进程彻底死)
|
||||
to_kill, survivors = [], []
|
||||
for item in self.pidList:
|
||||
if item.get("id") == udid:
|
||||
self._terminate_proc(item.get("target"))
|
||||
self.pidList = survivors
|
||||
(to_kill if item.get("id") == udid else survivors).append(item)
|
||||
|
||||
# Socket 发送(无锁)
|
||||
for item in to_kill:
|
||||
self._terminate_proc(item.get("target")) # 下面也给了加强版实现
|
||||
|
||||
self.pidList = survivors
|
||||
LogManager.info(f"设备下线 {udid},当前在线数:{len(self.deviceModelList)}")
|
||||
|
||||
# 4. 发 Socket
|
||||
retry = 3
|
||||
while retry:
|
||||
try:
|
||||
self.manager.send(model.toDict())
|
||||
print("删除了")
|
||||
break
|
||||
except Exception as e:
|
||||
print("有问题了", e)
|
||||
retry -= 1
|
||||
LogManager.error(f"发送下线事件失败,剩余重试 {retry}:{e}", udid)
|
||||
LogManager.error(f"发送下线事件失败,{udid} 剩余重试 {retry}:{e}")
|
||||
time.sleep(0.2)
|
||||
else:
|
||||
LogManager.error("发送下线事件彻底失败,主动崩溃防止状态不一致", udid)
|
||||
LogManager.error(f"发送下线事件彻底失败,{udid} 主动崩溃防止状态不一致")
|
||||
os._exit(1)
|
||||
|
||||
LogManager.info(f"设备下线,当前在线数:{len(self.deviceModelList)}", udid)
|
||||
|
||||
# region ===================== 端口分配与回收 =====================
|
||||
def _alloc_port(self) -> int:
|
||||
print(self.screenProxy)
|
||||
if self._port_pool:
|
||||
port = self._port_pool.pop()
|
||||
else:
|
||||
@@ -289,17 +286,63 @@ class Deviceinfo(object):
|
||||
return False
|
||||
|
||||
def relayDeviceScreenPort(self, udid: str, port: int) -> Optional[subprocess.Popen]:
|
||||
"""启动 iproxy 前:端口若仍被占用则先杀掉占用者,再启动"""
|
||||
if not self._spawn_iproxy:
|
||||
LogManager.error("iproxy 启动器未就绪,无法建立端口映射", udid)
|
||||
LogManager.error("iproxy 启动器未就绪", udid)
|
||||
return None
|
||||
|
||||
# --- 新增:端口冲突检查 + 强制清理 ---
|
||||
while self._port_in_use and self._is_port_open(port):
|
||||
# 先查是哪个进程占用
|
||||
pid = self._get_pid_by_port(port)
|
||||
if pid and pid != os.getpid():
|
||||
LogManager.warning(f"端口 {port} 仍被 PID {pid} 占用,尝试释放", udid)
|
||||
self._kill_pid_gracefully(pid)
|
||||
else:
|
||||
break
|
||||
# -------------------------------------
|
||||
|
||||
try:
|
||||
p = self._spawn_iproxy(udid, port, 9100)
|
||||
self._port_in_use.add(port)
|
||||
LogManager.info(f"启动 iproxy 成功,本地 {port} -> 设备 9100", udid)
|
||||
return p
|
||||
except Exception as e:
|
||||
LogManager.error(f"启动 iproxy 失败:{e}", udid)
|
||||
return None
|
||||
|
||||
# ------------------- 新增三个小工具 -------------------
|
||||
def _is_port_open(self, port: int) -> bool:
|
||||
import socket
|
||||
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
|
||||
return s.connect_ex(("127.0.0.1", port)) == 0
|
||||
|
||||
def _get_pid_by_port(self, port: int) -> Optional[int]:
|
||||
"""跨平台根据端口号查 PID,失败返回 None"""
|
||||
try:
|
||||
if os.name == "nt":
|
||||
cmd = ["netstat", "-ano", "-p", "tcp"]
|
||||
out = subprocess.check_output(cmd, text=True)
|
||||
for line in out.splitlines():
|
||||
if f"127.0.0.1:{port}" in line and "LISTENING" in line:
|
||||
return int(line.strip().split()[-1])
|
||||
else:
|
||||
cmd = ["lsof", "-t", f"-iTCP:{port}", "-sTCP:LISTEN"]
|
||||
out = subprocess.check_output(cmd, text=True)
|
||||
return int(out.strip().split()[0])
|
||||
except Exception:
|
||||
return None
|
||||
|
||||
def _kill_pid_gracefully(self, pid: int):
|
||||
"""先 terminate 再 kill -9"""
|
||||
try:
|
||||
os.kill(pid, signal.SIGTERM)
|
||||
time.sleep(1)
|
||||
os.kill(pid, signal.SIGKILL)
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
|
||||
def _terminate_proc(self, p: Optional[subprocess.Popen]):
|
||||
if not p or p.poll() is not None:
|
||||
return
|
||||
@@ -328,5 +371,4 @@ class Deviceinfo(object):
|
||||
for p in candidates:
|
||||
if p.exists():
|
||||
return p
|
||||
raise FileNotFoundError(f"iproxy not found, tried: {[str(c) for c in candidates]}")
|
||||
# endregion
|
||||
raise FileNotFoundError(f"iproxy not found, tried: {[str(c) for c in candidates]}")
|
||||
@@ -63,7 +63,6 @@ def start_socket_listener():
|
||||
while True:
|
||||
try:
|
||||
conn, addr = s.accept()
|
||||
LogManager.info(f"[INFO] Connection from {addr}")
|
||||
except Exception as e:
|
||||
LogManager.error(f"[ERROR] accept 失败: {e}")
|
||||
continue
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
import random
|
||||
import re
|
||||
import time
|
||||
|
||||
import tidevice
|
||||
import wda
|
||||
@@ -177,7 +179,25 @@ class ControlUtils(object):
|
||||
session.tap(left_x, center_y)
|
||||
|
||||
|
||||
# 点击一个随机范围
|
||||
@classmethod
|
||||
def tap_mini_cluster(cls, center_x: int, center_y: int, session, points=5, duration_ms=60):
|
||||
"""
|
||||
以 (center_x, center_y) 为中心,在 3×3 像素范围内摆 `points` 个邻近坐标,
|
||||
一次性同时按下,持续 `duration_ms` 后抬起。
|
||||
"""
|
||||
# 1. 生成邻像素坐标(±1 像素内)
|
||||
cluster = []
|
||||
for i in range(points):
|
||||
dx = random.randint(-1, 1)
|
||||
dy = random.randint(-1, 1)
|
||||
cluster.append((center_x + dx, center_y + dy))
|
||||
|
||||
# 2. 随机持续时间 50–100 ms
|
||||
duration_s = random.randint(50, 100) / 1000.0
|
||||
|
||||
# 3. 下发多点触控
|
||||
session.tap_hold(cluster, duration=duration_s)
|
||||
|
||||
# 检测五分钟前和当前的状态是否相同
|
||||
# @classmethod
|
||||
|
||||
Reference in New Issue
Block a user