feat: implement logging setup and CSV telemetry logging for performance metrics

This commit is contained in:
2026-05-12 22:15:50 +02:00
parent aec286c5ec
commit d62416db4e
9 changed files with 426 additions and 106 deletions

View File

@@ -9,6 +9,7 @@ from PySide6.QtGui import QAction, QActionGroup
from PySide6.QtWidgets import QMenuBar, QWidget
from app.camera.camera_enumerator import CameraInfo
from app.logging_setup import set_console_level
logger = logging.getLogger(__name__)
@@ -44,7 +45,7 @@ class AppMenuBar(QMenuBar):
self._build_menus()
# ------------------------------------------------------------------
# Public API — called after camera enumeration
# Public API
# ------------------------------------------------------------------
def populate_cameras(self, cameras: list[CameraInfo]) -> None:
@@ -52,7 +53,6 @@ class AppMenuBar(QMenuBar):
self._cameras = cameras
menu = self._camera_menu
# Remove existing camera actions (keep Reconnect + separator)
for action in list(menu.actions()):
if action not in (self._reconnect_action, self._cam_separator):
menu.removeAction(action)
@@ -69,8 +69,7 @@ class AppMenuBar(QMenuBar):
action.triggered.connect(self._on_camera_action)
if cameras:
first = self._camera_group.actions()[0]
first.setChecked(True)
self._camera_group.actions()[0].setChecked(True)
def populate_formats(self, camera_info: CameraInfo) -> None:
"""Populate Resolution and FPS menus based on a camera's supported formats."""
@@ -78,7 +77,6 @@ class AppMenuBar(QMenuBar):
self._populate_fps(camera_info)
def set_active_camera(self, camera_info: CameraInfo) -> None:
"""Check the menu item matching camera_info."""
if self._camera_group is None:
return
for action in self._camera_group.actions():
@@ -86,24 +84,31 @@ class AppMenuBar(QMenuBar):
action.setChecked(True)
return
def set_log_file_path(self, path: str) -> None:
"""Display the log file path as a disabled menu item in Debug menu."""
# Truncate long paths for display
display = path if len(path) <= 60 else "" + path[-57:]
self._log_file_action.setText(f"Log: {display}")
self._log_file_action.setToolTip(path)
# ------------------------------------------------------------------
# Menu construction
# ------------------------------------------------------------------
def _build_menus(self) -> None:
# --- Camera menu ---
# Camera menu
self._camera_menu = self.addMenu("Camera")
self._cam_separator = self._camera_menu.addSeparator()
self._reconnect_action = QAction("Reconnect", self)
self._reconnect_action.triggered.connect(self.reconnect_requested)
self._camera_menu.addAction(self._reconnect_action)
# --- Video menu ---
# Video menu
self._video_menu = self.addMenu("Video")
self._res_menu = self._video_menu.addMenu("Resolution")
self._fps_menu = self._video_menu.addMenu("FPS")
# --- Debug menu ---
# Debug menu
debug_menu = self.addMenu("Debug")
self._overlay_action = QAction("Show Overlay", self)
@@ -118,20 +123,26 @@ class AppMenuBar(QMenuBar):
self._log_action.toggled.connect(self._on_log_toggled)
debug_menu.addAction(self._log_action)
debug_menu.addSeparator()
self._log_file_action = QAction("Log: (not started)", self)
self._log_file_action.setEnabled(False)
debug_menu.addAction(self._log_file_action)
def _populate_resolutions(self, camera_info: CameraInfo) -> None:
self._res_menu.clear()
self._resolution_group = QActionGroup(self)
self._resolution_group.setExclusive(True)
seen: set[tuple[int, int]] = set()
for w, h, _ in camera_info.formats:
key = (w, h)
for fmt in camera_info.formats:
key = (fmt.width, fmt.height)
if key in seen:
continue
seen.add(key)
action = QAction(f"{w} × {h}", self)
action = QAction(f"{fmt.width} × {fmt.height}", self)
action.setCheckable(True)
action.setData((w, h))
action.setData((fmt.width, fmt.height))
self._resolution_group.addAction(action)
self._res_menu.addAction(action)
action.triggered.connect(self._on_resolution_action)
@@ -146,14 +157,14 @@ class AppMenuBar(QMenuBar):
self._fps_group.setExclusive(True)
seen: set[int] = set()
for _, _, fps in camera_info.formats:
key = round(fps)
for fmt in camera_info.formats:
key = round(fmt.max_fps)
if key in seen:
continue
seen.add(key)
action = QAction(f"{key} fps", self)
action.setCheckable(True)
action.setData(float(fps))
action.setData(float(fmt.max_fps))
self._fps_group.addAction(action)
self._fps_menu.addAction(action)
action.triggered.connect(self._on_fps_action)
@@ -193,6 +204,5 @@ class AppMenuBar(QMenuBar):
self.fps_selected.emit(fps)
def _on_log_toggled(self, enabled: bool) -> None:
level = logging.DEBUG if enabled else logging.WARNING
logging.getLogger().setLevel(level)
set_console_level(enabled)
self.log_toggled.emit(enabled)