# ResourceMonitor - Modulo di Monitoring Risorse Modulo Python standalone e riutilizzabile per il monitoring in tempo reale di CPU, RAM e Thread di un processo. Estratto dal progetto Target Simulator e reso completamente indipendente per essere integrato facilmente in altre applicazioni. ## 🎯 Caratteristiche - ✅ **Monitoring in background** tramite thread daemon - ✅ **Aggiornamenti periodici** configurabili (polling interval personalizzabile) - ✅ **Callback personalizzati** per gestire i dati come preferisci - ✅ **Integrazione Tkinter** con classe specializzata `TkinterResourceMonitor` - ✅ **Thread-safe** per uso in applicazioni multi-thread - ✅ **Gestione errori robusta** - non crasha mai l'applicazione - ✅ **Metriche accurate**: - CPU normalizzata per sistemi multi-core (0-100%) - Memoria USS (Unique Set Size) quando disponibile, fallback a RSS - Conteggio thread attivi - ✅ **Zero dipendenze obbligatorie** - graceful degradation se psutil non disponibile ## 📦 Installazione ### Dipendenze Il modulo funziona senza dipendenze obbligatorie, ma **richiede psutil** per il monitoring effettivo: ```powershell pip install psutil ``` ### Integrazione nel tuo progetto Hai due opzioni: **Opzione 1: Copia diretta del modulo** ```powershell # Copia il file nella tua applicazione cp target_simulator/utils/resource_monitor.py / ``` **Opzione 2: Import dal progetto Target Simulator** ```python from target_simulator.utils.resource_monitor import ResourceMonitor ``` ## 🚀 Quick Start ### Esempio 1: Console Semplice ```python from target_simulator.utils.resource_monitor import ResourceMonitor def print_stats(stats_string): print(f"\r{stats_string}", end="", flush=True) monitor = ResourceMonitor( update_callback=print_stats, poll_interval=1.0 # Aggiorna ogni secondo ) monitor.start() # ... la tua applicazione gira ... monitor.stop() # Quando hai finito ``` ### Esempio 2: Tkinter GUI ```python import tkinter as tk from target_simulator.utils.resource_monitor import TkinterResourceMonitor root = tk.Tk() # StringVar per visualizzare le statistiche status_var = tk.StringVar() tk.Label(root, textvariable=status_var, font=("Courier", 10)).pack() # Crea e avvia il monitor monitor = TkinterResourceMonitor( tk_widget=root, string_var=status_var, poll_interval=1.0 ) monitor.start() # Alla chiusura def on_closing(): monitor.stop() root.destroy() root.protocol("WM_DELETE_WINDOW", on_closing) root.mainloop() ``` ### Esempio 3: Lettura Sincrona (Senza Thread) ```python from target_simulator.utils.resource_monitor import ResourceMonitor # Callback dummy se non serve monitoring continuo monitor = ResourceMonitor(update_callback=lambda s: None) # Ottieni statistiche on-demand stats = monitor.get_current_stats() if stats: print(f"CPU: {stats['cpu_percent']:.1f}%") print(f"RAM: {stats['memory_mb']:.1f} MB ({stats['memory_percent']:.1f}%)") print(f"Thread: {stats['thread_count']}") ``` ## 📚 Documentazione API ### Classe `ResourceMonitor` Monitor delle risorse di sistema che gira in background. #### Constructor ```python ResourceMonitor( update_callback: Callable[[str], None], poll_interval: float = 1.0, process_pid: Optional[int] = None ) ``` **Parametri:** - `update_callback`: Funzione chiamata ad ogni aggiornamento con la stringa formattata delle statistiche - Esempio output: `"CPU 5% · MEM 120MB (2.5%) [USS] · Thr 12"` - `poll_interval`: Intervallo in secondi tra le letture (default: 1.0) - `process_pid`: PID del processo da monitorare (default: processo corrente) #### Metodi Principali ##### `start() -> bool` Avvia il monitoring in background. **Returns:** `True` se avviato con successo, `False` se psutil non disponibile o già in esecuzione ##### `stop() -> None` Ferma il monitoring. Thread-safe, può essere chiamato più volte. ##### `get_current_stats() -> Optional[Dict[str, Any]]` Ottiene statistiche correnti in modo sincrono (senza avviare il thread). **Returns:** Dizionario con chiavi: - `cpu_percent` (float): Percentuale CPU normalizzata (0-100) - `memory_mb` (float): Memoria in MiB - `memory_percent` (float): Percentuale memoria del sistema - `memory_type` (str): "USS" o "RSS" - `thread_count` (int): Numero di thread Oppure `None` se psutil non disponibile o errore. #### Proprietà - `is_running` (bool): True se il monitor è attivo - `poll_interval` (float): Intervallo di polling corrente ### Classe `TkinterResourceMonitor` Specializzazione di `ResourceMonitor` per GUI Tkinter con aggiornamento thread-safe automatico. #### Constructor ```python TkinterResourceMonitor( tk_widget, # Any widget with .after() method string_var, # tk.StringVar to update poll_interval: float = 1.0, process_pid: Optional[int] = None ) ``` **Parametri:** - `tk_widget`: Qualsiasi widget Tkinter (root, Frame, etc.) con metodo `.after()` - `string_var`: `tk.StringVar` da aggiornare automaticamente - Altri parametri come `ResourceMonitor` **Esempio:** ```python import tkinter as tk from target_simulator.utils.resource_monitor import TkinterResourceMonitor root = tk.Tk() stats_var = tk.StringVar() monitor = TkinterResourceMonitor(root, stats_var) monitor.start() ``` ### Funzioni Utility #### `is_psutil_available() -> bool` Verifica se psutil è disponibile nel sistema. ```python from target_simulator.utils.resource_monitor import is_psutil_available if is_psutil_available(): print("psutil OK") else: print("Installa psutil: pip install psutil") ``` ## 🧪 Testing & Esempi ### Esecuzione Test Standalone Il modulo può essere eseguito direttamente per un test rapido: ```powershell $env:PYTHONPATH='C:\src\____GitProjects\target_simulator' python -m target_simulator.utils.resource_monitor ``` Output esempio: ``` === Test ResourceMonitor === Monitoraggio risorse per 10 secondi... Premi Ctrl+C per interrompere CPU 8% · MEM 145MB (1.8%) [USS] · Thr 15 Monitor fermato. Statistiche finali: CPU: 8.2% Memoria: 145.3 MB (1.8%) Tipo memoria: USS Thread: 15 ``` ### Esempi Completi Esegui il file di esempi interattivo: ```powershell python target_simulator/utils/examples/resource_monitor_example.py ``` Il file contiene 5 esempi: 1. **Console semplice** - Stampa su terminale 2. **Lettura sincrona** - Lettura singola senza thread 3. **Callback personalizzato** - Analisi e logging custom 4. **Tkinter GUI** - Integrazione completa con finestra 5. **Monitoring altro processo** - Specifica PID esplicito ## 🔧 Integrazione nel Tuo Progetto ### Scenario 1: Applicazione Console ```python from target_simulator.utils.resource_monitor import ResourceMonitor import logging logger = logging.getLogger(__name__) def log_resources(stats: str): logger.info(f"Resources: {stats}") # Avvia monitoring all'inizio dell'app monitor = ResourceMonitor(log_resources, poll_interval=5.0) monitor.start() # ... la tua applicazione ... # Cleanup alla fine monitor.stop() ``` ### Scenario 2: Web Server (Flask/FastAPI) ```python from target_simulator.utils.resource_monitor import ResourceMonitor from flask import Flask app = Flask(__name__) current_stats = {"data": "Not started"} def update_stats(stats: str): current_stats["data"] = stats monitor = ResourceMonitor(update_stats) monitor.start() @app.route('/health') def health(): return {"status": "ok", "resources": current_stats["data"]} if __name__ == '__main__': try: app.run() finally: monitor.stop() ``` ### Scenario 3: GUI Tkinter (Application Completa) ```python import tkinter as tk from tkinter import ttk from target_simulator.utils.resource_monitor import TkinterResourceMonitor class MyApplication(tk.Tk): def __init__(self): super().__init__() # UI self.title("My App") self.stats_var = tk.StringVar(value="Inizializzazione...") # Status bar con resource monitor status_bar = ttk.Frame(self, relief=tk.SUNKEN) status_bar.pack(side=tk.BOTTOM, fill=tk.X) ttk.Label(status_bar, textvariable=self.stats_var).pack( side=tk.RIGHT, padx=5 ) # Avvia monitor self.monitor = TkinterResourceMonitor( tk_widget=self, string_var=self.stats_var, poll_interval=1.0 ) self.monitor.start() self.protocol("WM_DELETE_WINDOW", self.on_closing) def on_closing(self): self.monitor.stop() self.destroy() if __name__ == '__main__': app = MyApplication() app.mainloop() ``` ## ⚙️ Dettagli Tecnici ### Normalizzazione CPU Multi-Core Su sistemi multi-core, `psutil.Process.cpu_percent()` può ritornare valori >100% perché misura l'utilizzo totale su tutti i core. Il `ResourceMonitor` normalizza automaticamente questo valore dividendo per il numero di CPU logiche, rendendo il risultato comparabile con Task Manager (scala 0-100%). ```python cpu_proc = proc.cpu_percent(None) # Può essere >100% ncpu = psutil.cpu_count(logical=True) or 1 cpu = cpu_proc / ncpu # Normalizzato 0-100% ``` ### Memoria: USS vs RSS Il monitor preferisce **USS (Unique Set Size)** quando disponibile: - **USS**: Memoria unica del processo (più accurata, esclude shared memory) - **RSS**: Resident Set Size (fallback, include shared memory) USS è più vicino al "Private Working Set" mostrato da Task Manager. ### Thread Safety Il monitoring avviene in un thread daemon separato. Gli aggiornamenti UI (Tkinter) sono schedulati sul main thread usando `widget.after(0, ...)` per garantire thread-safety. ### Gestione Errori Il modulo è progettato per **mai** crashare l'applicazione host: - Se psutil non disponibile → graceful degradation - Errori nel callback → ignorati, monitoring continua - Widget Tkinter distrutto → errori soppressi - Processo terminato → thread si ferma automaticamente ## 🐛 Troubleshooting ### "psutil non disponibile" ```powershell # Installa psutil pip install psutil # Verifica installazione python -c "import psutil; print(psutil.__version__)" ``` ### Valori CPU strani (molto alti o molto bassi) La prima lettura CPU può essere imprecisa. Il monitor fa un "priming" iniziale (`proc.cpu_percent(None)`) per risolvere questo problema. ### Memory leak con Tkinter Assicurati di chiamare sempre `monitor.stop()` prima di chiudere l'applicazione: ```python def on_closing(): monitor.stop() # IMPORTANTE! root.destroy() root.protocol("WM_DELETE_WINDOW", on_closing) ``` ### Thread non si ferma Il thread è daemon, quindi termina automaticamente quando l'app termina. `stop()` ferma il polling ma se serve attendere la terminazione: ```python monitor.stop() if monitor._thread: monitor._thread.join(timeout=2.0) ``` ## 📄 Licenza Stesso del progetto parent (Target Simulator). ## 🤝 Contributi Questo modulo è estratto da un progetto più grande. Per bug o miglioramenti, aggiorna il file originale in `target_simulator/utils/resource_monitor.py`. ## 📞 Supporto Per domande o problemi: 1. Controlla gli esempi in `examples/resource_monitor_example.py` 2. Verifica che psutil sia installato correttamente 3. Prova il test standalone del modulo --- **Versione:** 1.0 **Estratto da:** Target Simulator Project **Data:** Novembre 2025