Coverage for orchestr_ant_ion / pipeline / ui / viewmodel.py: 34%
79 statements
« prev ^ index » next coverage.py v7.13.5, created at 2026-03-19 08:36 +0000
« prev ^ index » next coverage.py v7.13.5, created at 2026-03-19 08:36 +0000
1"""Shared view-model that formats raw metrics into display strings.
3Both :class:`DearPyGuiViewer` and :class:`WxPythonViewer` delegate to
4this module so that formatting logic is defined in exactly one place.
5"""
7from __future__ import annotations
9from dataclasses import dataclass
10from typing import TYPE_CHECKING
13if TYPE_CHECKING:
14 from orchestr_ant_ion.pipeline.types import (
15 PerformanceMetrics,
16 SystemStats,
17 )
20@dataclass(slots=True)
21class ViewLabels:
22 """Pre-formatted label strings ready for display."""
24 resolution: str = ""
25 capture: str = ""
26 backend: str = ""
27 pipeline: str = ""
28 cpu_model: str = ""
29 ram_total: str = ""
30 gpu_model: str = ""
31 vram_total: str = ""
32 detections: str = ""
33 camera_fps: str = ""
34 inference_ms: str = ""
35 budget: str = ""
36 headroom: str = ""
37 sys_cpu: str = ""
38 sys_ram: str = ""
39 gpu: str = ""
40 vram: str = ""
41 power: str = ""
42 energy: str = ""
43 proc: str = ""
44 classification: str = ""
47def format_labels( # noqa: C901
48 *,
49 perf_metrics: PerformanceMetrics | None,
50 sys_stats: SystemStats | None,
51 proc_stats: dict | None,
52 camera_info: dict | None,
53 detections_count: int | None,
54 hardware_info: dict | None,
55 power_info: dict | None,
56 classification: dict | None,
57 frame_size: tuple[int, int],
58) -> ViewLabels:
59 """Return a :class:`ViewLabels` from the raw data sources."""
60 labels = ViewLabels()
62 if (
63 perf_metrics is None
64 or sys_stats is None
65 or proc_stats is None
66 or camera_info is None
67 ):
68 return labels
70 w, h = frame_size
71 labels.resolution = f"Resolution: {w}x{h}"
73 # Camera / backend
74 backend_display = camera_info.get("backend", "unknown")
75 labels.capture = f"Capture: {backend_display}"
76 labels.backend = f"Backend: {backend_display}"
77 pipeline = camera_info.get("pipeline", "")
78 if pipeline:
79 if len(pipeline) > 70:
80 pipeline = pipeline[:67] + "..."
81 labels.pipeline = f"Pipeline: {pipeline}"
82 if detections_count is not None:
83 labels.detections = f"Detections: {detections_count}"
85 # Hardware
86 if hardware_info is not None:
87 cpu_model = hardware_info.get("cpu_model", "N/A")
88 ram_total = hardware_info.get("ram_total_gb", 0.0)
89 gpu_model = hardware_info.get("gpu_model", "N/A")
90 vram_total = hardware_info.get("vram_total_gb", 0.0)
91 labels.cpu_model = f"CPU: {cpu_model}"
92 labels.ram_total = f"RAM: {ram_total:.1f} GB"
93 if gpu_model and gpu_model != "N/A":
94 labels.gpu_model = f"GPU: {gpu_model}"
95 labels.vram_total = f"VRAM: {vram_total:.1f} GB"
97 # Performance
98 labels.camera_fps = f"Camera Input: {perf_metrics.camera_fps:.1f} FPS"
99 labels.inference_ms = f"Inference Latency: {perf_metrics.inference_ms:.1f} ms/frame"
100 labels.budget = f"Frame Budget Used: {perf_metrics.frame_budget_percent:.1f}%"
101 headroom = 100 - perf_metrics.frame_budget_percent
102 labels.headroom = (
103 f"GPU Headroom: {headroom:.0f}% "
104 f"(capacity: {perf_metrics.inference_capacity_fps:.0f} FPS)"
105 )
107 # System
108 labels.sys_cpu = f"System CPU: {sys_stats.cpu_percent:.1f}%"
109 labels.sys_ram = (
110 f"System RAM: {sys_stats.ram_used_gb:.1f}/{sys_stats.ram_total_gb:.1f} GB "
111 f"({sys_stats.ram_percent:.1f}%)"
112 )
113 if sys_stats.gpu_name != "N/A":
114 labels.gpu = (
115 f"GPU Load: {sys_stats.gpu_percent:.0f}% | "
116 f"Temp: {sys_stats.gpu_temp_celsius:.0f}C | "
117 f"{sys_stats.gpu_power_watts:.0f}W"
118 )
119 labels.vram = (
120 f"VRAM: {sys_stats.gpu_memory_used_gb:.1f}/"
121 f"{sys_stats.gpu_memory_total_gb:.1f} GB "
122 f"({sys_stats.gpu_memory_percent:.0f}%)"
123 )
125 # Power
126 if power_info is not None:
127 system_power = power_info.get("system_power_watts", 0.0)
128 cpu_power = power_info.get("cpu_power_watts", 0.0)
129 gpu_power = power_info.get("gpu_power_watts", 0.0)
130 energy_wh = power_info.get("energy_wh", 0.0)
131 if system_power > 0.0:
132 labels.power = (
133 f"Power: {system_power:.0f}W "
134 f"(CPU {cpu_power:.0f}W, GPU {gpu_power:.0f}W)"
135 )
136 elif gpu_power > 0.0:
137 labels.power = f"Power: GPU {gpu_power:.0f}W"
138 if energy_wh > 0.0:
139 labels.energy = f"Energy: {energy_wh:.3f} Wh"
141 # Process
142 labels.proc = (
143 f"CPU: {proc_stats['cpu_percent']:.1f}% | "
144 f"RAM: {proc_stats['memory_mb']:.0f}MB | "
145 f"Threads: {proc_stats['threads']}"
146 )
148 # Classification
149 if classification is not None:
150 class_label = classification.get("label", "unknown")
151 class_score = classification.get("score", 0.0)
152 labels.classification = f"Class: {class_label} ({class_score:.2f})"
154 return labels