diff --git a/.gitignore b/.gitignore
index 8b81175..90e4b38 100644
--- a/.gitignore
+++ b/.gitignore
@@ -122,4 +122,6 @@ dmypy.json
.pytype/
# Cython debug symbols
-cython_debug/
\ No newline at end of file
+cython_debug/
+
+*.bat
\ No newline at end of file
diff --git a/.idea/.gitignore b/.idea/.gitignore
new file mode 100644
index 0000000..10b731c
--- /dev/null
+++ b/.idea/.gitignore
@@ -0,0 +1,5 @@
+# 默认忽略的文件
+/shelf/
+/workspace.xml
+# 基于编辑器的 HTTP 客户端请求
+/httpRequests/
diff --git a/.idea/iOSAI.iml b/.idea/iOSAI.iml
index df5cbff..f571432 100644
--- a/.idea/iOSAI.iml
+++ b/.idea/iOSAI.iml
@@ -2,7 +2,7 @@
-
+
\ No newline at end of file
diff --git a/.idea/misc.xml b/.idea/misc.xml
index c27b771..db8786c 100644
--- a/.idea/misc.xml
+++ b/.idea/misc.xml
@@ -3,5 +3,5 @@
-
+
\ No newline at end of file
diff --git a/.idea/vcs.xml b/.idea/vcs.xml
index 35eb1dd..8306744 100644
--- a/.idea/vcs.xml
+++ b/.idea/vcs.xml
@@ -2,5 +2,6 @@
+
\ No newline at end of file
diff --git a/.idea/workspace.xml b/.idea/workspace.xml
index 808d1e0..43675f2 100644
--- a/.idea/workspace.xml
+++ b/.idea/workspace.xml
@@ -59,6 +59,8 @@
"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",
@@ -135,8 +137,7 @@
-
-
+
diff --git a/Module/DeviceInfo.py b/Module/DeviceInfo.py
index 0ff26f3..8b90693 100644
--- a/Module/DeviceInfo.py
+++ b/Module/DeviceInfo.py
@@ -214,45 +214,45 @@ class Deviceinfo(object):
def _removeDisconnected(self, current_list):
try:
- prev_udids = {getattr(d, "udid", None) for d in self.deviceArray if getattr(d, "udid", None)}
- now_udids = {getattr(d, "udid", None) for d in current_list if getattr(d, "udid", None)}
+ # 当前在线的 UDID 集合
+ now_udids = {d.udid for d in current_list if hasattr(d, 'udid')}
+
+ # 上一次记录的 UDID 集合
+ prev_udids = {d.udid for d in self.deviceArray if hasattr(d, 'udid')}
except Exception as e:
LogManager.error(f"收集 UDID 失败:{e}", "")
return
+
removed_udids = prev_udids - now_udids
if not removed_udids:
return
- if not hasattr(self, "_lock"):
- self._lock = threading.RLock()
with self._lock:
- for udid in list(removed_udids):
- for a in list(self.deviceModelList):
- if udid == getattr(a, "deviceId", None):
- a.type = 2
- try:
- self.manager.send(a.toDict())
- except Exception as e:
- LogManager.warning(f"发送下线事件失败:{e}", udid)
- try:
- self.deviceModelList.remove(a)
- except ValueError:
- pass
+ # 清理 deviceModelList
+ for model in list(self.deviceModelList):
+ if model.deviceId in removed_udids:
+ model.type = 2
+ try:
+ self.manager.send(model.toDict())
+ except Exception as e:
+ LogManager.warning(f"发送下线事件失败:{e}", model.deviceId)
+ self.deviceModelList.remove(model)
+ # 清理 pidList
survivors = []
- for k in list(self.pidList):
- kid = k.get("id")
- if kid in removed_udids:
- p = k.get("target")
+ for item in self.pidList:
+ if item.get("id") in removed_udids:
+ p = item.get("target")
try:
self._terminate_proc(p)
except Exception as e:
- LogManager.warning(f"关闭 iproxy 异常:{e}", kid)
+ LogManager.warning(f"关闭 iproxy 异常:{e}", item.get("id"))
else:
- survivors.append(k)
+ survivors.append(item)
self.pidList = survivors
- self.deviceArray = [d for d in self.deviceArray if getattr(d, "udid", None) not in removed_udids]
+ # 清理 deviceArray
+ self.deviceArray = [d for d in self.deviceArray if d.udid not in removed_udids]
for udid in removed_udids:
LogManager.info("设备已拔出,清理完成(下线通知 + 端口映射关闭 + 状态移除)", udid)
diff --git a/Module/FlaskService.py b/Module/FlaskService.py
index 38f6fa0..9cd9748 100644
--- a/Module/FlaskService.py
+++ b/Module/FlaskService.py
@@ -26,6 +26,8 @@ app.config['JSON_AS_ASCII'] = False # Flask jsonify 不转义中文/emoji
app.config['JSONIFY_MIMETYPE'] = "application/json; charset=utf-8"
listData = []
+listLock = threading.Lock()
+
dataQueue = Queue()
def start_socket_listener():
@@ -83,39 +85,29 @@ def start_socket_listener():
listener_thread = threading.Thread(target=start_socket_listener, daemon=True)
listener_thread.start()
-# 传递token,暂时用不到了
-# @app.route('/passToken', methods=['POST'])
-# def passToken():
-# try:
-# data = request.get_json()
-# token = data['token']
-# Requester.requestPrologue(token)
-# return ResultData(data="").toJson()
-# except Exception as e:
-# print(e)
-# return ResultData(data="").toJson()
-
-
# 获取设备列表
@app.route('/deviceList', methods=['GET'])
def deviceList():
try:
- while not dataQueue.empty():
- obj = dataQueue.get()
- type = obj["type"]
- if type == 1:
- listData.append(obj)
- else:
- for data in listData:
- if data.get("deviceId") == obj.get("deviceId") and data.get("screenPort") == obj.get("screenPort"):
- listData.remove(data)
- return ResultData(data=listData).toJson()
+ with listLock: # 1. 加锁
+ # 先一次性把队列全部消费完
+ while not dataQueue.empty():
+ obj = dataQueue.get()
+ if obj["type"] == 1:
+ listData.append(obj)
+ else:
+ # 倒序删除,安全
+ for i in range(len(listData) - 1, -1, -1):
+ d = listData[i]
+ if d.get("deviceId") == obj.get("deviceId") and \
+ d.get("screenPort") == obj.get("screenPort"):
+ listData.pop(i)
+ break # 同一端口同一设备只删一次
+ return ResultData(data=listData.copy()).toJson() # 2. 返回副本
except Exception as e:
- print(e)
LogManager.error("获取设备列表失败:", e)
return ResultData(data=[]).toJson()
-
# 获取设备应用列表
@app.route('/deviceAppList', methods=['POST'])
def deviceAppList():
@@ -124,7 +116,6 @@ def deviceAppList():
apps = ControlUtils.getDeviceAppList(udid)
return ResultData(data=apps).toJson()
-
# 打开指定app
@app.route('/launchApp', methods=['POST'])
def launchApp():
diff --git a/build.bat b/build.bat
index eb76759..928b8ca 100644
--- a/build.bat
+++ b/build.bat
@@ -1,25 +1,24 @@
-python -m nuitka "Module/Main.py"^
- --standalone^
- --msvc=latest^
- --windows-console-mode=disable^
- --remove-output^
- --output-dir=out^
- --output-filename=IOSAI^
- --include-package=Module,Utils,Entity,script^
- --include-module=flask^
- --include-module=flask_cors^
- --include-module=jinja2^
- --include-module=werkzeug^
- --include-module=cv2^
- --include-module=numpy^
- --include-module=lxml^
- --include-module=lxml.etree^
- --include-module=requests^
- --include-module=urllib3^
- --include-module=certifi^
- --include-module=idna^
- --include-data-dir="F:/company code/AI item/20250820/iOSAI/SupportFiles=SupportFiles"^
- --include-data-dir="F:/company code/AI item/20250820/iOSAI/resources=resources"^
- --include-data-files="F:/company code/AI item/20250820/iOSAI/resources/iproxy/*=resources/iproxy/"^
- --windows-icon-from-ico="F:/company code/AI item/20250820/iOSAI/resources/icon.ico"
-
+python -m nuitka "Module/Main.py" ^
+--standalone ^
+--msvc=latest ^
+--windows-console-mode=disable ^
+--remove-output ^
+--output-dir=out ^
+--output-filename=IOSAI ^
+--include-package=Module,Utils,Entity,script ^
+--include-module=flask ^
+--include-module=flask_cors ^
+--include-module=jinja2 ^
+--include-module=werkzeug ^
+--include-module=cv2 ^
+--include-module=numpy ^
+--include-module=lxml ^
+--include-module=lxml.etree ^
+--include-module=requests ^
+--include-module=urllib3 ^
+--include-module=certifi ^
+--include-module=idna ^
+--include-data-dir="E:/code/Python/iOSAI/SupportFiles=SupportFiles" ^
+--include-data-dir="E:/code/Python/iOSAI/resources=resources" ^
+--include-data-files="E:/code/Python/iOSAI/resources/iproxy/*=resources/iproxy/" ^
+--windows-icon-from-ico="E:/code/Python/iOSAI/resources/icon.ico"
\ No newline at end of file