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 BorderColors = new Dictionary(); private Dictionary DataseElapsed = new Dictionary(); private Dictionary DatasetCount = new Dictionary(); private Dictionary> DSBarCount = new Dictionary>(); private Dictionary> DSBarElapsed = new Dictionary>(); private Dictionary FillColors = new Dictionary(); private DateTime lastUpdate = DateTime.Now; private List ListX = new List(); private int numDay = 3; private int refrPeriod = 5; private List rtProcStats = new List(); 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 } }