refactor(GUI): 群組切mode/套用速度改並發(asyncio.gather),清per-drone client過期註解
- gui.py: _handle_group_mode_change 與 _on_engagement_apply_speed 由 單一 async 函式內 for 迴圈逐台 await(序列化,最壞 N×timeout)改為 asyncio.gather 並發,各自等各自 ACK;行為語意不變。 - communication.py: set_mode/arm_drone/set_speed 前 3 處過期註解 「避免多機並行時的競態條件」更正為共用 client + target_sysid 路由 (OOM 修復後已非 per-drone、無 per-drone 狀態)。 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -1163,7 +1163,8 @@ class DroneMonitor(Node):
|
||||
|
||||
_log("INFO", f"[SET_MODE] {drone_id} -> {mode_name} (custom_mode={custom_mode})")
|
||||
|
||||
# 獲取或創建該 drone 專用的 client(避免多機並行時的競態條件)
|
||||
# 取得共用 command_long client(OOM 修復後已非 per-drone,靠 request 的
|
||||
# target_sysid 路由;drone_id 僅用於一次性 lazy fallback,無 per-drone 狀態)
|
||||
client = self.get_or_create_client(drone_id)
|
||||
if not client:
|
||||
_log("ERROR", "[SET_MODE] CommandLongClient 無法初始化")
|
||||
@@ -1246,7 +1247,8 @@ class DroneMonitor(Node):
|
||||
action_name = "解鎖" if arm else "上鎖"
|
||||
_log("INFO", f"[ARM] {drone_id} -> {action_name}")
|
||||
|
||||
# 獲取或創建該 drone 專用的 client(避免多機並行時的競態條件)
|
||||
# 取得共用 command_long client(OOM 修復後已非 per-drone,靠 request 的
|
||||
# target_sysid 路由;drone_id 僅用於一次性 lazy fallback,無 per-drone 狀態)
|
||||
client = self.get_or_create_client(drone_id)
|
||||
if not client:
|
||||
_log("ERROR", "[ARM] CommandLongClient 無法初始化")
|
||||
@@ -1283,7 +1285,8 @@ class DroneMonitor(Node):
|
||||
|
||||
_log("INFO", f"[TAKEOFF] {drone_id} -> 起飛 (高度={altitude}m)")
|
||||
|
||||
# 獲取或創建該 drone 專用的 client(避免多機並行時的競態條件)
|
||||
# 取得共用 command_long client(OOM 修復後已非 per-drone,靠 request 的
|
||||
# target_sysid 路由;drone_id 僅用於一次性 lazy fallback,無 per-drone 狀態)
|
||||
client = self.get_or_create_client(drone_id)
|
||||
if not client:
|
||||
_log("ERROR", "[TAKEOFF] CommandLongClient 無法初始化")
|
||||
|
||||
+19
-15
@@ -1573,27 +1573,30 @@ class ControlStationUI(QMainWindow):
|
||||
_log("INFO", f"Group {group_id} 準備切換模式為 {mode}")
|
||||
self.statusBar().showMessage("正在切換模式", 1000)
|
||||
|
||||
# 使用 asyncio 執行(通過事件循環)
|
||||
async def do_mode_changes_async():
|
||||
for drone_id in group.selected_drone_ids:
|
||||
# 使用 asyncio 執行(通過事件循環)。
|
||||
# 並發:每台各自 set_mode + 各自等自己的 ACK,用 gather 同時發,
|
||||
# 避免逐台 await 造成序列化(最壞情況 N×timeout 一路卡著)。
|
||||
async def _change_one(drone_id):
|
||||
try:
|
||||
result = await self.monitor.set_mode(drone_id, mode)
|
||||
|
||||
if result:
|
||||
msg = f"{drone_id} 切換成功"
|
||||
_log("INFO", msg)
|
||||
self.message_queue.put((msg, 2000))
|
||||
else:
|
||||
msg = f"{drone_id} 切換失敗"
|
||||
_log("WARN", msg)
|
||||
self.message_queue.put((msg, 2000))
|
||||
|
||||
except Exception as e:
|
||||
msg = f"{drone_id} 錯誤: {str(e)}"
|
||||
_log("ERROR", msg)
|
||||
traceback.print_exc()
|
||||
self.message_queue.put((msg, 2000))
|
||||
|
||||
async def do_mode_changes_async():
|
||||
await asyncio.gather(
|
||||
*(_change_one(did) for did in group.selected_drone_ids)
|
||||
)
|
||||
|
||||
# 通過事件循環提交異步任務
|
||||
loop = asyncio.get_event_loop()
|
||||
asyncio.run_coroutine_threadsafe(
|
||||
@@ -2584,17 +2587,18 @@ class ControlStationUI(QMainWindow):
|
||||
if hasattr(self, 'orchestrator'):
|
||||
self.orchestrator.friendly_speed = float(friendly_speed)
|
||||
|
||||
async def _apply_one(did, speed, label):
|
||||
try:
|
||||
await self.monitor.set_speed(did, speed)
|
||||
except Exception as e:
|
||||
_log("ERROR", f"[套用速度] {label} {did} 失敗: {e}")
|
||||
|
||||
async def do_apply():
|
||||
for did in friendly_ids:
|
||||
try:
|
||||
await self.monitor.set_speed(did, friendly_speed)
|
||||
except Exception as e:
|
||||
_log("ERROR", f"[套用速度] 友機 {did} 失敗: {e}")
|
||||
# 並發:友機各自 + 敵機,用 gather 同時發,各自等各自 ACK
|
||||
tasks = [_apply_one(did, friendly_speed, "友機") for did in friendly_ids]
|
||||
if enemy_id:
|
||||
try:
|
||||
await self.monitor.set_speed(enemy_id, enemy_speed)
|
||||
except Exception as e:
|
||||
_log("ERROR", f"[套用速度] 敵機 {enemy_id} 失敗: {e}")
|
||||
tasks.append(_apply_one(enemy_id, enemy_speed, "敵機"))
|
||||
await asyncio.gather(*tasks)
|
||||
|
||||
asyncio.run_coroutine_threadsafe(do_apply(), asyncio.get_event_loop())
|
||||
self.statusBar().showMessage(
|
||||
|
||||
Reference in New Issue
Block a user