From 648f6cdfee3d14239ff76a773c26dae82efad111 Mon Sep 17 00:00:00 2001 From: milk <53408947@qq.com> Date: Wed, 26 Nov 2025 16:18:02 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BC=98=E5=8C=96quart=E6=8E=A5=E5=8F=A3?= =?UTF-8?q?=E3=80=82=E4=BC=98=E5=8C=96=E6=B7=BB=E5=8A=A0=E8=AE=BE=E5=A4=87?= =?UTF-8?q?=E9=80=BB=E8=BE=91=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Module/DeviceInfo.py | 30 +++++---- Module/FlaskService.py | 139 +++++++++++++++++------------------------ 2 files changed, 77 insertions(+), 92 deletions(-) diff --git a/Module/DeviceInfo.py b/Module/DeviceInfo.py index e5294a3..08e93bf 100644 --- a/Module/DeviceInfo.py +++ b/Module/DeviceInfo.py @@ -4,12 +4,10 @@ import socket import threading import time import subprocess -from typing import Dict, Optional - +from typing import Dict import tidevice import wda from tidevice import Usbmux, ConnectionType - from Entity.DeviceModel import DeviceModel from Entity.Variables import WdaAppBundleId, wdaFunctionPort from Module.FlaskSubprocessManager import FlaskSubprocessManager @@ -71,31 +69,33 @@ class DeviceInfo: LogManager.method_info("进入主循环", "listen", udid="system") print("[Listen] 开始监听设备上下线...") + last_broadcast = 0.0 # 用来做“心跳全量同步”的时间戳 + while True: try: usb = Usbmux().device_list() online = {d.udid for d in usb if d.conn_type == ConnectionType.USB} except Exception as e: LogManager.warning(f"[device_list] 异常:{e}", udid="system") - time.sleep(1) - continue + print("[Listen] device_list 出错,本轮视为无设备,防止状态脏死") + usb = [] + online = set() with self._lock: known = set(self._models.keys()) - # 1. 新设备 + # ---------- 1. 新设备 ---------- + now = time.time() for udid in online: - self._last_seen[udid] = time.time() + self._last_seen[udid] = now if udid not in known: try: self._add_device(udid) except Exception as e: - # 单设备异常不能干掉整个循环 LogManager.warning(f"[Add] 处理设备 {udid} 异常: {e}", udid=udid) print(f"[Add] 处理设备 {udid} 异常: {e}") - # 2. 可能离线的设备 - now = time.time() + # ---------- 2. 可能离线设备 ---------- for udid in list(known): if udid not in online: last = self._last_seen.get(udid, 0) @@ -106,6 +106,15 @@ class DeviceInfo: LogManager.method_error(f"移除失败:{e}", "listen", udid=udid) print(f"[Remove] 移除失败 {udid}: {e}") + # ---------- 3. 心跳:每 5 秒强制同步一次到 Flask ---------- + if now - last_broadcast > 5.0: + try: + self._manager_send() + except Exception as e: + print(f"[Listen] 周期同步到 Flask 失败: {e}") + else: + last_broadcast = now + time.sleep(1) # ========================== @@ -216,7 +225,6 @@ class DeviceInfo: # 给 WDA 一点启动缓冲时间 time.sleep(2.0) - for _ in range(max_retry): # 设备已移除就不再尝试 with self._lock: diff --git a/Module/FlaskService.py b/Module/FlaskService.py index 5d8e20d..bda8386 100644 --- a/Module/FlaskService.py +++ b/Module/FlaskService.py @@ -469,20 +469,38 @@ async def passAnchorData(): needReply = data.get("needReply", False) # 获取打招呼数据 ev.prologueList = data.get("prologueList", []) - needTranslate = data.get("needTranslate", False) # 添加主播数据 addModelToAnchorList(acList) - # 启动线程,执行脚本 + + failed_ids = [] + # 启动线程,执行脚本(单个设备异常不影响其它设备) for udid in idList: - manager = ScriptManager() - event = threading.Event() - # 启动脚本 - thread = threading.Thread(target=manager.safe_greetNewFollowers, - args=(udid, needReply, isComment, needTranslate, event,)) - # 添加到线程管理 - ThreadManager.add(udid, thread, event) + try: + manager = ScriptManager() + event = threading.Event() + thread = threading.Thread( + target=manager.safe_greetNewFollowers, + args=(udid, needReply, isComment, needTranslate, event,), + ) + ThreadManager.add(udid, thread, event) + except Exception as e: + failed_ids.append(udid) + LogManager.error(f"[passAnchorData] 设备 {udid} 启动脚本失败: {e}") + + # 如果所有设备都失败,可以考虑返回错误码 + if failed_ids and len(failed_ids) == len(idList): + return ResultData( + data="", + code=1001, + message=f"所有设备启动失败: {failed_ids}" + ).toJson() + + # 部分失败也算整体成功,只是记录一下 + if failed_ids: + LogManager.warning(f"[passAnchorData] 部分设备启动失败: {failed_ids}") + return ResultData(data="").toJson() except Exception as e: LogManager.error(e) @@ -493,6 +511,7 @@ async def followAndGreetUnion(): try: LogManager.method_info("关注打招呼", "关注打招呼(联盟号)") data: Dict[str, Any] = await request.get_json() + # 设备列表 idList = data.get("deviceList", []) # 主播列表 @@ -503,29 +522,45 @@ async def followAndGreetUnion(): # 是否需要回复 needReply = data.get("needReply", True) - needTranslate = data.get("needTranslate", False) - - - - # 获取打招呼数据 ev.prologueList = data.get("prologueList", []) # 添加主播数据 addModelToAnchorList(acList) - # 启动线程,执行脚本 + + failed_ids = [] + + # 启动线程,执行脚本(单个设备异常不影响其它设备) for udid in idList: - manager = ScriptManager() - event = threading.Event() - # 启动脚本 - thread = threading.Thread(target=manager.safe_followAndGreetUnion, - args=(udid, needReply, needTranslate, event)) - # 添加到线程管理 - ThreadManager.add(udid, thread, event) + try: + manager = ScriptManager() + event = threading.Event() + thread = threading.Thread( + target=manager.safe_followAndGreetUnion, + args=(udid, needReply, needTranslate, event), + ) + ThreadManager.add(udid, thread, event) + except Exception as e: + failed_ids.append(udid) + LogManager.error(f"[followAndGreetUnion] 设备 {udid} 启动脚本失败: {e}") + + # 如果所有设备都失败,可以返回错误码 + if failed_ids and len(failed_ids) == len(idList): + return ResultData( + data="", + code=1001, + message=f"所有设备启动失败: {failed_ids}", + ).toJson() + + # 部分失败也算整体成功,只是记录一下 + if failed_ids: + LogManager.warning(f"[followAndGreetUnion] 部分设备启动失败: {failed_ids}") + return ResultData(data="").toJson() + except Exception as e: - LogManager.error(e) + LogManager.error(f"[followAndGreetUnion] 接口级异常: {e}") return ResultData(data="", code=1001).toJson() # 获取私信数据 @@ -824,64 +859,6 @@ async def getDeviceNetStatus(): return ResultData(data=value, code=200).toJson() -@app.route('/test', methods=['POST']) -def test(): - import wda - import cv2 - import numpy as np - - # 设备的UDID - udid = "00008110-000120603C13801E" - - # 连接到设备 - client = wda.USBClient(udid) - session = client.session() - - # 设置Appium的截图深度 - session.appium_settings({"snapshotMaxDepth": 15}) - - # 获取当前屏幕截图 - screenshot = session.screenshot() - screenshot = cv2.imdecode(np.frombuffer(screenshot, np.uint8), cv2.IMREAD_COLOR) - - # 读取大图和小图 - large_image = screenshot # 这里使用截图作为大图 - template = cv2.imread(r'E:\python\Scrcpy_test\open-cv-tk\insert_comment.png', 0) # 0 表示以灰度模式读取 - - # 检查图像是否成功加载 - if template is None: - print("小图加载失败,请检查路径") - exit() - - # 获取模板的宽度和高度 - w, h = template.shape[::-1] - - # 使用模板匹配方法 - result = cv2.matchTemplate(large_image, template, cv2.TM_CCOEFF_NORMED) - - # 设定阈值 - threshold = 0.8 - loc = np.where(result >= threshold) - - # 遍历所有匹配点 - if loc[0].size > 0: # 检查是否有匹配点 - for pt in zip(*loc[::-1]): # 将坐标转换为 (x, y) 格式 - cv2.rectangle(large_image, pt, (pt[0] + w, pt[1] + h), (0, 255, 0), 2) - print(f"找到匹配区域,坐标:{pt},尺寸:{(w, h)}") - else: - print("未找到匹配区域,请检查模板和大图的内容,或调整阈值") - - # 保存结果 - cv2.imwrite('matched_result.png', large_image) - - # 显示结果 - cv2.imshow('Matched Result', large_image) - cv2.waitKey(0) - cv2.destroyAllWindows() - - # 关闭会话 - session.close() - # 获取ai配置 @app.route("/getAiConfig", methods=['GET']) def getAiConfig():