Files
2025-11-29 12:45:52 +01:00

229 lines
8.4 KiB
C#

/// <summary>
/// StatsCollector fornisce statistiche in tempo reale sulle prestazioni e sulle attività dell'applicazione.
///
/// Questa classe raccoglie e traccia i metri di esecuzione (ad esempio, durata, frequenza, ultima chiamata) per diverse funzioni.
/// Viene utilizzata per generare report dettagliati sul comportamento dell'applicazione, inclusi il tempo di avvio, l'uptime e le prestazioni delle funzioni.
///
/// Caratteristiche principali:
/// - Traccia la durata e la frequenza delle esecuzioni delle funzioni (ad esempio, controlli di servizio, scansione dati).
/// - Calcola l'uptime e il timestamp di avvio.
/// - Formatta i tempi in unità leggibili (ns, ms, sec, min).
/// - Utilizza dizionari concorrenti per garantire l'accesso sicuro in ambienti multithread.
/// - Esprime un dizionario di statistiche utilizzabile per report o logging.
/// </summary>
using System.Collections.Concurrent;
namespace IOB_MAN.Core
{
/// <summary>
/// Oggetto responsabile della raccolta e del reporting delle statistiche a livello di applicazione.
/// Utilizzato per monitorare i tempi di esecuzione, la frequenza e il comportamento in tempo reale delle funzioni.
/// </summary>
public class StatsCollector
{
#region Public Properties
/// <summary>
/// Calcola e restituisce l'uptime attuale dell'applicazione.
///
/// Formato:
/// - Se superiore a 1 giorno: "GG d HHh MMm"
/// - Altrimenti: "HHh MMm SS s"
///
/// Esempio: "2d 3h 45m" o "1h 23m 15 s"
/// </summary>
public static string UptimeCurr
{
get
{
string answ = "ND";
var upT = DateTime.Now.Subtract(StartTime);
// Formattazione in base alla durata
if (upT.TotalDays > 1)
{
answ = $"{upT.Days:00} d {upT.Hours:00}h {upT.Minutes:00}m";
}
else
{
answ = $"{upT.Hours:00}h {upT.Minutes:00}m {upT.Seconds:00} s";
}
return answ;
}
}
#endregion Public Properties
#region Public Methods
/// <summary>
/// Restituisce un dizionario contenente tutte le statistiche raccolte per ogni funzione.
///
/// Ogni voce include:
/// - Il numero di esecuzioni.
/// - La durata media dell'esecuzione (formattata).
/// - Il timestamp dell'ultima esecuzione.
///
/// Esempio:
/// {
/// "ScanIOB": "15 x 2.345 sec",
/// "ScanIOBLast": "2025-04-05 10:30:22",
/// "Startup": "2025-04-05 09:00:00",
/// "Uptime": "2d 3h 45m"
/// }
/// </summary>
/// <returns>Un dizionario delle statistiche a livello di funzione.</returns>
public static Dictionary<string, string> CurrentInfo()
{
Dictionary<string, string> result = new Dictionary<string, string>();
// Itera su tutte le funzioni tracciate
foreach (var item in Counter)
{
string executionCount = $"{Counter[item.Key]} x {formElaps((Duration[item.Key]) / Counter[item.Key])}";
result.Add(item.Key, executionCount);
result.Add($"{item.Key}Last", $"{LastCall[item.Key]:yyyy-MM-dd HH:mm:ss}");
}
// Aggiunge metadati di avvio e uptime
result.Add("Startup", $"{StartTime:yyyy-MM-dd HH:mm:ss}");
result.Add("Uptime", UptimeCurr);
return result;
}
/// <summary>
/// Resetta tutte le statistiche interne allo stato iniziale.
///
/// Azioni:
/// - Imposta il nuovo timestamp di avvio al momento attuale.
/// - Pulisce tutti i contatori, i tempi totali e i timestamp degli ultimi chiamati.
///
/// Caso d'uso: chiamato durante un riavvio dell'applicazione, un ricarico della configurazione o un reset per debug.
/// </summary>
public static void ResetData()
{
StartTime = DateTime.Now;
Counter = new ConcurrentDictionary<string, long>();
Duration = new ConcurrentDictionary<string, TimeSpan>();
LastCall = new ConcurrentDictionary<string, DateTime>();
}
/// <summary>
/// Aggiorna le statistiche di esecuzione per una funzione specifica.
///
/// Parametri:
/// - functionName: Nome della funzione (es. "DoScan", "CheckMemory").
/// - elaps: Durata dell'esecuzione della funzione (es. 123.45 ms).
///
/// Comportamento:
/// - Accumula la durata totale e il contatore di esecuzioni.
/// - Salva il timestamp attuale come ultima chiamata.
///
/// Sicurezza multithread:
/// - Utilizza dizionari concorrenti per garantire l'accesso sicuro da più thread.
/// </summary>
/// <param name="functionName">Il nome della funzione in esecuzione.</param>
/// <param name="elaps">Il tempo trascorso durante l'esecuzione della funzione.</param>
public static void UpdateStat(string functionName, TimeSpan elaps)
{
// Accumula la durata totale
if (!Duration.ContainsKey(functionName))
{
Duration.TryAdd(functionName, elaps);
}
else
{
Duration[functionName] += elaps;
}
// Incrementa il contatore di esecuzioni
if (!Counter.ContainsKey(functionName))
{
Counter.TryAdd(functionName, 1);
}
else
{
Counter[functionName]++;
}
// Salva il timestamp dell'ultima esecuzione
DateTime adesso = DateTime.Now;
if (!LastCall.ContainsKey(functionName))
{
LastCall.TryAdd(functionName, adesso);
}
else
{
LastCall[functionName] = adesso;
}
}
#endregion Public Methods
#region Private Fields
/// <summary>
/// Dizionario thread-safe che traccia quante volte è stata eseguita ogni funzione.
/// </summary>
private static ConcurrentDictionary<string, long> Counter = new ConcurrentDictionary<string, long>();
/// <summary>
/// Dizionario thread-safe che contiene la durata totale (somma) delle esecuzioni di ogni funzione.
/// </summary>
private static ConcurrentDictionary<string, TimeSpan> Duration = new ConcurrentDictionary<string, TimeSpan>();
/// <summary>
/// Dizionario thread-safe che memorizza il timestamp dell'ultima esecuzione di ogni funzione.
/// </summary>
private static ConcurrentDictionary<string, DateTime> LastCall = new ConcurrentDictionary<string, DateTime>();
/// <summary>
/// Timestamp dell'avvio dell'applicazione.
/// Utilizzato per calcolare l'uptime e il tempo di avvio.
/// </summary>
private static DateTime StartTime = DateTime.Now;
#endregion Private Fields
#region Private Methods
/// <summary>
/// Formatta un intervallo di tempo in una stringa leggibile in base alla grandezza.
///
/// Logica di formattazione:
/// - Inferiore a 1 ms → "X.XXX ns"
/// - Da 1 ms a 1 secondo → "X.X ms"
/// - Da 1 secondo a 300 secondi → "X.X sec"
/// - Superiore a 300 secondi → "X.X min"
///
/// Esempio: 123.456 ms → "123.456 ms", 1.234 sec → "1.234 sec", 360 sec → "6.0 min"
/// </summary>
/// <param name="ts">L'intervallo di tempo da formattare.</param>
/// <returns>Stringa formattata del tempo.</returns>
private static string formElaps(TimeSpan ts)
{
string answ = "";
if (ts.Milliseconds < 1)
{
answ = $"{ts.TotalMilliseconds:N3} ns";
}
else if (ts.TotalSeconds < 1)
{
answ = $"{ts.TotalMilliseconds:N1} ms";
}
else if (ts.TotalSeconds < 300)
{
answ = $"{ts.TotalSeconds:N1} sec";
}
else
{
answ = $"{ts.TotalMinutes:N1} min";
}
return answ;
}
#endregion Private Methods
}
}