169 lines
7.3 KiB
Python
169 lines
7.3 KiB
Python
|
|
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;}}")
|