Merge branch 'release/AddRuidReturn_01'
This commit is contained in:
@@ -0,0 +1,17 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace EgwCoreLib.Lux.Core.Stats
|
||||
{
|
||||
public class StatsRangeDto
|
||||
{
|
||||
public List<string> HourLabels { get; set; } = new();
|
||||
public List<double> Requests { get; set; } = new();
|
||||
public List<double> AvgProcessing { get; set; } = new();
|
||||
public List<double> MaxProcessing { get; set; } = new();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,327 @@
|
||||
using EgwCoreLib.Lux.Core.Stats;
|
||||
using StackExchange.Redis;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace EgwCoreLib.Lux.Data.Services
|
||||
{
|
||||
/// <summary>
|
||||
/// Gestione servizio indice richieste
|
||||
/// </summary>
|
||||
public class CalcRuidService
|
||||
{
|
||||
#region Public Constructors
|
||||
|
||||
public CalcRuidService(ConnectionMultiplexer redis, TimeSpan retention, string redisBaseKey)
|
||||
{
|
||||
_db = redis.GetDatabase();
|
||||
_retention = retention;
|
||||
_base = redisBaseKey.TrimEnd(':');
|
||||
//_base = redisBaseKey.EndsWith(":") ? redisBaseKey : redisBaseKey + ":";
|
||||
}
|
||||
|
||||
#endregion Public Constructors
|
||||
|
||||
#region Public Methods
|
||||
|
||||
/// <summary>
|
||||
/// Metodo Creazione nuova richiesta
|
||||
/// </summary>
|
||||
/// <param name="envir">Environment calcolo</param>
|
||||
/// <param name="tipo">Tipologia richiesta</param>
|
||||
/// <param name="uid">UID di riferimento</param>
|
||||
/// <returns>restituisce il valore del RUID (ID univoco richiesta)</returns>
|
||||
public async Task<string> AddRequestAsync(string envir, string tipo, string uid)
|
||||
{
|
||||
var ruid = GenerateRuid();
|
||||
var processStart = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();
|
||||
|
||||
var hashKey = GetRequestKey(ruid);
|
||||
var setKey = GetSortedSetKey(envir, tipo);
|
||||
var uidKey = GetUidSetKey(uid);
|
||||
var combKey = GetCombinationsKey();
|
||||
|
||||
var batch = _db.CreateBatch();
|
||||
|
||||
// NON await qui!
|
||||
var t1 = batch.HashSetAsync(hashKey, new HashEntry[]
|
||||
{
|
||||
new HashEntry("processStart", processStart),
|
||||
new HashEntry("UID", uid),
|
||||
new HashEntry("tipo", tipo),
|
||||
new HashEntry("envir", envir)
|
||||
});
|
||||
|
||||
var t2 = batch.SortedSetAddAsync(setKey, ruid, processStart);
|
||||
var t3 = batch.SetAddAsync(uidKey, ruid);
|
||||
|
||||
string comb = $"{envir}|{tipo}";
|
||||
var t4 = batch.SetAddAsync(combKey, comb);
|
||||
|
||||
RedisKey minuteKey = Key($"stats:requests:count:{DateTime.UtcNow:yyyyMMddHHmm}");
|
||||
RedisKey hourKey = Key($"stats:requests:count:{DateTime.UtcNow:yyyyMMddHH}");
|
||||
|
||||
var t5 = batch.StringIncrementAsync(minuteKey);
|
||||
var t6 = batch.StringIncrementAsync(hourKey);
|
||||
|
||||
// Esegue il batch
|
||||
batch.Execute();
|
||||
|
||||
// Ora puoi attendere le task
|
||||
await Task.WhenAll(t1, t2, t3, t4, t5, t6);
|
||||
|
||||
return ruid;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Metodo di Cleanup periodico
|
||||
/// </summary>
|
||||
/// <param name="environment">Environment calcolo</param>
|
||||
/// <param name="tipo">Tipologia richiesta</param>
|
||||
/// <returns></returns>
|
||||
public async Task CleanupOldRequestsAsync(string environment, string tipo)
|
||||
{
|
||||
var cutoff = DateTimeOffset.UtcNow.Add(-_retention).ToUnixTimeMilliseconds();
|
||||
var setKey = GetSortedSetKey(environment, tipo);
|
||||
|
||||
var oldIds = await _db.SortedSetRangeByScoreAsync(setKey, stop: cutoff);
|
||||
if (oldIds.Length == 0) return;
|
||||
|
||||
var batch = _db.CreateBatch();
|
||||
var tasks = new List<Task>();
|
||||
|
||||
foreach (var id in oldIds)
|
||||
{
|
||||
var ruid = id.ToString();
|
||||
var hashKey = GetRequestKey(ruid);
|
||||
|
||||
var uid = await _db.HashGetAsync(hashKey, "UID");
|
||||
if (!uid.IsNull)
|
||||
{
|
||||
var uidKey = GetUidSetKey(uid);
|
||||
tasks.Add(batch.SetRemoveAsync(uidKey, ruid));
|
||||
|
||||
tasks.Add(batch.SetLengthAsync(uidKey).ContinueWith(t =>
|
||||
{
|
||||
if (t.Result == 0)
|
||||
_db.KeyDelete(uidKey);
|
||||
}));
|
||||
}
|
||||
|
||||
tasks.Add(batch.KeyDeleteAsync(hashKey));
|
||||
}
|
||||
|
||||
tasks.Add(batch.SortedSetRemoveRangeByScoreAsync(setKey, double.NegativeInfinity, cutoff));
|
||||
|
||||
tasks.Add(batch.SortedSetLengthAsync(setKey).ContinueWith(t =>
|
||||
{
|
||||
if (t.Result == 0)
|
||||
_db.KeyDelete(setKey);
|
||||
}));
|
||||
|
||||
batch.Execute();
|
||||
await Task.WhenAll(tasks);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Metodo di Aggiornamento richiesta esistente
|
||||
/// </summary>
|
||||
/// <param name="ruid">RUID richiesta</param>
|
||||
/// <returns></returns>
|
||||
public async Task CompleteRequestAsync(string ruid)
|
||||
{
|
||||
var hashKey = GetRequestKey(ruid);
|
||||
var processEnd = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();
|
||||
|
||||
// Recupero processStart
|
||||
var processStartValue = await _db.HashGetAsync(hashKey, "processStart");
|
||||
if (processStartValue.IsNull) return;
|
||||
|
||||
var processStart = (long)processStartValue;
|
||||
var elapsed = (processEnd - processStart) / 1000.0; // secondi con decimali
|
||||
|
||||
// Recupero max corrente PRIMA del batch
|
||||
RedisKey hourKeySum = Key($"stats:processing:sum:{DateTime.UtcNow:yyyyMMddHH}");
|
||||
RedisKey hourKeyMax = Key($"stats:processing:max:{DateTime.UtcNow:yyyyMMddHH}");
|
||||
|
||||
var currentMaxValue = await _db.StringGetAsync(hourKeyMax);
|
||||
double currentMax = currentMaxValue.IsNull ? 0 : (double)currentMaxValue;
|
||||
|
||||
var batch = _db.CreateBatch();
|
||||
|
||||
// Aggiorno hash
|
||||
var t1 = batch.HashSetAsync(hashKey, new HashEntry[]
|
||||
{
|
||||
new HashEntry("processEnd", processEnd),
|
||||
new HashEntry("processElapsed", elapsed)
|
||||
});
|
||||
|
||||
// Incremento somma
|
||||
var t2 = batch.StringIncrementAsync(hourKeySum, elapsed);
|
||||
|
||||
// Aggiorno max se necessario
|
||||
Task t3 = Task.CompletedTask;
|
||||
if (elapsed > currentMax)
|
||||
t3 = batch.StringSetAsync(hourKeyMax, elapsed);
|
||||
|
||||
// Eseguo batch
|
||||
batch.Execute();
|
||||
|
||||
// Attendo completamento
|
||||
await Task.WhenAll(t1, t2, t3);
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Metodo Recupero combinazioni envir/tipo
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public async Task<IEnumerable<(string env, string tipo)>> GetCombinationsAsync()
|
||||
{
|
||||
var members = await _db.SetMembersAsync(GetCombinationsKey());
|
||||
return members
|
||||
.Select(x => x.ToString().Split('|'))
|
||||
.Select(a => (a[0], a[1]));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Metodo Recupero richieste per UID
|
||||
/// </summary>
|
||||
/// <param name="uid"></param>
|
||||
/// <returns></returns>
|
||||
public async Task<IEnumerable<string>> GetRequestsByUidAsync(string uid)
|
||||
{
|
||||
var uidKey = GetUidSetKey(uid);
|
||||
var members = await _db.SetMembersAsync(uidKey);
|
||||
return members.Select(x => x.ToString());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Metodo Statistiche aggregate
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public async Task<object> GetStatsAsync()
|
||||
{
|
||||
RedisKey minuteKey = Key($"stats:requests:count:{DateTime.UtcNow:yyyyMMddHHmm}");
|
||||
RedisKey hourKey = Key($"stats:requests:count:{DateTime.UtcNow:yyyyMMddHH}");
|
||||
RedisKey hourSumKey = Key($"stats:processing:sum:{DateTime.UtcNow:yyyyMMddHH}");
|
||||
RedisKey hourMaxKey = Key($"stats:processing:max:{DateTime.UtcNow:yyyyMMddHH}");
|
||||
|
||||
var minuteCount = await _db.StringGetAsync(minuteKey);
|
||||
var hourCount = await _db.StringGetAsync(hourKey);
|
||||
var sum = await _db.StringGetAsync(hourSumKey);
|
||||
var max = await _db.StringGetAsync(hourMaxKey);
|
||||
|
||||
long minCnt = minuteCount.IsNull ? 0 : (long)minuteCount;
|
||||
long hourCnt = hourCount.IsNull ? 0 : (long)hourCount;
|
||||
|
||||
double sumVal = sum.IsNull ? 0 : (double)sum;
|
||||
double maxVal = max.IsNull ? 0 : (double)max;
|
||||
|
||||
double avg = hourCnt > 0 ? sumVal / hourCnt : 0;
|
||||
|
||||
return new
|
||||
{
|
||||
RequestsLastMinute = minCnt,
|
||||
RequestsLastHour = hourCnt,
|
||||
ProcessingAvgLastHour = avg,
|
||||
ProcessingMaxLastHour = maxVal
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Restituisce le statistiche per range date
|
||||
/// </summary>
|
||||
/// <param name="from"></param>
|
||||
/// <param name="to"></param>
|
||||
/// <returns></returns>
|
||||
public async Task<StatsRangeDto> GetStatsRangeAsync(DateTime from, DateTime to)
|
||||
{
|
||||
var dto = new StatsRangeDto();
|
||||
|
||||
var hourKeys = new List<RedisKey>();
|
||||
var sumKeys = new List<RedisKey>();
|
||||
var maxKeys = new List<RedisKey>();
|
||||
|
||||
var cursor = from;
|
||||
|
||||
while (cursor <= to)
|
||||
{
|
||||
hourKeys.Add(Key($"stats:requests:count:{cursor:yyyyMMddHH}"));
|
||||
sumKeys.Add(Key($"stats:processing:sum:{cursor:yyyyMMddHH}"));
|
||||
maxKeys.Add(Key($"stats:processing:max:{cursor:yyyyMMddHH}"));
|
||||
|
||||
dto.HourLabels.Add(cursor.ToString("dd/MM HH:mm"));
|
||||
cursor = cursor.AddHours(1);
|
||||
}
|
||||
|
||||
var batch = _db.CreateBatch();
|
||||
|
||||
var hourTasks = hourKeys.Select(k => batch.StringGetAsync(k)).ToArray();
|
||||
var sumTasks = sumKeys.Select(k => batch.StringGetAsync(k)).ToArray();
|
||||
var maxTasks = maxKeys.Select(k => batch.StringGetAsync(k)).ToArray();
|
||||
|
||||
batch.Execute();
|
||||
|
||||
await Task.WhenAll(hourTasks);
|
||||
await Task.WhenAll(sumTasks);
|
||||
await Task.WhenAll(maxTasks);
|
||||
|
||||
for (int i = 0; i < hourTasks.Length; i++)
|
||||
{
|
||||
long req = hourTasks[i].Result.IsNull ? 0 : (long)hourTasks[i].Result;
|
||||
double sum = sumTasks[i].Result.IsNull ? 0 : (double)sumTasks[i].Result;
|
||||
double max = maxTasks[i].Result.IsNull ? 0 : (double)maxTasks[i].Result;
|
||||
|
||||
dto.Requests.Add(req);
|
||||
dto.MaxProcessing.Add(max);
|
||||
|
||||
double avg = req > 0 ? sum / req : 0;
|
||||
dto.AvgProcessing.Add(avg);
|
||||
}
|
||||
|
||||
return dto;
|
||||
}
|
||||
|
||||
#endregion Public Methods
|
||||
|
||||
#region Private Fields
|
||||
|
||||
private readonly string _base;
|
||||
private readonly IDatabase _db;
|
||||
private readonly TimeSpan _retention;
|
||||
private readonly Random _rnd = new Random();
|
||||
|
||||
#endregion Private Fields
|
||||
|
||||
#region Private Methods
|
||||
|
||||
/// <summary>
|
||||
/// Generatore RUID:
|
||||
/// ID incrementale = timestamp ms + random 4 chars HEX
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
private string GenerateRuid()
|
||||
{
|
||||
long ts = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();
|
||||
string rand = Convert.ToString(_rnd.Next(0x1000, 0xFFFF), 16).ToUpper();
|
||||
return $"{ts}-{rand}";
|
||||
}
|
||||
|
||||
private RedisKey GetCombinationsKey() => Key("req:combinations");
|
||||
|
||||
private RedisKey GetRequestKey(string ruid) => Key($"request:{ruid}");
|
||||
|
||||
private RedisKey GetSortedSetKey(string envir, string tipo) => Key($"requests:{envir}:{tipo}");
|
||||
|
||||
private RedisKey GetUidSetKey(string uid) => Key($"uid:{uid}:requests");
|
||||
|
||||
private RedisKey Key(string suffix) => (RedisKey)($"{_base}:RUID:{suffix}");
|
||||
|
||||
#endregion Private Methods
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
using EgwCoreLib.Lux.Data.Services;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.Hosting;
|
||||
using System;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace EgwCoreLib.Lux.Data.Services
|
||||
{
|
||||
public class CleanupService : BackgroundService
|
||||
{
|
||||
#region Public Constructors
|
||||
|
||||
/// <summary>
|
||||
/// Init classe con il servizio di gestione RUID
|
||||
/// </summary>
|
||||
/// <param name="crService"></param>
|
||||
public CleanupService(IConfiguration config, CalcRuidService crService)
|
||||
{
|
||||
_config = config;
|
||||
try
|
||||
{
|
||||
reqPeriodMinute = _config.GetValue<int>("ServerConf:CleanupPeriodMinutes");
|
||||
}
|
||||
catch
|
||||
{
|
||||
reqPeriodMinute = 60;
|
||||
}
|
||||
_calcRuidService = crService;
|
||||
}
|
||||
|
||||
#endregion Public Constructors
|
||||
|
||||
#region Protected Methods
|
||||
|
||||
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
|
||||
{
|
||||
while (!stoppingToken.IsCancellationRequested)
|
||||
{
|
||||
var combos = await _calcRuidService.GetCombinationsAsync();
|
||||
|
||||
foreach (var (env, tipo) in combos)
|
||||
await _calcRuidService.CleanupOldRequestsAsync(env, tipo);
|
||||
|
||||
// attesa tra le esecuzioni
|
||||
await Task.Delay(TimeSpan.FromMinutes(reqPeriodMinute), stoppingToken);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion Protected Methods
|
||||
|
||||
#region Private Fields
|
||||
|
||||
private readonly CalcRuidService _calcRuidService;
|
||||
private IConfiguration _config;
|
||||
private int reqPeriodMinute = 60;
|
||||
|
||||
#endregion Private Fields
|
||||
}
|
||||
}
|
||||
@@ -5,6 +5,7 @@ using EgwMultiEngineManager.Data;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using NLog;
|
||||
using System.Diagnostics;
|
||||
using ZXing.QrCode.Internal;
|
||||
|
||||
namespace Lux.API.Controllers
|
||||
{
|
||||
@@ -14,12 +15,13 @@ namespace Lux.API.Controllers
|
||||
{
|
||||
#region Public Constructors
|
||||
|
||||
public GenericController(IConfiguration config, IRedisService redisService, ImageCacheService imgServ)
|
||||
public GenericController(IConfiguration config, IRedisService redisService, ImageCacheService imgServ, CalcRuidService crService)
|
||||
{
|
||||
_config = config;
|
||||
_redisService = redisService;
|
||||
_imgService = imgServ;
|
||||
chPub = _config.GetValue<string>("ServerConf:ChannelPub") ?? "";
|
||||
_calcRuidService = crService;
|
||||
}
|
||||
|
||||
#endregion Public Constructors
|
||||
@@ -42,7 +44,10 @@ namespace Lux.API.Controllers
|
||||
// ...se ricevo percorso --> leggo jwd/svg cablato
|
||||
if (currReq != null)
|
||||
{
|
||||
// preparo variabili di base
|
||||
Dictionary<string, string> DictExec = currReq.DictExec;
|
||||
string envir = $"{currReq.EnvType}";
|
||||
string type = "ND";
|
||||
// controllo se mancassero parametri...
|
||||
if (!DictExec.ContainsKey("Mode"))
|
||||
{
|
||||
@@ -56,6 +61,16 @@ namespace Lux.API.Controllers
|
||||
{
|
||||
DictExec.Add("UID", id);
|
||||
}
|
||||
if (!DictExec.ContainsKey("RUID"))
|
||||
{
|
||||
var mode = DictExec["Mode"];
|
||||
var sub = DictExec["SubMode"];
|
||||
type = string.IsNullOrEmpty(sub) ? mode : $"{mode}-{sub}";
|
||||
// creo registrazione richiesta...
|
||||
var ruid = await _calcRuidService.AddRequestAsync(envir, type, id);
|
||||
// aggiungo RUID effettivo
|
||||
DictExec.Add("RUID", ruid);
|
||||
}
|
||||
int nId = 1;
|
||||
// da modificare con tipo richiesta...
|
||||
QuestionDTO currArgs = new QuestionDTO(nId, currReq.EnvType, DictExec);
|
||||
@@ -83,6 +98,7 @@ namespace Lux.API.Controllers
|
||||
|
||||
// se messaggio vuoto --> uso default!
|
||||
currSer = string.IsNullOrEmpty(currSer) ? "" : currSer;
|
||||
var bomEnvir = Constants.EXECENVIRONMENTS.WINDOW;
|
||||
|
||||
// ...se ricevo percorso --> leggo jwd/svg cablato
|
||||
if (!string.IsNullOrEmpty(currSer))
|
||||
@@ -92,10 +108,17 @@ namespace Lux.API.Controllers
|
||||
DictExec.Add("Mode", $"{(int)Enums.QuestionModes.BOM}");
|
||||
// UID cablato x ora...
|
||||
DictExec.Add("UID", id);
|
||||
|
||||
string envir = $"{bomEnvir}";
|
||||
string type = $"{Enums.QuestionModes.BOM}";
|
||||
var ruid = await _calcRuidService.AddRequestAsync(envir, type, id);
|
||||
// Aggiungo RUID effettivo
|
||||
DictExec.Add("RUID", ruid);
|
||||
// valore serializzato x BOM
|
||||
DictExec.Add("SerializedData", currSer);
|
||||
int nId = 1;
|
||||
// da modificare con tipo richiesta...
|
||||
QuestionDTO currArgs = new QuestionDTO(nId, EgwMultiEngineManager.Data.Constants.EXECENVIRONMENTS.WINDOW, DictExec);
|
||||
QuestionDTO currArgs = new QuestionDTO(nId, bomEnvir, DictExec);
|
||||
|
||||
await _redisService.PublishAsync(chPub, currArgs.sProcessArgs);
|
||||
retVal = "DONE";
|
||||
@@ -110,8 +133,10 @@ namespace Lux.API.Controllers
|
||||
#region Private Fields
|
||||
|
||||
private static Logger Log = LogManager.GetCurrentClassLogger();
|
||||
private readonly CalcRuidService _calcRuidService;
|
||||
private readonly IRedisService _redisService;
|
||||
private readonly string chPub = "";
|
||||
|
||||
private IConfiguration _config;
|
||||
|
||||
#endregion Private Fields
|
||||
@@ -121,5 +146,10 @@ namespace Lux.API.Controllers
|
||||
private ImageCacheService _imgService { get; set; }
|
||||
|
||||
#endregion Private Properties
|
||||
|
||||
#region Private Methods
|
||||
|
||||
|
||||
#endregion Private Methods
|
||||
}
|
||||
}
|
||||
@@ -15,13 +15,14 @@ namespace Lux.API.Controllers
|
||||
{
|
||||
#region Public Constructors
|
||||
|
||||
public WindowController(IConfiguration config, IRedisService redisService, ImageCacheService imgServ, ConfigDataService confServ)
|
||||
public WindowController(IConfiguration config, IRedisService redisService, ImageCacheService imgServ, ConfigDataService confServ, CalcRuidService crService)
|
||||
{
|
||||
_config = config;
|
||||
_redisService = redisService;
|
||||
_imgService = imgServ;
|
||||
_confService = confServ;
|
||||
chPub = _config.GetValue<string>("ServerConf:ChannelPub") ?? "";
|
||||
_calcRuidService = crService;
|
||||
}
|
||||
|
||||
#endregion Public Constructors
|
||||
@@ -60,10 +61,18 @@ namespace Lux.API.Controllers
|
||||
// ...se ricevo percorso --> leggo jwd/svg cablato
|
||||
if (!string.IsNullOrEmpty(currJwd))
|
||||
{
|
||||
// init vars
|
||||
Dictionary<string, string> DictExec = new Dictionary<string, string>();
|
||||
DictExec.Add("Mode", $"{(int)Egw.Window.Data.Enums.QuestionModes.BOM}");
|
||||
var cMode = Egw.Window.Data.Enums.QuestionModes.BOM;
|
||||
|
||||
DictExec.Add("Mode", $"{(int)cMode}");
|
||||
// UID cablato x ora...
|
||||
DictExec.Add("UID", id);
|
||||
// creo registrazione richiesta...
|
||||
var ruid = await _calcRuidService.AddRequestAsync($"{cEnvir}", $"{cMode}", id);
|
||||
// aggiungo RUID effettivo
|
||||
DictExec.Add("RUID", ruid);
|
||||
|
||||
DictExec.Add("SerializedData", currJwd);
|
||||
int nId = 1;
|
||||
// da modificare con tipo richiesta...
|
||||
@@ -97,25 +106,7 @@ namespace Lux.API.Controllers
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Esegue invio effettivo richiesta elenco HW list
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
private async Task sendHwReq()
|
||||
{
|
||||
Dictionary<string, string> DictExec = new Dictionary<string, string>();
|
||||
DictExec.Add("Mode", $"{(int)Egw.Window.Data.Enums.QuestionModes.HARDWARE}");
|
||||
// da rivedere?
|
||||
DictExec.Add("UID", "HW.AGB");
|
||||
DictExec.Add("SubMode", $"{(int)Egw.Window.Data.Enums.QuestionHwSubModes.LIST}");
|
||||
DictExec.Add("Manufacturer", $"{(int)Egw.Window.Data.Enums.HardwareManufacturers.AGB}");
|
||||
int nId = 1;
|
||||
// da modificare con tipo richiesta...
|
||||
QuestionDTO currArgs = new QuestionDTO(nId, EgwMultiEngineManager.Data.Constants.EXECENVIRONMENTS.WINDOW, DictExec);
|
||||
await _redisService.PublishAsync(chPub, currArgs.sProcessArgs);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Chiamata GET:
|
||||
/// Chiamata GET:
|
||||
/// - se trova in cache risponde con elenco hw già ricevuto
|
||||
/// - se non trova invia richiesta modo 3 (HardwareModelList) che sarà poi salvata
|
||||
/// GET: api/window/hwlist/nome_produttore
|
||||
@@ -161,7 +152,7 @@ namespace Lux.API.Controllers
|
||||
// ...se ricevo percorso --> leggo jwd/svg cablato
|
||||
if (!string.IsNullOrEmpty(id))
|
||||
{
|
||||
// bonifica nome svg da
|
||||
// bonifica nome svg da
|
||||
svgContent = _imgService.LoadSvg(id);
|
||||
}
|
||||
sw.Stop();
|
||||
@@ -192,7 +183,7 @@ namespace Lux.API.Controllers
|
||||
id = id.Replace(".svg", "");
|
||||
}
|
||||
// se contiene i caratteri casuali x forzare reload --> li levo
|
||||
if(id.Contains("-"))
|
||||
if (id.Contains("-"))
|
||||
{
|
||||
id = id.Substring(0, id.IndexOf("-"));
|
||||
}
|
||||
@@ -238,14 +229,21 @@ namespace Lux.API.Controllers
|
||||
// ...se ricevo percorso --> leggo jwd/svg cablato
|
||||
if (!string.IsNullOrEmpty(currJwd))
|
||||
{
|
||||
// init vars
|
||||
Dictionary<string, string> DictExec = new Dictionary<string, string>();
|
||||
DictExec.Add("Mode", $"{(int)Egw.Window.Data.Enums.QuestionModes.PREVIEW}");
|
||||
var cMode = Egw.Window.Data.Enums.QuestionModes.PREVIEW;
|
||||
|
||||
DictExec.Add("Mode", $"{(int)cMode}");
|
||||
// UID cablato x ora...
|
||||
DictExec.Add("UID", id);
|
||||
// creo registrazione richiesta...
|
||||
var ruid = await _calcRuidService.AddRequestAsync($"{cEnvir}", $"{cMode}", id);
|
||||
// aggiungo RUID effettivo
|
||||
DictExec.Add("RUID", ruid);
|
||||
DictExec.Add("SerializedData", currJwd);
|
||||
int nId = 1;
|
||||
// da modificare con tipo richiesta...
|
||||
QuestionDTO currArgs = new QuestionDTO(nId, EgwMultiEngineManager.Data.Constants.EXECENVIRONMENTS.WINDOW, DictExec);
|
||||
QuestionDTO currArgs = new QuestionDTO(nId, cEnvir, DictExec);
|
||||
|
||||
await _redisService.PublishAsync(chPub, currArgs.sProcessArgs);
|
||||
svgContent = "DONE";
|
||||
@@ -260,10 +258,14 @@ namespace Lux.API.Controllers
|
||||
#region Private Fields
|
||||
|
||||
private static Logger Log = LogManager.GetCurrentClassLogger();
|
||||
private readonly CalcRuidService _calcRuidService;
|
||||
private readonly IRedisService _redisService;
|
||||
private readonly string chPub = "";
|
||||
|
||||
private IConfiguration _config;
|
||||
|
||||
private EgwMultiEngineManager.Data.Constants.EXECENVIRONMENTS cEnvir = EgwMultiEngineManager.Data.Constants.EXECENVIRONMENTS.WINDOW;
|
||||
|
||||
/// <summary>
|
||||
/// Demorichiesta jwd x fare test richiesta calcolo
|
||||
/// </summary>
|
||||
@@ -273,9 +275,42 @@ namespace Lux.API.Controllers
|
||||
|
||||
#region Private Properties
|
||||
|
||||
private ImageCacheService _imgService { get; set; }
|
||||
private ConfigDataService _confService { get; set; }
|
||||
|
||||
private ImageCacheService _imgService { get; set; }
|
||||
|
||||
#endregion Private Properties
|
||||
|
||||
#region Private Methods
|
||||
|
||||
/// <summary>
|
||||
/// Esegue invio effettivo richiesta elenco HW list
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
private async Task sendHwReq()
|
||||
{
|
||||
// init vars
|
||||
Dictionary<string, string> DictExec = new Dictionary<string, string>();
|
||||
var cMode = Egw.Window.Data.Enums.QuestionModes.HARDWARE;
|
||||
var cSubMode = Egw.Window.Data.Enums.QuestionHwSubModes.LIST;
|
||||
var cManufact = Egw.Window.Data.Enums.HardwareManufacturers.AGB;
|
||||
// compongo richiesta
|
||||
DictExec.Add("Mode", $"{(int)cMode}");
|
||||
// aggiungo dati ID
|
||||
string uid = "HW.AGB";
|
||||
DictExec.Add("UID", uid);
|
||||
// creo registrazione richiesta...
|
||||
var ruid = await _calcRuidService.AddRequestAsync($"{cEnvir}", $"{cMode}-{cSubMode}", uid);
|
||||
// aggiungo RUID effettivo
|
||||
DictExec.Add("RUID", ruid);
|
||||
DictExec.Add("SubMode", $"{(int)cSubMode}");
|
||||
DictExec.Add("Manufacturer", $"{(int)cManufact}");
|
||||
int nId = 1;
|
||||
// da modificare con tipo richiesta...
|
||||
QuestionDTO currArgs = new QuestionDTO(nId, cEnvir, DictExec);
|
||||
await _redisService.PublishAsync(chPub, currArgs.sProcessArgs);
|
||||
}
|
||||
|
||||
#endregion Private Methods
|
||||
}
|
||||
}
|
||||
@@ -4,7 +4,7 @@
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<Nullable>enable</Nullable>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Version>0.9.2512.1210</Version>
|
||||
<Version>0.9.2512.1311</Version>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
||||
@@ -53,6 +53,18 @@ builder.Services.AddSingleton<ExternalMessageProcessor>();
|
||||
builder.Services.AddHostedService<RedisSubscriberService>();
|
||||
builder.Services.AddSingleton<ConfigDataService>();
|
||||
builder.Services.AddSingleton<ProdService>();
|
||||
// init servizio gestone ReqIndex
|
||||
string cleanupDayTTL = configuration.GetValue<string>("ServerConf:CleanupDayTTL") ?? "360";
|
||||
string rBaseKey = configuration.GetValue<string>("ServerConf:RedisBaseKey") ?? "Lux";
|
||||
int dayTTL = 360;
|
||||
int.TryParse(cleanupDayTTL, out dayTTL);
|
||||
builder.Services.AddSingleton(new CalcRuidService(
|
||||
redisConn,
|
||||
retention: TimeSpan.FromDays(dayTTL),
|
||||
redisBaseKey: rBaseKey
|
||||
));
|
||||
|
||||
builder.Services.AddHostedService<CleanupService>();
|
||||
|
||||
var app = builder.Build();
|
||||
|
||||
|
||||
@@ -18,12 +18,14 @@ namespace Lux.API.Services
|
||||
/// </summary>
|
||||
/// <param name="imgService"></param>
|
||||
/// <param name="dlService"></param>
|
||||
public ExternalMessageProcessor(ImageCacheService imgService, DataLayerServices dlService)
|
||||
public ExternalMessageProcessor(ImageCacheService imgService, DataLayerServices dlService, CalcRuidService crService)
|
||||
{
|
||||
cacheService = imgService;
|
||||
dbService = dlService;
|
||||
_calcRuidService = crService;
|
||||
}
|
||||
|
||||
private readonly CalcRuidService _calcRuidService;
|
||||
#endregion Public Constructors
|
||||
|
||||
#region Public Methods
|
||||
@@ -98,6 +100,14 @@ namespace Lux.API.Services
|
||||
* ----------------------------------------*/
|
||||
string UID = retData.Args["UID"];
|
||||
|
||||
// gestione RUID
|
||||
if (retData.Args.ContainsKey("RUID"))
|
||||
{
|
||||
string RUID = retData.Args["RUID"];
|
||||
// salvo SUBITO chiusura statistiche...
|
||||
await _calcRuidService.CompleteRequestAsync(RUID);
|
||||
}
|
||||
|
||||
// gestione ritorno preview SVG
|
||||
if (retData.Args.ContainsKey("Svg"))
|
||||
{
|
||||
|
||||
@@ -71,6 +71,9 @@
|
||||
"ImageCalcTag": "svg-preview",
|
||||
"ImageLiveTag": "svg",
|
||||
"ImageFileTag": "svgfile",
|
||||
"FileSharePath": "\\\\stor01\\TEAM DRIVES\\40_FileUpload\\LuxUploads"
|
||||
"FileSharePath": "\\\\stor01\\TEAM DRIVES\\40_FileUpload\\LuxUploads",
|
||||
"RedisBaseKey": "Lux",
|
||||
"CleanupPeriodMinutes": 120,
|
||||
"CleanupDayTTL": 180
|
||||
}
|
||||
}
|
||||
|
||||
@@ -45,6 +45,8 @@
|
||||
}
|
||||
});
|
||||
</script> *@
|
||||
<script type="text/javascript" src="lib/chart.js/chart.umd.js"></script>
|
||||
<script type="text/javascript" src="lib/js/chartsInterop.js"></script>
|
||||
<script type="text/javascript" src="lib/bootstrap/js/bootstrap.min.js"></script>
|
||||
</body>
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using EgwCoreLib.Lux.Core.RestPayload;
|
||||
using EgwCoreLib.Lux.Core.RestPayload;
|
||||
using EgwCoreLib.Lux.Data.DbModel.Config;
|
||||
using EgwCoreLib.Lux.Data.DbModel.Sales;
|
||||
using EgwCoreLib.Lux.Data.Services;
|
||||
@@ -151,6 +151,8 @@ namespace Lux.UI.Components.Compo.Config
|
||||
DictExec.Add("Mode", $"{(int)Egw.Window.Data.Enums.QuestionModes.HARDWARE}");
|
||||
// da rivedere?
|
||||
DictExec.Add("UID", reqUid);
|
||||
// FixMe! todo! gestire con VERE richieste ID da servizio ReqIndexServicer!!!!
|
||||
DictExec.Add("RUID", GenerateId());
|
||||
DictExec.Add("SubMode", $"{(int)Egw.Window.Data.Enums.QuestionHwSubModes.LIST}");
|
||||
DictExec.Add("Manufacturer", $"{(int)Egw.Window.Data.Enums.HardwareManufacturers.AGB}");
|
||||
CalcRequestDTO req = new CalcRequestDTO()
|
||||
@@ -166,6 +168,17 @@ namespace Lux.UI.Components.Compo.Config
|
||||
await CService.CallRestPost($"{apiUrl}/{genericBasePath}", $"{calcTag}/{reqUid}", req);
|
||||
}
|
||||
|
||||
private readonly Random _rnd = new Random();
|
||||
// ---------------------------------------------------------
|
||||
// ✅ ID incrementale: timestamp ms + random 4-6 chars
|
||||
// ---------------------------------------------------------
|
||||
private string GenerateId()
|
||||
{
|
||||
long ts = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();
|
||||
string rand = Convert.ToString(_rnd.Next(0x1000, 0xFFFF), 16).ToUpper();
|
||||
return $"{ts}-{rand}";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Ricevuto update da Calc x elenco HW: aggiorno!
|
||||
/// </summary>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using EgwCoreLib.Lux.Core.RestPayload;
|
||||
using EgwCoreLib.Lux.Core.RestPayload;
|
||||
using EgwCoreLib.Lux.Data.DbModel.Config;
|
||||
using EgwCoreLib.Lux.Data.Services;
|
||||
using Microsoft.AspNetCore.Components;
|
||||
@@ -148,6 +148,10 @@ namespace Lux.UI.Components.Compo.Config
|
||||
// preparo args
|
||||
string reqUid = "Default";
|
||||
DictExec.Add("UID", reqUid);
|
||||
|
||||
// FixMe! todo! gestire con VERE richieste ID da servizio ReqIndexServicer!!!!
|
||||
DictExec.Add("RUID", GenerateId());
|
||||
|
||||
DictExec.Add("SubMode", $"{(int)Egw.Window.Data.Enums.QuestionConfSubModes.PROFILELIST}");
|
||||
CalcRequestDTO req = new CalcRequestDTO()
|
||||
{
|
||||
@@ -162,6 +166,17 @@ namespace Lux.UI.Components.Compo.Config
|
||||
await CService.CallRestPost($"{apiUrl}/{genericBasePath}", $"{calcTag}/{reqUid}", req);
|
||||
}
|
||||
|
||||
private readonly Random _rnd = new Random();
|
||||
// ---------------------------------------------------------
|
||||
// ✅ ID incrementale: timestamp ms + random 4-6 chars
|
||||
// ---------------------------------------------------------
|
||||
private string GenerateId()
|
||||
{
|
||||
long ts = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();
|
||||
string rand = Convert.ToString(_rnd.Next(0x1000, 0xFFFF), 16).ToUpper();
|
||||
return $"{ts}-{rand}";
|
||||
}
|
||||
|
||||
private void FullUpdate()
|
||||
{
|
||||
ReloadData();
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using EgwCoreLib.Lux.Core;
|
||||
using EgwCoreLib.Lux.Core;
|
||||
using EgwCoreLib.Lux.Core.RestPayload;
|
||||
using EgwCoreLib.Lux.Data.DbModel.Sales;
|
||||
using EgwCoreLib.Lux.Data.Services;
|
||||
@@ -146,6 +146,10 @@ namespace Lux.UI.Components.Compo
|
||||
// preparo args
|
||||
string reqUid = "Default";
|
||||
DictExec.Add("UID", reqUid);
|
||||
|
||||
// FixMe! todo! gestire con VERE richieste ID da servizio ReqIndexServicer!!!!
|
||||
DictExec.Add("RUID", GenerateId());
|
||||
|
||||
DictExec.Add("SubMode", $"{(int)Egw.Window.Data.Enums.QuestionConfSubModes.PROFILELIST}");
|
||||
CalcRequestDTO req = new CalcRequestDTO()
|
||||
{
|
||||
@@ -156,6 +160,18 @@ namespace Lux.UI.Components.Compo
|
||||
await CService.CallRestPost($"{apiUrl}/{genericBasePath}", $"{calcTag}/{reqUid}", req);
|
||||
}
|
||||
|
||||
|
||||
private readonly Random _rnd = new Random();
|
||||
// ---------------------------------------------------------
|
||||
// ✅ ID incrementale: timestamp ms + random 4-6 chars
|
||||
// ---------------------------------------------------------
|
||||
private string GenerateId()
|
||||
{
|
||||
long ts = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();
|
||||
string rand = Convert.ToString(_rnd.Next(0x1000, 0xFFFF), 16).ToUpper();
|
||||
return $"{ts}-{rand}";
|
||||
}
|
||||
|
||||
private void ConfInit()
|
||||
{
|
||||
apiUrl = Config.GetValue<string>("ServerConf:Prog.ApiUrl") ?? "";
|
||||
|
||||
@@ -763,6 +763,10 @@ namespace Lux.UI.Components.Compo
|
||||
// preparo args
|
||||
string reqUid = "Default";
|
||||
DictExec.Add("UID", reqUid);
|
||||
|
||||
// FixMe! todo! gestire con VERE richieste ID da servizio ReqIndexServicer!!!!
|
||||
DictExec.Add("RUID", GenerateId());
|
||||
|
||||
DictExec.Add("SubMode", $"{(int)Egw.Window.Data.Enums.QuestionConfSubModes.PROFILELIST}");
|
||||
CalcRequestDTO req = new CalcRequestDTO()
|
||||
{
|
||||
@@ -773,6 +777,18 @@ namespace Lux.UI.Components.Compo
|
||||
await CService.CallRestPost($"{apiUrl}/{genericBasePath}", $"{calcTag}/{reqUid}", req);
|
||||
}
|
||||
|
||||
|
||||
private readonly Random _rnd = new Random();
|
||||
// ---------------------------------------------------------
|
||||
// ✅ ID incrementale: timestamp ms + random 4-6 chars
|
||||
// ---------------------------------------------------------
|
||||
private string GenerateId()
|
||||
{
|
||||
long ts = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();
|
||||
string rand = Convert.ToString(_rnd.Next(0x1000, 0xFFFF), 16).ToUpper();
|
||||
return $"{ts}-{rand}";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Chiude edit andando eventualmente a salvare
|
||||
/// </summary>
|
||||
|
||||
@@ -486,7 +486,9 @@ else if (WorkLoadRecord != null)
|
||||
}
|
||||
else if (CurrEditMode == EditMode.WorkLoadDetailTag)
|
||||
{
|
||||
<div class="modal" tabindex="-1" style="display:block; background-color: rgba(10,10,10,.6);" role="dialog">
|
||||
<PartStatus DetailRecord="@WorkLoadRecord" EC_ReRunReq="ReRunJob" EC_ClosePopup="ClosePopup"></PartStatus>
|
||||
|
||||
@* <div class="modal" tabindex="-1" style="display:block; background-color: rgba(10,10,10,.6);" role="dialog">
|
||||
<div class="modal-dialog modal-xl">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header row modal-title">
|
||||
@@ -583,7 +585,7 @@ else if (WorkLoadRecord != null)
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div> *@
|
||||
}
|
||||
@* else if (CurrEditMode == EditMode.DetailOkVin)
|
||||
{
|
||||
|
||||
@@ -12,6 +12,7 @@ using NLog;
|
||||
using System.Xml;
|
||||
using WebWindowComplex;
|
||||
using WebWindowComplex.DTO;
|
||||
using Lux.UI.Components.Compo.WorkLoad;
|
||||
using static EgwCoreLib.Lux.Core.Enums;
|
||||
|
||||
namespace Lux.UI.Components.Compo
|
||||
@@ -912,6 +913,10 @@ namespace Lux.UI.Components.Compo
|
||||
// preparo args
|
||||
string reqUid = "Default";
|
||||
DictExec.Add("UID", reqUid);
|
||||
|
||||
// FixMe! todo! gestire con VERE richieste ID da servizio ReqIndexServicer!!!!
|
||||
DictExec.Add("RUID", GenerateId());
|
||||
|
||||
DictExec.Add("SubMode", $"{(int)Egw.Window.Data.Enums.QuestionConfSubModes.PROFILELIST}");
|
||||
CalcRequestDTO req = new CalcRequestDTO()
|
||||
{
|
||||
@@ -922,6 +927,18 @@ namespace Lux.UI.Components.Compo
|
||||
await CService.CallRestPost($"{apiUrl}/{genericBasePath}", $"{calcTag}/{reqUid}", req);
|
||||
}
|
||||
|
||||
|
||||
private readonly Random _rnd = new Random();
|
||||
// ---------------------------------------------------------
|
||||
// ✅ ID incrementale: timestamp ms + random 4-6 chars
|
||||
// ---------------------------------------------------------
|
||||
private string GenerateId()
|
||||
{
|
||||
long ts = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();
|
||||
string rand = Convert.ToString(_rnd.Next(0x1000, 0xFFFF), 16).ToUpper();
|
||||
return $"{ts}-{rand}";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Chiude edit andando eventualmente a salvare
|
||||
/// </summary>
|
||||
|
||||
@@ -0,0 +1,98 @@
|
||||
@using EgwCoreLib.Lux.Data.Services
|
||||
@inject CalcRuidService Calc
|
||||
@inject IJSRuntime JSRuntime
|
||||
|
||||
|
||||
<div class="card">
|
||||
<div class="card-header d-flex justify-content-between">
|
||||
<div class="px-0">
|
||||
<h3>Statistiche Storiche</h3>
|
||||
</div>
|
||||
<div class="px-0">
|
||||
<div>
|
||||
<label>Da:</label>
|
||||
<input type="datetime-local" @bind="from" />
|
||||
<label>A:</label>
|
||||
<input type="datetime-local" @bind="to" />
|
||||
<button class="btn btn-sm btn-primary" @onclick="Load">Carica</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<canvas id="chartHistoricalRequests" width="600" height="120"></canvas>
|
||||
<canvas id="chartHistoricalAvg" width="600" height="120"></canvas>
|
||||
<canvas id="chartHistoricalMax" width="600" height="120"></canvas>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@code {
|
||||
|
||||
private DateTime from = DateTime.Now.AddHours(-24);
|
||||
private DateTime to = DateTime.Now;
|
||||
|
||||
private List<string> labels = new();
|
||||
private List<double> reqData = new();
|
||||
private List<double> avgData = new();
|
||||
private List<double> maxData = new();
|
||||
|
||||
private async Task Load()
|
||||
{
|
||||
var stats = await Calc.GetStatsRangeAsync(from, to);
|
||||
|
||||
labels = stats.HourLabels;
|
||||
reqData = stats.Requests;
|
||||
avgData = stats.AvgProcessing;
|
||||
maxData = stats.MaxProcessing;
|
||||
|
||||
await JSRuntime.InvokeVoidAsync("chartsInterop.createChart", "chartHistoricalRequests", new
|
||||
{
|
||||
type = "line",
|
||||
data = new
|
||||
{
|
||||
labels,
|
||||
datasets = new[]
|
||||
{
|
||||
new {
|
||||
label = "Richieste/ora",
|
||||
data = reqData,
|
||||
borderColor = "blue"
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
await JSRuntime.InvokeVoidAsync("chartsInterop.createChart", "chartHistoricalAvg", new
|
||||
{
|
||||
type = "line",
|
||||
data = new
|
||||
{
|
||||
labels,
|
||||
datasets = new[]
|
||||
{
|
||||
new {
|
||||
label = "Tempo medio (s)",
|
||||
data = avgData,
|
||||
borderColor = "orange"
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
await JSRuntime.InvokeVoidAsync("chartsInterop.createChart", "chartHistoricalMax", new
|
||||
{
|
||||
type = "line",
|
||||
data = new
|
||||
{
|
||||
labels,
|
||||
datasets = new[]
|
||||
{
|
||||
new {
|
||||
label = "Tempo massimo (s)",
|
||||
data = maxData,
|
||||
borderColor = "red"
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,145 @@
|
||||
@using EgwCoreLib.Lux.Data.Services
|
||||
@inject CalcRuidService Calc
|
||||
@inject IJSRuntime JSRuntime
|
||||
|
||||
<div class="card">
|
||||
<div class="card-header d-flex justify-content-between">
|
||||
<div class="px-0">
|
||||
<h3>Statistiche in tempo reale</h3>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<canvas id="chartRequestsMinute" width="400" height="150"></canvas>
|
||||
</div>
|
||||
<div class="col">
|
||||
<canvas id="chartRequestsHour" width="400" height="150"></canvas>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col">
|
||||
<canvas id="chartAvgProcessing" width="400" height="150"></canvas>
|
||||
</div>
|
||||
<div class="col">
|
||||
<canvas id="chartMaxProcessing" width="400" height="150"></canvas>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@code {
|
||||
private dynamic? stats;
|
||||
|
||||
protected override async Task OnAfterRenderAsync(bool firstRender)
|
||||
{
|
||||
if (firstRender)
|
||||
{
|
||||
await InitCharts();
|
||||
_ = UpdateLoop();
|
||||
}
|
||||
}
|
||||
|
||||
private async Task InitCharts()
|
||||
{
|
||||
await JSRuntime.InvokeVoidAsync("chartsInterop.createChart", "chartRequestsMinute", new
|
||||
{
|
||||
type = "line",
|
||||
data = new
|
||||
{
|
||||
labels = new[] { "Now" },
|
||||
datasets = new[]
|
||||
{
|
||||
new {
|
||||
label = "Richieste/minuto",
|
||||
data = new[] { 0 },
|
||||
borderColor = "blue"
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
await JSRuntime.InvokeVoidAsync("chartsInterop.createChart", "chartRequestsHour", new
|
||||
{
|
||||
type = "bar",
|
||||
data = new
|
||||
{
|
||||
labels = new[] { "Now" },
|
||||
datasets = new[]
|
||||
{
|
||||
new {
|
||||
label = "Richieste/ora",
|
||||
data = new[] { 0 },
|
||||
backgroundColor = "green"
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
await JSRuntime.InvokeVoidAsync("chartsInterop.createChart", "chartAvgProcessing", new
|
||||
{
|
||||
type = "line",
|
||||
data = new
|
||||
{
|
||||
labels = new[] { "Now" },
|
||||
datasets = new[]
|
||||
{
|
||||
new {
|
||||
label = "Tempo medio (s)",
|
||||
data = new[] { 0 },
|
||||
borderColor = "orange"
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
await JSRuntime.InvokeVoidAsync("chartsInterop.createChart", "chartMaxProcessing", new
|
||||
{
|
||||
type = "line",
|
||||
data = new
|
||||
{
|
||||
labels = new[] { "Now" },
|
||||
datasets = new[]
|
||||
{
|
||||
new {
|
||||
label = "Tempo massimo (s)",
|
||||
data = new[] { 0 },
|
||||
borderColor = "red"
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private async Task UpdateLoop()
|
||||
{
|
||||
var timer = new PeriodicTimer(TimeSpan.FromSeconds(5));
|
||||
|
||||
while (await timer.WaitForNextTickAsync())
|
||||
{
|
||||
stats = await Calc.GetStatsAsync();
|
||||
|
||||
await JSRuntime.InvokeVoidAsync("chartsInterop.updateChart",
|
||||
"chartRequestsMinute",
|
||||
DateTime.Now.ToString("HH:mm:ss"),
|
||||
(double)stats.RequestsLastMinute);
|
||||
|
||||
await JSRuntime.InvokeVoidAsync("chartsInterop.updateChart",
|
||||
"chartRequestsHour",
|
||||
DateTime.Now.ToString("HH:mm:ss"),
|
||||
(double)stats.RequestsLastHour);
|
||||
|
||||
await JSRuntime.InvokeVoidAsync("chartsInterop.updateChart",
|
||||
"chartAvgProcessing",
|
||||
DateTime.Now.ToString("HH:mm:ss"),
|
||||
(double)stats.ProcessingAvgLastHour);
|
||||
|
||||
await JSRuntime.InvokeVoidAsync("chartsInterop.updateChart",
|
||||
"chartMaxProcessing",
|
||||
DateTime.Now.ToString("HH:mm:ss"),
|
||||
(double)stats.ProcessingMaxLastHour);
|
||||
|
||||
StateHasChanged();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,100 @@
|
||||
<div class="modal" tabindex="-1" style="display:block; background-color: rgba(10,10,10,.6);" role="dialog">
|
||||
<div class="modal-dialog modal-xl">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header row modal-title">
|
||||
<div class="col-4 fs-3">
|
||||
Dettaglio Workload
|
||||
@if (DetailRecord.Workable)
|
||||
{
|
||||
<i class="fa-solid fa-thumbs-up text-success"></i>
|
||||
}
|
||||
else
|
||||
{
|
||||
<i class="fa-solid fa-thumbs-down text-danger"></i>
|
||||
}
|
||||
</div>
|
||||
<div class="col-4 text-center border border-2 rounded">
|
||||
<div class="fw-bold">Impianti considerati</div>
|
||||
<small class="small">@DetailRecord.ListMachines</small>
|
||||
</div>
|
||||
<div class="col-4 text-end fs-4">
|
||||
<button class="btn btn-lg btn-primary" @onclick="() => ReRunJob()"><i class="fa-solid fa-share-from-square" title="Riesecuzione Estimate"></i> Re-Run</button>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close" @onclick="() => ClosePopup()">
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<div class="accordion" id="accordionPanelsStayOpenExample">
|
||||
@if (DetailRecord.NumKo > 0)
|
||||
{
|
||||
<div class="accordion-item">
|
||||
<h2 class="accordion-header">
|
||||
<button class="accordion-button" type="button" data-bs-toggle="collapse" data-bs-target="#panelsStayOpen-collapseOne" aria-expanded="true" aria-controls="panelsStayOpen-collapseOne">
|
||||
Non producibili: <span class="fw-bold px-1">@DetailRecord.NumKo</span>
|
||||
</button>
|
||||
</h2>
|
||||
<div id="panelsStayOpen-collapseOne" class="accordion-collapse collapse show">
|
||||
<div class="accordion-body">
|
||||
<div class="row gx-2">
|
||||
@foreach (var item in DetailRecord.ListUnWorkable)
|
||||
{
|
||||
<div class="col-2">
|
||||
<div class="alert alert-danger p-1 mb-2 text-center align-content-center">@item</div>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
@if (DetailRecord.NumOkVin > 0)
|
||||
{
|
||||
<div class="accordion-item">
|
||||
<h2 class="accordion-header">
|
||||
<button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#panelsStayOpen-collapseTwo" aria-expanded="false" aria-controls="panelsStayOpen-collapseTwo">
|
||||
Vincolati: <span class="fw-bold px-1">@DetailRecord.NumOkVin</span>
|
||||
</button>
|
||||
</h2>
|
||||
<div id="panelsStayOpen-collapseTwo" class="accordion-collapse collapse">
|
||||
<div class="accordion-body">
|
||||
<div class="row gx-2">
|
||||
@foreach (var item in DetailRecord.ListVincolated)
|
||||
{
|
||||
<div class="col-2">
|
||||
<div class="alert alert-info p-1 mb-2 text-center align-content-center">@item</div>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
@if (DetailRecord.NumOk > 0)
|
||||
{
|
||||
<div class="accordion-item">
|
||||
<h2 class="accordion-header">
|
||||
<button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#panelsStayOpen-collapseThree" aria-expanded="false" aria-controls="panelsStayOpen-collapseThree">
|
||||
Producibili: <span class="fw-bold px-1">@DetailRecord.NumOk</span>
|
||||
</button>
|
||||
</h2>
|
||||
<div id="panelsStayOpen-collapseThree" class="accordion-collapse collapse">
|
||||
<div class="accordion-body">
|
||||
<div class="row gx-2">
|
||||
@foreach (var item in DetailRecord.ListWorkable)
|
||||
{
|
||||
<div class="col-2">
|
||||
<div class="alert alert-success p-1 mb-2 text-center align-content-center">@item</div>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
using EgwCoreLib.Lux.Core.RestPayload;
|
||||
using Microsoft.AspNetCore.Components;
|
||||
|
||||
namespace Lux.UI.Components.Compo.WorkLoad
|
||||
{
|
||||
public partial class PartStatus
|
||||
{
|
||||
#region Public Properties
|
||||
|
||||
[Parameter]
|
||||
public WorkLoadDetailDTO DetailRecord { get; set; } = null!;
|
||||
|
||||
[Parameter]
|
||||
public EventCallback<bool> EC_ClosePopup { get; set; }
|
||||
|
||||
[Parameter]
|
||||
public EventCallback<bool> EC_ReRunReq { get; set; }
|
||||
|
||||
#endregion Public Properties
|
||||
|
||||
#region Protected Methods
|
||||
|
||||
protected async Task ClosePopup()
|
||||
{
|
||||
await EC_ClosePopup.InvokeAsync(true);
|
||||
}
|
||||
|
||||
protected async Task ReRunJob()
|
||||
{
|
||||
await EC_ReRunReq.InvokeAsync(true);
|
||||
}
|
||||
|
||||
#endregion Protected Methods
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
<h3>TimeEstim</h3>
|
||||
|
||||
@code {
|
||||
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
using EgwCoreLib.Lux.Core;
|
||||
using EgwCoreLib.Lux.Core;
|
||||
using EgwCoreLib.Lux.Core.Generic;
|
||||
using EgwCoreLib.Lux.Core.RestPayload;
|
||||
using EgwCoreLib.Lux.Data;
|
||||
@@ -149,13 +149,13 @@ namespace Lux.UI.Components.Pages
|
||||
* - generazione della lista delle etichette + riga d'ordine da inviare
|
||||
* - invio chiamata su channelRedis
|
||||
*
|
||||
* il puro invio dovrà poter essere fatto anche dalla tab ordini... e serve visualizzazione delle estim pending
|
||||
* il puro invio dovrà poter essere fatto anche dalla tab ordini... e serve visualizzazione delle estim pending
|
||||
* --------------------------------- */
|
||||
|
||||
// in primis: se è già confermata chiede una autorizzazione di conferma speciale
|
||||
// in primis: se è già confermata chiede una autorizzazione di conferma speciale
|
||||
if (currRec.OffertState == OfferStates.Confirmed)
|
||||
{
|
||||
if (!await JSRuntime.InvokeAsync<bool>("confirm", $"Confermi di voler modificare l'offerta già confermata?"))
|
||||
if (!await JSRuntime.InvokeAsync<bool>("confirm", $"Confermi di voler modificare l'offerta già confermata?"))
|
||||
return;
|
||||
}
|
||||
// se va verso conferma ricorda che ora l'ordine passa in pianificazione (carico macchine)
|
||||
@@ -192,6 +192,10 @@ namespace Lux.UI.Components.Pages
|
||||
// preparo richiesta serializzata e la accodo (viene inviata richiesta calcolo)
|
||||
Dictionary<string, string> dictArgs = new Dictionary<string, string>();
|
||||
dictArgs.Add("UID", rigaOrd.OrderRowUID);
|
||||
|
||||
// FixMe! todo! gestire con VERE richieste ID da servizio ReqIndexServicer!!!!
|
||||
dictArgs.Add("RUID", GenerateId());
|
||||
|
||||
dictArgs.Add("OrderUID", rigaOrd.OrderNav.OrderCode);
|
||||
dictArgs.Add("Mode", $"{(int)rMode}");
|
||||
dictArgs.Add("TagsList", serTagList);
|
||||
@@ -258,6 +262,17 @@ namespace Lux.UI.Components.Pages
|
||||
UpdateTable();
|
||||
}
|
||||
|
||||
private readonly Random _rnd = new Random();
|
||||
// ---------------------------------------------------------
|
||||
// ✅ ID incrementale: timestamp ms + random 4-6 chars
|
||||
// ---------------------------------------------------------
|
||||
private string GenerateId()
|
||||
{
|
||||
long ts = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();
|
||||
string rand = Convert.ToString(_rnd.Next(0x1000, 0xFFFF), 16).ToUpper();
|
||||
return $"{ts}-{rand}";
|
||||
}
|
||||
|
||||
#endregion Protected Methods
|
||||
|
||||
#region Private Fields
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using EgwCoreLib.Lux.Core;
|
||||
using EgwCoreLib.Lux.Core;
|
||||
using EgwCoreLib.Lux.Core.Generic;
|
||||
using EgwCoreLib.Lux.Core.RestPayload;
|
||||
using EgwCoreLib.Lux.Data.DbModel.Config;
|
||||
@@ -139,7 +139,7 @@ namespace Lux.UI.Components.Pages
|
||||
/// <returns></returns>
|
||||
protected async Task ResetHistory(OrderModel currRec)
|
||||
{
|
||||
if (!await JSRuntime.InvokeAsync<bool>("confirm", $"Sicuro di voler resettare l'history dell'ordine corrente? L'operazione non è revertibile."))
|
||||
if (!await JSRuntime.InvokeAsync<bool>("confirm", $"Sicuro di voler resettare l'history dell'ordine corrente? L'operazione non è revertibile."))
|
||||
return;
|
||||
|
||||
currRec.LogHistory = new List<TaskHistDTO>();
|
||||
@@ -166,7 +166,7 @@ namespace Lux.UI.Components.Pages
|
||||
/// <returns></returns>
|
||||
protected async Task ResetWaitQueue()
|
||||
{
|
||||
if (!await JSRuntime.InvokeAsync<bool>("confirm", $"Sicuro di voler resettarela coda di attesa calcolo eliminando le richieste in attesa? L'operazione non è revertibile."))
|
||||
if (!await JSRuntime.InvokeAsync<bool>("confirm", $"Sicuro di voler resettarela coda di attesa calcolo eliminando le richieste in attesa? L'operazione non è revertibile."))
|
||||
return;
|
||||
|
||||
await PService.QueueResetAsync(ProdService.QueueType.waiting);
|
||||
@@ -187,7 +187,7 @@ namespace Lux.UI.Components.Pages
|
||||
* - registra l'history serializzata sul record..
|
||||
* --------------------------------- */
|
||||
|
||||
if (!await JSRuntime.InvokeAsync<bool>("confirm", $"Confermi di voler inviare una richiesta di stima tempi per l'ordine in oggetto? l'esecuzione non avverà in realtime e sarà accodata per l'esecuzione."))
|
||||
if (!await JSRuntime.InvokeAsync<bool>("confirm", $"Confermi di voler inviare una richiesta di stima tempi per l'ordine in oggetto? l'esecuzione non avverà in realtime e sarà accodata per l'esecuzione."))
|
||||
return;
|
||||
|
||||
// processo riga ordine x riga ordine creando per ogni riga una richiesta...
|
||||
@@ -211,6 +211,10 @@ namespace Lux.UI.Components.Pages
|
||||
// preparo richiesta serializzata e la accodo (viene inviata richiesta calcolo)
|
||||
Dictionary<string, string> dictArgs = new Dictionary<string, string>();
|
||||
dictArgs.Add("UID", rigaOrd.OrderRowUID);
|
||||
|
||||
// FixMe! todo! gestire con VERE richieste ID da servizio ReqIndexServicer!!!!
|
||||
dictArgs.Add("RUID", GenerateId());
|
||||
|
||||
dictArgs.Add("OrderUID", rigaOrd.OrderNav.OrderCode);
|
||||
dictArgs.Add("Mode", $"{(int)rMode}");
|
||||
dictArgs.Add("TagsList", serTagList);
|
||||
@@ -288,6 +292,19 @@ namespace Lux.UI.Components.Pages
|
||||
await UpdateJobQueue();
|
||||
}
|
||||
|
||||
private readonly Random _rnd = new Random();
|
||||
// ---------------------------------------------------------
|
||||
// ✅ ID incrementale: timestamp ms + random 4-6 chars
|
||||
// ---------------------------------------------------------
|
||||
private string GenerateId()
|
||||
{
|
||||
long ts = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds();
|
||||
string rand = Convert.ToString(_rnd.Next(0x1000, 0xFFFF), 16).ToUpper();
|
||||
return $"{ts}-{rand}";
|
||||
}
|
||||
|
||||
|
||||
|
||||
#endregion Protected Methods
|
||||
|
||||
#region Private Fields
|
||||
@@ -459,7 +476,7 @@ namespace Lux.UI.Components.Pages
|
||||
#endif
|
||||
|
||||
/// <summary>
|
||||
/// Modalità gestione code calcolo + display history
|
||||
/// Modalità gestione code calcolo + display history
|
||||
/// </summary>
|
||||
/// <param name="curRec"></param>
|
||||
private async Task ManageCalcReq(OrderModel? curRec)
|
||||
|
||||
@@ -1,27 +1,58 @@
|
||||
@page "/scratch"
|
||||
@using Lux.UI.Components.Compo.Stats
|
||||
@page "/scratch"
|
||||
|
||||
<div class="row">
|
||||
<div class="col-6">
|
||||
<div class="form-floating">
|
||||
<input type="text" class="form-control" placeholder="UID Finestra" @bind="@windowUid">
|
||||
<label for="floatingInput">UID Finestra</label>
|
||||
<div class="card">
|
||||
<div class="card-header d-flex justify-content-between">
|
||||
<div class="px-0">
|
||||
<h2>Scratch & test page</h2>
|
||||
</div>
|
||||
|
||||
<div class="form-floating my-2">
|
||||
<textarea class="form-control small" style="min-height: 30rem;" @bind="@demoJwd"></textarea>
|
||||
<label for="floatingInput">JWD demo</label>
|
||||
</div>
|
||||
|
||||
<div class="d-flex justify-content-between">
|
||||
<div class="px-0">
|
||||
<button class="btn btn-primary" @onclick="() => SendCalc()">Req Calc</button>
|
||||
</div>
|
||||
<div class="px-0">
|
||||
<button class="btn btn-primary" @onclick="Reset">Reset</button>
|
||||
<div class="px-0">
|
||||
<div class="input-group">
|
||||
<label class="input-group-text" for="modeSelect">Modalità</label>
|
||||
<select class="form-select" id="modeSelect" @bind="CurrMode">
|
||||
<option value="TestSvg">TestSvg</option>
|
||||
<option value="RealtimeStats">RealtimeStats</option>
|
||||
<option value="HistStats">HistStats</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-6">
|
||||
@outSvg
|
||||
<div class="card-body">
|
||||
@if (CurrMode == ControlMode.RealtimeStats)
|
||||
{
|
||||
<RealTimeStats></RealTimeStats>
|
||||
}
|
||||
else if (CurrMode == ControlMode.HistStats)
|
||||
{
|
||||
<HistoricalStats></HistoricalStats>
|
||||
}
|
||||
else if (CurrMode == ControlMode.TestSvg)
|
||||
{
|
||||
<div class="row">
|
||||
<div class="col-6">
|
||||
<div class="form-floating">
|
||||
<input type="text" class="form-control" placeholder="UID Finestra" @bind="@windowUid">
|
||||
<label for="floatingInput">UID Finestra</label>
|
||||
</div>
|
||||
|
||||
<div class="form-floating my-2">
|
||||
<textarea class="form-control small" style="min-height: 30rem;" @bind="@demoJwd"></textarea>
|
||||
<label for="floatingInput">JWD demo</label>
|
||||
</div>
|
||||
|
||||
<div class="d-flex justify-content-between">
|
||||
<div class="px-0">
|
||||
<button class="btn btn-primary" @onclick="() => SendCalc()">Req Calc</button>
|
||||
</div>
|
||||
<div class="px-0">
|
||||
<button class="btn btn-primary" @onclick="Reset">Reset</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-6">
|
||||
@outSvg
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -16,6 +16,16 @@ namespace Lux.UI.Components.Pages
|
||||
|
||||
#endregion Public Methods
|
||||
|
||||
private ControlMode CurrMode = ControlMode.HistStats;
|
||||
|
||||
protected enum ControlMode
|
||||
{
|
||||
None,
|
||||
HistStats,
|
||||
RealtimeStats,
|
||||
TestSvg
|
||||
}
|
||||
|
||||
#region Protected Properties
|
||||
|
||||
[Inject]
|
||||
|
||||
@@ -21,3 +21,4 @@
|
||||
@using Lux.UI.Components.Compo.Config
|
||||
@using Lux.UI.Components.Compo.FileMan
|
||||
@using Lux.UI.Components.Compo.JobTask
|
||||
@using Lux.UI.Components.Compo.WorkLoad
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
<Nullable>enable</Nullable>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<UserSecretsId>aspnet-Lux.UI-a758c101-a2f4-4e38-977d-1c4887dbbd50</UserSecretsId>
|
||||
<Version>0.9.2512.1210</Version>
|
||||
<Version>0.9.2512.1311</Version>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
@@ -25,8 +25,8 @@
|
||||
<PackageReference Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" Version="8.0.21" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="8.0.21" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="8.0.21">
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="NLog" Version="6.0.1" />
|
||||
<PackageReference Include="NLog.Web.AspNetCore" Version="6.0.1" />
|
||||
|
||||
@@ -70,6 +70,18 @@ builder.Services.AddSingleton<CalcRequestService>();
|
||||
builder.Services.AddSingleton<ConfigDataService>();
|
||||
builder.Services.AddSingleton<ProdService>();
|
||||
|
||||
// init servizio gestone ReqIndex
|
||||
string cleanupDayTTL = configuration.GetValue<string>("ServerConf:CleanupDayTTL") ?? "360";
|
||||
string rBaseKey = configuration.GetValue<string>("ServerConf:RedisBaseKey") ?? "Lux";
|
||||
int dayTTL = 360;
|
||||
int.TryParse(cleanupDayTTL, out dayTTL);
|
||||
builder.Services.AddSingleton(new CalcRuidService(
|
||||
redisConn,
|
||||
retention: TimeSpan.FromDays(dayTTL),
|
||||
redisBaseKey: rBaseKey
|
||||
));
|
||||
|
||||
builder.Services.AddHostedService<CleanupService>();
|
||||
|
||||
var app = builder.Build();
|
||||
|
||||
|
||||
@@ -77,6 +77,9 @@
|
||||
"ImageFileTag": "cache",
|
||||
"ImageLiveTag": "svg",
|
||||
"BaseUrl": "/lux/ui/",
|
||||
"FileSharePath": "\\\\stor01\\TEAM DRIVES\\40_FileUpload\\LuxUploads"
|
||||
"FileSharePath": "\\\\stor01\\TEAM DRIVES\\40_FileUpload\\LuxUploads",
|
||||
"RedisBaseKey": "Lux",
|
||||
"CleanupPeriodMinutes": 120,
|
||||
"CleanupDayTTL": 180
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,6 +13,11 @@
|
||||
{
|
||||
"library": "bootstrap-icons@1.13.1",
|
||||
"destination": "wwwroot/lib/bootstrap-icons/"
|
||||
},
|
||||
{
|
||||
"provider": "cdnjs",
|
||||
"library": "Chart.js@4.5.0",
|
||||
"destination": "wwwroot/lib/chart.js/"
|
||||
}
|
||||
]
|
||||
}
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
+1
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+14
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"helpers.cjs","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
|
||||
@@ -0,0 +1,9 @@
|
||||
/*!
|
||||
* Chart.js v4.5.0
|
||||
* https://www.chartjs.org
|
||||
* (c) 2025 Chart.js Contributors
|
||||
* Released under the MIT License
|
||||
*/
|
||||
export { H as HALF_PI, b3 as INFINITY, P as PI, b2 as PITAU, b5 as QUARTER_PI, b4 as RAD_PER_DEG, T as TAU, b6 as TWO_THIRDS_PI, R as _addGrace, X as _alignPixel, a2 as _alignStartEnd, p as _angleBetween, b7 as _angleDiff, _ as _arrayUnique, a8 as _attachContext, au as _bezierCurveTo, ar as _bezierInterpolation, az as _boundSegment, ap as _boundSegments, a5 as _capitalize, ao as _computeSegments, a9 as _createResolver, aL as _decimalPlaces, aW as _deprecated, aa as _descriptors, ai as _elementsEqual, N as _factorize, aP as _filterBetween, I as _getParentNode, q as _getStartAndCountOfVisiblePoints, W as _int16Range, ak as _isBetween, aj as _isClickEvent, M as _isDomSupported, C as _isPointInArea, S as _limitValue, aO as _longestText, aQ as _lookup, B as _lookupByKey, V as _measureText, aU as _merger, aV as _mergerIf, al as _normalizeAngle, y as _parseObjectDataRadialScale, as as _pointInLine, am as _readValueToProps, A as _rlookupByKey, w as _scaleRangesChanged, aH as _setMinAndMaxByKey, aX as _splitKey, aq as _steppedInterpolation, at as _steppedLineTo, aC as _textX, a1 as _toLeftRightCenter, an as _updateBezierControlPoints, aw as addRoundedRectPath, aK as almostEquals, aJ as almostWhole, Q as callback, af as clearCanvas, Y as clipArea, aT as clone, c as color, j as createContext, ad as debounce, h as defined, aF as distanceBetweenPoints, av as drawPoint, aE as drawPointLegend, F as each, e as easingEffects, O as finiteOrDefault, b0 as fontString, o as formatNumber, D as getAngleFromPoint, ah as getDatasetClipArea, aS as getHoverColor, G as getMaximumSize, z as getRelativePosition, aA as getRtlAdapter, a$ as getStyle, b as isArray, g as isFinite, a7 as isFunction, k as isNullOrUndef, x as isNumber, i as isObject, aR as isPatternOrGradient, l as listenArrayEvents, aN as log10, a4 as merge, ab as mergeIf, aI as niceNum, aG as noop, aB as overrideTextDirection, J as readUsedSize, Z as renderText, r as requestAnimFrame, a as resolve, f as resolveObjectKey, aD as restoreTextDirection, ae as retinaScale, ag as setsEqual, s as sign, aZ as splineCurve, a_ as splineCurveMonotone, K as supportsEventListenerOptions, L as throttled, U as toDegrees, n as toDimension, a0 as toFont, aY as toFontString, b1 as toLineHeight, E as toPadding, m as toPercentage, t as toRadians, ax as toTRBL, ay as toTRBLCorners, ac as uid, $ as unclipArea, u as unlistenArrayEvents, v as valueOrDefault } from './chunks/helpers.dataset.js';
|
||||
import '@kurkle/color';
|
||||
//# sourceMappingURL=helpers.js.map
|
||||
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"helpers.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;"}
|
||||
+1
@@ -0,0 +1 @@
|
||||
export{H as HALF_PI,b3 as INFINITY,P as PI,b2 as PITAU,b5 as QUARTER_PI,b4 as RAD_PER_DEG,T as TAU,b6 as TWO_THIRDS_PI,R as _addGrace,X as _alignPixel,a2 as _alignStartEnd,p as _angleBetween,b7 as _angleDiff,_ as _arrayUnique,a8 as _attachContext,au as _bezierCurveTo,ar as _bezierInterpolation,az as _boundSegment,ap as _boundSegments,a5 as _capitalize,ao as _computeSegments,a9 as _createResolver,aL as _decimalPlaces,aW as _deprecated,aa as _descriptors,ai as _elementsEqual,N as _factorize,aP as _filterBetween,I as _getParentNode,q as _getStartAndCountOfVisiblePoints,W as _int16Range,ak as _isBetween,aj as _isClickEvent,M as _isDomSupported,C as _isPointInArea,S as _limitValue,aO as _longestText,aQ as _lookup,B as _lookupByKey,V as _measureText,aU as _merger,aV as _mergerIf,al as _normalizeAngle,y as _parseObjectDataRadialScale,as as _pointInLine,am as _readValueToProps,A as _rlookupByKey,w as _scaleRangesChanged,aH as _setMinAndMaxByKey,aX as _splitKey,aq as _steppedInterpolation,at as _steppedLineTo,aC as _textX,a1 as _toLeftRightCenter,an as _updateBezierControlPoints,aw as addRoundedRectPath,aK as almostEquals,aJ as almostWhole,Q as callback,af as clearCanvas,Y as clipArea,aT as clone,c as color,j as createContext,ad as debounce,h as defined,aF as distanceBetweenPoints,av as drawPoint,aE as drawPointLegend,F as each,e as easingEffects,O as finiteOrDefault,b0 as fontString,o as formatNumber,D as getAngleFromPoint,ah as getDatasetClipArea,aS as getHoverColor,G as getMaximumSize,z as getRelativePosition,aA as getRtlAdapter,a$ as getStyle,b as isArray,g as isFinite,a7 as isFunction,k as isNullOrUndef,x as isNumber,i as isObject,aR as isPatternOrGradient,l as listenArrayEvents,aN as log10,a4 as merge,ab as mergeIf,aI as niceNum,aG as noop,aB as overrideTextDirection,J as readUsedSize,Z as renderText,r as requestAnimFrame,a as resolve,f as resolveObjectKey,aD as restoreTextDirection,ae as retinaScale,ag as setsEqual,s as sign,aZ as splineCurve,a_ as splineCurveMonotone,K as supportsEventListenerOptions,L as throttled,U as toDegrees,n as toDimension,a0 as toFont,aY as toFontString,b1 as toLineHeight,E as toPadding,m as toPercentage,t as toRadians,ax as toTRBL,ay as toTRBLCorners,ac as uid,$ as unclipArea,u as unlistenArrayEvents,v as valueOrDefault}from"./chunks/helpers.dataset.js";import"@kurkle/color";
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
export * from './controllers/index.js';
|
||||
export * from './core/index.js';
|
||||
export * from './elements/index.js';
|
||||
export * from './platform/index.js';
|
||||
export * from './plugins/index.js';
|
||||
export * from './scales/index.js';
|
||||
import * as controllers from './controllers/index.js';
|
||||
import * as elements from './elements/index.js';
|
||||
import * as plugins from './plugins/index.js';
|
||||
import * as scales from './scales/index.js';
|
||||
export { controllers, elements, plugins, scales, };
|
||||
export declare const registerables: (typeof controllers | typeof elements | typeof plugins | typeof scales)[];
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
/**
|
||||
* @namespace Chart
|
||||
*/
|
||||
import Chart from './core/core.controller.js';
|
||||
export default Chart;
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
/**
|
||||
* Temporary entry point of the types at the time of the transition.
|
||||
* After transition done need to remove it in favor of index.ts
|
||||
*/
|
||||
export * from './index.js';
|
||||
/**
|
||||
* Explicitly re-exporting to resolve the ambiguity.
|
||||
*/
|
||||
export { BarController, BubbleController, DoughnutController, LineController, PieController, PolarAreaController, RadarController, ScatterController, Animation, Animations, Chart, DatasetController, Interaction, Scale, Ticks, defaults, layouts, registry, ArcElement, BarElement, LineElement, PointElement, BasePlatform, BasicPlatform, DomPlatform, Decimation, Filler, Legend, SubTitle, Title, Tooltip, CategoryScale, LinearScale, LogarithmicScale, RadialLinearScale, TimeScale, TimeSeriesScale, PluginOptionsByType, ElementOptionsByType, ChartDatasetProperties, UpdateModeEnum, registerables } from './types/index.js';
|
||||
export * from './types/index.js';
|
||||
@@ -0,0 +1,32 @@
|
||||
window.chartsInterop = {
|
||||
charts: {},
|
||||
|
||||
createChart: function (canvasId, config) {
|
||||
const ctx = document.getElementById(canvasId).getContext('2d');
|
||||
this.charts[canvasId] = new Chart(ctx, config);
|
||||
},
|
||||
|
||||
//updateChart: function (canvasId, labels, data) {
|
||||
// const chart = this.charts[canvasId];
|
||||
// if (!chart) return;
|
||||
|
||||
// chart.data.labels = labels;
|
||||
// chart.data.datasets[0].data = data;
|
||||
// chart.update();
|
||||
//}
|
||||
updateChart: function (canvasId, label, value) {
|
||||
const chart = this.charts[canvasId];
|
||||
if (!chart) return;
|
||||
|
||||
chart.data.labels.push(label);
|
||||
chart.data.datasets[0].data.push(value);
|
||||
|
||||
// Mantieni solo gli ultimi 20 punti (opzionale)
|
||||
if (chart.data.labels.length > 20) {
|
||||
chart.data.labels.shift();
|
||||
chart.data.datasets[0].data.shift();
|
||||
}
|
||||
|
||||
chart.update();
|
||||
}
|
||||
};
|
||||
@@ -1,6 +1,6 @@
|
||||
<body>
|
||||
<i>LUX - Web Windows MES</i>
|
||||
<h4>Versione: 0.9.2512.1210</h4>
|
||||
<h4>Versione: 0.9.2512.1311</h4>
|
||||
<br /> Note di rilascio:
|
||||
<ul>
|
||||
<li>
|
||||
|
||||
@@ -1 +1 @@
|
||||
0.9.2512.1210
|
||||
0.9.2512.1311
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<item>
|
||||
<version>0.9.2512.1210</version>
|
||||
<version>0.9.2512.1311</version>
|
||||
<url>http://nexus.steamware.net/repository/SWS/GPW/stable/GPW.UI.zip</url>
|
||||
<changelog>http://nexus.steamware.net/repository/SWS/GPW/stable/ChangeLog.html</changelog>
|
||||
<mandatory>false</mandatory>
|
||||
|
||||
Reference in New Issue
Block a user