Files
mapo-core/MP.IOC/Components/Pages/CallStats.razor.cs
T
2026-05-12 07:19:24 +02:00

312 lines
11 KiB
C#

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<StatDataDTO> 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<string> lineTitles = new List<string>();
private int numDays = 5;
private List<StatInfoDto> ParetoDay = new();
private string scaleFormat = "linear";
private bool showLegend = true;
private List<chartJsData.chartJsTSerie> tsData = new List<chartJsData.chartJsTSerie>();
private List<ChartSeriesDto> tsDataDetail = new();
private List<List<chartJsData.chartJsTSerie>> TSDataMulti = new();
#endregion Private Fields
#region Private Properties
/// <summary>
/// Genera colori sfondo 33% rosso / arancione / giallo
/// </summary>
/// <returns></returns>
private List<string> bgColors
{
get => GetSemaforicColors(currData.Count, "0.3");
}
private List<string> bgColorsMLine
{
get => GetDistinctColors(tsDataDetail.Count, "0.3");
}
private List<double> DatiPareto
{
get => currData.OrderByDescending(x => x.Value).Select(x => x.Value).ToList();
}
private List<string> LabelPareto
{
get => currData.OrderByDescending(x => x.Value).Select(x => x.Label).ToList();
}
private List<string> LabelPlot
{
get => tsData.Select(r => $"{r.x:yyyy-MM-dd}").ToList();
}
/// <summary>
/// Genera colori sfondo 33% rosso / arancione / giallo
/// </summary>
/// <returns></returns>
private List<string> lineColors
{
get => GetSemaforicColors(currData.Count, "1");
}
private List<string> 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<string> GetDistinctColors(int numRecords, string alpha)
{
List<string> colors = new List<string>();
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<string> GetSemaforicColors(int numRecords, string alpha)
{
List<string> colors = new List<string>();
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
});
}
}
/// <summary>
/// abilita visualizzaione grafico dettagli x metodo indicato
/// </summary>
/// <param name="selDetail"></param>
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<StatsDetailModel> 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
}
}