Merge remote-tracking branch 'origin/main'

This commit is contained in:
2025-09-10 16:54:21 +08:00
9 changed files with 78 additions and 79 deletions

4
.gitignore vendored
View File

@@ -122,4 +122,6 @@ dmypy.json
.pytype/ .pytype/
# Cython debug symbols # Cython debug symbols
cython_debug/ cython_debug/
*.bat

5
.idea/.gitignore generated vendored Normal file
View File

@@ -0,0 +1,5 @@
# 默认忽略的文件
/shelf/
/workspace.xml
# 基于编辑器的 HTTP 客户端请求
/httpRequests/

2
.idea/iOSAI.iml generated
View File

@@ -2,7 +2,7 @@
<module type="PYTHON_MODULE" version="4"> <module type="PYTHON_MODULE" version="4">
<component name="NewModuleRootManager"> <component name="NewModuleRootManager">
<content url="file://$MODULE_DIR$" /> <content url="file://$MODULE_DIR$" />
<orderEntry type="jdk" jdkName="Python 3.12 (IOS-AI)" jdkType="Python SDK" /> <orderEntry type="jdk" jdkName="Python 3.12" jdkType="Python SDK" />
<orderEntry type="sourceFolder" forTests="false" /> <orderEntry type="sourceFolder" forTests="false" />
</component> </component>
</module> </module>

2
.idea/misc.xml generated
View File

@@ -3,5 +3,5 @@
<component name="Black"> <component name="Black">
<option name="sdkName" value="Python 3.12" /> <option name="sdkName" value="Python 3.12" />
</component> </component>
<component name="ProjectRootManager" version="2" project-jdk-name="Python 3.12 (IOS-AI)" project-jdk-type="Python SDK" /> <component name="ProjectRootManager" version="2" project-jdk-name="Python 3.12" project-jdk-type="Python SDK" />
</project> </project>

1
.idea/vcs.xml generated
View File

@@ -2,5 +2,6 @@
<project version="4"> <project version="4">
<component name="VcsDirectoryMappings"> <component name="VcsDirectoryMappings">
<mapping directory="" vcs="Git" /> <mapping directory="" vcs="Git" />
<mapping directory="$PROJECT_DIR$" vcs="Git" />
</component> </component>
</project> </project>

5
.idea/workspace.xml generated
View File

@@ -59,6 +59,8 @@
&quot;Python.Main.executor&quot;: &quot;Run&quot;, &quot;Python.Main.executor&quot;: &quot;Run&quot;,
&quot;Python.tidevice_entry.executor&quot;: &quot;Run&quot;, &quot;Python.tidevice_entry.executor&quot;: &quot;Run&quot;,
&quot;RunOnceActivity.ShowReadmeOnStart&quot;: &quot;true&quot;, &quot;RunOnceActivity.ShowReadmeOnStart&quot;: &quot;true&quot;,
&quot;RunOnceActivity.TerminalTabsStorage.copyFrom.TerminalArrangementManager.252&quot;: &quot;true&quot;,
&quot;RunOnceActivity.git.unshallow&quot;: &quot;true&quot;,
&quot;SHARE_PROJECT_CONFIGURATION_FILES&quot;: &quot;true&quot;, &quot;SHARE_PROJECT_CONFIGURATION_FILES&quot;: &quot;true&quot;,
&quot;git-widget-placeholder&quot;: &quot;main&quot;, &quot;git-widget-placeholder&quot;: &quot;main&quot;,
&quot;javascript.nodejs.core.library.configured.version&quot;: &quot;20.17.0&quot;, &quot;javascript.nodejs.core.library.configured.version&quot;: &quot;20.17.0&quot;,
@@ -135,8 +137,7 @@
<component name="SharedIndexes"> <component name="SharedIndexes">
<attachedChunks> <attachedChunks>
<set> <set>
<option value="bundled-js-predefined-1d06a55b98c1-0b3e54e931b4-JavaScript-PY-241.18034.82" /> <option value="bundled-python-sdk-ce6832f46686-7b97d883f26b-com.jetbrains.pycharm.pro.sharedIndexes.bundled-PY-252.25557.178" />
<option value="bundled-python-sdk-975db3bf15a3-2767605e8bc2-com.jetbrains.pycharm.pro.sharedIndexes.bundled-PY-241.18034.82" />
</set> </set>
</attachedChunks> </attachedChunks>
</component> </component>

View File

