import logging from PyQt5.QtCore import Qt from PyQt5.QtWidgets import ( QMainWindow, QWidget, QVBoxLayout, QHBoxLayout, QSplitter, QGroupBox, QLabel, QPushButton, QProgressBar ) from registry.action_registry import action_factory from uas.action.action_executor import action_executor from uas.action.action_model import ActionState # ── Action 模块 ─────────────────────────────────────────────────── from uas.action.action_type import ActionCategory, action_registry from uas.event.event_bus import event_bus from uas.event.event_model import make_event # ── Event 模块(复用)──────────────────────────────────────────── from uas.event.event_type import EventType from uas.event.handlers.device_handlers import ( DeviceErrorHandler, DeviceTimeoutHandler, EquipmentFaultHandler ) logging.basicConfig( level = logging.DEBUG, format = "%(asctime)s [%(levelname)-8s] %(name)s - %(message)s", ) logger = logging.getLogger("main") # ───────────────────────────────────────────────────────────────── # 主窗口 # ───────────────────────────────────────────────────────────────── class ActionMonitorWindow(QMainWindow): STATE_COLORS = { ActionState.COMPLETED: "#4CAF50", ActionState.FAILED: "#f44336", ActionState.CANCELLED: "#FF9800", ActionState.RUNNING: "#2196F3", ActionState.QUEUED: "#607D8B", ActionState.TIMEOUT: "#9C27B0", ActionState.INTERRUPTED: "#FF5722", } def __init__(self): super().__init__() self.setWindowTitle("🛰️ Action 监控系统 — PyQt5 演示") self.resize(1300, 800) self._setup_modules() self._setup_ui() self._auto_demo() # ── 模块初始化 ──────────────────────────────────────────────── def _setup_modules(self): # 1. 注册 EventBus 处理器 event_bus.register_handler(DeviceErrorHandler()) event_bus.register_handler(DeviceTimeoutHandler()) event_bus.register_handler(EquipmentFaultHandler()) event_bus.scan_decorators() # 2. 将 EventBus 注入 ActionExecutor(Action产生的Event自动提交) action_executor.set_event_bus(event_bus) # 3. 注册自定义 Action 类型(演示扩展) action_registry.register( "perception.thermal_scan", category = ActionCategory.PERCEPTION, description = "红外热成像扫描", timeout_sec = 20.0, ) from uas.action.action_context import ActionContext from uas.action.action_model import Action def handle_thermal_scan(action: Action, ctx: ActionContext): import time, random ctx.report_progress(30, "预热红外传感器...") time.sleep(0.1) ctx.check_cancel() ctx.report_progress(70, "采集热成像数据...") time.sleep(0.1) ctx.emit_event(make_event( EventType.DEVICE_DATA_RECEIVED, source = action.source, message = "红外热成像采集完成", data_type = "thermal", max_temp = round(random.uniform(20, 80), 1), min_temp = round(random.uniform(-10, 20), 1), )) ctx.report_progress(100, "热成像完成") return {"thermal": "ok"} action_factory.register("perception.thermal_scan", handle_thermal_scan) # 4. 连接 ActionExecutor 信号到 UI 槽 action_executor.action_submitted.connect(self._on_action_submitted) action_executor.action_completed.connect(self._on_action_completed) action_executor.action_failed.connect(self._on_action_failed) action_executor.action_cancelled.connect(self._on_action_cancelled) action_executor.action_progress.connect(self._on_action_progress) action_executor.events_produced.connect(self._on_events_produced) action_executor.stats_updated.connect(self._on_stats_updated) # 5. 连接 EventBus 信号到 UI 槽 event_bus.event_handled.connect(self._on_event_handled) # ── UI 构建 ─────────────────────────────────────────────────── def _setup_ui(self): central = QWidget() self.setCentralWidget(central) root = QVBoxLayout(central) root.setContentsMargins(6, 6, 6, 6) splitter = QSplitter(Qt.Horizontal) # ── 左侧:控制面板 ──────────────────────────────────────── left = QWidget() left.setFixedWidth(300) left_layout = QVBoxLayout(left) # 统计卡片 stats_box = QGroupBox("📊 执行统计") stats_layout = QHBoxLayout(stats_box) self._lbl_submitted = self._stat_card("提交", "0", "#2196F3") self._lbl_completed = self._stat_card("完成", "0", "#4CAF50") self._lbl_failed = self._stat_card("失败", "0", "#f44336") self._lbl_cancelled = self._stat_card("取消", "0", "#FF9800") for lbl in [self._lbl_submitted, self._lbl_completed, self._lbl_failed, self._lbl_cancelled]: stats_layout.addWidget(lbl) left_layout.addWidget(stats_box) # 进度条 prog_box = QGroupBox("⚙️ 当前进度") prog_layout = QVBoxLayout(prog_box) self._prog_label = QLabel("等待任务...") self._prog_label.setStyleSheet("color: gray; font-size: 11px;") self._progress_bar = QProgressBar() self._progress_bar.setRange(0, 100) self._progress_bar.setValue(0) prog_layout.addWidget(self._prog_label) prog_layout.addWidget(self._progress_bar) left_layout.addWidget(prog_box) # Action 触发按钮 btn_box = QGroupBox("🎯 触发 Action") btn_layout = QVBoxLayout(btn_box) btn_defs = [ ("⏸ 暂停", self._do_pause, "#607D8B"), ("▶️ 继续", self._do_resume, "#4CAF50"), ("⏹ 停止", self._do_stop, "#f44336"), ("🔄 重置", self._do_reset, "#FF9800"), ("📷 拍照", self._do_capture_photo, "#2196F3"), ("🎯 识别目标", self._do_recognize, "#9C27B0"), ("🔍 环境扫描", self._do_scan, "#00BCD4"), ("🌡️ 红外扫描", self._do_thermal, "#FF5722"), ("🔧 系统诊断", self._do_diagnose, "#795548"), ("📐 设备校准", self._do_calibrate, "#009688"), ("🚀 批量执行(5个)", self._do_batch, "#673AB7"), ] for text, slot, color in btn_defs: btn = QPushButton(text) btn.setMinimumHeight(30) btn.setStyleSheet( f"QPushButton{{background:{color};color:white;" f"border-radius:4px;font-size:12px;}}")