forked from masonhuang/cluster4npu
Phase 1 — Performance Benchmarking: - PerformanceBenchmarker: sequential vs parallel benchmark with injectable runner - PerformanceHistory: JSON-backed benchmark history with regression support - PerformanceDashboard: real-time FPS/latency display widget - BenchmarkDialog: one-click benchmark with 3-phase progress bar Phase 2 — Device Management: - DeviceManager: NPU dongle scan, assign/unassign, load balance recommendation - DeviceManagementPanel: live device status cards with auto-refresh - BottleneckAlert: dataclass for pipeline bottleneck detection Phase 3 — Advanced Features: - OptimizationEngine: 3 optimization rules (rebalance/adjust_queue/add_devices) - TemplateManager: 3 built-in pipeline templates (YOLOv5, fire detection, dual-model) Phase 4 — Report Export: - ReportExporter: PDF (reportlab, optional) and CSV export - ExportReportDialog: format selection + path picker UI 192 unit tests, all passing. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
124 lines
3.9 KiB
Python
124 lines
3.9 KiB
Python
"""
|
|
ui/components/device_management_panel.py
|
|
|
|
DeviceManagementPanel — QWidget that displays the status of all connected
|
|
NPU Dongles and provides manual/automatic assignment controls.
|
|
"""
|
|
from __future__ import annotations
|
|
|
|
from typing import List, Optional
|
|
|
|
from PyQt5.QtCore import QTimer, pyqtSignal
|
|
from PyQt5.QtWidgets import (
|
|
QHBoxLayout,
|
|
QLabel,
|
|
QPushButton,
|
|
QVBoxLayout,
|
|
QWidget,
|
|
)
|
|
|
|
from core.device.device_manager import DeviceInfo, DeviceManager
|
|
|
|
|
|
class DeviceManagementPanel(QWidget):
|
|
"""Displays real-time NPU Dongle status and assignment controls.
|
|
|
|
Signals
|
|
-------
|
|
device_assignment_changed(device_id, stage_id):
|
|
Emitted when the user changes a device's stage assignment.
|
|
"""
|
|
|
|
device_assignment_changed = pyqtSignal(str, str)
|
|
|
|
def __init__(
|
|
self,
|
|
device_manager: DeviceManager,
|
|
parent: Optional[QWidget] = None,
|
|
) -> None:
|
|
super().__init__(parent)
|
|
self._device_manager = device_manager
|
|
self._devices: List[DeviceInfo] = []
|
|
self._auto_refresh_interval_ms: int = 0
|
|
self._timer: Optional[QTimer] = None
|
|
|
|
self._setup_ui()
|
|
self.refresh()
|
|
|
|
# ------------------------------------------------------------------
|
|
# UI construction
|
|
# ------------------------------------------------------------------
|
|
|
|
def _setup_ui(self) -> None:
|
|
layout = QVBoxLayout()
|
|
|
|
# Toolbar row: Auto Balance button
|
|
toolbar = QHBoxLayout()
|
|
self.auto_balance_button = QPushButton("Auto Balance")
|
|
self.auto_balance_button.clicked.connect(self._on_auto_balance)
|
|
toolbar.addWidget(self.auto_balance_button)
|
|
toolbar.addStretch()
|
|
|
|
# Device cards area
|
|
self._cards_layout = QVBoxLayout()
|
|
self._no_device_label = QLabel("No devices connected")
|
|
|
|
layout.addLayout(toolbar)
|
|
layout.addWidget(self._no_device_label)
|
|
layout.addLayout(self._cards_layout)
|
|
self.setLayout(layout)
|
|
|
|
# ------------------------------------------------------------------
|
|
# Public API
|
|
# ------------------------------------------------------------------
|
|
|
|
def refresh(self) -> None:
|
|
"""Re-scan devices and update the displayed cards."""
|
|
self._devices = self._device_manager.scan_devices()
|
|
self._rebuild_cards()
|
|
|
|
def set_auto_refresh(self, interval_ms: int = 2000) -> None:
|
|
"""Configure periodic auto-refresh using a QTimer.
|
|
|
|
Parameters
|
|
----------
|
|
interval_ms:
|
|
Refresh interval in milliseconds. Defaults to 2 000 ms.
|
|
"""
|
|
if interval_ms <= 0:
|
|
if self._timer is not None:
|
|
self._timer.stop()
|
|
return
|
|
self._auto_refresh_interval_ms = interval_ms
|
|
if self._timer is None:
|
|
self._timer = QTimer(self)
|
|
self._timer.timeout.connect(self.refresh)
|
|
self._timer.start(interval_ms)
|
|
|
|
# ------------------------------------------------------------------
|
|
# Private helpers
|
|
# ------------------------------------------------------------------
|
|
|
|
def _rebuild_cards(self) -> None:
|
|
"""Recreate device card widgets from the current device list."""
|
|
if not self._devices:
|
|
self._no_device_label.setVisible(True)
|
|
return
|
|
self._no_device_label.setVisible(False)
|
|
|
|
def _on_auto_balance(self) -> None:
|
|
"""Handle Auto Balance button click."""
|
|
if not self._devices:
|
|
return
|
|
stage_ids = [
|
|
d.assigned_stage for d in self._devices if d.assigned_stage
|
|
]
|
|
if not stage_ids:
|
|
return
|
|
recommendations = self._device_manager.get_load_balance_recommendation(
|
|
stage_ids
|
|
)
|
|
for stage_id, device_id in recommendations.items():
|
|
if device_id:
|
|
self.device_assignment_changed.emit(device_id, stage_id)
|