Update IOC x stats preliminari

This commit is contained in:
Samuele Locatelli
2026-04-09 16:49:43 +02:00
parent f1320c971f
commit 936feccec9
13 changed files with 168 additions and 127 deletions
@@ -11,6 +11,8 @@ namespace MP.Data.Repository.Utils
/// </summary>
public interface IStatsDetailRepository
{
#region Public Methods
/// <summary>
/// Recupera l'elenco delle statistiche di dettaglio per un periodo specifico, con filtri opzionali.
/// </summary>
@@ -21,6 +23,17 @@ namespace MP.Data.Repository.Utils
/// <returns>L'elenco delle statistiche di dettaglio ordinate cronologicamente.</returns>
Task<List<StatsDetailModel>> GetFiltAsync(DateTime dtStart, DateTime dtEnd, string sEnvir = "", string sType = "");
/// <summary>
/// Elenco record in modalità pareto
/// </summary>
/// <param name="dtStart"></param>
/// <param name="dtEnd"></param>
/// <param name="maxRec"></param>
/// <param name="sDest"></param>
/// <param name="sType"></param>
/// <returns></returns>
Task<List<StatsDetailModel>> GetParetoAsync(DateTime dtStart, DateTime dtEnd, int maxRec, string sDest = "", string sType = "");
/// <summary>
/// Recupera l'intervallo temporale disponibile nel database per le statistiche di dettaglio.
/// </summary>
@@ -36,5 +49,7 @@ namespace MP.Data.Repository.Utils
/// <param name="removeOld">Se true, elimina preventivamente i record nel periodo richiesto.</param>
/// <returns>Il numero di record inseriti.</returns>
Task<int> UpsertManyAsync(List<StatsDetailModel> listRecords, bool removeOld);
#endregion Public Methods
}
}
@@ -47,6 +47,34 @@ namespace MP.Data.Repository.Utils
return answ;
}
/// <inheritdoc />
public async Task<List<StatsDetailModel>> GetParetoAsync(DateTime dtStart, DateTime dtEnd, int maxRec, string sDest = "", string sType = "")
{
await using var dbCtx = await CreateContextAsync();
List<StatsDetailModel> answ = new List<StatsDetailModel>();
// recupero ed ordino per data-ora
var query = dbCtx.DbSetStatsDet
.Where(x => x.Hour >= dtStart && x.Hour <= dtEnd);
if (!string.IsNullOrEmpty(sDest))
query = query.Where(x => x.Destination == sDest);
if (!string.IsNullOrEmpty(sType))
query = query.Where(x => x.Type == sType);
answ = await query
.AsNoTracking()
.OrderByDescending(x => x.RequestCount)
.ThenBy(x => x.Hour)
.ThenBy(x => x.Destination)
.ThenBy(x => x.Type)
.Take(maxRec)
.ToListAsync();
return answ;
}
/// <inheritdoc />
public async Task<DtUtils.Periodo> GetRangeAsync(string sEnvir, string sType)
{
@@ -68,7 +96,6 @@ namespace MP.Data.Repository.Utils
return answ;
}
/// <inheritdoc />
public async Task<int> UpsertManyAsync(List<StatsDetailModel> listRecords, bool removeOld)
{
@@ -161,6 +188,8 @@ namespace MP.Data.Repository.Utils
}
}
#endregion Public Methods
#if false
/// <inheritdoc />
public async Task<int> UpsertManyAsyncOrig(List<StatsDetailModel> listRecords, bool removeOld)
@@ -181,7 +210,6 @@ namespace MP.Data.Repository.Utils
DateTime startDate = firstRec.Hour;
DateTime endDate = lastRec.Hour;
var items = await dbCtx.DbSetStatsDet
.Where(x => x.Hour >= startDate && x.Hour <= endDate)
.ToListAsync();
@@ -209,11 +237,9 @@ namespace MP.Data.Repository.Utils
await tx.RollbackAsync();
throw;
}
}
}
#endif
#endregion Public Methods
#region Protected Fields
protected static NLog.Logger Log = LogManager.GetCurrentClassLogger();
+13 -2
View File
@@ -16,9 +16,20 @@ namespace MP.Data.Services.Utils
/// </summary>
/// <param name="dtStart">Data inizio periodo</param>
/// <param name="dtEnd">Data fine periodo</param>
/// <param name="sEnvir">Filtro opzionale per ambiente (default: vuoto = tutti)</param>
/// <param name="sDest">Filtro opzionale per destinazione (default: vuoto = tutti)</param>
/// <param name="sType">Filtro opzionale per tipo (default: vuoto = tutti)</param>
Task<List<StatsDetailModel>> GetFiltAsync(DateTime dtStart, DateTime dtEnd, string sEnvir = "", string sType = "");
Task<List<StatsDetailModel>> GetFiltAsync(DateTime dtStart, DateTime dtEnd, string sDest = "", string sType = "");
/// <summary>
/// restituisce un Pareto degli eventi più frequenti dato periodo ed eventuale filtro Destinazione/Tipo
/// </summary>
/// <param name="dtStart">Data inizio periodo</param>
/// <param name="dtEnd">Data fine periodo</param>
/// <param name="maxRec"></param>
/// <param name="sDest">Filtro opzionale per destinazione (default: vuoto = tutti)</param>
/// <param name="sType">Filtro opzionale per tipo (default: vuoto = tutti)</param>
/// <returns></returns>
Task<List<StatsDetailModel>> GetParetoAsync(DateTime dtStart, DateTime dtEnd, int maxRec, string sDest = "", string sType = "");
/// <summary>
/// Recupera il range di periodi valido per le chiamate di dettaglio.
+33 -2
View File
@@ -26,17 +26,48 @@ namespace MP.Data.Services.Utils
#region Public Methods
/// <inheritdoc />
public async Task<List<StatsDetailModel>> GetFiltAsync(DateTime dtStart, DateTime dtEnd, string sEnvir = "", string sType = "")
public async Task<List<StatsDetailModel>> GetFiltAsync(DateTime dtStart, DateTime dtEnd, string sDest = "", string sType = "")
{
return await TraceAsync($"{_className}.GetFilt", async (activity) =>
{
string cacheKey = $"{_redisBaseKey}:{_className}:DT:{dtStart:yyyyMMdd}:{dtEnd:yyyyMMdd}";
if (!string.IsNullOrEmpty(sDest))
{
cacheKey += $":{sDest}";
}
if (!string.IsNullOrEmpty(sType))
{
cacheKey += $":{sType}";
}
return await GetOrSetCacheAsync(
$"{_redisBaseKey}:{_className}:DT:{dtStart:yyyyMMdd}:{dtEnd:yyyyMMdd}",
cacheKey,
async () => await _repo.GetFiltAsync(dtStart, dtEnd),
UltraLongCache
);
});
}
/// <inheritdoc />
public async Task<List<StatsDetailModel>> GetParetoAsync(DateTime dtStart, DateTime dtEnd, int maxRec, string sDest = "", string sType = "")
{
return await TraceAsync($"{_className}.GetFilt", async (activity) =>
{
string cacheKey = $"{_redisBaseKey}:{_className}:PARETO:{dtStart:yyyyMMdd}:{dtEnd:yyyyMMdd}";
if (!string.IsNullOrEmpty(sDest))
{
cacheKey += $":{sDest}";
}
if (!string.IsNullOrEmpty(sType))
{
cacheKey += $":{sType}";
}
return await GetOrSetCacheAsync(
cacheKey,
async () => await _repo.GetParetoAsync(dtStart, dtEnd, maxRec, sDest, sType),
UltraLongCache
);
});
}
/// <inheritdoc />
public async Task<DtUtils.Periodo> GetRangeAsync(string sEnvir, string sType)
+2 -2
View File
@@ -59,7 +59,7 @@
</NavLink>
</div> *@
<div class="nav-item px-2">
<NavLink class="nav-link py-0 px-2 mb-0" href="About">
<NavLink class="nav-link py-0 px-2 mb-0" href="about">
<span class="fas fa-info-circle fs-4 pe-2" aria-hidden="true"></span>
@if (showText)
{
@@ -68,7 +68,7 @@
</NavLink>
</div>
<div class="nav-item px-2">
<NavLink class="nav-link py-0 px-2 mb-0" href="Contacts">
<NavLink class="nav-link py-0 px-2 mb-0" href="contacts">
<span class="fas fa-envelope fs-4 pe-2" aria-hidden="true"></span>
@if (showText)
{
+44 -1
View File
@@ -1,7 +1,8 @@
@page "/"
@page "/Home"
@page "/Index"
<div class="card">
<div class="card shadow">
<div class="card-header bg-dark text-light">
<div class="row">
<div class="col-3 p-2">
@@ -87,5 +88,47 @@
</ul>
</div>
</div>
<div class="row my-2">
<div class="col-3">
<ul class="list-group">
<li class="list-group-item active d-flex justify-content-between align-items-start">
<b>Top Call</b>
</li>
@foreach (var item in ListParetoCall)
{
<li class="list-group-item d-flex justify-content-between align-items-start small py-1">
<div>
@item.Destination | <b>@item.Type</b>
</div>
<div class="">
<b>@($"{item.RequestCount:N0}")</b>
</div>
</li>
}
</ul>
</div>
<div class="col-6 text-center">
perc IO/IOC
</div>
<div class="col-3">
<ul class="list-group">
<li class="list-group-item active d-flex justify-content-between align-items-start">
<b>Call Processed</b>
</li>
@foreach (var item in ListGlobalCall)
{
<li class="list-group-item d-flex justify-content-between align-items-start small py-1">
<div>
@($"{item.Hour:yyyy-MM-dd}")
</div>
<div class="">
<b>@($"{item.RequestCount:N0}")</b>
</div>
</li>
}
</ul>
</div>
</div>
</div>
</div>
+26
View File
@@ -1,7 +1,33 @@
using Microsoft.AspNetCore.Components;
using MP.Data.DbModels.Utils;
using MP.Data.Services.Utils;
namespace MP.IOC.Components.Pages
{
public partial class Index
{
protected override async Task OnInitializedAsync()
{
await ReloadData();
}
private async Task ReloadData()
{
DateTime oggi = DateTime.Today;
DateTime adesso = DateTime.Now;
var rawData = await StatsAggrService.GetFiltAsync(oggi.AddDays(-5), oggi);
ListGlobalCall = rawData.OrderByDescending(x => x.Hour).ToList();
ListParetoCall = await StatsDetService.GetParetoAsync(adesso.AddHours(-1), adesso, 5);
}
private List<StatsAggregatedModel> ListGlobalCall = new();
private List<StatsDetailModel> ListParetoCall = new();
[Inject]
private IStatsAggrService StatsAggrService { get; set; } = null!;
[Inject]
private IStatsDetailService StatsDetService { get; set; } = null!;
}
}
+1 -1
View File
@@ -4,7 +4,7 @@
<TargetFramework>net8.0</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
<Version>6.16.2604.915</Version>
<Version>6.16.2604.916</Version>
</PropertyGroup>
<ItemGroup>
-8
View File
@@ -77,14 +77,6 @@ string utilsConnString = builder.Configuration.GetConnectionString("MP.Utils") ?
builder.Services.AddDbContextFactory<MoonPro_UtilsContext>(options =>
options.UseSqlServer(utilsConnString));
#if false
// gestione generica MP
// aggiunta helper local/session storage service
builder.Services.AddScoped<ISessionStorageService, SessionStorageService>();
builder.Services.AddScoped<ILocalStorageService, LocalStorageService>();
builder.Services.AddScoped<MessageService>();
#endif
// MP.Data Services Utils - Statistiche DB
builder.Services.AddScoped<IStatsAggrRepository, StatsAggrRepository>();
builder.Services.AddScoped<IStatsDetailRepository, StatsDetailRepository>();
+1 -1
View File
@@ -1,6 +1,6 @@
<body>
<i>Modulo MP-IOC </i>
<h4>Versione: 6.16.2604.915</h4>
<h4>Versione: 6.16.2604.916</h4>
<br /> Note di rilascio:
<ul>
<li>
+1 -1
View File
@@ -1 +1 @@
6.16.2604.915
6.16.2604.916
+1 -1
View File
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<item>
<version>6.16.2604.915</version>
<version>6.16.2604.916</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>
-103
View File
@@ -1,103 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>MP IOC</title>
<link rel="stylesheet" href="lib/bootstrap/css/bootstrap.css" />
<link rel="stylesheet" href="lib/font-awesome/css/all.css" />
</head>
<body>
<div class="container py-3">
<div class="card">
<div class="card-header bg-dark text-light">
<div class="row">
<div class="col-3 p-2">
<img src="images/LogoMapo.png" class="image-fluid" height="64" />
</div>
<div class="col-6 p-2 text-center align-content-center">
<div class="fs-1 fw-bold">MAPO MES</div>
<div class="fs-6 text-secondary">IN/OUT module</div>
</div>
<div class="col-3 text-end p-2">
<img src="images/LogoEgw.png" class="image-fluid" height="64" />
</div>
</div>
</div>
<div class="card-body">
<div class="row">
<div class="col-3">
<ul class="list-group">
<li class="list-group-item d-flex justify-content-between align-items-start">
<div>
<b>Alive</b>
</div>
<div class="text-success">
<i class="fa-solid fa-circle-check"></i>
</div>
</li>
<li class="list-group-item d-flex justify-content-between align-items-start">
<div>
<b>IOB-PI</b> I/O
</div>
<div class="text-danger">
<i class="fa-solid fa-circle-check"></i>
</div>
</li>
<li class="list-group-item d-flex justify-content-between align-items-start">
<div>
<b>IOB-WIN</b> I/O
</div>
<div class="text-danger">
<i class="fa-solid fa-circle-check"></i>
</div>
</li>
<li class="list-group-item d-flex justify-content-between align-items-start">
<div>
<b>File</b> upload
</div>
<div class="text-danger">
<i class="fa-solid fa-circle-check"></i>
</div>
</li>
</ul>
</div>
<div class="col-6 text-center">
<h1>MP-IOC</h1>
<h4>.net Core 8.0</h4>
</div>
<div class="col-3 text-end">
<ul class="list-group">
<li class="list-group-item d-flex justify-content-between align-items-start">
<div>
<b>Bench</b> test
</div>
<div class="text-warning">
<i class="fa-solid fa-circle-check"></i>
</div>
</li>
<li class="list-group-item d-flex justify-content-between align-items-start">
<div>
<b>Parameters</b> management
</div>
<div class="text-danger">
<i class="fa-solid fa-circle-check"></i>
</div>
</li>
<li class="list-group-item d-flex justify-content-between align-items-start">
<div>
<b>Recipe</b> management
</div>
<div class="text-success">
<i class="fa-solid fa-circle-check"></i>
</div>
</li>
</ul>
</div>
</div>
</div>
</div>
</div>
<script src="lib/bootstrap/js/bootstrap.js"></script>
</body>
</html>