Merge remote-tracking branch 'origin/main'
# Conflicts: # .idea/workspace.xml
This commit is contained in:
21
.idea/workspace.xml
generated
21
.idea/workspace.xml
generated
@@ -4,14 +4,10 @@
|
|||||||
<option name="autoReloadType" value="SELECTIVE" />
|
<option name="autoReloadType" value="SELECTIVE" />
|
||||||
</component>
|
</component>
|
||||||
<component name="ChangeListManager">
|
<component name="ChangeListManager">
|
||||||
<list default="true" id="eceeff5e-51c1-459c-a911-d21ec090a423" name="Changes" comment="20250916-正式上线测试">
|
<list default="true" id="eceeff5e-51c1-459c-a911-d21ec090a423" name="Changes" comment="20250904-初步功能已完成">
|
||||||
<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$/Utils/ControlUtils.py" beforeDir="false" afterPath="$PROJECT_DIR$/Utils/ControlUtils.py" afterDir="false" />
|
<change beforePath="$PROJECT_DIR$/Utils/ControlUtils.py" beforeDir="false" afterPath="$PROJECT_DIR$/Utils/ControlUtils.py" afterDir="false" />
|
||||||
<change beforePath="$PROJECT_DIR$/script/ScriptManager.py" beforeDir="false" afterPath="$PROJECT_DIR$/script/ScriptManager.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" />
|
||||||
@@ -70,7 +66,7 @@
|
|||||||
"git-widget-placeholder": "main",
|
"git-widget-placeholder": "main",
|
||||||
"javascript.nodejs.core.library.configured.version": "20.17.0",
|
"javascript.nodejs.core.library.configured.version": "20.17.0",
|
||||||
"javascript.nodejs.core.library.typings.version": "20.17.58",
|
"javascript.nodejs.core.library.typings.version": "20.17.58",
|
||||||
"last_opened_file_path": "C:/Users/zhangkai/Desktop/20250915 iosao-随机点击坐标/iOSAI",
|
"last_opened_file_path": "F:/company code/AI item/20250820/iOSAI",
|
||||||
"node.js.detected.package.eslint": "true",
|
"node.js.detected.package.eslint": "true",
|
||||||
"node.js.detected.package.tslint": "true",
|
"node.js.detected.package.tslint": "true",
|
||||||
"node.js.selected.package.eslint": "(autodetect)",
|
"node.js.selected.package.eslint": "(autodetect)",
|
||||||
@@ -190,8 +186,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>
|
||||||
@@ -257,8 +252,6 @@
|
|||||||
<workItem from="1757506636968" duration="5910000" />
|
<workItem from="1757506636968" duration="5910000" />
|
||||||
<workItem from="1757567423145" duration="16668000" />
|
<workItem from="1757567423145" duration="16668000" />
|
||||||
<workItem from="1757998910052" duration="3676000" />
|
<workItem from="1757998910052" duration="3676000" />
|
||||||
<workItem from="1758017511127" duration="138000" />
|
|
||||||
<workItem from="1758021469541" duration="8164000" />
|
|
||||||
</task>
|
</task>
|
||||||
<task id="LOCAL-00001" summary="ai 开始测试">
|
<task id="LOCAL-00001" summary="ai 开始测试">
|
||||||
<option name="closed" value="true" />
|
<option name="closed" value="true" />
|
||||||
@@ -330,8 +323,8 @@
|
|||||||
<SUITE FILE_PATH="coverage/iOSAI$LogManager.coverage" NAME="LogManager 覆盖结果" MODIFIED="1756711414832" SOURCE_PROVIDER="com.intellij.coverage.DefaultCoverageFileProvider" RUNNER="coverage.py" COVERAGE_BY_TEST_ENABLED="false" COVERAGE_TRACING_ENABLED="false" WORKING_DIRECTORY="$PROJECT_DIR$/Utils" />
|
<SUITE FILE_PATH="coverage/iOSAI$LogManager.coverage" NAME="LogManager 覆盖结果" MODIFIED="1756711414832" SOURCE_PROVIDER="com.intellij.coverage.DefaultCoverageFileProvider" RUNNER="coverage.py" COVERAGE_BY_TEST_ENABLED="false" COVERAGE_TRACING_ENABLED="false" WORKING_DIRECTORY="$PROJECT_DIR$/Utils" />
|
||||||
<SUITE FILE_PATH="coverage/iOSAI$FlaskService.coverage" NAME="FlaskService 覆盖结果" MODIFIED="1756730187792" SOURCE_PROVIDER="com.intellij.coverage.DefaultCoverageFileProvider" RUNNER="coverage.py" COVERAGE_BY_TEST_ENABLED="false" COVERAGE_TRACING_ENABLED="false" WORKING_DIRECTORY="$PROJECT_DIR$/Module" />
|
<SUITE FILE_PATH="coverage/iOSAI$FlaskService.coverage" NAME="FlaskService 覆盖结果" MODIFIED="1756730187792" SOURCE_PROVIDER="com.intellij.coverage.DefaultCoverageFileProvider" RUNNER="coverage.py" COVERAGE_BY_TEST_ENABLED="false" COVERAGE_TRACING_ENABLED="false" WORKING_DIRECTORY="$PROJECT_DIR$/Module" />
|
||||||
<SUITE FILE_PATH="coverage/iOSAI$test.coverage" NAME="test 覆盖结果" MODIFIED="1756467664420" SOURCE_PROVIDER="com.intellij.coverage.DefaultCoverageFileProvider" RUNNER="coverage.py" COVERAGE_BY_TEST_ENABLED="false" COVERAGE_TRACING_ENABLED="false" WORKING_DIRECTORY="$PROJECT_DIR$" />
|
<SUITE FILE_PATH="coverage/iOSAI$test.coverage" NAME="test 覆盖结果" MODIFIED="1756467664420" SOURCE_PROVIDER="com.intellij.coverage.DefaultCoverageFileProvider" RUNNER="coverage.py" COVERAGE_BY_TEST_ENABLED="false" COVERAGE_TRACING_ENABLED="false" WORKING_DIRECTORY="$PROJECT_DIR$" />
|
||||||
<SUITE FILE_PATH="coverage/iOSAI$1352.coverage" NAME="1352 覆盖结果" MODIFIED="1757662777051" SOURCE_PROVIDER="com.intellij.coverage.DefaultCoverageFileProvider" RUNNER="coverage.py" COVERAGE_BY_TEST_ENABLED="false" COVERAGE_TRACING_ENABLED="false" WORKING_DIRECTORY="$PROJECT_DIR$" />
|
|
||||||
<SUITE FILE_PATH="coverage/iOSAI$windows_run.coverage" NAME="windows_run Coverage Results" MODIFIED="1756473558532" SOURCE_PROVIDER="com.intellij.coverage.DefaultCoverageFileProvider" RUNNER="coverage.py" COVERAGE_BY_TEST_ENABLED="false" COVERAGE_TRACING_ENABLED="false" WORKING_DIRECTORY="$PROJECT_DIR$/script" />
|
<SUITE FILE_PATH="coverage/iOSAI$windows_run.coverage" NAME="windows_run Coverage Results" MODIFIED="1756473558532" SOURCE_PROVIDER="com.intellij.coverage.DefaultCoverageFileProvider" RUNNER="coverage.py" COVERAGE_BY_TEST_ENABLED="false" COVERAGE_TRACING_ENABLED="false" WORKING_DIRECTORY="$PROJECT_DIR$/script" />
|
||||||
|
<SUITE FILE_PATH="coverage/iOSAI$1352.coverage" NAME="1352 覆盖结果" MODIFIED="1757662777051" SOURCE_PROVIDER="com.intellij.coverage.DefaultCoverageFileProvider" RUNNER="coverage.py" COVERAGE_BY_TEST_ENABLED="false" COVERAGE_TRACING_ENABLED="false" WORKING_DIRECTORY="$PROJECT_DIR$" />
|
||||||
<SUITE FILE_PATH="coverage/iOSAI$mac_wda_agent.coverage" NAME="mac_wda_agent Coverage Results" MODIFIED="1756473148639" SOURCE_PROVIDER="com.intellij.coverage.DefaultCoverageFileProvider" RUNNER="coverage.py" COVERAGE_BY_TEST_ENABLED="false" COVERAGE_TRACING_ENABLED="false" WORKING_DIRECTORY="$PROJECT_DIR$/script" />
|
<SUITE FILE_PATH="coverage/iOSAI$mac_wda_agent.coverage" NAME="mac_wda_agent Coverage Results" MODIFIED="1756473148639" SOURCE_PROVIDER="com.intellij.coverage.DefaultCoverageFileProvider" RUNNER="coverage.py" COVERAGE_BY_TEST_ENABLED="false" COVERAGE_TRACING_ENABLED="false" WORKING_DIRECTORY="$PROJECT_DIR$/script" />
|
||||||
<SUITE FILE_PATH="coverage/iOSAI$123456.coverage" NAME="123456 覆盖结果" MODIFIED="1757672582575" SOURCE_PROVIDER="com.intellij.coverage.DefaultCoverageFileProvider" RUNNER="coverage.py" COVERAGE_BY_TEST_ENABLED="false" COVERAGE_TRACING_ENABLED="false" WORKING_DIRECTORY="$PROJECT_DIR$" />
|
<SUITE FILE_PATH="coverage/iOSAI$123456.coverage" NAME="123456 覆盖结果" MODIFIED="1757672582575" SOURCE_PROVIDER="com.intellij.coverage.DefaultCoverageFileProvider" RUNNER="coverage.py" COVERAGE_BY_TEST_ENABLED="false" COVERAGE_TRACING_ENABLED="false" WORKING_DIRECTORY="$PROJECT_DIR$" />
|
||||||
<SUITE FILE_PATH="coverage/iOSAI$2111.coverage" NAME="2111 覆盖结果" MODIFIED="1757330714370" SOURCE_PROVIDER="com.intellij.coverage.DefaultCoverageFileProvider" RUNNER="coverage.py" COVERAGE_BY_TEST_ENABLED="false" COVERAGE_TRACING_ENABLED="false" WORKING_DIRECTORY="$PROJECT_DIR$" />
|
<SUITE FILE_PATH="coverage/iOSAI$2111.coverage" NAME="2111 覆盖结果" MODIFIED="1757330714370" SOURCE_PROVIDER="com.intellij.coverage.DefaultCoverageFileProvider" RUNNER="coverage.py" COVERAGE_BY_TEST_ENABLED="false" COVERAGE_TRACING_ENABLED="false" WORKING_DIRECTORY="$PROJECT_DIR$" />
|
||||||
@@ -339,7 +332,7 @@
|
|||||||
<SUITE FILE_PATH="coverage/iOSAI$123__1_.coverage" NAME="123 (1) 覆盖结果" MODIFIED="1756897091135" SOURCE_PROVIDER="com.intellij.coverage.DefaultCoverageFileProvider" RUNNER="coverage.py" COVERAGE_BY_TEST_ENABLED="false" COVERAGE_TRACING_ENABLED="false" WORKING_DIRECTORY="$PROJECT_DIR$" />
|
<SUITE FILE_PATH="coverage/iOSAI$123__1_.coverage" NAME="123 (1) 覆盖结果" MODIFIED="1756897091135" SOURCE_PROVIDER="com.intellij.coverage.DefaultCoverageFileProvider" RUNNER="coverage.py" COVERAGE_BY_TEST_ENABLED="false" COVERAGE_TRACING_ENABLED="false" WORKING_DIRECTORY="$PROJECT_DIR$" />
|
||||||
<SUITE FILE_PATH="coverage/iOSAI$tidevice_entry.coverage" NAME="tidevice_entry 覆盖结果" MODIFIED="1757061969626" SOURCE_PROVIDER="com.intellij.coverage.DefaultCoverageFileProvider" RUNNER="coverage.py" COVERAGE_BY_TEST_ENABLED="false" COVERAGE_TRACING_ENABLED="false" WORKING_DIRECTORY="$PROJECT_DIR$" />
|
<SUITE FILE_PATH="coverage/iOSAI$tidevice_entry.coverage" NAME="tidevice_entry 覆盖结果" MODIFIED="1757061969626" SOURCE_PROVIDER="com.intellij.coverage.DefaultCoverageFileProvider" RUNNER="coverage.py" COVERAGE_BY_TEST_ENABLED="false" COVERAGE_TRACING_ENABLED="false" WORKING_DIRECTORY="$PROJECT_DIR$" />
|
||||||
<SUITE FILE_PATH="coverage/iOSAI$ScriptManager.coverage" NAME="ScriptManager 覆盖结果" MODIFIED="1756896057801" SOURCE_PROVIDER="com.intellij.coverage.DefaultCoverageFileProvider" RUNNER="coverage.py" COVERAGE_BY_TEST_ENABLED="false" COVERAGE_TRACING_ENABLED="false" WORKING_DIRECTORY="$PROJECT_DIR$/script" />
|
<SUITE FILE_PATH="coverage/iOSAI$ScriptManager.coverage" NAME="ScriptManager 覆盖结果" MODIFIED="1756896057801" SOURCE_PROVIDER="com.intellij.coverage.DefaultCoverageFileProvider" RUNNER="coverage.py" COVERAGE_BY_TEST_ENABLED="false" COVERAGE_TRACING_ENABLED="false" WORKING_DIRECTORY="$PROJECT_DIR$/script" />
|
||||||
<SUITE FILE_PATH="coverage/iOSAI$Main.coverage" NAME="Main 覆盖结果" MODIFIED="1758029025099" SOURCE_PROVIDER="com.intellij.coverage.DefaultCoverageFileProvider" RUNNER="coverage.py" COVERAGE_BY_TEST_ENABLED="false" COVERAGE_TRACING_ENABLED="false" WORKING_DIRECTORY="" />
|
<SUITE FILE_PATH="coverage/iOSAI$Main.coverage" NAME="Main 覆盖结果" MODIFIED="1758002271600" SOURCE_PROVIDER="com.intellij.coverage.DefaultCoverageFileProvider" RUNNER="coverage.py" COVERAGE_BY_TEST_ENABLED="false" COVERAGE_TRACING_ENABLED="false" WORKING_DIRECTORY="" />
|
||||||
<SUITE FILE_PATH="coverage/iOSAI$123.coverage" NAME="123 覆盖结果" MODIFIED="1758028808718" SOURCE_PROVIDER="com.intellij.coverage.DefaultCoverageFileProvider" RUNNER="coverage.py" COVERAGE_BY_TEST_ENABLED="false" COVERAGE_TRACING_ENABLED="false" WORKING_DIRECTORY="$PROJECT_DIR$" />
|
<SUITE FILE_PATH="coverage/iOSAI$123.coverage" NAME="123 覆盖结果" MODIFIED="1758002344317" SOURCE_PROVIDER="com.intellij.coverage.DefaultCoverageFileProvider" RUNNER="coverage.py" COVERAGE_BY_TEST_ENABLED="false" COVERAGE_TRACING_ENABLED="false" WORKING_DIRECTORY="$PROJECT_DIR$" />
|
||||||
</component>
|
</component>
|
||||||
</project>
|
</project>
|
||||||
@@ -1,3 +1,5 @@
|
|||||||
|
from getpass import fallback_getpass
|
||||||
|
|
||||||
|
|
||||||
# 设备模型
|
# 设备模型
|
||||||
class DeviceModel(object):
|
class DeviceModel(object):
|
||||||
@@ -16,6 +18,7 @@ class DeviceModel(object):
|
|||||||
# 1 添加 2删除
|
# 1 添加 2删除
|
||||||
self.type = type
|
self.type = type
|
||||||
self.ready = False
|
self.ready = False
|
||||||
|
self.deleting = False
|
||||||
|
|
||||||
# 转字典
|
# 转字典
|
||||||
def toDict(self):
|
def toDict(self):
|
||||||
|
|||||||
@@ -97,15 +97,20 @@ class Deviceinfo(object):
|
|||||||
now_udids = {d.udid for d in lists if d.conn_type == ConnectionType.USB}
|
now_udids = {d.udid for d in lists if d.conn_type == ConnectionType.USB}
|
||||||
|
|
||||||
# 1. 失踪登记 & 累加
|
# 1. 失踪登记 & 累加
|
||||||
|
need_remove = None # ← 新增:放锁外记录
|
||||||
with self._lock:
|
with self._lock:
|
||||||
for udid in list(self._model_index.keys()):
|
for udid in list(self._model_index.keys()):
|
||||||
if udid not in now_udids:
|
if udid not in now_udids:
|
||||||
self._miss_count[udid] = self._miss_count.get(udid, 0) + 1
|
self._miss_count[udid] = self._miss_count.get(udid, 0) + 1
|
||||||
if self._miss_count[udid] >= 3:
|
if self._miss_count[udid] >= 3:
|
||||||
self._remove_model(udid)
|
|
||||||
self._miss_count.pop(udid, None)
|
self._miss_count.pop(udid, None)
|
||||||
|
need_remove = udid # ← 只记录,不调用
|
||||||
else:
|
else:
|
||||||
self._miss_count.pop(udid, None) # 设备又出现,清零
|
self._miss_count.pop(udid, None)
|
||||||
|
|
||||||
|
# 🔓 锁已释放,再删设备(不会重入)
|
||||||
|
if need_remove:
|
||||||
|
self._remove_model(need_remove)
|
||||||
|
|
||||||
# 2. 全新插入(只处理未在线且信任且未满)
|
# 2. 全新插入(只处理未在线且信任且未满)
|
||||||
for d in lists:
|
for d in lists:
|
||||||
@@ -114,13 +119,13 @@ class Deviceinfo(object):
|
|||||||
udid = d.udid
|
udid = d.udid
|
||||||
with self._lock:
|
with self._lock:
|
||||||
if udid in self._model_index:
|
if udid in self._model_index:
|
||||||
continue # 已存在,跳过
|
continue
|
||||||
if not self.is_device_trusted(udid):
|
if not self.is_device_trusted(udid):
|
||||||
continue
|
continue
|
||||||
if len(self.deviceModelList) >= self.maxDeviceCount:
|
if len(self.deviceModelList) >= self.maxDeviceCount:
|
||||||
continue
|
continue
|
||||||
try:
|
try:
|
||||||
self.connectDevice(udid) # 内部会 _add_model
|
self.connectDevice(udid)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
LogManager.error(f"连接设备失败 {udid}: {e}", udid)
|
LogManager.error(f"连接设备失败 {udid}: {e}", udid)
|
||||||
|
|
||||||
@@ -175,50 +180,80 @@ class Deviceinfo(object):
|
|||||||
self.manager.send(model.toDict())
|
self.manager.send(model.toDict())
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
LogManager.warning(f"{model.deviceId} 发送上线事件失败:{e}")
|
LogManager.warning(f"{model.deviceId} 发送上线事件失败:{e}")
|
||||||
LogManager.info(f"{model.deviceId} 设备上线,当前在线数:{len(self.deviceModelList)}")
|
LogManager.method_info(f"{model.deviceId} 加入设备成功,当前在线数:{len(self.deviceModelList)}",method="device_count")
|
||||||
|
|
||||||
|
|
||||||
# 删除设备
|
# 删除设备
|
||||||
def _remove_model(self, udid: str):
|
def _remove_model(self, udid: str):
|
||||||
"""线程安全:把设备从内存、端口、iproxy 完全抹掉"""
|
print(f"【删】进入删除方法 udid={udid}")
|
||||||
model = self._model_index.pop(udid, None)
|
LogManager.method_info(f"【删】进入删除方法 udid={udid}", method="device_count")
|
||||||
if not model:
|
# 1. 纯内存临界区——毫秒级
|
||||||
LogManager.error(f"没有找到需要删除的设备 {udid}")
|
with self._lock:
|
||||||
return
|
print(f"【删】拿到锁 udid={udid}")
|
||||||
model.type = 2
|
LogManager.method_info(f"【删】拿到锁 udid={udid}",
|
||||||
|
method="device_count")
|
||||||
|
model = self._model_index.pop(udid, None)
|
||||||
|
if not model:
|
||||||
|
print(f"【删】模型已空,直接返回 udid={udid}")
|
||||||
|
LogManager.method_info(f"【删】模型已空,直接返回 udid={udid}",method="device_count")
|
||||||
|
return
|
||||||
|
if model.deleting:
|
||||||
|
print(f"【删】正在删除中,幂等返回 udid={udid}")
|
||||||
|
LogManager.method_info(method="device_count", text=f"【删】正在删除中,幂等返回 udid={udid}")
|
||||||
|
return
|
||||||
|
model.deleting = True
|
||||||
|
# 标记维删除设备
|
||||||
|
model.type = 2
|
||||||
|
print(f"【删】标记 deleting=True udid={udid}")
|
||||||
|
LogManager.method_info("【删】标记 deleting=True udid={udid}","device_count")
|
||||||
|
# 过滤列表
|
||||||
|
before = len(self.deviceModelList)
|
||||||
|
self.deviceModelList = [m for m in self.deviceModelList if m.deviceId != udid]
|
||||||
|
after = len(self.deviceModelList)
|
||||||
|
print(f"【删】列表过滤 before={before} → after={after} udid={udid}")
|
||||||
|
LogManager.method_info(f"【删】列表过滤 before={before} → after={after} udid={udid}","device_count")
|
||||||
|
|
||||||
# 1. 内存列表删除
|
# 端口
|
||||||
try:
|
self._port_in_use.discard(model.screenPort)
|
||||||
self.deviceModelList.remove(model)
|
self._port_pool.append(model.screenPort)
|
||||||
except ValueError:
|
print(f"【删】回收端口 port={model.screenPort} udid={udid}")
|
||||||
pass
|
LogManager.method_info(f"【删】回收端口 port={model.screenPort} udid={udid}", method="device_count")
|
||||||
|
|
||||||
# 2. 端口回收
|
# 进程
|
||||||
self._free_port(model.screenPort)
|
to_kill = [item for item in self.pidList if item.get("id") == udid]
|
||||||
|
self.pidList = [item for item in self.pidList if item.get("id") != udid]
|
||||||
|
print(f"【删】待杀进程数 count={len(to_kill)} udid={udid}")
|
||||||
|
LogManager.method_info(f"【删】待杀进程数 count={len(to_kill)} udid={udid}", method="device_count")
|
||||||
|
|
||||||
# 3. 原子化清理 iproxy(确保进程彻底死)
|
# 2. IO 区无锁
|
||||||
to_kill, survivors = [], []
|
for idx, item in enumerate(to_kill, 1):
|
||||||
for item in self.pidList:
|
print(f"【删】杀进程 {idx}/{len(to_kill)} pid={item.get('target').pid} udid={udid}")
|
||||||
(to_kill if item.get("id") == udid else survivors).append(item)
|
LogManager.method_error(f"【删】杀进程 {idx}/{len(to_kill)} pid={item.get('target').pid} udid={udid}", method="device_count")
|
||||||
|
self._terminate_proc(item.get("target"))
|
||||||
|
print(f"【删】进程清理完成 udid={udid}")
|
||||||
|
LogManager.method_info(f"【删】进程清理完成 udid={udid}", method="device_count")
|
||||||
|
|
||||||
for item in to_kill:
|
# 3. 网络 IO
|
||||||
self._terminate_proc(item.get("target")) # 下面也给了加强版实现
|
|
||||||
|
|
||||||
self.pidList = survivors
|
|
||||||
LogManager.info(f"设备下线 {udid},当前在线数:{len(self.deviceModelList)}")
|
|
||||||
|
|
||||||
# 4. 发 Socket
|
|
||||||
retry = 3
|
retry = 3
|
||||||
while retry:
|
while retry:
|
||||||
try:
|
try:
|
||||||
self.manager.send(model.toDict())
|
self.manager.send(model.toDict())
|
||||||
|
print(f"【删】下线事件已发送 udid={udid}")
|
||||||
|
LogManager.method_info(f"【删】下线事件已发送 udid={udid}", method="device_count")
|
||||||
break
|
break
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
retry -= 1
|
retry -= 1
|
||||||
LogManager.error(f"发送下线事件失败,{udid} 剩余重试 {retry}:{e}")
|
print(f"【删】发送事件失败 retry={retry} err={e} udid={udid}")
|
||||||
|
LogManager.method_error(f"【删】发送事件失败 retry={retry} err={e} udid={udid}", method="device_count")
|
||||||
time.sleep(0.2)
|
time.sleep(0.2)
|
||||||
else:
|
else:
|
||||||
LogManager.error(f"发送下线事件彻底失败,{udid} 主动崩溃防止状态不一致")
|
print(f"【删】发送事件彻底失败,主动退出 udid={udid}")
|
||||||
os._exit(1)
|
LogManager.method_error(f"【删】发送事件彻底失败,主动退出 udid={udid}", method="device_count")
|
||||||
|
|
||||||
|
print(f"【删】===== 设备 {udid} 删除全流程结束 =====")
|
||||||
|
LogManager.method_info(f"【删】===== 设备 {udid} 删除全流程结束 =====", method="device_count")
|
||||||
|
print(len(self.deviceModelList))
|
||||||
|
LogManager.method_info(f"当前剩余设备数量:{len(self.deviceModelList)}", method="device_count")
|
||||||
|
|
||||||
# region ===================== 端口分配与回收 =====================
|
# region ===================== 端口分配与回收 =====================
|
||||||
def _alloc_port(self) -> int:
|
def _alloc_port(self) -> int:
|
||||||
@@ -274,8 +309,6 @@ class Deviceinfo(object):
|
|||||||
if target:
|
if target:
|
||||||
self.pidList.append({"target": target, "id": udid})
|
self.pidList.append({"target": target, "id": udid})
|
||||||
|
|
||||||
# endregion
|
|
||||||
|
|
||||||
# region ===================== 工具方法 =====================
|
# region ===================== 工具方法 =====================
|
||||||
def is_device_trusted(self, udid: str) -> bool:
|
def is_device_trusted(self, udid: str) -> bool:
|
||||||
try:
|
try:
|
||||||
|
|||||||
@@ -211,22 +211,8 @@ class ControlUtils(object):
|
|||||||
# 点击一个随机范围
|
# 点击一个随机范围
|
||||||
@classmethod
|
@classmethod
|
||||||
def tap_mini_cluster(cls, center_x: int, center_y: int, session, points=5, duration_ms=60):
|
def tap_mini_cluster(cls, center_x: int, center_y: int, session, points=5, duration_ms=60):
|
||||||
"""
|
pass
|
||||||
以 (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_coords(cluster, duration=duration_s)
|
|
||||||
# 检测五分钟前和当前的状态是否相同
|
# 检测五分钟前和当前的状态是否相同
|
||||||
# @classmethod
|
# @classmethod
|
||||||
# def compareCurrentWithPreviousState(cls,xml):
|
# def compareCurrentWithPreviousState(cls,xml):
|
||||||
|
|||||||
@@ -240,7 +240,7 @@ def _force_utf8_everywhere():
|
|||||||
except Exception:
|
except Exception:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
_force_utf8_everywhere()
|
# _force_utf8_everywhere()
|
||||||
|
|
||||||
|
|
||||||
# ========= 全局:强制 UTF-8 + 关闭缓冲(运行期立刻生效) =========
|
# ========= 全局:强制 UTF-8 + 关闭缓冲(运行期立刻生效) =========
|
||||||
|
|||||||
Reference in New Issue
Block a user