@@ -214,45 +214,45 @@ class Deviceinfo(object):
def _removeDisconnected(self, current_list): def _removeDisconnected(self, current_list):
try: try:
prev_udids = {getattr(d, "udid", None) for d in self.deviceArray if getattr(d, "udid", None)} # 当前在线的 UDID 集合
now_udids = {getattr(d, "udid", None) for d in current_list if getattr(d, "udid", None)} 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: except Exception as e:
LogManager.error(f"收集 UDID 失败:{e}", "") LogManager.error(f"收集 UDID 失败:{e}", "")
return return
removed_udids = prev_udids - now_udids removed_udids = prev_udids - now_udids
if not removed_udids: if not removed_udids:
return return
if not hasattr(self, "_lock"):
self._lock = threading.RLock()
with self._lock: with self._lock:
for udid in list(removed_udids): # 清理 deviceModelList
for a in list(self.deviceModelList): for model in list(self.deviceModelList):
if udid == getattr(a, "deviceId", None): if model.deviceId in removed_udids:
a.type = 2 model.type = 2
try: try:
self.manager.send(a.toDict()) self.manager.send(model.toDict())
except Exception as e: except Exception as e:
LogManager.warning(f"发送下线事件失败:{e}", udid) LogManager.warning(f"发送下线事件失败:{e}", model.deviceId)
try: self.deviceModelList.remove(model)
self.deviceModelList.remove(a)
except ValueError:
pass
# 清理 pidList
survivors = [] survivors = []
for k in list(self.pidList): for item in self.pidList:
kid = k.get("id") if item.get("id") in removed_udids:
if kid in removed_udids: p = item.get("target")
p = k.get("target")
try: try:
self._terminate_proc(p) self._terminate_proc(p)
except Exception as e: except Exception as e:
LogManager.warning(f"关闭 iproxy 异常:{e}", kid) LogManager.warning(f"关闭 iproxy 异常:{e}", item.get("id"))
else: else:
survivors.append(k) survivors.append(item)
self.pidList = survivors 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: for udid in removed_udids:
LogManager.info("设备已拔出,清理完成(下线通知 + 端口映射关闭 + 状态移除)", udid) LogManager.info("设备已拔出,清理完成(下线通知 + 端口映射关闭 + 状态移除)", udid)

View File

@@ -26,6 +26,8 @@ app.config['JSON_AS_ASCII'] = False # Flask jsonify 不转义中文/emoji
app.config['JSONIFY_MIMETYPE'] = "application/json; charset=utf-8" app.config['JSONIFY_MIMETYPE'] = "application/json; charset=utf-8"
listData = [] listData = []
listLock = threading.Lock()
dataQueue = Queue() dataQueue = Queue()
def start_socket_listener(): def start_socket_listener():
@@ -83,39 +85,29 @@ def start_socket_listener():
listener_thread = threading.Thread(target=start_socket_listener, daemon=True) listener_thread = threading.Thread(target=start_socket_listener, daemon=True)
listener_thread.start() 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']) @app.route('/deviceList', methods=['GET'])
def deviceList(): def deviceList():
try: try:
while not dataQueue.empty(): with listLock: # 1. 加锁
obj = dataQueue.get() # 先一次性把队列全部消费完
type = obj["type"] while not dataQueue.empty():
if type == 1: obj = dataQueue.get()
listData.append(obj) if obj["type"] == 1:
else: listData.append(obj)
for data in listData: else:
if data.get("deviceId") == obj.get("deviceId") and data.get("screenPort") == obj.get("screenPort"): # 倒序删除,安全
listData.remove(data) for i in range(len(listData) - 1, -1, -1):
return ResultData(data=listData).toJson() 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: except Exception as e:
print(e)
LogManager.error("获取设备列表失败:", e) LogManager.error("获取设备列表失败:", e)
return ResultData(data=[]).toJson() return ResultData(data=[]).toJson()
# 获取设备应用列表 # 获取设备应用列表
@app.route('/deviceAppList', methods=['POST']) @app.route('/deviceAppList', methods=['POST'])
def deviceAppList(): def deviceAppList():
@@ -124,7 +116,6 @@ def deviceAppList():
apps = ControlUtils.getDeviceAppList(udid) apps = ControlUtils.getDeviceAppList(udid)
return ResultData(data=apps).toJson() return ResultData(data=apps).toJson()
# 打开指定app # 打开指定app
@app.route('/launchApp', methods=['POST']) @app.route('/launchApp', methods=['POST'])
def launchApp(): def launchApp():

View File

@@ -1,25 +1,24 @@
python -m nuitka "Module/Main.py"^ python -m nuitka "Module/Main.py" ^
--standalone^ --standalone ^
--msvc=latest^ --msvc=latest ^
--windows-console-mode=disable^ --windows-console-mode=disable ^
--remove-output^ --remove-output ^
--output-dir=out^ --output-dir=out ^
--output-filename=IOSAI^ --output-filename=IOSAI ^
--include-package=Module,Utils,Entity,script^ --include-package=Module,Utils,Entity,script ^
--include-module=flask^ --include-module=flask ^
--include-module=flask_cors^ --include-module=flask_cors ^
--include-module=jinja2^ --include-module=jinja2 ^
--include-module=werkzeug^ --include-module=werkzeug ^
--include-module=cv2^ --include-module=cv2 ^
--include-module=numpy^ --include-module=numpy ^
--include-module=lxml^ --include-module=lxml ^
--include-module=lxml.etree^ --include-module=lxml.etree ^
--include-module=requests^ --include-module=requests ^
--include-module=urllib3^ --include-module=urllib3 ^
--include-module=certifi^ --include-module=certifi ^
--include-module=idna^ --include-module=idna ^
--include-data-dir="F:/company code/AI item/20250820/iOSAI/SupportFiles=SupportFiles"^ --include-data-dir="E:/code/Python/iOSAI/SupportFiles=SupportFiles" ^
--include-data-dir="F:/company code/AI item/20250820/iOSAI/resources=resources"^ --include-data-dir="E:/code/Python/iOSAI/resources=resources" ^
--include-data-files="F:/company code/AI item/20250820/iOSAI/resources/iproxy/*=resources/iproxy/"^ --include-data-files="E:/code/Python/iOSAI/resources/iproxy/*=resources/iproxy/" ^
--windows-icon-from-ico="F:/company code/AI item/20250820/iOSAI/resources/icon.ico" --windows-icon-from-ico="E:/code/Python/iOSAI/resources/icon.ico"