Files
lux/Lux.UI/Components/Compo/Stats/RealTimeStats.razor.cs
2026-03-25 09:05:06 +01:00

228 lines
7.2 KiB
C#

using EgwCoreLib.Lux.Core.Stats;
namespace Lux.UI.Components.Compo.Stats
{
public partial class RealTimeStats : IDisposable
{
#region Public Methods
public void Dispose()
{
_cts?.Cancel();
_cts?.Dispose();
}
#endregion Public Methods
#region Protected Methods
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if (firstRender)
{
_cts = new CancellationTokenSource();
_ = UpdateLoop(_cts.Token);
}
}
protected override Task OnInitializedAsync()
{
return ReloadStatsRT();
}
#endregion Protected Methods
#region Private Fields
private CancellationTokenSource? _cts;
private Dictionary<string, string> BorderColors = new Dictionary<string, string>();
private Dictionary<string, double> DataseElapsed = new Dictionary<string, double>();
private Dictionary<string, double> DatasetCount = new Dictionary<string, double>();
private Dictionary<string, List<decimal>> DSBarCount = new Dictionary<string, List<decimal>>();
private Dictionary<string, List<decimal>> DSBarElapsed = new Dictionary<string, List<decimal>>();
private Dictionary<string, string> FillColors = new Dictionary<string, string>();
private DateTime lastUpdate = DateTime.Now;
private List<string> ListX = new List<string>();
private int numDay = 3;
private int refrPeriod = 5;
private List<RealtimeProcDto> rtProcStats = new List<RealtimeProcDto>();
private Enums.RuidGroupMode SelGroup = Enums.RuidGroupMode.Hour;
private Enums.RuidTagMode SelMode = Enums.RuidTagMode.Envir;
private StatsRealtimeDto? stats;
#endregion Private Fields
#region Private Properties
[Inject]
private ICalcRuidService Calc { get; set; } = null!;
[Inject]
private IJSRuntime JSRuntime { get; set; } = null!;
private int NumDays
{
get => numDay;
set
{
if (numDay != value)
{
numDay = value > 1 ? value : 1;
_ = ReloadStatsRT();
}
}
}
private int RefPeriod
{
get => refrPeriod;
set
{
if (refrPeriod != value)
{
refrPeriod = value > 1 ? value : 1;
_ = ReloadStatsRT();
}
}
}
#endregion Private Properties
#region Private Methods
private async Task OnGroupChanged(Enums.RuidGroupMode value)
{
if (SelGroup != value)
{
SelGroup = value;
await ReloadStatsRT();
}
}
private async Task OnTagChanged(Enums.RuidTagMode value)
{
if (SelMode != value)
{
SelMode = value;
await ReloadStatsRT();
}
}
private async Task ReloadStatsRT()
{
rtProcStats = await Calc.RealTimeDataStats(SelGroup, SelMode, DateTime.Today.AddDays(-numDay));
// calcolo i valori da mostrare...
if (rtProcStats != null && rtProcStats.Count > 0)
{
// Estrazione dei tag unici dalla lista dei dati
var uniqueTags = rtProcStats
.Select(x => x.TagClass)
.Distinct()
.OrderBy(x => x)
.ToList();
// Generazione Palette (ritorna hsla con alpha 0.6)
var palette = ColorHelper.GeneratePalette(uniqueTags.Count, 0.6);
for (int i = 0; i < uniqueTags.Count; i++)
{
string tag = uniqueTags[i];
string bgColor = palette[i]; // hsla(..., 0.6)
// Questo ora funziona anche su HSL!
string borderColor = ColorHelper.ToOpaque(bgColor); // hsla(..., 1.0)
FillColors[tag] = bgColor;
BorderColors[tag] = borderColor;
}
// generazione dei restanti dataset
DatasetCount = rtProcStats
.GroupBy(p => p.TagClass)
.ToDictionary(
g => g.Key,
g => (double)g.Sum(p => p.EventCount)
);
DataseElapsed = rtProcStats
.GroupBy(p => p.TagClass)
.ToDictionary(
g => g.Key,
g => g.Sum(p => p.Elapsed)
);
// 1. Definiamo le etichette dell'asse X (es. le ultime 24 ore o quelle presenti nei dati)
ListX = rtProcStats
.Select(p => p.Hour.ToString("yyyy-MM-dd HH:mm"))
.Distinct()
.OrderBy(t => t)
.ToList();
// 2. Identifichiamo tutti i TagClass unici (saranno i nostri dataset)
var allTags = rtProcStats.Select(p => p.TagClass).Distinct().ToList();
// 3. Costruiamo il DSBarCount: per ogni Tag, creiamo una lista di valori allineata a ListX
DSBarCount = allTags.ToDictionary(
tag => tag,
tag => ListX.Select(hour =>
{
// Cerchiamo il valore per quel tag in quell'ora specifica
return (decimal)(rtProcStats
.FirstOrDefault(p => p.TagClass == tag && p.Hour.ToString("yyyy-MM-dd HH:mm") == hour)
?.EventCount ?? 0); // Se non esiste, mettiamo 0
}).ToList()
);
// 4. Costruiamo il DSBarElapsed: per ogni Tag, creiamo una lista di valori allineata a ListX
DSBarElapsed = allTags.ToDictionary(
tag => tag,
tag => ListX.Select(hour =>
{
// Cerchiamo il valore per quel tag in quell'ora specifica
return (decimal)(rtProcStats
.FirstOrDefault(p => p.TagClass == tag && p.Hour.ToString("yyyy-MM-dd HH:mm") == hour)
?.Elapsed ?? 0); // Se non esiste, mettiamo 0
}).ToList()
);
lastUpdate = DateTime.Now;
// forzatura update
await InvokeAsync(StateHasChanged);
}
}
private async Task UpdateLoop(CancellationToken token)
{
try
{
while (!token.IsCancellationRequested)
{
await ReloadStatsRT();
// attesa RefPeriod secondi, rispettando il token
await Task.Delay(TimeSpan.FromSeconds(RefPeriod), token);
}
}
catch (OperationCanceledException)
{
// loop fermato
}
}
#endregion Private Methods
}
}