using Microsoft.AspNetCore.Components; using MP.Core.DTO; using MP.Data; using MP.Data.DbModels.Utils; using MP.Data.DTO; using MP.Data.Services.IOC; using MP.Data.Services.Utils; namespace MP.IOC.Components.Pages { public partial class CallStats { #region Protected Methods protected override async Task OnInitializedAsync() { await ReloadData(); } #endregion Protected Methods #region Private Fields private bool beginAtZero = false; private List currData = new(); private string currDetail = ""; private string currHistId = ""; private string currPieId = ""; private StatInfoDto? currStatSel = null; private string currTitle = ""; private string currTsId = ""; private bool lineStacked = false; private List lineTitles = new List(); private int numDays = 5; private List ParetoDay = new(); private string scaleFormat = "linear"; private bool showLegend = true; private List tsData = new List(); private List tsDataDetail = new(); private List> TSDataMulti = new(); #endregion Private Fields #region Private Properties /// /// Genera colori sfondo 33% rosso / arancione / giallo /// /// private List bgColors { get => GetSemaforicColors(currData.Count, "0.3"); } private List bgColorsMLine { get => GetDistinctColors(tsDataDetail.Count, "0.3"); } private List DatiPareto { get => currData.OrderByDescending(x => x.Value).Select(x => x.Value).ToList(); } private List LabelPareto { get => currData.OrderByDescending(x => x.Value).Select(x => x.Label).ToList(); } private List LabelPlot { get => tsData.Select(r => $"{r.x:yyyy-MM-dd}").ToList(); } /// /// Genera colori sfondo 33% rosso / arancione / giallo /// /// private List lineColors { get => GetSemaforicColors(currData.Count, "1"); } private List lineColorsMLine { get => GetDistinctColors(tsDataDetail.Count, "1"); } [Inject] private IStatsAggrService SAggService { get; set; } = null!; [Inject] private IStatsDetailService SDetService { get; set; } = null!; [Inject] private IIocService IocService { get; set; } = null!; #endregion Private Properties #region Private Methods private string CheckSelect(string curKey) { return currStatSel != null && currStatSel.Title == curKey ? "active" : ""; } private async Task DoForceReload() { await SDetService.ResetCache(); await IocService.ClearFusionCache(); DoReset(); } private void DoReset() { currStatSel = null; currHistId = ""; currPieId = ""; currTsId = ""; currData = new(); } private void DoSelect(StatInfoDto currStat) { // salvo dati! currStatSel = currStat; lineStacked = currStat.Grouping != StatInfoDto.AggrLevel.Method; showLegend = currStat.Grouping == StatInfoDto.AggrLevel.Method; beginAtZero = currStat.Grouping != StatInfoDto.AggrLevel.Method; scaleFormat = currStat.Grouping == StatInfoDto.AggrLevel.Method ? "logarithmic" : "linear"; string reqKey = currStat.Title; currHistId = $"Bar_{reqKey}"; currPieId = $"Pie_{reqKey}"; currTsId = $"TS_{reqKey}"; currTitle = $"Pareto | {reqKey}"; currData = currStat.DataCollection; tsDataDetail = new(); } private List GetDistinctColors(int numRecords, string alpha) { List colors = new List(); if (numRecords <= 0) return colors; // Partiamo dal Blu (240°) invece che dal Rosso (0°) double startHue = 240.0; for (int i = 0; i < numRecords; i++) { // Distribuiamo la tonalità aggiungendo l'offset iniziale // L'operatore % 360 assicura di rimanere nel cerchio se superiamo il rosso double hue = (startHue + ((double)i * 360 / numRecords)) % 360; // Strategia di distinzione (attiva sempre, ma più efficace sopra i 5 colori) // Alterniamo i valori per i record pari/dispari string saturation = "70%"; string lightness = "50%"; if (numRecords > 5) { // Se i colori sono molti, alterniamo luminosità e saturazione // I record dispari saranno più chiari e saturi, i pari più scuri e tenui lightness = (i % 2 == 0) ? "45%" : "65%"; saturation = (i % 2 == 0) ? "80%" : "60%"; } colors.Add($"hsla({hue:0.##}, {saturation}, {lightness}, {alpha})"); } return colors; } private List GetSemaforicColors(int numRecords, string alpha) { List colors = new List(); if (numRecords <= 0) return colors; if (numRecords == 1) { colors.Add($"rgba(54, 235, 82, {alpha})"); return colors; } // Definiamo i punti chiave (R, G, B) (int r, int g, int b) green = (54, 235, 82); (int r, int g, int b) yellow = (255, 206, 86); (int r, int g, int b) red = (255, 99, 132); for (int i = 0; i < numRecords; i++) { // t va da 0.0 (primo record) a 1.0 (ultimo record) double t = (double)i / (numRecords - 1); int r, g, b; if (t < 0.5) { // Da Verde a Giallo (mappiamo 0->0.5 su 0->1) double localT = t * 2; r = (int)(green.r + (yellow.r - green.r) * localT); g = (int)(green.g + (yellow.g - green.g) * localT); b = (int)(green.b + (yellow.b - green.b) * localT); } else { // Da Giallo a Rosso (mappiamo 0.5->1 su 0->1) double localT = (t - 0.5) * 2; r = (int)(yellow.r + (red.r - yellow.r) * localT); g = (int)(yellow.g + (red.g - yellow.g) * localT); b = (int)(yellow.b + (red.b - yellow.b) * localT); } colors.Add($"rgba({r}, {g}, {b}, {alpha})"); } return colors; } private async Task OnDaysChangedAsync() { await ShowDetail(currDetail); } private async Task ReloadData() { ParetoDay = await SDetService.GetParetoStatsDayAsync(); //calcolo le statistiche aggregate e le aggiungo var machDay = await SAggService.GetParetoStatsDayAsync(0); foreach (var item in machDay) { ParetoDay.Add(new StatInfoDto() { Title = item.Key, DataCollection = item.Value.Where(x => x.Label != "ALL").ToList(), Grouping = StatInfoDto.AggrLevel.Machine, Type = item.Key.Contains("#") ? StatInfoDto.DataType.Count : StatInfoDto.DataType.AvgDuration }); } } /// /// abilita visualizzaione grafico dettagli x metodo indicato /// /// private async Task ShowDetail(string selDetail) { currDetail = selDetail; if (currStatSel != null) { DateTime adesso = DateTime.Now; bool isCount = currStatSel.Type == StatInfoDto.DataType.Count; bool grpMachine = currStatSel.Grouping == StatInfoDto.AggrLevel.Machine; switch (currStatSel.Grouping) { case StatInfoDto.AggrLevel.Service: case StatInfoDto.AggrLevel.Machine: // recupero statistiche aggregate 7 gg.. var rawAggrData = await SAggService.GetFiltAsync(adesso.AddDays(-numDays), adesso); // escludo gli "all", filtro x selezione (macchina o servizio)... if (grpMachine) { rawAggrData = rawAggrData .Where(x => x.MachineId == selDetail) //.OrderByDescending(x => x.RequestCount) .ToList(); } else { rawAggrData = rawAggrData .Where(x => !x.MachineId.Equals("all", StringComparison.InvariantCultureIgnoreCase) && x.Destination == selDetail) //.OrderByDescending(x => x.RequestCount) .ToList(); } // conversione con grouping tsData = rawAggrData.Select(r => new chartJsData.chartJsTSerie() { x = r.Hour, y = (double)r.AvgDuration }) .OrderBy(o => o.x) .ToList(); // ...secondo tipo richiesto (macchina/server e duration/count) tsDataDetail = SAggService.GetTimeSeriesData(rawAggrData, grpMachine, isCount); break; case StatInfoDto.AggrLevel.Method: // recupero dettaglio 7gg... List rawData = await SDetService.GetFiltAsync(adesso.AddDays(-numDays), adesso, "", selDetail); // conversione con grouping tsData = rawData.Select(r => new chartJsData.chartJsTSerie() { x = r.Hour, y = (double)r.AvgDuration }) .OrderBy(o => o.x) .ToList(); // ...secondo tipo richiesto (duration/count) tsDataDetail = SDetService.GetTimeSeriesData(rawData, isCount); break; case StatInfoDto.AggrLevel.None: default: break; } lineTitles = tsDataDetail .Select(x => x.SeriesName) .ToList(); TSDataMulti = tsDataDetail .Select(x => x.DataPoints) .ToList(); } } #endregion Private Methods } }