# Goal Zbudować aplikację do podglądu kamery w czasie rzeczywistym (PySide6) z telemetrią, overlayami i systemem logowania do pliku — docelowo na Mac Mini z kamerą ELP. --- # Constraints & Preferences - Python 3.12.10, venv w `.venv-win` - Dev: Windows 11 - Target: Mac Mini Intel i7, macOS Ventura, kamera ELP USB - `PySide6 6.11.0`, `psutil`, `pytest`, `ruff` - `QVideoWidget` porzucony — na Windows zasłania wszystkie child/sibling widgets przez natywny HWND D3D - Logi: - `logs/` w katalogu projektu - nowy plik z timestamp per sesja - max 20 plików - CSV telemetrii: - co 5 sekund - `flush` po każdym wierszu - CPU: - pokazywać oba: - sys % - per-core % (jak Task Manager) - Zmiana formatu kamery przez `stop+start` - nie `setCameraFormat` na żywej kamerze --- # Progress ## Done ### Faza 0–7 MVP - scaffolding - `CameraService` - `FrameDispatcher` - `TelemetryCollector` - `CameraView` - `TelemetryOverlay` - `IOverlayLayer` - `AppMenuBar` - `MainWindow` ### Rendering - Usunięto `QVideoWidget` - `CameraView(QWidget)` z `paintEvent` - render + overlay w jednym przejściu ### Telemetria `TelemetrySnapshot` — pola: - `fps` - `target_fps` - `frame_time_ms` - `dropped_frames` - `cpu_percent_sys` - `cpu_percent_core` - `memory_mb` ### Kamera - `CameraService.format_changed(float)` sygnał - `stop+start` przy: - `set_resolution` - `set_fps` ### Logowanie - `_log_actual_format()` - loguje rzeczywisty format po starcie kamery - warning jeśli FPS się nie zgadza ### CameraFormat `CameraFormat` dataclass z: ```python pixel_format: str ``` Przykłady: - `MJPG` - `YUY2` - `NV12` w `camera_enumerator.py` ### Logging `logging_setup.py` - `FileHandler` - `DEBUG` - zawsze aktywny - `StreamHandler` - `WARNING` - przełączalny - nagłówek sesji: - wersja - platforma - Python - PySide6 - CPU - RAM - pruning starych logów ### CSV `csv_logger.py` - `CsvTelemetryLogger` - throttling: - 5 s - `flush` po każdym wierszu ### Menu `menu_bar.py` - dostosowany do `CameraFormat` - `set_log_file_path()` - `set_console_level()` ### Config `config.py` - `LOG_DIR` - `MAX_LOG_FILES` - `TELEMETRY_CSV_INTERVAL_S` ### Main `main.py` - wywołuje `setup_logging()` - przekazuje `log_path` do `MainWindow` ### Main Window `main_window.py` - przyjmuje: ```python log_path: Path | None ``` - tworzy: ```python CsvTelemetryLogger(log_path.with_suffix(".csv")) ``` - podpina do: - `telemetry.metrics_updated` - wywołuje: - `menu.set_log_file_path()` - `csv_logger.close()` w `closeEvent` ### Repo `.gitignore` - dodano `logs/` ### Testy i jakość - 20 testów jednostkowych - wszystkie zielone - `ruff` - czysty - wszystkie błędy naprawione ### Artefakty sesji Para plików per sesja: ```text logs/duck-preview_.log logs/duck-preview_.csv ``` --- ## In Progress - (none) --- ## Blocked - (none) --- # Key Decisions ## Rendering ### `QVideoWidget` → własny `CameraView(QWidget)` Jedyny skuteczny sposób obejścia problemu z natywnym HWND na Windows. ## Overlay Architecture ### `IOverlayLayer` ABC Pluggable overlaye bez modyfikacji `CameraView`. Gotowe pod: - YOLO - OCR - inne overlaye ## Pipeline ### `FrameDispatcher` Pub/sub z `drop_if_busy`: - render może gubić klatki - telemetria nigdy ## CPU Metrics - CPU per-core (`psutil`) - dzielone przez `cpu_count` - wynik zgodny z Task Manager ## Memory Metrics - `memory_info().wset` (Windows) - zamiast `rss` - odpowiada `Private Working Set` ## Logging Strategy Dwa osobne pliki per sesja: - `.log` — diagnostyka - `.csv` — metryki szeregów czasowych ## CameraFormat `CameraFormat` dataclass zamiast krotki: - niesie `pixel_format` - potrzebny w logach --- # Next Steps 1. Przetestować na Windows: - czy logi powstają w `logs/` - czy CSV zapisuje dane co ~5 s 2. Przenieść na Mac Mini i przetestować z kamerą ELP: - sprawdzić `pixel_format` w logu - sprawdzić wykrycie backendu AVFoundation 3. Zweryfikować overlay telemetrii na żywym obrazie 4. (Opcjonalnie) dodać kolejne `IOverlayLayer` - YOLO - OCR --- # Critical Context ## Camera Enumerator `CameraEnumerator.list_cameras()` zwraca: ```python list[CameraInfo] ``` gdzie: ```python formats: list[CameraFormat] ``` `menu_bar.py` i `camera_service.py` iterują po: - `fmt.width` - `fmt.height` - `fmt.max_fps` --- ## CSV Logger Ścieżka CSV: ```python log_path.with_suffix(".csv") ``` Powstaje para: - `.log` - `.csv` z tym samym timestamp. --- ## Console Logging ```python logging_setup.set_console_level(debug: bool) ``` wywoływane z: ```python menu_bar._on_log_toggled ``` --- ## Pixel Format ```python pixel_format_name() ``` eksportowane z: ```python camera_enumerator.py ``` używane również w: ```python camera_service.py ``` --- ## Log Header Nagłówek logu zawiera: - wersję aplikacji - platformę - Python - PySide6 - liczbę CPU - RAM --- ## Uruchamianie ```bash .venv-win\Scripts\python.exe -m app.main ``` --- ## Ruff Fixes Naprawiono: - `E501` - długa linia w `camera_enumerator.py` - `F401` - `I001` - w `camera_service.py` - w `main.py` --- # Relevant Files ## Entry Point - `app/main.py` - wywołuje `setup_logging()` - przekazuje `log_path` ## Config - `app/config.py` - `LOG_DIR` - `MAX_LOG_FILES` - `TELEMETRY_CSV_INTERVAL_S` ## Logging - `app/logging_setup.py` - konfiguracja logowania - nagłówek sesji - pruning ## Telemetry - `app/telemetry/csv_logger.py` - zapis CSV z throttlingiem - `app/telemetry/telemetry_collector.py` - `TelemetrySnapshot` - `TelemetryCollector` ## Camera - `app/camera/camera_enumerator.py` - `CameraFormat` - `CameraInfo` - `pixel_format_name()` - `app/camera/camera_service.py` - `CameraService` - `format_changed` - `_log_actual_format()` ## UI - `app/ui/main_window.py` - kompletny - podpięty `CsvTelemetryLogger` - `app/ui/menu_bar.py` - `set_log_file_path()` - `set_console_level()` - `app/ui/camera_view.py` - `CameraView` - rejestr `IOverlayLayer` ## Overlay - `app/overlay/overlay_layer.py` - `IOverlayLayer` ABC - `app/overlay/telemetry_overlay.py` - `TelemetryOverlay(IOverlayLayer)` ## Pipeline - `app/pipeline/frame_dispatcher.py` - pub/sub - `drop_if_busy` ## Tests - `tests/test_telemetry_collector.py` - 12 testów - mockuje `cpu_count` - `tests/test_frame_dispatcher.py` - 8 testów ## Repo / Docs - `.gitignore` - zawiera `logs/` - `notes/01-mvp-preview.md` - PRD - `notes/01-mvp-plan.md` - plan implementacji - `notes/02-mvp-app.md` - stan aplikacji - historia prób i decyzji architektonicznych