|
""" |
|
System monitoring tab for Video Model Studio UI. |
|
Displays system metrics like CPU, memory usage, and temperatures. |
|
""" |
|
|
|
import gradio as gr |
|
import time |
|
import logging |
|
from pathlib import Path |
|
import os |
|
import psutil |
|
from typing import Dict, Any, List, Optional, Tuple |
|
from datetime import datetime, timedelta |
|
|
|
from vms.utils.base_tab import BaseTab |
|
from vms.config import STORAGE_PATH |
|
from vms.ui.monitoring.utils import get_folder_size, human_readable_size |
|
|
|
logger = logging.getLogger(__name__) |
|
logger.setLevel(logging.INFO) |
|
|
|
class GeneralTab(BaseTab): |
|
"""Monitor tab for general system resource monitoring""" |
|
|
|
def __init__(self, app_state): |
|
super().__init__(app_state) |
|
self.id = "General_tab" |
|
self.title = "General stats" |
|
self.refresh_interval = 8 |
|
|
|
def create(self, parent=None) -> gr.TabItem: |
|
"""Create the Monitor tab UI components""" |
|
with gr.TabItem(self.title, id=self.id) as tab: |
|
with gr.Row(): |
|
gr.Markdown("## System Monitoring") |
|
|
|
|
|
with gr.Row(): |
|
with gr.Column(scale=1): |
|
self.components["current_metrics"] = gr.Markdown("Loading current metrics...") |
|
|
|
|
|
with gr.Tabs() as metrics_tabs: |
|
with gr.Tab(label="CPU Usage") as cpu_tab: |
|
self.components["cpu_plot"] = gr.Plot() |
|
|
|
with gr.Tab(label="Memory Usage") as memory_tab: |
|
self.components["memory_plot"] = gr.Plot() |
|
|
|
|
|
|
|
|
|
|
|
with gr.Row(): |
|
with gr.Column(scale=1): |
|
gr.Markdown("### System Information") |
|
self.components["system_info"] = gr.Markdown("Loading system information...") |
|
|
|
with gr.Column(scale=1): |
|
gr.Markdown("### CPU Information") |
|
self.components["cpu_info"] = gr.Markdown("Loading CPU information...") |
|
|
|
with gr.Row(): |
|
with gr.Column(scale=1): |
|
gr.Markdown("### Memory Information") |
|
self.components["memory_info"] = gr.Markdown("Loading memory information...") |
|
|
|
with gr.Column(scale=1): |
|
gr.Markdown("### Storage Information") |
|
self.components["storage_info"] = gr.Markdown("Loading storage information...") |
|
|
|
|
|
with gr.Row(): |
|
self.components["auto_refresh"] = gr.Checkbox( |
|
label=f"Auto refresh (every {self.refresh_interval} seconds)", |
|
value=True, |
|
info="Automatically refresh system metrics" |
|
) |
|
self.components["refresh_btn"] = gr.Button("Refresh Now") |
|
|
|
|
|
self.components["refresh_timer"] = gr.Timer( |
|
value=self.refresh_interval |
|
) |
|
|
|
return tab |
|
|
|
def connect_events(self) -> None: |
|
"""Connect event handlers to UI components""" |
|
|
|
self.components["refresh_btn"].click( |
|
fn=self.refresh_all, |
|
outputs=[ |
|
self.components["system_info"], |
|
self.components["cpu_info"], |
|
self.components["memory_info"], |
|
self.components["storage_info"], |
|
self.components["current_metrics"], |
|
self.components["cpu_plot"], |
|
self.components["memory_plot"], |
|
|
|
] |
|
) |
|
|
|
|
|
self.components["refresh_timer"].tick( |
|
fn=self.conditional_refresh, |
|
inputs=[self.components["auto_refresh"]], |
|
outputs=[ |
|
self.components["system_info"], |
|
self.components["cpu_info"], |
|
self.components["memory_info"], |
|
self.components["storage_info"], |
|
self.components["current_metrics"], |
|
self.components["cpu_plot"], |
|
self.components["memory_plot"], |
|
|
|
] |
|
) |
|
|
|
def on_enter(self): |
|
"""Called when the tab is selected""" |
|
|
|
if not self.app.monitoring.is_running: |
|
self.app.monitoring.start_monitoring() |
|
|
|
|
|
return self.refresh_all() |
|
|
|
def conditional_refresh(self, auto_refresh: bool) -> Tuple: |
|
"""Only refresh if auto-refresh is enabled |
|
|
|
Args: |
|
auto_refresh: Whether auto-refresh is enabled |
|
|
|
Returns: |
|
Updated components or unchanged components |
|
""" |
|
if auto_refresh: |
|
return self.refresh_all() |
|
|
|
|
|
return ( |
|
self.components["system_info"].value, |
|
self.components["cpu_info"].value, |
|
self.components["memory_info"].value, |
|
self.components["storage_info"].value, |
|
self.components["current_metrics"].value, |
|
self.components["cpu_plot"].value, |
|
self.components["memory_plot"].value, |
|
|
|
) |
|
|
|
def refresh_all(self) -> Tuple: |
|
"""Refresh all monitoring components |
|
|
|
Returns: |
|
Updated values for all components |
|
""" |
|
try: |
|
|
|
system_info = self.app.monitoring.get_system_info() |
|
|
|
|
|
system_info_html = self.format_system_info(system_info) |
|
cpu_info_html = self.format_cpu_info(system_info) |
|
memory_info_html = self.format_memory_info(system_info) |
|
storage_info_html = self.format_storage_info() |
|
|
|
|
|
|
|
metrics_html = "" |
|
|
|
|
|
cpu_plot = self.app.monitoring.generate_cpu_plot() |
|
memory_plot = self.app.monitoring.generate_memory_plot() |
|
|
|
|
|
return ( |
|
system_info_html, |
|
cpu_info_html, |
|
memory_info_html, |
|
storage_info_html, |
|
metrics_html, |
|
cpu_plot, |
|
memory_plot, |
|
|
|
) |
|
|
|
except Exception as e: |
|
logger.error(f"Error refreshing monitoring data: {str(e)}", exc_info=True) |
|
error_msg = f"Error retrieving data: {str(e)}" |
|
return ( |
|
error_msg, |
|
error_msg, |
|
error_msg, |
|
error_msg, |
|
error_msg, |
|
None, |
|
None, |
|
|
|
) |
|
|
|
def format_system_info(self, system_info: Dict[str, Any]) -> str: |
|
"""Format system information as HTML |
|
|
|
Args: |
|
system_info: System information dictionary |
|
|
|
Returns: |
|
Formatted HTML string |
|
""" |
|
sys = system_info['system'] |
|
uptime_str = self.format_uptime(sys['uptime']) |
|
|
|
html = f""" |
|
**System:** {sys['system']} ({sys['platform']}) |
|
**Hostname:** {sys['hostname']} |
|
**Uptime:** {uptime_str} |
|
**Python Version:** {sys['python_version']} |
|
""" |
|
return html |
|
|
|
def format_cpu_info(self, system_info: Dict[str, Any]) -> str: |
|
"""Format CPU information as HTML |
|
|
|
Args: |
|
system_info: System information dictionary |
|
|
|
Returns: |
|
Formatted HTML string |
|
""" |
|
cpu = system_info['cpu'] |
|
sys = system_info['system'] |
|
|
|
|
|
cpu_freq = "N/A" |
|
if cpu['current_frequency']: |
|
cpu_freq = f"{cpu['current_frequency'] / 1000:.2f} GHz" |
|
|
|
html = f""" |
|
**Processor:** {sys['processor'] or cpu['architecture']} |
|
**Physical Cores:** {cpu['cores_physical']} |
|
**Logical Cores:** {cpu['cores_logical']} |
|
**Current Frequency:** {cpu_freq} |
|
""" |
|
return html |
|
|
|
def format_memory_info(self, system_info: Dict[str, Any]) -> str: |
|
"""Format memory information as HTML |
|
|
|
Args: |
|
system_info: System information dictionary |
|
|
|
Returns: |
|
Formatted HTML string |
|
""" |
|
memory = system_info['memory'] |
|
|
|
html = f""" |
|
**Total Memory:** {memory['total']:.2f} GB |
|
**Available Memory:** {memory['available']:.2f} GB |
|
**Used Memory:** {memory['used']:.2f} GB |
|
**Usage:** {memory['percent']}% |
|
""" |
|
return html |
|
|
|
def format_storage_info(self) -> str: |
|
"""Format storage information as HTML, focused on STORAGE_PATH |
|
|
|
Returns: |
|
Formatted HTML string |
|
""" |
|
try: |
|
|
|
total_size = get_folder_size(STORAGE_PATH) |
|
total_size_readable = human_readable_size(total_size) |
|
|
|
html = f"**Total Storage Used:** {total_size_readable}\n\n" |
|
|
|
|
|
html += "**Subfolder Sizes:**\n\n" |
|
|
|
for subfolder in sorted(STORAGE_PATH.iterdir()): |
|
if subfolder.is_dir(): |
|
folder_size = get_folder_size(subfolder) |
|
folder_size_readable = human_readable_size(folder_size) |
|
percentage = (folder_size / total_size * 100) if total_size > 0 else 0 |
|
|
|
folder_name = subfolder.name |
|
html += f"* **{folder_name}**: {folder_size_readable} ({percentage:.1f}%)\n" |
|
|
|
return html |
|
|
|
except Exception as e: |
|
logger.error(f"Error getting folder sizes: {str(e)}", exc_info=True) |
|
return f"Error getting folder sizes: {str(e)}" |
|
|
|
def format_current_metrics(self, metrics: Dict[str, Any]) -> str: |
|
"""Format current metrics as HTML |
|
|
|
Args: |
|
metrics: Current metrics dictionary |
|
|
|
Returns: |
|
Formatted HTML string |
|
""" |
|
timestamp = metrics['timestamp'].strftime('%Y-%m-%d %H:%M:%S') |
|
|
|
|
|
cpu_style = "color: green;" |
|
if metrics['cpu_percent'] > 90: |
|
cpu_style = "color: red; font-weight: bold;" |
|
elif metrics['cpu_percent'] > 70: |
|
cpu_style = "color: orange;" |
|
|
|
|
|
mem_style = "color: green;" |
|
if metrics['memory_percent'] > 90: |
|
mem_style = "color: red; font-weight: bold;" |
|
elif metrics['memory_percent'] > 70: |
|
mem_style = "color: orange;" |
|
|
|
|
|
temp_html = "" |
|
if metrics['cpu_temp'] is not None: |
|
temp_style = "color: green;" |
|
if metrics['cpu_temp'] > 80: |
|
temp_style = "color: red; font-weight: bold;" |
|
elif metrics['cpu_temp'] > 70: |
|
temp_style = "color: orange;" |
|
|
|
temp_html = f""" |
|
**CPU Temperature:** <span style="{temp_style}">{metrics['cpu_temp']:.1f}°C</span> |
|
""" |
|
|
|
html = f""" |
|
**CPU Usage:** <span style="{cpu_style}">{metrics['cpu_percent']:.1f}%</span> |
|
**Memory Usage:** <span style="{mem_style}">{metrics['memory_percent']:.1f}% ({metrics['memory_used']:.2f}/{metrics['memory_available']:.2f} GB)</span> |
|
{temp_html} |
|
""" |
|
|
|
|
|
html += "\n" |
|
|
|
per_cpu = metrics['per_cpu_percent'] |
|
cols = 4 |
|
|
|
|
|
for i in range(0, len(per_cpu), cols): |
|
row_cores = per_cpu[i:i+cols] |
|
row_html = "" |
|
|
|
for j, usage in enumerate(row_cores): |
|
core_id = i + j |
|
core_style = "color: green;" |
|
if usage > 90: |
|
core_style = "color: red; font-weight: bold;" |
|
elif usage > 70: |
|
core_style = "color: orange;" |
|
|
|
row_html += f"**Core {core_id}:** <span style='{core_style}'>{usage:.1f}%</span> " |
|
|
|
html += row_html + "\n" |
|
|
|
return html |
|
|
|
def format_uptime(self, seconds: float) -> str: |
|
"""Format uptime in seconds to a human-readable string |
|
|
|
Args: |
|
seconds: Uptime in seconds |
|
|
|
Returns: |
|
Formatted uptime string |
|
""" |
|
days = int(seconds // 86400) |
|
seconds %= 86400 |
|
hours = int(seconds // 3600) |
|
seconds %= 3600 |
|
minutes = int(seconds // 60) |
|
|
|
parts = [] |
|
if days > 0: |
|
parts.append(f"{days} day{'s' if days != 1 else ''}") |
|
if hours > 0 or days > 0: |
|
parts.append(f"{hours} hour{'s' if hours != 1 else ''}") |
|
parts.append(f"{minutes} minute{'s' if minutes != 1 else ''}") |
|
|
|
return ", ".join(parts) |