优化拔出逻辑

This commit is contained in:
2025-09-16 20:03:17 +08:00
parent 9490da09f4
commit c29a663300
4 changed files with 58 additions and 35 deletions

9
.idea/workspace.xml generated
View File

@@ -5,10 +5,10 @@
</component> </component>
<component name="ChangeListManager"> <component name="ChangeListManager">
<list default="true" id="eceeff5e-51c1-459c-a911-d21ec090a423" name="Changes" comment="20250904-初步功能已完成"> <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$/Utils/ControlUtils.py" beforeDir="false" afterPath="$PROJECT_DIR$/Utils/ControlUtils.py" afterDir="false" /> <change beforePath="$PROJECT_DIR$/Entity/DeviceModel.py" beforeDir="false" afterPath="$PROJECT_DIR$/Entity/DeviceModel.py" afterDir="false" />
<change beforePath="$PROJECT_DIR$/Module/DeviceInfo.py" beforeDir="false" afterPath="$PROJECT_DIR$/Module/DeviceInfo.py" afterDir="false" />
<change beforePath="$PROJECT_DIR$/Utils/LogManager.py" beforeDir="false" afterPath="$PROJECT_DIR$/Utils/LogManager.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" />
@@ -187,8 +187,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

@@ -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):

View File

@@ -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)
@@ -179,47 +184,63 @@ class Deviceinfo(object):
# 删除设备 # 删除设备
def _remove_model(self, udid: str): def _remove_model(self, udid: str):
"""线程安全把设备从内存、端口、iproxy 完全抹掉""" print(f"【删】进入删除方法 udid={udid}")
# 1. 纯内存临界区——毫秒级
with self._lock:
print(f"【删】拿到锁 udid={udid}")
model = self._model_index.pop(udid, None) model = self._model_index.pop(udid, None)
if not model: if not model:
LogManager.error(f"没有找到需要删除的设备 {udid}") print(f"【删】模型已空,直接返回 udid={udid}")
return return
if model.deleting:
print(f"【删】正在删除中,幂等返回 udid={udid}")
return
model.deleting = True
# 标记维删除设备
model.type = 2 model.type = 2
print(f"【删】标记 deleting=True udid={udid}")
# 1. 内存列表删除 # 过滤列表
try: before = len(self.deviceModelList)
self.deviceModelList.remove(model) self.deviceModelList = [m for m in self.deviceModelList if m.deviceId != udid]
except ValueError: after = len(self.deviceModelList)
pass print(f"【删】列表过滤 before={before} → after={after} udid={udid}")
# 2. 端口回收 # 端口
self._free_port(model.screenPort) self._port_in_use.discard(model.screenPort)
self._port_pool.append(model.screenPort)
print(f"【删】回收端口 port={model.screenPort} udid={udid}")
# 3. 原子化清理 iproxy确保进程彻底死 # 进程
to_kill, survivors = [], [] to_kill = [item for item in self.pidList if item.get("id") == udid]
for item in self.pidList: self.pidList = [item for item in self.pidList if item.get("id") != udid]
(to_kill if item.get("id") == udid else survivors).append(item) print(f"【删】待杀进程数 count={len(to_kill)} udid={udid}")
for item in to_kill: # 2. IO 区无锁
self._terminate_proc(item.get("target")) # 下面也给了加强版实现 for idx, item in enumerate(to_kill, 1):
print(f"【删】杀进程 {idx}/{len(to_kill)} pid={item.get('target').pid} udid={udid}")
self._terminate_proc(item.get("target"))
print(f"【删】进程清理完成 udid={udid}")
self.pidList = survivors # 3. 网络 IO
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}")
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}")
time.sleep(0.2) time.sleep(0.2)
else: else:
LogManager.error(f"发送下线事件彻底失败,{udid} 主动崩溃防止状态不一致") print(f"【删】发送事件彻底失败,主动退出 udid={udid}")
os._exit(1) os._exit(1)
print(f"【删】===== 设备 {udid} 删除全流程结束 =====")
print(len(self.deviceModelList))
# region ===================== 端口分配与回收 ===================== # region ===================== 端口分配与回收 =====================
def _alloc_port(self) -> int: def _alloc_port(self) -> int:
if self._port_pool: if self._port_pool:

View File

@@ -240,7 +240,7 @@ def _force_utf8_everywhere():
except Exception: except Exception:
pass pass
_force_utf8_everywhere() # _force_utf8_everywhere()
# ========= 全局:强制 UTF-8 + 关闭缓冲(运行期立刻生效) ========= # ========= 全局:强制 UTF-8 + 关闭缓冲(运行期立刻生效) =========