435 lines
17 KiB
Python
435 lines
17 KiB
Python
|
|
# -*- coding: utf-8 -*-
|
|||
|
|
from __future__ import annotations
|
|||
|
|
|
|||
|
|
import logging
|
|||
|
|
import queue
|
|||
|
|
import threading
|
|||
|
|
import traceback
|
|||
|
|
from enum import Enum
|
|||
|
|
from time import sleep
|
|||
|
|
from typing import Optional, Tuple
|
|||
|
|
|
|||
|
|
from PyQt5.QtCore import QObject, pyqtSlot, pyqtSignal, QThread
|
|||
|
|
|
|||
|
|
from uas.action.action_executor import action_executor
|
|||
|
|
from uas.event.event_bus import event_bus
|
|||
|
|
from uas.event.handlers.device_handlers import DeviceErrorHandler, DeviceTimeoutHandler, EquipmentFaultHandler
|
|||
|
|
from uas.task.task_executor import task_executor
|
|||
|
|
from uas.task.task_model import Task, TaskState
|
|||
|
|
from uas.uas_control.base_controller import BaseUASController
|
|||
|
|
|
|||
|
|
logger = logging.getLogger(__name__)
|
|||
|
|
|
|||
|
|
class AgentState(Enum):
|
|||
|
|
IDLE = "IDLE"
|
|||
|
|
READY = "READY"
|
|||
|
|
RUNNING = "RUNNING"
|
|||
|
|
PAUSED = "PAUSED"
|
|||
|
|
STOPPED = "STOPPED"
|
|||
|
|
SWITCHING = "SWITCHING"
|
|||
|
|
|
|||
|
|
# ─────────────────────────────────────────────────────────────────
|
|||
|
|
# 装备模型抽象基类
|
|||
|
|
# ─────────────────────────────────────────────────────────────────
|
|||
|
|
|
|||
|
|
class AgentTaskWorker(QObject):
|
|||
|
|
# 信号:用于向主线程发送结果
|
|||
|
|
finished = pyqtSignal()
|
|||
|
|
result = pyqtSignal(object) # 可传数据
|
|||
|
|
|
|||
|
|
def __init__(self, agent:"Agent", dt:int):
|
|||
|
|
super().__init__()
|
|||
|
|
self.agent = agent
|
|||
|
|
self.dt = dt
|
|||
|
|
|
|||
|
|
@pyqtSlot()
|
|||
|
|
def do_work(self):
|
|||
|
|
"""真正的耗时任务"""
|
|||
|
|
try:
|
|||
|
|
# 执行你的任务
|
|||
|
|
print("线程开始执行")
|
|||
|
|
res = self._start_task()
|
|||
|
|
# 发送结果
|
|||
|
|
self.result.emit(res)
|
|||
|
|
finally:
|
|||
|
|
self.finished.emit()
|
|||
|
|
|
|||
|
|
def _start_task(self):
|
|||
|
|
self.agent._model.start()
|
|||
|
|
self.agent.state = AgentState.RUNNING
|
|||
|
|
while self.agent.state != AgentState.STOPPED and not self.agent._task_queue.empty():
|
|||
|
|
self.agent.current_task = self.agent._task_queue.get()
|
|||
|
|
priority, task = self.agent.current_task
|
|||
|
|
assert isinstance(task, Task)
|
|||
|
|
self.agent._planning_task(priority, task)
|
|||
|
|
task_executor.submit(task)
|
|||
|
|
|
|||
|
|
while task.state in(TaskState.RUNNING, TaskState.PLANNED, TaskState.READY):
|
|||
|
|
# 如果需要切换任务
|
|||
|
|
if self.agent.from_to(AgentState.RUNNING, AgentState.SWITCHING):
|
|||
|
|
self.agent._save_task(priority, task)
|
|||
|
|
task_executor.cancel(task.task_id)
|
|||
|
|
break
|
|||
|
|
# 如果需要停止任务
|
|||
|
|
elif self.agent.from_to(AgentState.RUNNING, AgentState.STOPPED):
|
|||
|
|
task_executor.cancel(task.task_id)
|
|||
|
|
break
|
|||
|
|
elif self.agent.from_to(AgentState.RUNNING, AgentState.PAUSED):
|
|||
|
|
task_executor.pause(task.task_id)
|
|||
|
|
elif self.agent.from_to(AgentState.PAUSED, AgentState.RUNNING):
|
|||
|
|
task_executor.resume(task.task_id)
|
|||
|
|
try:
|
|||
|
|
if task.state == TaskState.RUNNING and self.agent.state == AgentState.RUNNING:
|
|||
|
|
result = self.agent._model.step(self.dt)
|
|||
|
|
# 控制仿真速率(实时:sleep = dt;加速:sleep < dt)
|
|||
|
|
sleep(max(.050, (self.dt/1000) * 0.200))
|
|||
|
|
except Exception as e:
|
|||
|
|
traceback.print_exc()
|
|||
|
|
print(f"[SimThread] 仿真异常: {e}")
|
|||
|
|
break
|
|||
|
|
self.agent.state = AgentState.IDLE
|
|||
|
|
self.agent.current_task = None
|
|||
|
|
self.agent._model.stop()
|
|||
|
|
|
|||
|
|
|
|||
|
|
def _start_task2(self):
|
|||
|
|
self.agent.state = AgentState.RUNNING
|
|||
|
|
while self.agent.state != AgentState.STOPPED and not self.agent._task_queue.empty():
|
|||
|
|
self.agent.current_task = self.agent._task_queue.get()
|
|||
|
|
priority, task = self.agent.current_task
|
|||
|
|
assert isinstance(task, Task)
|
|||
|
|
self.agent._planning_task(priority, task)
|
|||
|
|
task_executor.submit(task)
|
|||
|
|
|
|||
|
|
route = task.route
|
|||
|
|
for seg_index, segment in enumerate(task.route):
|
|||
|
|
pass
|
|||
|
|
|
|||
|
|
while task.state in(TaskState.RUNNING, TaskState.PLANNED, TaskState.READY):
|
|||
|
|
|
|||
|
|
# 如果需要切换任务
|
|||
|
|
if self.agent.from_to(AgentState.RUNNING, AgentState.SWITCHING):
|
|||
|
|
self.agent._save_task(priority, task)
|
|||
|
|
task_executor.cancel(task.task_id)
|
|||
|
|
break
|
|||
|
|
# 如果需要停止任务
|
|||
|
|
elif self.agent.from_to(AgentState.RUNNING, AgentState.STOPPED):
|
|||
|
|
task_executor.cancel(task.task_id)
|
|||
|
|
break
|
|||
|
|
elif self.agent.from_to(AgentState.RUNNING, AgentState.PAUSED):
|
|||
|
|
task_executor.pause(task.task_id)
|
|||
|
|
elif self.agent.from_to(AgentState.PAUSED, AgentState.RUNNING):
|
|||
|
|
task_executor.resume(task.task_id)
|
|||
|
|
try:
|
|||
|
|
if task.state == TaskState.RUNNING and self.agent.state == AgentState.RUNNING:
|
|||
|
|
result = self.agent._model.step(self.dt)
|
|||
|
|
# 控制仿真速率(实时:sleep = dt;加速:sleep < dt)
|
|||
|
|
sleep(max(.050, (self.dt/1000) * 0.200))
|
|||
|
|
except Exception as e:
|
|||
|
|
traceback.print_exc()
|
|||
|
|
print(f"[SimThread] 仿真异常: {e}")
|
|||
|
|
break
|
|||
|
|
self.agent.state = AgentState.IDLE
|
|||
|
|
self.agent.current_task = None
|
|||
|
|
self.agent._model.stop()
|
|||
|
|
|
|||
|
|
class Agent(QObject):
|
|||
|
|
state_updated = pyqtSignal(object)
|
|||
|
|
|
|||
|
|
def __init__(
|
|||
|
|
self,
|
|||
|
|
controller: BaseUASController,
|
|||
|
|
parent: QObject = None,
|
|||
|
|
):
|
|||
|
|
super().__init__(parent)
|
|||
|
|
self._controller = controller
|
|||
|
|
# self._model = model
|
|||
|
|
# 日志
|
|||
|
|
self.logger = logging.getLogger(
|
|||
|
|
f"{self.__class__.__name__}[{self._model.equipment_id}]"
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
self._prev_state: object = AgentState.IDLE
|
|||
|
|
self._state: object = AgentState.IDLE
|
|||
|
|
|
|||
|
|
self._current_task: Optional[Tuple[int, Task]] = None
|
|||
|
|
self._task_lock = threading.Lock()
|
|||
|
|
self._task_queue = queue.PriorityQueue()
|
|||
|
|
self._task_thread = None
|
|||
|
|
|
|||
|
|
self._setup()
|
|||
|
|
|
|||
|
|
def _setup(self):
|
|||
|
|
# 注册事件类处理器
|
|||
|
|
event_bus.register_handler(DeviceErrorHandler())
|
|||
|
|
event_bus.register_handler(DeviceTimeoutHandler())
|
|||
|
|
event_bus.register_handler(EquipmentFaultHandler())
|
|||
|
|
# 扫描 @on_event 装饰器处理器
|
|||
|
|
count = event_bus.scan_decorators()
|
|||
|
|
event_bus.event_submitted.connect(self._on_event_submitted)
|
|||
|
|
event_bus.event_handled.connect(self._on_event_handled)
|
|||
|
|
event_bus.event_failed.connect(self._on_event_failed)
|
|||
|
|
event_bus.event_ignored.connect(self._on_event_ignored)
|
|||
|
|
event_bus.stats_updated.connect(self._on_stats_updated)
|
|||
|
|
|
|||
|
|
# ── 连接 ActionExecutor 信号 ──────────────────────────────
|
|||
|
|
action_executor.action_completed.connect(self._on_action_completed)
|
|||
|
|
action_executor.action_progress.connect(self._on_action_progress)
|
|||
|
|
action_executor.events_produced.connect(self._on_events_produced)
|
|||
|
|
|
|||
|
|
# ── 连接 TaskExecutor 信号 ────────────────────────────────
|
|||
|
|
task_executor.task_submitted.connect(self._on_task_submitted)
|
|||
|
|
task_executor.task_started.connect(self._on_task_started)
|
|||
|
|
task_executor.task_completed.connect(self._on_task_completed)
|
|||
|
|
task_executor.task_failed.connect(self._on_task_failed)
|
|||
|
|
task_executor.task_cancelled.connect(self._on_task_cancelled)
|
|||
|
|
task_executor.task_paused.connect(self._on_task_paused)
|
|||
|
|
task_executor.task_resumed.connect(self._on_task_resumed)
|
|||
|
|
task_executor.segment_entered.connect(self._on_segment_entered)
|
|||
|
|
task_executor.segment_exited.connect(self._on_segment_exited)
|
|||
|
|
action_executor.set_event_bus(event_bus)
|
|||
|
|
task_executor.set_action_executor(action_executor)
|
|||
|
|
|
|||
|
|
def _state_trans_handler(self):
|
|||
|
|
pass
|
|||
|
|
|
|||
|
|
|
|||
|
|
|
|||
|
|
def put_task(self, priority: int, task: Task):
|
|||
|
|
with self._task_lock:
|
|||
|
|
self._task_queue.put((priority, task))
|
|||
|
|
if self._state == AgentState.IDLE:
|
|||
|
|
self._state = AgentState.READY
|
|||
|
|
self.state_updated.emit(self._state)
|
|||
|
|
|
|||
|
|
def remove_task(self, task: Optional[Task|str]):
|
|||
|
|
with self._task_lock:
|
|||
|
|
temp_queue = queue.PriorityQueue()
|
|||
|
|
while not self._task_queue.empty():
|
|||
|
|
priorty, _task = self._task_queue.get()
|
|||
|
|
assert isinstance(_task, Task)
|
|||
|
|
if isinstance(task, Task):
|
|||
|
|
if _task != task:
|
|||
|
|
temp_queue.put((priorty, task))
|
|||
|
|
elif isinstance(task, str):
|
|||
|
|
if _task.task_id != task:
|
|||
|
|
temp_queue.put((priorty, _task))
|
|||
|
|
self._task_queue = temp_queue
|
|||
|
|
if self._task_queue.empty() and self._state == AgentState.READY:
|
|||
|
|
self._state = AgentState.IDLE
|
|||
|
|
|
|||
|
|
@property
|
|||
|
|
def state(self):
|
|||
|
|
with self._task_lock:
|
|||
|
|
return self._state
|
|||
|
|
|
|||
|
|
@state.setter
|
|||
|
|
def state(self, state: AgentState):
|
|||
|
|
with self._task_lock:
|
|||
|
|
self._prev_state = self._state
|
|||
|
|
self._state = state
|
|||
|
|
self.state_updated.emit(state)
|
|||
|
|
|
|||
|
|
@property
|
|||
|
|
def current_task(self):
|
|||
|
|
with self._task_lock:
|
|||
|
|
return self._current_task
|
|||
|
|
|
|||
|
|
@current_task.setter
|
|||
|
|
def current_task(self, task):
|
|||
|
|
with self._task_lock:
|
|||
|
|
self._current_task = task
|
|||
|
|
|
|||
|
|
def schedule_task(self):
|
|||
|
|
self.state = AgentState.SWITCHING
|
|||
|
|
|
|||
|
|
def stop(self):
|
|||
|
|
self.state = AgentState.STOPPED
|
|||
|
|
|
|||
|
|
def pause(self):
|
|||
|
|
self.state = AgentState.PAUSED
|
|||
|
|
|
|||
|
|
def resume(self):
|
|||
|
|
self.state = AgentState.RUNNING
|
|||
|
|
|
|||
|
|
def from_to(self, from_state, to_state):
|
|||
|
|
with self._task_lock:
|
|||
|
|
if self._prev_state == from_state and self._state == to_state:
|
|||
|
|
return True
|
|||
|
|
else:
|
|||
|
|
return False
|
|||
|
|
|
|||
|
|
def _process_task_queue(self, dt: float):
|
|||
|
|
if self.state == AgentState.RUNNING:
|
|||
|
|
return
|
|||
|
|
self._model.start()
|
|||
|
|
self.state = AgentState.RUNNING
|
|||
|
|
while self.state != AgentState.STOPPED and not self._task_queue.empty():
|
|||
|
|
self.current_task = self._task_queue.get()
|
|||
|
|
priority, task = self.current_task
|
|||
|
|
assert isinstance(task, Task)
|
|||
|
|
self._planning_task(priority, task)
|
|||
|
|
task_executor.submit(task)
|
|||
|
|
|
|||
|
|
while task.state in(TaskState.RUNNING, TaskState.PLANNED, TaskState.READY):
|
|||
|
|
# 如果需要切换任务
|
|||
|
|
if self.from_to(AgentState.RUNNING, AgentState.SWITCHING):
|
|||
|
|
self._save_task(priority, task)
|
|||
|
|
task_executor.cancel(task.task_id)
|
|||
|
|
break
|
|||
|
|
# 如果需要停止任务
|
|||
|
|
elif self.from_to(AgentState.RUNNING, AgentState.STOPPED):
|
|||
|
|
task_executor.cancel(task.task_id)
|
|||
|
|
break
|
|||
|
|
elif self.from_to(AgentState.RUNNING, AgentState.PAUSED):
|
|||
|
|
task_executor.pause(task.task_id)
|
|||
|
|
elif self.from_to(AgentState.PAUSED, AgentState.RUNNING):
|
|||
|
|
task_executor.resume(task.task_id)
|
|||
|
|
try:
|
|||
|
|
if task.state == TaskState.RUNNING and self.state == AgentState.RUNNING:
|
|||
|
|
result = self._model.step(dt)
|
|||
|
|
# 控制仿真速率(实时:sleep = dt;加速:sleep < dt)
|
|||
|
|
sleep(max(.050, (dt/1000) * 0.200))
|
|||
|
|
except Exception as e:
|
|||
|
|
traceback.print_exc()
|
|||
|
|
print(f"[SimThread] 仿真异常: {e}")
|
|||
|
|
break
|
|||
|
|
self.state = AgentState.IDLE
|
|||
|
|
self.current_task = None
|
|||
|
|
self._model.stop()
|
|||
|
|
|
|||
|
|
def start(self, dt):
|
|||
|
|
if self.state == AgentState.RUNNING:
|
|||
|
|
return
|
|||
|
|
self._task_thread = QThread()
|
|||
|
|
self._worker = AgentTaskWorker(self, dt)
|
|||
|
|
self._worker.moveToThread(self._task_thread)
|
|||
|
|
self._task_thread.started.connect(self._worker.do_work)
|
|||
|
|
self._worker.finished.connect(self._task_thread.quit)
|
|||
|
|
self._worker.finished.connect(self._task_thread.quit) # 任务结束 → 退出线程
|
|||
|
|
self._worker.finished.connect(self._worker.deleteLater) # 自动释放
|
|||
|
|
self._task_thread.finished.connect(self._task_thread.deleteLater) # 自动释放
|
|||
|
|
self._task_thread.finished.connect(self._on_worker_finished) # 任务结束后恢复按钮
|
|||
|
|
self._task_thread.start()
|
|||
|
|
|
|||
|
|
@pyqtSlot()
|
|||
|
|
def _on_worker_finished(self):
|
|||
|
|
self._task_thread = None
|
|||
|
|
|
|||
|
|
# ── 属性(只读)──────────────────────────────────────────────
|
|||
|
|
def _planning_task(self, priority, task):
|
|||
|
|
pass
|
|||
|
|
|
|||
|
|
def _log(self, message, style):
|
|||
|
|
print(message)
|
|||
|
|
|
|||
|
|
def _save_task(self, priority, task):
|
|||
|
|
pass
|
|||
|
|
|
|||
|
|
@pyqtSlot(object)
|
|||
|
|
def _on_event_submitted(self, event):
|
|||
|
|
self._log(f"📥 提交 | {event.type_key} | src={event.source}", "#569cd6")
|
|||
|
|
|
|||
|
|
@pyqtSlot(object)
|
|||
|
|
def _on_event_handled(self, event):
|
|||
|
|
results_str = " | ".join(str(r) for r in event.results)
|
|||
|
|
self._log(
|
|||
|
|
f"✅ 完成 | {event.type_key} | {event.elapsed_ms:.1f}ms | {results_str}",
|
|||
|
|
"#4ec9b0"
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
@pyqtSlot(object, str)
|
|||
|
|
def _on_event_failed(self, event, error):
|
|||
|
|
self._log(f"❌ 失败 | {event.type_key} | {error}", "#f44747")
|
|||
|
|
|
|||
|
|
@pyqtSlot(object)
|
|||
|
|
def _on_event_ignored(self, event):
|
|||
|
|
self._log(f"⚪ 忽略 | {event.type_key} | 无处理器", "#808080")
|
|||
|
|
|
|||
|
|
@pyqtSlot(int, int, int)
|
|||
|
|
def _on_stats_updated(self, total: int, handled: int, failed: int):
|
|||
|
|
pass
|
|||
|
|
|
|||
|
|
|
|||
|
|
# ── TaskExecutor 信号槽 ───────────────────────────────────────
|
|||
|
|
@pyqtSlot(object)
|
|||
|
|
def _on_task_submitted(self, task):
|
|||
|
|
self._log(
|
|||
|
|
f"📥 任务提交: {task.name} [{task.task_id[:8]}]", "#569cd6"
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
@pyqtSlot(object)
|
|||
|
|
def _on_task_started(self, task):
|
|||
|
|
self._log(f"▶ 任务开始执行: {task.name}", "#4ec9b0")
|
|||
|
|
|
|||
|
|
@pyqtSlot(object)
|
|||
|
|
def _on_task_completed(self, task):
|
|||
|
|
self._log(
|
|||
|
|
f"✅ 任务完成: {task.name} 耗时={task.elapsed_s:.1f}s",
|
|||
|
|
"#4CAF50"
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
@pyqtSlot(object, str)
|
|||
|
|
def _on_task_failed(self, task, error):
|
|||
|
|
self._log(f"❌ 任务失败: {task.name} {error}", "#f44747")
|
|||
|
|
|
|||
|
|
@pyqtSlot(object)
|
|||
|
|
def _on_task_cancelled(self, task):
|
|||
|
|
self._log(f"🚫 任务取消: {task.name}", "#FF9800")
|
|||
|
|
|
|||
|
|
@pyqtSlot(object)
|
|||
|
|
def _on_task_paused(self, task):
|
|||
|
|
self._log(f"⏸ 任务暂停: {task.name}", "#9E9E9E")
|
|||
|
|
# self._add_rec_row("state_change", "任务已暂停", "")
|
|||
|
|
|
|||
|
|
@pyqtSlot(object)
|
|||
|
|
def _on_task_resumed(self, task):
|
|||
|
|
self._log(f"▶ 任务恢复: {task.name}", "#4ec9b0")
|
|||
|
|
# self._add_rec_row("state_change", "任务已恢复", "")
|
|||
|
|
|
|||
|
|
@pyqtSlot(object, int)
|
|||
|
|
def _on_segment_entered(self, task, seg_index):
|
|||
|
|
if task.has_route and seg_index < len(task.route.segments):
|
|||
|
|
seg = task.route.segments[seg_index]
|
|||
|
|
msg = (f"进入航段[{seg_index}] "
|
|||
|
|
f"{seg.wp_from.identifier}→{seg.wp_to.identifier} "
|
|||
|
|
f"({seg.distance / 1000:.1f}km)")
|
|||
|
|
else:
|
|||
|
|
msg = f"进入航段[{seg_index}]"
|
|||
|
|
self._log(f"✈️ {msg}", "#dcdcaa")
|
|||
|
|
# self._add_rec_row("segment", msg, f"seg_index={seg_index}")
|
|||
|
|
|
|||
|
|
@pyqtSlot(object, int)
|
|||
|
|
def _on_segment_exited(self, task, seg_index):
|
|||
|
|
self._log(f"🏁 离开航段[{seg_index}]", "#808080")
|
|||
|
|
|
|||
|
|
# ── ActionExecutor 信号槽 ─────────────────────────────────────
|
|||
|
|
|
|||
|
|
@pyqtSlot(object)
|
|||
|
|
def _on_action_completed(self, action):
|
|||
|
|
self._log(
|
|||
|
|
f" ✅ Action完成: {action.type_key} "
|
|||
|
|
f"src={action.source} {action.elapsed_ms:.0f}ms",
|
|||
|
|
"#6a9955",
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
@pyqtSlot(str, int, str)
|
|||
|
|
def _on_action_progress(self, action_id: str, percent: int, message: str):
|
|||
|
|
pass
|
|||
|
|
|
|||
|
|
@pyqtSlot(object, list)
|
|||
|
|
def _on_events_produced(self, action, events):
|
|||
|
|
# self._event_count += len(events)
|
|||
|
|
for ev in events:
|
|||
|
|
self._log(
|
|||
|
|
f" ⚡ Event产生: {ev.type_key} src={ev.source}",
|
|||
|
|
"#c586c0",
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
def __repr__(self) -> str:
|
|||
|
|
return (f"{self.__class__.__name__}("
|
|||
|
|
f"id={self.equipment_id}, "
|
|||
|
|
f"name={self.name}, "
|
|||
|
|
f"lc={self.lifecycle.value})")
|
|||
|
|
|