| """ |
| 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 pandas as pd |
| 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: |
| empty_cpu_df = pd.DataFrame({'time': [], 'CPU Usage (%)': []}) |
| self.components["cpu_plot"] = gr.LinePlot( |
| empty_cpu_df, |
| show_label=False, |
| height=400, |
| x="time", |
| y=["CPU Usage (%)"], |
| y_title="Value", |
| title="CPU Usage and Temperature" |
| ) |
| |
| with gr.Tab(label="Memory Usage") as memory_tab: |
| empty_memory_df = pd.DataFrame({'time': [], 'Memory Usage (%)': [], 'Memory Used (GB)': [], 'Memory Available (GB)': []}) |
| |
| self.components["memory_plot"] = gr.LinePlot( |
| empty_memory_df, |
| show_label=False, |
| height=400, |
| x="time", |
| y=["Memory Usage (%)", "Memory Used (GB)", "Memory Available (GB)"], |
| y_title="Value", |
| title="Memory Usage" |
| ) |
| |
| |
| 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_df = self.app.monitoring.get_cpu_data() |
| memory_plot_df = self.app.monitoring.get_memory_data() |
| |
| return ( |
| system_info_html, |
| cpu_info_html, |
| memory_info_html, |
| storage_info_html, |
| metrics_html, |
| cpu_plot_df, |
| memory_plot_df, |
| |
| ) |
| |
| 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) |