Update display statistiche chiamata

This commit is contained in:
Samuele Locatelli
2026-04-10 09:55:53 +02:00
parent f612eb7cdd
commit efd7fab27c
11 changed files with 121 additions and 116 deletions
+4 -4
View File
@@ -6,7 +6,6 @@
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
@* <base href="/" /> *@
<link rel="stylesheet" href="lib/bootstrap/css/bootstrap.min.css" />
<link rel="stylesheet" href="bootstrap/bootstrap.min.css" />
<link rel="stylesheet" href="lib/font-awesome/css/all.min.css" />
<link rel="stylesheet" href="app.css" />
<link rel="stylesheet" href="MP.IOC.styles.css" />
@@ -20,9 +19,10 @@
<Routes @rendermode="@(new InteractiveServerRenderMode(prerender: false))" />
<script src="_framework/blazor.web.js"></script>
<script type="text/javascript" src="lib/bootstrap/js/bootstrap.min.js"></script>
<script src="lib/Chart.js/chart.js"></script>
<script src="lib/luxon/luxon.js"></script>
<script src="lib/chartjs-adapter-luxon/chartjs-adapter-luxon.js"></script>
@* <script src="lib/Chart.js/chart.js"></script> *@
<script src="lib/Chart.js/chart.umd.js"></script>
@* <script src="lib/luxon/luxon.js"></script>
<script src="lib/chartjs-adapter-luxon/chartjs-adapter-luxon.umd.js"></script> *@
<script src="lib/chartBoot.js"></script>
</body>
+21 -11
View File
@@ -2,48 +2,58 @@
<div class="card shadow">
<div class="card-header">
<h3>Current Call Stats </h3>(24h)
<h3>Current Call Stats (24h)</h3>
</div>
<div class="card-body p-1">
<div class="row mb-2">
<div class="col-2">
<div class="col-3">
<ul class="list-group shadow">
<li class="list-group-item active d-flex justify-content-between align-items-start">
<b>Avail Data</b>
<button class="btn btn-sm btn-info" @onclick="() => DoReset()">
<i class="fa-solid fa-arrow-rotate-right"></i>
</button>
</li>
@foreach (var item in ParetoDay)
{
<li class="list-group-item d-flex justify-content-between align-items-start small py-1">
<span class="align-items-center">@item.Key</span>
<button class="btn btn-sm btn-info">
<button class="btn btn-sm btn-info" @onclick="() => DoSelect(item.Key)">
<i class="fa-solid fa-magnifying-glass"></i>
</button>
</li>
}
</ul>
</div>
<div class="col-7">
Pareto Chart
<div class="col-6">
@if (string.IsNullOrEmpty(@currSelect))
{
<div class="alert alert-info">selezionare la metrica</div>
}
else
{
<BarPlot Id="@currSelect" AspRatio="2" Data="@DatiPareto" Labels="@LabelPareto" Legenda="@currTitle" lineColor="@lineColors" backColor="@bgColors"></BarPlot>
}
</div>
<div class="col-3">
Pie chart
<PieChart Id="PieSelected" AspRatio="1" LegendPos="none" Data="@DatiPareto" Labels="@LabelPareto" lineColor="@lineColors" backColor="@bgColors" Title="@currTitle"></PieChart>
</div>
</div>
<div class="row mb-2">
<div class="col-2">
lista dettaglio (5/10 pag)
<div class="col-3">
<ParetoDetail Title="@currSelect" ParetoList="@currData"></ParetoDetail>
</div>
<div class="col-10">
<div class="col-9">
timeserie selezionata x num call/tempo
</div>
</div>
<div class="row small">
@* <div class="row small">
@foreach (var item in ParetoDay)
{
<div class="col-4 mb-2">
<ParetoDetail Title="@item.Key" ParetoList="@item.Value"></ParetoDetail>
</div>
}
</div>
</div> *@
</div>
</div>
@@ -19,6 +19,76 @@ namespace MP.IOC.Components.Pages
private Dictionary<string, List<StatDataDTO>> ParetoDay = new();
private void DoReset()
{
currSelect = "";
currData = new();
}
private void DoSelect(string reqKey)
{
if (ParetoDay.ContainsKey(reqKey))
{
currSelect = reqKey;
currTitle = $"Pareto | {reqKey}";
currData = ParetoDay[reqKey];
}
}
private string currSelect = "";
private string currTitle = "";
private List<StatDataDTO> currData = new();
private List<string> LabelPareto
{
get => currData.Select(x => x.Label).ToList();
}
private List<double> DatiPareto
{
get => currData.Select(x => x.Value).ToList();
}
/// <summary>
/// Genera colori sfondo 33% rosso / arancione / giallo
/// </summary>
/// <param name="numRecords"></param>
/// <param name="alpha"></param>
/// <returns></returns>
private List<string> semaphColors(int numRecords, string alpha)
{
List<string> answ = new List<string>();
// verde...
for (int i = 0; i < numRecords / 3; i++)
{
answ.Add($"rgba(54, 235, 82, {alpha})");
}
// arancione
for (int i = 0; i < numRecords / 3; i++)
{
answ.Add($"rgba(255, 206, 86, {alpha})");
}
while (answ.Count < numRecords)
{
answ.Add($"rgba(255, 99, 132, {alpha}");
}
return answ;
}
/// <summary>
/// Genera colori sfondo 33% rosso / arancione / giallo
/// </summary>
/// <returns></returns>
protected List<string> bgColors
{
get => semaphColors(currData.Count, "0.3");
}
/// <summary>
/// Genera colori sfondo 33% rosso / arancione / giallo
/// </summary>
/// <returns></returns>
private List<string> lineColors
{
get => semaphColors(currData.Count, "1");
}
#endregion Private Fields
#region Private Properties
+1 -1
View File
@@ -4,7 +4,7 @@
<TargetFramework>net8.0</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
<Version>6.16.2604.1008</Version>
<Version>6.16.2604.1009</Version>
</PropertyGroup>
<ItemGroup>
+1 -1
View File
@@ -1,6 +1,6 @@
<body>
<i>Modulo MP-IOC </i>
<h4>Versione: 6.16.2604.1008</h4>
<h4>Versione: 6.16.2604.1009</h4>
<br /> Note di rilascio:
<ul>
<li>
+1 -1
View File
@@ -1 +1 @@
6.16.2604.1008
6.16.2604.1009
+1 -1
View File
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<item>
<version>6.16.2604.1008</version>
<version>6.16.2604.1009</version>
<url>https://nexus.steamware.net/repository/SWS/MP-IOC/stable/LAST/MP.IOC.zip</url>
<changelog>https://nexus.steamware.net/repository/SWS/MP-IOC/stable/LAST/ChangeLog.html</changelog>
<mandatory>false</mandatory>
-94
View File
@@ -191,100 +191,6 @@ namespace MP.IOC.Services
}
}
}
#if false
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
var interval = _config.GetValue<int>("RouteMan:MetricCalcIntervalSeconds", 20);
while (!stoppingToken.IsCancellationRequested)
{
try
{
await Task.Delay(TimeSpan.FromSeconds(interval), stoppingToken);
var snapshot = _stats.Snapshot();
if (snapshot.Count == 0) continue;
var adesso = DateTime.Now;
var hourStart = new DateTime(adesso.Year, adesso.Month, adesso.Day, adesso.Hour, 0, 0);
var dayStart = new DateTime(adesso.Year, adesso.Month, adesso.Day, 0, 0, 0);
foreach (var kv in snapshot)
{
string dest = "IO";
string method = "NA";
// verifico se ho chiave completo (dest+method) o parziale
string rawKey = kv.Key;
if (rawKey.Contains("|"))
{
var splitVal = rawKey.Split("|");
dest = splitVal[0];
method = splitVal[1];
}
else
{
method = rawKey;
}
var stat = kv.Value;
var count = Interlocked.Read(ref stat.Count);
var totalMs = stat.TotalDuration.TotalMilliseconds;
var maxMs = stat.MaxDuration.TotalMilliseconds;
var minMs = (stat.MinDuration == TimeSpan.MaxValue) ? 0 : stat.MinDuration.TotalMilliseconds;
Log.Info($"Dest {dest} | Method {method} | Count {count} | TotalDurationMs {totalMs} | MaxDurationMs {maxMs} | MinDurationMs {minMs}");
if (_db == null) continue;
// Keys
var hourKey = HourBucketKey(dest, method, hourStart);
var hoursIndex = HoursIndexKey(dest, method);
var dayKey = DayBucketKey(dest, dayStart);
var daysIndex = DaysIndexKey(dest);
// Use batch to reduce roundtrips
var batch = _db.CreateBatch();
// Increment hash fields (count and totalMs)
var taskHourCount = batch.HashIncrementAsync(hourKey, "count", count);
var taskHourTotal = batch.HashIncrementAsync(hourKey, "totalMs", totalMs);
var taskHourMax = batch.HashSetAsync(hourKey, "maxMs", maxMs.ToString());
var taskHourMin = batch.HashSetAsync(hourKey, "minMs", minMs.ToString());
var taskDayCount = batch.HashIncrementAsync(dayKey, "count", count);
var taskDayTotal = batch.HashIncrementAsync(dayKey, "totalMs", totalMs);
// For day bucket min/max: save as separate fields to be aggregated later
// Using HashSet with comparison logic handled during flush
var taskDayMax = batch.HashSetAsync(dayKey, "maxMs", maxMs.ToString());
var taskDayMin = batch.HashSetAsync(dayKey, "minMs", minMs.ToString());
// Add to sorted set indices with score = epoch seconds of bucket start
var hourScore = ToEpochSeconds(hourStart);
var dayScore = ToEpochSeconds(dayStart);
var taskZAddHour = batch.SortedSetAddAsync(hoursIndex, hourKey, hourScore);
var taskZAddDay = batch.SortedSetAddAsync(daysIndex, dayKey, dayScore);
// Execute batch
batch.Execute();
// Await tasks to ensure completion
await Task.WhenAll(taskHourCount, taskHourTotal, taskHourMax, taskHourMin,
taskDayCount, taskDayTotal, taskDayMax, taskDayMin,
taskZAddHour, taskZAddDay);
}
_stats.Clear();
}
catch (TaskCanceledException) { }
catch (Exception ex)
{
Log.Error(ex, "Error flushing metrics");
}
}
}
#endif
#endregion Protected Methods
#region Private Fields
+3 -1
View File
@@ -79,6 +79,7 @@ namespace MP.IOC.Services
public List<WeightDTO> GetAllWeights()
{
var result = new List<WeightDTO>();
#if false
var keys = _db.KeyScan($"{_keyPrefix}*");
foreach (var key in keys)
@@ -99,7 +100,8 @@ namespace MP.IOC.Services
newW = Math.Clamp(parsedNew, 0, 100);
result.Add(new WeightDTO { Method = methodName, OldWeight = oldW, NewWeight = newW });
}
}
#endif
return result;
}
+1
View File
@@ -1,4 +1,5 @@
{
"DetailedErrors": true,
"Logging": {
"LogLevel": {
"Default": "Information",
+18 -2
View File
@@ -1,9 +1,25 @@
// Setup del chart desiderato con id univoco
window.setup = (id, config) => {
var ctx = document.getElementById(id).getContext('2d');
const el = document.getElementById(id);
if (!el) {
//console.error("Elemento canvas non trovato:", id);
return;
}
// TECNICA INFALLIBILE:
// Chiediamo a Chart.js di trovare l'istanza associata a questo elemento DOM
// Indipendentemente da quale ID avesse prima.
const existingChart = Chart.getChart(el);
if (existingChart) {
existingChart.destroy();
}
const ctx = el.getContext('2d');
//var ctx = document.getElementById(id).getContext('2d');
//let currentDate = new Date();
//console.log(currentDate + " - Calling setup...");
//console.log(id);
console.log(id);
if (window['chart-' + id] instanceof Chart) {
//window.myChart.destroy();
window['chart-' + id].destroy();